Upgrade fonttools to 4.37.1 am: ae8de171b8 am: ae308ea7c8 am: 14ad209ddd am: e47f00b752 am: 26b56f9a65

Original change: https://android-review.googlesource.com/c/platform/external/fonttools/+/2193410

Change-Id: I9b56a36aaf77772d4db094f4dde1d14eb079842e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..5ace460
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+  - package-ecosystem: "github-actions"
+    directory: "/"
+    schedule:
+      interval: "weekly"
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ec6abe2..ea5ebc9 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -9,14 +9,17 @@
     tags:
       - '*.*.*' # e.g. 1.0.0 or 20.15.10
 
+permissions:
+  contents: read
+
 jobs:
   deploy:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Set up Python
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: '3.x'
     - name: Install dependencies
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fb314ef..0ce1c2d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -6,15 +6,18 @@
   pull_request:
     branches: [main]
 
+permissions:
+  contents: read
+
 jobs:
   lint:
     runs-on: ubuntu-latest
     # https://github.community/t/github-actions-does-not-respect-skip-ci/17325/8
     if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Set up Python 3.x
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: "3.x"
     - name: Install packages
@@ -27,21 +30,17 @@
     if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
     strategy:
       matrix:
-        python-version: [3.7, 3.8, 3.9]
+        python-version: ["3.7", "3.10"]
         platform: [ubuntu-latest, macos-latest, windows-latest]
-        exclude: # Only test on the oldest and latest supported stable Python on macOS and Windows.
+        exclude: # Only test on the latest supported stable Python on macOS and Windows.
           - platform: macos-latest
             python-version: 3.7
-          - platform: macos-latest
-            python-version: 3.9
           - platform: windows-latest
             python-version: 3.7
-          - platform: windows-latest
-            python-version: 3.9
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.python-version }}
     - name: Install packages
@@ -55,7 +54,7 @@
         coverage combine
         coverage xml
     - name: Upload coverage to Codecov
-      uses: codecov/codecov-action@v2
+      uses: codecov/codecov-action@v3
       with:
         file: coverage.xml
         flags: unittests
@@ -66,11 +65,11 @@
     runs-on: ubuntu-latest
     if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Set up Python 3.x
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
-        python-version: 3.9
+        python-version: "3.10"
     - name: Install packages
       run: pip install tox
     - name: Run Tox
@@ -80,9 +79,9 @@
     runs-on: ubuntu-latest
     if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - name: Set up Python pypy3
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: "pypy-3.7"
     - name: Install packages
diff --git a/Doc/docs-requirements.txt b/Doc/docs-requirements.txt
index 5016e34..59f1cd1 100644
--- a/Doc/docs-requirements.txt
+++ b/Doc/docs-requirements.txt
@@ -1,4 +1,4 @@
-sphinx==4.3.2
+sphinx==5.1.1
 sphinx_rtd_theme==1.0.0
-reportlab==3.6.5
-freetype-py==2.2.0
+reportlab==3.6.11
+freetype-py==2.3.0
diff --git a/Doc/source/designspaceLib/xml.rst b/Doc/source/designspaceLib/xml.rst
index 58cf6a7..6267b02 100644
--- a/Doc/source/designspaceLib/xml.rst
+++ b/Doc/source/designspaceLib/xml.rst
@@ -717,7 +717,10 @@
         <axis-subset name="Weight" userminimum="400" usermaximum="500" userdefault="400"/>
 
   3. a specific value along that axis; then the axis is not functional in the VF
-     but the design space is sliced at the given location.
+     but the design space is sliced at the given location. *Note:* While valid to have a
+     specific value that doesn’t have a matching ``<source>`` at that value, currently there
+     isn’t an implentation that supports this. See `this fontmake issue
+     <https://github.com/googlefonts/fontmake/issues/920>`.
 
      .. code:: xml
 
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index 9a39ea0..5b2cca1 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -3,6 +3,6 @@
 
 log = logging.getLogger(__name__)
 
-version = __version__ = "4.33.3"
+version = __version__ = "4.37.1"
 
 __all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/cffLib/__init__.py b/Lib/fontTools/cffLib/__init__.py
index fc82bb2..3eda9ba 100644
--- a/Lib/fontTools/cffLib/__init__.py
+++ b/Lib/fontTools/cffLib/__init__.py
@@ -1037,6 +1037,8 @@
 		return len(self.data)
 
 	def getNumRegions(self, vsIndex):
+		if vsIndex is None:
+			vsIndex = 0
 		varData = self.otVarStore.VarData[vsIndex]
 		numRegions = varData.VarRegionCount
 		return numRegions
diff --git a/Lib/fontTools/cffLib/specializer.py b/Lib/fontTools/cffLib/specializer.py
index fbfefa9..677f03b 100644
--- a/Lib/fontTools/cffLib/specializer.py
+++ b/Lib/fontTools/cffLib/specializer.py
@@ -304,7 +304,7 @@
 	deltaArgs = args[numBlends:]
 	numDeltaValues = len(deltaArgs)
 	deltaList = [ deltaArgs[i:i + numRegions] for i in range(0, numDeltaValues, numRegions) ]
-	blend_args = [ a + b for a, b in zip(defaultArgs,deltaList)]
+	blend_args = [ a + b + [1] for a, b in zip(defaultArgs,deltaList)]
 	return blend_args
 
 def generalizeCommands(commands, ignoreErrors=False):
@@ -399,10 +399,10 @@
 		else:
 			prev_stack_use = stack_use
 			# The arg is a tuple of blend values.
-			# These are each (master 0,delta 1..delta n)
+			# These are each (master 0,delta 1..delta n, 1)
 			# Combine as many successive tuples as we can,
 			# up to the max stack limit.
-			num_sources = len(arg)
+			num_sources = len(arg) - 1
 			blendlist = [arg]
 			i += 1
 			stack_use += 1 + num_sources  # 1 for the num_blends arg
@@ -427,7 +427,8 @@
 			for arg in blendlist:
 				blend_args.append(arg[0])
 			for arg in blendlist:
-				blend_args.extend(arg[1:])
+				assert arg[-1] == 1
+				blend_args.extend(arg[1:-1])
 			blend_args.append(num_blends)
 			new_args.append(blend_args)
 			stack_use = prev_stack_use + num_blends
@@ -437,12 +438,13 @@
 def _addArgs(a, b):
 	if isinstance(b, list):
 		if isinstance(a, list):
-			if len(a) != len(b):
+			if len(a) != len(b) or a[-1] != b[-1]:
 				raise ValueError()
-			return [_addArgs(va, vb) for va,vb in zip(a, b)]
+			return [_addArgs(va, vb) for va,vb in zip(a[:-1], b[:-1])] + [a[-1]]
 		else:
 			a, b = b, a
 	if isinstance(a, list):
+		assert a[-1] == 1
 		return [_addArgs(a[0], b)] + a[1:]
 	return a + b
 
@@ -739,12 +741,27 @@
 	if len(sys.argv) == 1:
 		import doctest
 		sys.exit(doctest.testmod().failed)
-	program = stringToProgram(sys.argv[1:])
+
+	import argparse
+
+	parser = argparse.ArgumentParser(
+		"fonttools cffLib.specialer", description="CFF CharString generalizer/specializer")
+	parser.add_argument(
+		"program", metavar="command", nargs="*", help="Commands.")
+	parser.add_argument(
+		"--num-regions", metavar="NumRegions", nargs="*", default=None,
+		help="Number of variable-font regions for blend opertaions.")
+
+	options = parser.parse_args(sys.argv[1:])
+
+	getNumRegions = None if options.num_regions is None else lambda vsIndex: int(options.num_regions[0 if vsIndex is None else vsIndex])
+
+	program = stringToProgram(options.program)
 	print("Program:"); print(programToString(program))
-	commands = programToCommands(program)
+	commands = programToCommands(program, getNumRegions)
 	print("Commands:"); print(commands)
 	program2 = commandsToProgram(commands)
 	print("Program from commands:"); print(programToString(program2))
 	assert program == program2
-	print("Generalized program:"); print(programToString(generalizeProgram(program)))
-	print("Specialized program:"); print(programToString(specializeProgram(program)))
+	print("Generalized program:"); print(programToString(generalizeProgram(program, getNumRegions)))
+	print("Specialized program:"); print(programToString(specializeProgram(program, getNumRegions)))
diff --git a/Lib/fontTools/cffLib/width.py b/Lib/fontTools/cffLib/width.py
index 00b859b..303c946 100644
--- a/Lib/fontTools/cffLib/width.py
+++ b/Lib/fontTools/cffLib/width.py
@@ -135,13 +135,13 @@
 	dfltC = nomnCost[nominal] - bestCost[nominal]
 	ends = []
 	if dfltC == dfltCostU[nominal]:
-		starts = [nominal, nominal-108, nominal-1131]
+		starts = [nominal, nominal-108, nominal-1132]
 		for start in starts:
 			while cumMaxU[start] and cumMaxU[start] == cumMaxU[start-1]:
 				start -= 1
 			ends.append(start)
 	else:
-		starts = [nominal, nominal+108, nominal+1131]
+		starts = [nominal, nominal+108, nominal+1132]
 		for start in starts:
 			while cumMaxD[start] and cumMaxD[start] == cumMaxD[start+1]:
 				start += 1
diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py
index 2577fa7..442bc20 100644
--- a/Lib/fontTools/colorLib/builder.py
+++ b/Lib/fontTools/colorLib/builder.py
@@ -23,6 +23,7 @@
 )
 from fontTools.misc.arrayTools import intRect
 from fontTools.misc.fixedTools import fixedToFloat
+from fontTools.misc.treeTools import build_n_ary_tree
 from fontTools.ttLib.tables import C_O_L_R_
 from fontTools.ttLib.tables import C_P_A_L_
 from fontTools.ttLib.tables import _n_a_m_e
@@ -186,10 +187,12 @@
 def buildCOLR(
     colorGlyphs: _ColorGlyphsDict,
     version: Optional[int] = None,
+    *,
     glyphMap: Optional[Mapping[str, int]] = None,
     varStore: Optional[ot.VarStore] = None,
     varIndexMap: Optional[ot.DeltaSetIndexMap] = None,
     clipBoxes: Optional[Dict[str, _ClipBoxInput]] = None,
+    allowLayerReuse: bool = True,
 ) -> C_O_L_R_.table_C_O_L_R_:
     """Build COLR table from color layers mapping.
 
@@ -231,7 +234,11 @@
 
     populateCOLRv0(colr, colorGlyphsV0, glyphMap)
 
-    colr.LayerList, colr.BaseGlyphList = buildColrV1(colorGlyphsV1, glyphMap)
+    colr.LayerList, colr.BaseGlyphList = buildColrV1(
+        colorGlyphsV1,
+        glyphMap,
+        allowLayerReuse=allowLayerReuse,
+    )
 
     if version is None:
         version = 1 if (varStore or colorGlyphsV1) else 0
@@ -242,9 +249,6 @@
     if version == 0:
         self.ColorLayers = self._decompileColorLayersV0(colr)
     else:
-        clipBoxes = {
-            name: clipBoxes[name] for name in clipBoxes or {} if name in colorGlyphsV1
-        }
         colr.ClipList = buildClipList(clipBoxes) if clipBoxes else None
         colr.VarIndexMap = varIndexMap
         colr.VarStore = varStore
@@ -443,29 +447,16 @@
             yield (lbound, ubound)
 
 
-class LayerListBuilder:
-    layers: List[ot.Paint]
+class LayerReuseCache:
     reusePool: Mapping[Tuple[Any, ...], int]
     tuples: Mapping[int, Tuple[Any, ...]]
     keepAlive: List[ot.Paint]  # we need id to remain valid
 
     def __init__(self):
-        self.layers = []
         self.reusePool = {}
         self.tuples = {}
         self.keepAlive = []
 
-        # We need to intercept construction of PaintColrLayers
-        callbacks = _buildPaintCallbacks()
-        callbacks[
-            (
-                BuildCallback.BEFORE_BUILD,
-                ot.Paint,
-                ot.PaintFormat.PaintColrLayers,
-            )
-        ] = self._beforeBuildPaintColrLayers
-        self.tableBuilder = TableBuilder(callbacks)
-
     def _paint_tuple(self, paint: ot.Paint):
         # start simple, who even cares about cyclic graphs or interesting field types
         def _tuple_safe(value):
@@ -491,25 +482,7 @@
     def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]:
         return tuple(self._paint_tuple(p) for p in paints)
 
-    # COLR layers is unusual in that it modifies shared state
-    # so we need a callback into an object
-    def _beforeBuildPaintColrLayers(self, dest, source):
-        # Sketchy gymnastics: a sequence input will have dropped it's layers
-        # into NumLayers; get it back
-        if isinstance(source.get("NumLayers", None), collections.abc.Sequence):
-            layers = source["NumLayers"]
-        else:
-            layers = source["Layers"]
-
-        # Convert maps seqs or whatever into typed objects
-        layers = [self.buildPaint(l) for l in layers]
-
-        # No reason to have a colr layers with just one entry
-        if len(layers) == 1:
-            return layers[0], {}
-
-        # Look for reuse, with preference to longer sequences
-        # This may make the layer list smaller
+    def try_reuse(self, layers: List[ot.Paint]) -> List[ot.Paint]:
         found_reuse = True
         while found_reuse:
             found_reuse = False
@@ -532,10 +505,63 @@
                 layers = layers[:lbound] + [new_slice] + layers[ubound:]
                 found_reuse = True
                 break
+        return layers
+
+    def add(self, layers: List[ot.Paint], first_layer_index: int):
+        for lbound, ubound in _reuse_ranges(len(layers)):
+            self.reusePool[self._as_tuple(layers[lbound:ubound])] = (
+                lbound + first_layer_index
+            )
+
+
+class LayerListBuilder:
+    layers: List[ot.Paint]
+    cache: LayerReuseCache
+    allowLayerReuse: bool
+
+    def __init__(self, *, allowLayerReuse=True):
+        self.layers = []
+        if allowLayerReuse:
+            self.cache = LayerReuseCache()
+        else:
+            self.cache = None
+
+        # We need to intercept construction of PaintColrLayers
+        callbacks = _buildPaintCallbacks()
+        callbacks[
+            (
+                BuildCallback.BEFORE_BUILD,
+                ot.Paint,
+                ot.PaintFormat.PaintColrLayers,
+            )
+        ] = self._beforeBuildPaintColrLayers
+        self.tableBuilder = TableBuilder(callbacks)
+
+    # COLR layers is unusual in that it modifies shared state
+    # so we need a callback into an object
+    def _beforeBuildPaintColrLayers(self, dest, source):
+        # Sketchy gymnastics: a sequence input will have dropped it's layers
+        # into NumLayers; get it back
+        if isinstance(source.get("NumLayers", None), collections.abc.Sequence):
+            layers = source["NumLayers"]
+        else:
+            layers = source["Layers"]
+
+        # Convert maps seqs or whatever into typed objects
+        layers = [self.buildPaint(l) for l in layers]
+
+        # No reason to have a colr layers with just one entry
+        if len(layers) == 1:
+            return layers[0], {}
+
+        if self.cache is not None:
+            # Look for reuse, with preference to longer sequences
+            # This may make the layer list smaller
+            layers = self.cache.try_reuse(layers)
 
         # The layer list is now final; if it's too big we need to tree it
         is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT
-        layers = _build_n_ary_tree(layers, n=MAX_PAINT_COLR_LAYER_COUNT)
+        layers = build_n_ary_tree(layers, n=MAX_PAINT_COLR_LAYER_COUNT)
 
         # We now have a tree of sequences with Paint leaves.
         # Convert the sequences into PaintColrLayers.
@@ -563,11 +589,8 @@
 
         # Register our parts for reuse provided we aren't a tree
         # If we are a tree the leaves registered for reuse and that will suffice
-        if not is_tree:
-            for lbound, ubound in _reuse_ranges(len(layers)):
-                self.reusePool[self._as_tuple(layers[lbound:ubound])] = (
-                    lbound + paint.FirstLayerIndex
-                )
+        if self.cache is not None and not is_tree:
+            self.cache.add(layers, paint.FirstLayerIndex)
 
         # we've fully built dest; empty source prevents generalized build from kicking in
         return paint, {}
@@ -603,6 +626,8 @@
 def buildColrV1(
     colorGlyphs: _ColorGlyphsDict,
     glyphMap: Optional[Mapping[str, int]] = None,
+    *,
+    allowLayerReuse: bool = True,
 ) -> Tuple[Optional[ot.LayerList], ot.BaseGlyphList]:
     if glyphMap is not None:
         colorGlyphItems = sorted(
@@ -613,7 +638,7 @@
 
     errors = {}
     baseGlyphs = []
-    layerBuilder = LayerListBuilder()
+    layerBuilder = LayerListBuilder(allowLayerReuse=allowLayerReuse)
     for baseGlyph, paint in colorGlyphItems:
         try:
             baseGlyphs.append(buildBaseGlyphPaintRecord(baseGlyph, layerBuilder, paint))
@@ -632,45 +657,3 @@
     glyphs.BaseGlyphCount = len(baseGlyphs)
     glyphs.BaseGlyphPaintRecord = baseGlyphs
     return (layers, glyphs)
-
-
-def _build_n_ary_tree(leaves, n):
-    """Build N-ary tree from sequence of leaf nodes.
-
-    Return a list of lists where each non-leaf node is a list containing
-    max n nodes.
-    """
-    if not leaves:
-        return []
-
-    assert n > 1
-
-    depth = ceil(log(len(leaves), n))
-
-    if depth <= 1:
-        return list(leaves)
-
-    # Fully populate complete subtrees of root until we have enough leaves left
-    root = []
-    unassigned = None
-    full_step = n ** (depth - 1)
-    for i in range(0, len(leaves), full_step):
-        subtree = leaves[i : i + full_step]
-        if len(subtree) < full_step:
-            unassigned = subtree
-            break
-        while len(subtree) > n:
-            subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)]
-        root.append(subtree)
-
-    if unassigned:
-        # Recurse to fill the last subtree, which is the only partially populated one
-        subtree = _build_n_ary_tree(unassigned, n)
-        if len(subtree) <= n - len(root):
-            # replace last subtree with its children if they can still fit
-            root.extend(subtree)
-        else:
-            root.append(subtree)
-        assert len(root) <= n
-
-    return root
diff --git a/Lib/fontTools/colorLib/unbuilder.py b/Lib/fontTools/colorLib/unbuilder.py
index 0345890..ac24355 100644
--- a/Lib/fontTools/colorLib/unbuilder.py
+++ b/Lib/fontTools/colorLib/unbuilder.py
@@ -13,12 +13,12 @@
     }
 
 
-def _flatten(lst):
-    for el in lst:
-        if isinstance(el, list):
-            yield from _flatten(el)
+def _flatten_layers(lst):
+    for paint in lst:
+        if paint["Format"] == ot.PaintFormat.PaintColrLayers:
+            yield from _flatten_layers(paint["Layers"])
         else:
-            yield el
+            yield paint
 
 
 class LayerListUnbuilder:
@@ -41,7 +41,7 @@
         assert source["Format"] == ot.PaintFormat.PaintColrLayers
 
         layers = list(
-            _flatten(
+            _flatten_layers(
                 [
                     self.unbuildPaint(childPaint)
                     for childPaint in self.layers[
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
index 400e960..c74b550 100644
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -8,7 +8,7 @@
 import posixpath
 from io import BytesIO, StringIO
 from textwrap import indent
-from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union
+from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union, cast
 
 from fontTools.misc import etree as ET
 from fontTools.misc import plistlib
@@ -22,9 +22,20 @@
 """
 
 __all__ = [
-    'DesignSpaceDocumentError', 'DesignSpaceDocument', 'SourceDescriptor',
-    'InstanceDescriptor', 'AxisDescriptor', 'RuleDescriptor', 'BaseDocReader',
-    'BaseDocWriter'
+    'AxisDescriptor',
+    'AxisLabelDescriptor',
+    'BaseDocReader',
+    'BaseDocWriter',
+    'DesignSpaceDocument',
+    'DesignSpaceDocumentError',
+    'DiscreteAxisDescriptor',
+    'InstanceDescriptor',
+    'LocationLabelDescriptor',
+    'RangeAxisSubsetDescriptor',
+    'RuleDescriptor',
+    'SourceDescriptor',
+    'ValueAxisSubsetDescriptor',
+    'VariableFontDescriptor',
 ]
 
 # ElementTree allows to find namespace-prefixed elements, but not attributes
@@ -950,6 +961,7 @@
 
         a2 = DiscreteAxisDescriptor()
         a2.values = [0, 1]
+        a2.default = 0
         a2.name = "Italic"
         a2.tag = "ITAL"
         a2.labelNames['fr'] = "Italique"
@@ -1352,7 +1364,7 @@
         minVersion = self.documentObject.formatTuple
         if (
             any(
-                isinstance(axis, DiscreteAxisDescriptor) or
+                hasattr(axis, 'values') or
                 axis.axisOrdering is not None or
                 axis.axisLabels
                 for axis in self.documentObject.axes
@@ -1445,10 +1457,10 @@
             for label in axisObject.axisLabels:
                 self._addAxisLabel(labelsElement, label)
             axisElement.append(labelsElement)
-        if isinstance(axisObject, AxisDescriptor):
+        if hasattr(axisObject, "minimum"):
             axisElement.attrib['minimum'] = self.intOrFloat(axisObject.minimum)
             axisElement.attrib['maximum'] = self.intOrFloat(axisObject.maximum)
-        elif isinstance(axisObject, DiscreteAxisDescriptor):
+        elif hasattr(axisObject, "values"):
             axisElement.attrib['values'] = " ".join(self.intOrFloat(v) for v in axisObject.values)
         axisElement.attrib['default'] = self.intOrFloat(axisObject.default)
         if axisObject.hidden:
@@ -1682,14 +1694,19 @@
             for subset in vf.axisSubsets:
                 subsetElement = ET.Element('axis-subset')
                 subsetElement.attrib['name'] = subset.name
-                if isinstance(subset, RangeAxisSubsetDescriptor):
+                # Mypy doesn't support narrowing union types via hasattr()
+                # https://mypy.readthedocs.io/en/stable/type_narrowing.html
+                # TODO(Python 3.10): use TypeGuard
+                if hasattr(subset, "userMinimum"):
+                    subset = cast(RangeAxisSubsetDescriptor, subset)
                     if subset.userMinimum != -math.inf:
                         subsetElement.attrib['userminimum'] = self.intOrFloat(subset.userMinimum)
                     if subset.userMaximum != math.inf:
                         subsetElement.attrib['usermaximum'] = self.intOrFloat(subset.userMaximum)
                     if subset.userDefault is not None:
                         subsetElement.attrib['userdefault'] = self.intOrFloat(subset.userDefault)
-                elif isinstance(subset, ValueAxisSubsetDescriptor):
+                elif hasattr(subset, "userValue"):
+                    subset = cast(ValueAxisSubsetDescriptor, subset)
                     subsetElement.attrib['uservalue'] = self.intOrFloat(subset.userValue)
                 subsetsElement.append(subsetElement)
             vfElement.append(subsetsElement)
@@ -2904,8 +2921,12 @@
         discreteAxes = []
         rangeAxisSubsets: List[Union[RangeAxisSubsetDescriptor, ValueAxisSubsetDescriptor]] = []
         for axis in self.axes:
-            if isinstance(axis, DiscreteAxisDescriptor):
-                discreteAxes.append(axis)
+            if hasattr(axis, "values"):
+                # Mypy doesn't support narrowing union types via hasattr()
+                # TODO(Python 3.10): use TypeGuard
+                # https://mypy.readthedocs.io/en/stable/type_narrowing.html
+                axis = cast(DiscreteAxisDescriptor, axis)
+                discreteAxes.append(axis)  # type: ignore
             else:
                 rangeAxisSubsets.append(RangeAxisSubsetDescriptor(name=axis.name))
         valueCombinations = itertools.product(*[axis.values for axis in discreteAxes])
diff --git a/Lib/fontTools/designspaceLib/split.py b/Lib/fontTools/designspaceLib/split.py
index 2a09418..408de70 100644
--- a/Lib/fontTools/designspaceLib/split.py
+++ b/Lib/fontTools/designspaceLib/split.py
@@ -7,7 +7,7 @@
 import itertools
 import logging
 import math
-from typing import Any, Callable, Dict, Iterator, List, Tuple
+from typing import Any, Callable, Dict, Iterator, List, Tuple, cast
 
 from fontTools.designspaceLib import (
     AxisDescriptor,
@@ -21,9 +21,9 @@
 )
 from fontTools.designspaceLib.statNames import StatNames, getStatNames
 from fontTools.designspaceLib.types import (
+    ConditionSet,
     Range,
     Region,
-    ConditionSet,
     getVFUserRegion,
     locationInRegion,
     regionInRegion,
@@ -87,11 +87,18 @@
     discreteAxes = []
     interpolableUserRegion: Region = {}
     for axis in doc.axes:
-        if isinstance(axis, DiscreteAxisDescriptor):
+        if hasattr(axis, "values"):
+            # Mypy doesn't support narrowing union types via hasattr()
+            # TODO(Python 3.10): use TypeGuard
+            # https://mypy.readthedocs.io/en/stable/type_narrowing.html
+            axis = cast(DiscreteAxisDescriptor, axis)
             discreteAxes.append(axis)
         else:
+            axis = cast(AxisDescriptor, axis)
             interpolableUserRegion[axis.name] = Range(
-                axis.minimum, axis.maximum, axis.default
+                axis.minimum,
+                axis.maximum,
+                axis.default,
             )
     valueCombinations = itertools.product(*[axis.values for axis in discreteAxes])
     for values in valueCombinations:
@@ -191,7 +198,11 @@
 
     for axis in doc.axes:
         range = userRegion[axis.name]
-        if isinstance(range, Range) and isinstance(axis, AxisDescriptor):
+        if isinstance(range, Range) and hasattr(axis, "minimum"):
+            # Mypy doesn't support narrowing union types via hasattr()
+            # TODO(Python 3.10): use TypeGuard
+            # https://mypy.readthedocs.io/en/stable/type_narrowing.html
+            axis = cast(AxisDescriptor, axis)
             subDoc.addAxis(
                 AxisDescriptor(
                     # Same info
diff --git a/Lib/fontTools/designspaceLib/statNames.py b/Lib/fontTools/designspaceLib/statNames.py
index 0a475c8..1b67270 100644
--- a/Lib/fontTools/designspaceLib/statNames.py
+++ b/Lib/fontTools/designspaceLib/statNames.py
@@ -88,21 +88,30 @@
         # Then build names for all these languages, but fallback to English
         # whenever a translation is missing.
         labels = _getAxisLabelsForUserLocation(doc.axes, userLocation)
-        languages = set(language for label in labels for language in label.labelNames)
-        languages.add("en")
-        for language in languages:
-            styleName = " ".join(
-                label.labelNames.get(language, label.defaultName)
-                for label in labels
-                if not label.elidable
-            )
-            if not styleName and doc.elidedFallbackName is not None:
-                styleName = doc.elidedFallbackName
-            styleNames[language] = styleName
+        if labels:
+            languages = set(language for label in labels for language in label.labelNames)
+            languages.add("en")
+            for language in languages:
+                styleName = " ".join(
+                    label.labelNames.get(language, label.defaultName)
+                    for label in labels
+                    if not label.elidable
+                )
+                if not styleName and doc.elidedFallbackName is not None:
+                    styleName = doc.elidedFallbackName
+                styleNames[language] = styleName
 
-    postScriptFontName = None
-    if "en" in familyNames and "en" in styleNames:
-        postScriptFontName = f"{familyNames['en']}-{styleNames['en']}".replace(" ", "")
+    if "en" not in familyNames or "en" not in styleNames:
+        # Not enough information to compute PS names of styleMap names
+        return StatNames(
+            familyNames=familyNames,
+            styleNames=styleNames,
+            postScriptFontName=None,
+            styleMapFamilyNames={},
+            styleMapStyleName=None,
+        )
+
+    postScriptFontName = f"{familyNames['en']}-{styleNames['en']}".replace(" ", "")
 
     styleMapStyleName, regularUserLocation = _getRibbiStyle(doc, userLocation)
 
diff --git a/Lib/fontTools/designspaceLib/types.py b/Lib/fontTools/designspaceLib/types.py
index 8afea96..80ba9d6 100644
--- a/Lib/fontTools/designspaceLib/types.py
+++ b/Lib/fontTools/designspaceLib/types.py
@@ -1,12 +1,15 @@
 from __future__ import annotations
 
 from dataclasses import dataclass
-from typing import Dict, List, Optional, Union
+from typing import Dict, List, Optional, Union, cast
 
 from fontTools.designspaceLib import (
+    AxisDescriptor,
     DesignSpaceDocument,
+    DesignSpaceDocumentError,
     RangeAxisSubsetDescriptor,
     SimpleLocationDict,
+    ValueAxisSubsetDescriptor,
     VariableFontDescriptor,
 )
 
@@ -89,6 +92,10 @@
     designRegion = {}
     for name, value in userRegion.items():
         axis = doc.getAxis(name)
+        if axis is None:
+            raise DesignSpaceDocumentError(
+                f"Cannot find axis named '{name}' for region."
+            )
         if isinstance(value, (float, int)):
             designRegion[name] = axis.map_forward(value)
         else:
@@ -107,16 +114,34 @@
     #  - it's a single location = use it to know which rules should apply in the VF
     for axisSubset in vf.axisSubsets:
         axis = doc.getAxis(axisSubset.name)
-        if isinstance(axisSubset, RangeAxisSubsetDescriptor):
+        if axis is None:
+            raise DesignSpaceDocumentError(
+                f"Cannot find axis named '{axisSubset.name}' for variable font '{vf.name}'."
+            )
+        if hasattr(axisSubset, "userMinimum"):
+            # Mypy doesn't support narrowing union types via hasattr()
+            # TODO(Python 3.10): use TypeGuard
+            # https://mypy.readthedocs.io/en/stable/type_narrowing.html
+            axisSubset = cast(RangeAxisSubsetDescriptor, axisSubset)
+            if not hasattr(axis, "minimum"):
+                raise DesignSpaceDocumentError(
+                    f"Cannot select a range over '{axis.name}' for variable font '{vf.name}' "
+                    "because it's a discrete axis, use only 'userValue' instead."
+                )
+            axis = cast(AxisDescriptor, axis)
             vfUserRegion[axis.name] = Range(
                 max(axisSubset.userMinimum, axis.minimum),
                 min(axisSubset.userMaximum, axis.maximum),
                 axisSubset.userDefault or axis.default,
             )
         else:
+            axisSubset = cast(ValueAxisSubsetDescriptor, axisSubset)
             vfUserRegion[axis.name] = axisSubset.userValue
     # Any axis not mentioned explicitly has a single location = default value
     for axis in doc.axes:
         if axis.name not in vfUserRegion:
+            assert isinstance(
+                axis.default, (int, float)
+            ), f"Axis '{axis.name}' has no valid default value."
             vfUserRegion[axis.name] = axis.default
     return vfUserRegion
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
index a164487..0a99176 100644
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -230,8 +230,6 @@
                 self.font["GDEF"] = gdef
             elif "GDEF" in self.font:
                 del self.font["GDEF"]
-        elif self.varstorebuilder:
-            raise FeatureLibError("Must save GDEF when compiling a variable font")
         if "BASE" in tables:
             base = self.buildBASE()
             if base:
@@ -764,7 +762,7 @@
         gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000
         if self.varstorebuilder:
             store = self.varstorebuilder.finish()
-            if store.VarData:
+            if store:
                 gdef.Version = 0x00010003
                 gdef.VarStore = store
                 varidx_map = store.optimize()
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
index fd53573..04ff603 100644
--- a/Lib/fontTools/feaLib/parser.py
+++ b/Lib/fontTools/feaLib/parser.py
@@ -73,6 +73,7 @@
         self.next_token_location_ = None
         lexerClass = IncludingLexer if followIncludes else NonIncludingLexer
         self.lexer_ = lexerClass(featurefile, includeDir=includeDir)
+        self.missing = {}
         self.advance_lexer_(comments=True)
 
     def parse(self):
@@ -125,6 +126,16 @@
                     ),
                     self.cur_token_location_,
                 )
+        # Report any missing glyphs at the end of parsing
+        if self.missing:
+            error = [
+                " %s (first found at %s)" % (name, loc)
+                for name, loc in self.missing.items()
+            ]
+            raise FeatureLibError(
+                "The following glyph names are referenced but are missing from the "
+                "glyph set:\n" + ("\n".join(error)), None
+            )
         return self.doc_
 
     def parse_anchor_(self):
@@ -1242,14 +1253,6 @@
             raise FeatureLibError(
                 "Name id value cannot be greater than 32767", self.cur_token_location_
             )
-        if 1 <= nameID <= 6:
-            log.warning(
-                "Name id %d cannot be set from the feature file. "
-                "Ignoring record" % nameID
-            )
-            self.parse_name_()  # skip to the next record
-            return None
-
         platformID, platEncID, langID, string = self.parse_name_()
         return self.ast.NameRecord(
             nameID, platformID, platEncID, langID, string, location=location
@@ -2073,19 +2076,18 @@
         raise FeatureLibError("Expected a glyph name or CID", self.cur_token_location_)
 
     def check_glyph_name_in_glyph_set(self, *names):
-        """Raises if glyph name (just `start`) or glyph names of a
-        range (`start` and `end`) are not in the glyph set.
+        """Adds a glyph name (just `start`) or glyph names of a
+        range (`start` and `end`) which are not in the glyph set
+        to the "missing list" for future error reporting.
 
         If no glyph set is present, does nothing.
         """
         if self.glyphNames_:
-            missing = [name for name in names if name not in self.glyphNames_]
-            if missing:
-                raise FeatureLibError(
-                    "The following glyph names are referenced but are missing from the "
-                    f"glyph set: {', '.join(missing)}",
-                    self.cur_token_location_,
-                )
+            for name in names:
+                if name in self.glyphNames_:
+                    continue
+                if name not in self.missing:
+                    self.missing[name] = self.cur_token_location_
 
     def expect_markClass_reference_(self):
         name = self.expect_class_name_()
diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py
index ad7180c..6038268 100644
--- a/Lib/fontTools/fontBuilder.py
+++ b/Lib/fontTools/fontBuilder.py
@@ -838,6 +838,7 @@
         varStore=None,
         varIndexMap=None,
         clipBoxes=None,
+        allowLayerReuse=True,
     ):
         """Build new COLR table using color layers dictionary.
 
@@ -853,6 +854,7 @@
             varStore=varStore,
             varIndexMap=varIndexMap,
             clipBoxes=clipBoxes,
+            allowLayerReuse=allowLayerReuse,
         )
 
     def setupCPAL(
diff --git a/Lib/fontTools/merge/cmap.py b/Lib/fontTools/merge/cmap.py
index 7ade4ac..7d98b58 100644
--- a/Lib/fontTools/merge/cmap.py
+++ b/Lib/fontTools/merge/cmap.py
@@ -18,10 +18,10 @@
 		for i,glyphName in enumerate(glyphOrder):
 			if glyphName in megaOrder:
 				n = megaOrder[glyphName]
-				while (glyphName + "#" + repr(n)) in megaOrder:
+				while (glyphName + "." + repr(n)) in megaOrder:
 					n += 1
 				megaOrder[glyphName] = n
-				glyphName += "#" + repr(n)
+				glyphName += "." + repr(n)
 				glyphOrder[i] = glyphName
 			megaOrder[glyphName] = 1
 	merger.glyphOrder = megaOrder = list(megaOrder.keys())
diff --git a/Lib/fontTools/misc/cliTools.py b/Lib/fontTools/misc/cliTools.py
index e8c1767..e7dadf9 100644
--- a/Lib/fontTools/misc/cliTools.py
+++ b/Lib/fontTools/misc/cliTools.py
@@ -6,7 +6,7 @@
 numberAddedRE = re.compile(r"#\d+$")
 
 
-def makeOutputFileName(input, outputDir=None, extension=None, overWrite=False):
+def makeOutputFileName(input, outputDir=None, extension=None, overWrite=False, suffix=""):
     """Generates a suitable file name for writing output.
 
     Often tools will want to take a file, do some kind of transformation to it,
@@ -14,6 +14,7 @@
     output file, through one or more of the following steps:
 
     - changing the output directory
+    - appending suffix before file extension
     - replacing the file extension
     - suffixing the filename with a number (``#1``, ``#2``, etc.) to avoid
       overwriting an existing file.
@@ -21,6 +22,8 @@
     Args:
         input: Name of input file.
         outputDir: Optionally, a new directory to write the file into.
+        suffix: Optionally, a string suffix is appended to file name before
+            the extension.
         extension: Optionally, a replacement for the current file extension.
         overWrite: Overwriting an existing file is permitted if true; if false
             and the proposed filename exists, a new name will be generated by
@@ -36,11 +39,11 @@
     fileName = numberAddedRE.split(fileName)[0]
     if extension is None:
         extension = os.path.splitext(input)[1]
-    output = os.path.join(dirName, fileName + extension)
+    output = os.path.join(dirName, fileName + suffix + extension)
     n = 1
     if not overWrite:
         while os.path.exists(output):
             output = os.path.join(
-                dirName, fileName + "#" + repr(n) + extension)
+                dirName, fileName + suffix + "#" + repr(n) + extension)
             n += 1
     return output
diff --git a/Lib/fontTools/misc/symfont.py b/Lib/fontTools/misc/symfont.py
index a1a8730..3ff2b5d 100644
--- a/Lib/fontTools/misc/symfont.py
+++ b/Lib/fontTools/misc/symfont.py
@@ -108,16 +108,34 @@
 MomentXYPen = partial(GreenPen, func=x*y)
 
 
-def printGreenPen(penName, funcs, file=sys.stdout):
+def printGreenPen(penName, funcs, file=sys.stdout, docstring=None):
+
+	if docstring is not None:
+		print('"""%s"""' % docstring)
 
 	print(
-'''from fontTools.pens.basePen import BasePen
+'''from fontTools.pens.basePen import BasePen, OpenContourError
+try:
+	import cython
+except ImportError:
+	# if cython not installed, use mock module with no-op decorators and types
+	from fontTools.misc import cython
+
+if cython.compiled:
+	# Yep, I'm compiled.
+	COMPILED = True
+else:
+	# Just a lowly interpreted script.
+	COMPILED = False
+
+
+__all__ = ["%s"]
 
 class %s(BasePen):
 
 	def __init__(self, glyphset=None):
 		BasePen.__init__(self, glyphset)
-'''%penName, file=file)
+'''% (penName, penName), file=file)
 	for name,f in funcs:
 		print('		self.%s = 0' % name, file=file)
 	print('''
@@ -133,32 +151,14 @@
 		p0 = self._getCurrentPoint()
 		if p0 != self.__startPoint:
 			# Green theorem is not defined on open contours.
-			raise NotImplementedError
+			raise OpenContourError(
+							"Green theorem is not defined on open contours."
+			)
 ''', end='', file=file)
 
 	for n in (1, 2, 3):
 
-		if n == 1:
-			print('''
-	def _lineTo(self, p1):
-		x0,y0 = self._getCurrentPoint()
-		x1,y1 = p1
-''', file=file)
-		elif n == 2:
-			print('''
-	def _qCurveToOne(self, p1, p2):
-		x0,y0 = self._getCurrentPoint()
-		x1,y1 = p1
-		x2,y2 = p2
-''', file=file)
-		elif n == 3:
-			print('''
-	def _curveToOne(self, p1, p2, p3):
-		x0,y0 = self._getCurrentPoint()
-		x1,y1 = p1
-		x2,y2 = p2
-		x3,y3 = p3
-''', file=file)
+
 		subs = {P[i][j]: [X, Y][j][i] for i in range(n+1) for j in range(2)}
 		greens = [green(f, BezierCurve[n]) for name,f in funcs]
 		greens = [sp.gcd_terms(f.collect(sum(P,()))) for f in greens] # Optimize
@@ -166,8 +166,43 @@
 		defs, exprs = sp.cse(greens,
 				     optimizations='basic',
 				     symbols=(sp.Symbol('r%d'%i) for i in count()))
+
+		print()
+		for name,value in defs:
+			print('	@cython.locals(%s=cython.double)' % name, file=file)
+		if n == 1:
+			print('''\
+	@cython.locals(x0=cython.double, y0=cython.double)
+	@cython.locals(x1=cython.double, y1=cython.double)
+	def _lineTo(self, p1):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+''', file=file)
+		elif n == 2:
+			print('''\
+	@cython.locals(x0=cython.double, y0=cython.double)
+	@cython.locals(x1=cython.double, y1=cython.double)
+	@cython.locals(x2=cython.double, y2=cython.double)
+	def _qCurveToOne(self, p1, p2):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+		x2,y2 = p2
+''', file=file)
+		elif n == 3:
+			print('''\
+	@cython.locals(x0=cython.double, y0=cython.double)
+	@cython.locals(x1=cython.double, y1=cython.double)
+	@cython.locals(x2=cython.double, y2=cython.double)
+	@cython.locals(x3=cython.double, y3=cython.double)
+	def _curveToOne(self, p1, p2, p3):
+		x0,y0 = self._getCurrentPoint()
+		x1,y1 = p1
+		x2,y2 = p2
+		x3,y3 = p3
+''', file=file)
 		for name,value in defs:
 			print('		%s = %s' % (name, value), file=file)
+
 		print(file=file)
 		for name,value in zip([f[0] for f in funcs], exprs):
 			print('		self.%s += %s' % (name, value), file=file)
diff --git a/Lib/fontTools/misc/treeTools.py b/Lib/fontTools/misc/treeTools.py
new file mode 100644
index 0000000..24e10ba
--- /dev/null
+++ b/Lib/fontTools/misc/treeTools.py
@@ -0,0 +1,45 @@
+"""Generic tools for working with trees."""
+
+from math import ceil, log
+
+
+def build_n_ary_tree(leaves, n):
+    """Build N-ary tree from sequence of leaf nodes.
+
+    Return a list of lists where each non-leaf node is a list containing
+    max n nodes.
+    """
+    if not leaves:
+        return []
+
+    assert n > 1
+
+    depth = ceil(log(len(leaves), n))
+
+    if depth <= 1:
+        return list(leaves)
+
+    # Fully populate complete subtrees of root until we have enough leaves left
+    root = []
+    unassigned = None
+    full_step = n ** (depth - 1)
+    for i in range(0, len(leaves), full_step):
+        subtree = leaves[i : i + full_step]
+        if len(subtree) < full_step:
+            unassigned = subtree
+            break
+        while len(subtree) > n:
+            subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)]
+        root.append(subtree)
+
+    if unassigned:
+        # Recurse to fill the last subtree, which is the only partially populated one
+        subtree = build_n_ary_tree(unassigned, n)
+        if len(subtree) <= n - len(root):
+            # replace last subtree with its children if they can still fit
+            root.extend(subtree)
+        else:
+            root.append(subtree)
+        assert len(root) <= n
+
+    return root
diff --git a/Lib/fontTools/misc/visitor.py b/Lib/fontTools/misc/visitor.py
new file mode 100644
index 0000000..3d28135
--- /dev/null
+++ b/Lib/fontTools/misc/visitor.py
@@ -0,0 +1,143 @@
+"""Generic visitor pattern implementation for Python objects."""
+
+import enum
+
+
+class Visitor(object):
+
+    defaultStop = False
+
+    @classmethod
+    def _register(celf, clazzes_attrs):
+        assert celf != Visitor, "Subclass Visitor instead."
+        if "_visitors" not in celf.__dict__:
+            celf._visitors = {}
+
+        def wrapper(method):
+            assert method.__name__ == "visit"
+            for clazzes, attrs in clazzes_attrs:
+                if type(clazzes) != tuple:
+                    clazzes = (clazzes,)
+                if type(attrs) == str:
+                    attrs = (attrs,)
+                for clazz in clazzes:
+                    _visitors = celf._visitors.setdefault(clazz, {})
+                    for attr in attrs:
+                        assert attr not in _visitors, (
+                            "Oops, class '%s' has visitor function for '%s' defined already."
+                            % (clazz.__name__, attr)
+                        )
+                        _visitors[attr] = method
+            return None
+
+        return wrapper
+
+    @classmethod
+    def register(celf, clazzes):
+        if type(clazzes) != tuple:
+            clazzes = (clazzes,)
+        return celf._register([(clazzes, (None,))])
+
+    @classmethod
+    def register_attr(celf, clazzes, attrs):
+        clazzes_attrs = []
+        if type(clazzes) != tuple:
+            clazzes = (clazzes,)
+        if type(attrs) == str:
+            attrs = (attrs,)
+        for clazz in clazzes:
+            clazzes_attrs.append((clazz, attrs))
+        return celf._register(clazzes_attrs)
+
+    @classmethod
+    def register_attrs(celf, clazzes_attrs):
+        return celf._register(clazzes_attrs)
+
+    @classmethod
+    def _visitorsFor(celf, thing, _default={}):
+        typ = type(thing)
+
+        for celf in celf.mro():
+
+            _visitors = getattr(celf, "_visitors", None)
+            if _visitors is None:
+                break
+
+            m = celf._visitors.get(typ, None)
+            if m is not None:
+                return m
+
+        return _default
+
+    def visitObject(self, obj, *args, **kwargs):
+        """Called to visit an object. This function loops over all non-private
+        attributes of the objects and calls any user-registered (via
+        @register_attr() or @register_attrs()) visit() functions.
+
+        If there is no user-registered visit function, of if there is and it
+        returns True, or it returns None (or doesn't return anything) and
+        visitor.defaultStop is False (default), then the visitor will proceed
+        to call self.visitAttr()"""
+
+        keys = sorted(vars(obj).keys())
+        _visitors = self._visitorsFor(obj)
+        defaultVisitor = _visitors.get("*", None)
+        for key in keys:
+            if key[0] == "_":
+                continue
+            value = getattr(obj, key)
+            visitorFunc = _visitors.get(key, defaultVisitor)
+            if visitorFunc is not None:
+                ret = visitorFunc(self, obj, key, value, *args, **kwargs)
+                if ret == False or (ret is None and self.defaultStop):
+                    continue
+            self.visitAttr(obj, key, value, *args, **kwargs)
+
+    def visitAttr(self, obj, attr, value, *args, **kwargs):
+        """Called to visit an attribute of an object."""
+        self.visit(value, *args, **kwargs)
+
+    def visitList(self, obj, *args, **kwargs):
+        """Called to visit any value that is a list."""
+        for value in obj:
+            self.visit(value, *args, **kwargs)
+
+    def visitDict(self, obj, *args, **kwargs):
+        """Called to visit any value that is a dictionary."""
+        for value in obj.values():
+            self.visit(value, *args, **kwargs)
+
+    def visitLeaf(self, obj, *args, **kwargs):
+        """Called to visit any value that is not an object, list,
+        or dictionary."""
+        pass
+
+    def visit(self, obj, *args, **kwargs):
+        """This is the main entry to the visitor. The visitor will visit object
+        obj.
+
+        The visitor will first determine if there is a registered (via
+        @register()) visit function for the type of object. If there is, it
+        will be called, and (visitor, obj, *args, **kwargs) will be passed to
+        the user visit function.
+
+        If there is no user-registered visit function, of if there is and it
+        returns True, or it returns None (or doesn't return anything) and
+        visitor.defaultStop is False (default), then the visitor will proceed
+        to dispatch to one of self.visitObject(), self.visitList(),
+        self.visitDict(), or self.visitLeaf() (any of which can be overriden in
+        a subclass)."""
+
+        visitorFunc = self._visitorsFor(obj).get(None, None)
+        if visitorFunc is not None:
+            ret = visitorFunc(self, obj, *args, **kwargs)
+            if ret == False or (ret is None and self.defaultStop):
+                return
+        if hasattr(obj, "__dict__") and not isinstance(obj, enum.Enum):
+            self.visitObject(obj, *args, **kwargs)
+        elif isinstance(obj, list):
+            self.visitList(obj, *args, **kwargs)
+        elif isinstance(obj, dict):
+            self.visitDict(obj, *args, **kwargs)
+        else:
+            self.visitLeaf(obj, *args, **kwargs)
diff --git a/Lib/fontTools/mtiLib/__init__.py b/Lib/fontTools/mtiLib/__init__.py
index 667a216..f117a74 100644
--- a/Lib/fontTools/mtiLib/__init__.py
+++ b/Lib/fontTools/mtiLib/__init__.py
@@ -121,7 +121,7 @@
 				script = script[0].Script
 			else:
 				scriptRec = ot.ScriptRecord()
-				scriptRec.ScriptTag = scriptTag
+				scriptRec.ScriptTag = scriptTag + ' '*(4 - len(scriptTag))
 				scriptRec.Script = ot.Script()
 				records.append(scriptRec)
 				script = scriptRec.Script
@@ -1165,7 +1165,7 @@
 
 
 def main(args=None, font=None):
-	"""Convert a FontDame OTL file to TTX XML.
+	"""Convert a FontDame OTL file to TTX XML
 
 	Writes XML output to stdout.
 
diff --git a/Lib/fontTools/otlLib/optimize/__init__.py b/Lib/fontTools/otlLib/optimize/__init__.py
index a9512fb..25bce9c 100644
--- a/Lib/fontTools/otlLib/optimize/__init__.py
+++ b/Lib/fontTools/otlLib/optimize/__init__.py
@@ -4,7 +4,7 @@
 
 
 def main(args=None):
-    """Optimize the layout tables of an existing font."""
+    """Optimize the layout tables of an existing font"""
     from argparse import ArgumentParser
 
     from fontTools import configLogger
diff --git a/Lib/fontTools/pens/basePen.py b/Lib/fontTools/pens/basePen.py
index e06c00e..f981f80 100644
--- a/Lib/fontTools/pens/basePen.py
+++ b/Lib/fontTools/pens/basePen.py
@@ -47,6 +47,9 @@
 class PenError(Exception):
 	"""Represents an error during penning."""
 
+class OpenContourError(PenError):
+	pass
+
 
 class AbstractPen:
 
diff --git a/Lib/fontTools/pens/cairoPen.py b/Lib/fontTools/pens/cairoPen.py
new file mode 100644
index 0000000..9cd5da9
--- /dev/null
+++ b/Lib/fontTools/pens/cairoPen.py
@@ -0,0 +1,26 @@
+"""Pen to draw to a Cairo graphics library context."""
+
+from fontTools.pens.basePen import BasePen
+
+
+__all__ = ["CairoPen"]
+
+
+class CairoPen(BasePen):
+    """Pen to draw to a Cairo graphics library context."""
+
+    def __init__(self, glyphSet, context):
+        BasePen.__init__(self, glyphSet)
+        self.context = context
+
+    def _moveTo(self, p):
+        self.context.move_to(*p)
+
+    def _lineTo(self, p):
+        self.context.line_to(*p)
+
+    def _curveToOne(self, p1, p2, p3):
+        self.context.curve_to(*p1, *p2, *p3)
+
+    def _closePath(self):
+        self.context.close_path()
diff --git a/Lib/fontTools/pens/momentsPen.py b/Lib/fontTools/pens/momentsPen.py
index 8c90f70..7cd8791 100644
--- a/Lib/fontTools/pens/momentsPen.py
+++ b/Lib/fontTools/pens/momentsPen.py
@@ -1,15 +1,20 @@
-"""Pen calculating 0th, 1st, and 2nd moments of area of glyph shapes.
-This is low-level, autogenerated pen. Use statisticsPen instead."""
-from fontTools.pens.basePen import BasePen
+from fontTools.pens.basePen import BasePen, OpenContourError
+try:
+	import cython
+except ImportError:
+	# if cython not installed, use mock module with no-op decorators and types
+	from fontTools.misc import cython
+
+if cython.compiled:
+	# Yep, I'm compiled.
+	COMPILED = True
+else:
+	# Just a lowly interpreted script.
+	COMPILED = False
 
 
 __all__ = ["MomentsPen"]
 
-
-class OpenContourError(NotImplementedError):
-    pass
-
-
 class MomentsPen(BasePen):
 
 	def __init__(self, glyphset=None):
@@ -33,10 +38,26 @@
 	def _endPath(self):
 		p0 = self._getCurrentPoint()
 		if p0 != self.__startPoint:
+			# Green theorem is not defined on open contours.
 			raise OpenContourError(
 							"Green theorem is not defined on open contours."
 			)
 
+	@cython.locals(r0=cython.double)
+	@cython.locals(r1=cython.double)
+	@cython.locals(r2=cython.double)
+	@cython.locals(r3=cython.double)
+	@cython.locals(r4=cython.double)
+	@cython.locals(r5=cython.double)
+	@cython.locals(r6=cython.double)
+	@cython.locals(r7=cython.double)
+	@cython.locals(r8=cython.double)
+	@cython.locals(r9=cython.double)
+	@cython.locals(r10=cython.double)
+	@cython.locals(r11=cython.double)
+	@cython.locals(r12=cython.double)
+	@cython.locals(x0=cython.double, y0=cython.double)
+	@cython.locals(x1=cython.double, y1=cython.double)
 	def _lineTo(self, p1):
 		x0,y0 = self._getCurrentPoint()
 		x1,y1 = p1
@@ -44,246 +65,431 @@
 		r0 = x1*y0
 		r1 = x1*y1
 		r2 = x1**2
-		r3 = x0**2
-		r4 = 2*y0
-		r5 = y0 - y1
-		r6 = r5*x0
-		r7 = y0**2
-		r8 = y1**2
-		r9 = x1**3
-		r10 = r4*y1
+		r3 = r2*y1
+		r4 = y0 - y1
+		r5 = r4*x0
+		r6 = x0**2
+		r7 = 2*y0
+		r8 = y0**2
+		r9 = y1**2
+		r10 = x1**3
 		r11 = y0**3
 		r12 = y1**3
 
 		self.area += -r0/2 - r1/2 + x0*(y0 + y1)/2
-		self.momentX += -r2*y0/6 - r2*y1/3 + r3*(r4 + y1)/6 - r6*x1/6
-		self.momentY += -r0*y1/6 - r7*x1/6 - r8*x1/6 + x0*(r7 + r8 + y0*y1)/6
-		self.momentXX += -r2*r6/12 - r3*r5*x1/12 - r9*y0/12 - r9*y1/4 + x0**3*(3*y0 + y1)/12
-		self.momentXY += -r10*r2/24 - r2*r7/24 - r2*r8/8 + r3*(r10 + 3*r7 + r8)/24 - x0*x1*(r7 - r8)/12
-		self.momentYY += -r0*r8/12 - r1*r7/12 - r11*x1/12 - r12*x1/12 + x0*(r11 + r12 + r7*y1 + r8*y0)/12
+		self.momentX += -r2*y0/6 - r3/3 - r5*x1/6 + r6*(r7 + y1)/6
+		self.momentY += -r0*y1/6 - r8*x1/6 - r9*x1/6 + x0*(r8 + r9 + y0*y1)/6
+		self.momentXX += -r10*y0/12 - r10*y1/4 - r2*r5/12 - r4*r6*x1/12 + x0**3*(3*y0 + y1)/12
+		self.momentXY += -r2*r8/24 - r2*r9/8 - r3*r7/24 + r6*(r7*y1 + 3*r8 + r9)/24 - x0*x1*(r8 - r9)/12
+		self.momentYY += -r0*r9/12 - r1*r8/12 - r11*x1/12 - r12*x1/12 + x0*(r11 + r12 + r8*y1 + r9*y0)/12
 
+	@cython.locals(r0=cython.double)
+	@cython.locals(r1=cython.double)
+	@cython.locals(r2=cython.double)
+	@cython.locals(r3=cython.double)
+	@cython.locals(r4=cython.double)
+	@cython.locals(r5=cython.double)
+	@cython.locals(r6=cython.double)
+	@cython.locals(r7=cython.double)
+	@cython.locals(r8=cython.double)
+	@cython.locals(r9=cython.double)
+	@cython.locals(r10=cython.double)
+	@cython.locals(r11=cython.double)
+	@cython.locals(r12=cython.double)
+	@cython.locals(r13=cython.double)
+	@cython.locals(r14=cython.double)
+	@cython.locals(r15=cython.double)
+	@cython.locals(r16=cython.double)
+	@cython.locals(r17=cython.double)
+	@cython.locals(r18=cython.double)
+	@cython.locals(r19=cython.double)
+	@cython.locals(r20=cython.double)
+	@cython.locals(r21=cython.double)
+	@cython.locals(r22=cython.double)
+	@cython.locals(r23=cython.double)
+	@cython.locals(r24=cython.double)
+	@cython.locals(r25=cython.double)
+	@cython.locals(r26=cython.double)
+	@cython.locals(r27=cython.double)
+	@cython.locals(r28=cython.double)
+	@cython.locals(r29=cython.double)
+	@cython.locals(r30=cython.double)
+	@cython.locals(r31=cython.double)
+	@cython.locals(r32=cython.double)
+	@cython.locals(r33=cython.double)
+	@cython.locals(r34=cython.double)
+	@cython.locals(r35=cython.double)
+	@cython.locals(r36=cython.double)
+	@cython.locals(r37=cython.double)
+	@cython.locals(r38=cython.double)
+	@cython.locals(r39=cython.double)
+	@cython.locals(r40=cython.double)
+	@cython.locals(r41=cython.double)
+	@cython.locals(r42=cython.double)
+	@cython.locals(r43=cython.double)
+	@cython.locals(r44=cython.double)
+	@cython.locals(r45=cython.double)
+	@cython.locals(r46=cython.double)
+	@cython.locals(r47=cython.double)
+	@cython.locals(r48=cython.double)
+	@cython.locals(r49=cython.double)
+	@cython.locals(r50=cython.double)
+	@cython.locals(r51=cython.double)
+	@cython.locals(r52=cython.double)
+	@cython.locals(r53=cython.double)
+	@cython.locals(x0=cython.double, y0=cython.double)
+	@cython.locals(x1=cython.double, y1=cython.double)
+	@cython.locals(x2=cython.double, y2=cython.double)
 	def _qCurveToOne(self, p1, p2):
 		x0,y0 = self._getCurrentPoint()
 		x1,y1 = p1
 		x2,y2 = p2
 
-		r0 = 2*x1
-		r1 = r0*y2
-		r2 = 2*y1
-		r3 = r2*x2
-		r4 = 3*y2
-		r5 = r4*x2
-		r6 = 3*y0
-		r7 = x1**2
-		r8 = 2*y2
-		r9 = x2**2
-		r10 = 4*y1
-		r11 = 10*y2
-		r12 = r0*x2
-		r13 = x0**2
-		r14 = 10*y0
-		r15 = x2*y2
-		r16 = r0*y1 + r15
-		r17 = 4*x1
-		r18 = x2*y0
-		r19 = r10*r15
-		r20 = y1**2
-		r21 = 2*r20
-		r22 = y2**2
-		r23 = r22*x2
-		r24 = 5*r23
-		r25 = y0**2
-		r26 = y0*y2
-		r27 = 5*r25
-		r28 = 8*x1**3
-		r29 = x2**3
-		r30 = 30*y1
-		r31 = 6*y1
-		r32 = 10*r9*x1
-		r33 = 4*r7
-		r34 = 5*y2
-		r35 = 12*r7
-		r36 = r5 + 20*x1*y1
-		r37 = 30*x1
-		r38 = 12*x1
-		r39 = 20*r7
-		r40 = 8*r7*y1
-		r41 = r34*r9
-		r42 = 60*y1
-		r43 = 20*r20
-		r44 = 4*r20
-		r45 = 15*r22
-		r46 = r38*x2
-		r47 = y1*y2
-		r48 = 8*r20*x1 + r24
-		r49 = 6*x1
-		r50 = 8*y1**3
-		r51 = y2**3
-		r52 = y0**3
-		r53 = 10*y1
-		r54 = 12*y1
-		r55 = 12*r20
+		r0 = 2*y1
+		r1 = r0*x2
+		r2 = x2*y2
+		r3 = 3*r2
+		r4 = 2*x1
+		r5 = 3*y0
+		r6 = x1**2
+		r7 = x2**2
+		r8 = 4*y1
+		r9 = 10*y2
+		r10 = 2*y2
+		r11 = r4*x2
+		r12 = x0**2
+		r13 = 10*y0
+		r14 = r4*y2
+		r15 = x2*y0
+		r16 = 4*x1
+		r17 = r0*x1 + r2
+		r18 = r2*r8
+		r19 = y1**2
+		r20 = 2*r19
+		r21 = y2**2
+		r22 = r21*x2
+		r23 = 5*r22
+		r24 = y0**2
+		r25 = y0*y2
+		r26 = 5*r24
+		r27 = x1**3
+		r28 = x2**3
+		r29 = 30*y1
+		r30 = 6*y1
+		r31 = 10*r7*x1
+		r32 = 5*y2
+		r33 = 12*r6
+		r34 = 30*x1
+		r35 = x1*y1
+		r36 = r3 + 20*r35
+		r37 = 12*x1
+		r38 = 20*r6
+		r39 = 8*r6*y1
+		r40 = r32*r7
+		r41 = 60*y1
+		r42 = 20*r19
+		r43 = 4*r19
+		r44 = 15*r21
+		r45 = 12*x2
+		r46 = 12*y2
+		r47 = 6*x1
+		r48 = 8*r19*x1 + r23
+		r49 = 8*y1**3
+		r50 = y2**3
+		r51 = y0**3
+		r52 = 10*y1
+		r53 = 12*y1
 
-		self.area += r1/6 - r3/6 - r5/6 + x0*(r2 + r6 + y2)/6 - y0*(r0 + x2)/6
-		self.momentX += -r10*r9/30 - r11*r9/30 - r12*(-r8 + y1)/30 + r13*(r10 + r14 + y2)/30 + r7*r8/30 + x0*(r1 + r16 - r17*y0 - r18)/30 - y0*(r12 + 2*r7 + r9)/30
-		self.momentY += r1*(r8 + y1)/30 - r19/30 - r21*x2/30 - r24/30 - r25*(r17 + x2)/30 + x0*(r10*y0 + r2*y2 + r21 + r22 + r26 + r27)/30 - y0*(r16 + r3)/30
-		self.momentXX += r13*(r11*x1 - 5*r18 + r3 + r36 - r37*y0)/420 + r28*y2/420 - r29*r30/420 - r29*y2/4 - r32*(r2 - r4)/420 - r33*x2*(r2 - r34)/420 + x0**3*(r31 + 21*y0 + y2)/84 - x0*(-r15*r38 + r18*r38 + r2*r9 - r35*y2 + r39*y0 - r40 - r41 + r6*r9)/420 - y0*(r28 + 5*r29 + r32 + r35*x2)/420
-		self.momentXY += r13*(r14*y2 + 3*r22 + 105*r25 + r42*y0 + r43 + 12*r47)/840 - r17*x2*(r44 - r45)/840 - r22*r9/8 - r25*(r39 + r46 + 3*r9)/840 + r33*y2*(r10 + r34)/840 - r42*r9*y2/840 - r43*r9/840 + x0*(-r10*r18 + r17*r26 + r19 + r22*r49 - r25*r37 - r27*x2 + r38*r47 + r48)/420 - y0*(r15*r17 + r31*r9 + r40 + r41 + r46*y1)/420
-		self.momentYY += r1*(r11*y1 + r44 + r45)/420 - r15*r43/420 - r23*r30/420 - r25*(r1 + r36 + r53*x2)/420 - r50*x2/420 - r51*x2/12 - r52*(r49 + x2)/84 + x0*(r22*r53 + r22*r6 + r25*r30 + r25*r34 + r26*r54 + r43*y0 + r50 + 5*r51 + 35*r52 + r55*y2)/420 - y0*(-r0*r22 + r15*r54 + r48 + r55*x2)/420
+		self.area += -r1/6 - r3/6 + x0*(r0 + r5 + y2)/6 + x1*y2/3 - y0*(r4 + x2)/6
+		self.momentX += -r11*(-r10 + y1)/30 + r12*(r13 + r8 + y2)/30 + r6*y2/15 - r7*r8/30 - r7*r9/30 + x0*(r14 - r15 - r16*y0 + r17)/30 - y0*(r11 + 2*r6 + r7)/30
+		self.momentY += -r18/30 - r20*x2/30 - r23/30 - r24*(r16 + x2)/30 + x0*(r0*y2 + r20 + r21 + r25 + r26 + r8*y0)/30 + x1*y2*(r10 + y1)/15 - y0*(r1 + r17)/30
+		self.momentXX += r12*(r1 - 5*r15 - r34*y0 + r36 + r9*x1)/420 + 2*r27*y2/105 - r28*r29/420 - r28*y2/4 - r31*(r0 - 3*y2)/420 - r6*x2*(r0 - r32)/105 + x0**3*(r30 + 21*y0 + y2)/84 - x0*(r0*r7 + r15*r37 - r2*r37 - r33*y2 + r38*y0 - r39 - r40 + r5*r7)/420 - y0*(8*r27 + 5*r28 + r31 + r33*x2)/420
+		self.momentXY += r12*(r13*y2 + 3*r21 + 105*r24 + r41*y0 + r42 + r46*y1)/840 - r16*x2*(r43 - r44)/840 - r21*r7/8 - r24*(r38 + r45*x1 + 3*r7)/840 - r41*r7*y2/840 - r42*r7/840 + r6*y2*(r32 + r8)/210 + x0*(-r15*r8 + r16*r25 + r18 + r21*r47 - r24*r34 - r26*x2 + r35*r46 + r48)/420 - y0*(r16*r2 + r30*r7 + r35*r45 + r39 + r40)/420
+		self.momentYY += -r2*r42/420 - r22*r29/420 - r24*(r14 + r36 + r52*x2)/420 - r49*x2/420 - r50*x2/12 - r51*(r47 + x2)/84 + x0*(r19*r46 + r21*r5 + r21*r52 + r24*r29 + r25*r53 + r26*y2 + r42*y0 + r49 + 5*r50 + 35*r51)/420 + x1*y2*(r43 + r44 + r9*y1)/210 - y0*(r19*r45 + r2*r53 - r21*r4 + r48)/420
 
+	@cython.locals(r0=cython.double)
+	@cython.locals(r1=cython.double)
+	@cython.locals(r2=cython.double)
+	@cython.locals(r3=cython.double)
+	@cython.locals(r4=cython.double)
+	@cython.locals(r5=cython.double)
+	@cython.locals(r6=cython.double)
+	@cython.locals(r7=cython.double)
+	@cython.locals(r8=cython.double)
+	@cython.locals(r9=cython.double)
+	@cython.locals(r10=cython.double)
+	@cython.locals(r11=cython.double)
+	@cython.locals(r12=cython.double)
+	@cython.locals(r13=cython.double)
+	@cython.locals(r14=cython.double)
+	@cython.locals(r15=cython.double)
+	@cython.locals(r16=cython.double)
+	@cython.locals(r17=cython.double)
+	@cython.locals(r18=cython.double)
+	@cython.locals(r19=cython.double)
+	@cython.locals(r20=cython.double)
+	@cython.locals(r21=cython.double)
+	@cython.locals(r22=cython.double)
+	@cython.locals(r23=cython.double)
+	@cython.locals(r24=cython.double)
+	@cython.locals(r25=cython.double)
+	@cython.locals(r26=cython.double)
+	@cython.locals(r27=cython.double)
+	@cython.locals(r28=cython.double)
+	@cython.locals(r29=cython.double)
+	@cython.locals(r30=cython.double)
+	@cython.locals(r31=cython.double)
+	@cython.locals(r32=cython.double)
+	@cython.locals(r33=cython.double)
+	@cython.locals(r34=cython.double)
+	@cython.locals(r35=cython.double)
+	@cython.locals(r36=cython.double)
+	@cython.locals(r37=cython.double)
+	@cython.locals(r38=cython.double)
+	@cython.locals(r39=cython.double)
+	@cython.locals(r40=cython.double)
+	@cython.locals(r41=cython.double)
+	@cython.locals(r42=cython.double)
+	@cython.locals(r43=cython.double)
+	@cython.locals(r44=cython.double)
+	@cython.locals(r45=cython.double)
+	@cython.locals(r46=cython.double)
+	@cython.locals(r47=cython.double)
+	@cython.locals(r48=cython.double)
+	@cython.locals(r49=cython.double)
+	@cython.locals(r50=cython.double)
+	@cython.locals(r51=cython.double)
+	@cython.locals(r52=cython.double)
+	@cython.locals(r53=cython.double)
+	@cython.locals(r54=cython.double)
+	@cython.locals(r55=cython.double)
+	@cython.locals(r56=cython.double)
+	@cython.locals(r57=cython.double)
+	@cython.locals(r58=cython.double)
+	@cython.locals(r59=cython.double)
+	@cython.locals(r60=cython.double)
+	@cython.locals(r61=cython.double)
+	@cython.locals(r62=cython.double)
+	@cython.locals(r63=cython.double)
+	@cython.locals(r64=cython.double)
+	@cython.locals(r65=cython.double)
+	@cython.locals(r66=cython.double)
+	@cython.locals(r67=cython.double)
+	@cython.locals(r68=cython.double)
+	@cython.locals(r69=cython.double)
+	@cython.locals(r70=cython.double)
+	@cython.locals(r71=cython.double)
+	@cython.locals(r72=cython.double)
+	@cython.locals(r73=cython.double)
+	@cython.locals(r74=cython.double)
+	@cython.locals(r75=cython.double)
+	@cython.locals(r76=cython.double)
+	@cython.locals(r77=cython.double)
+	@cython.locals(r78=cython.double)
+	@cython.locals(r79=cython.double)
+	@cython.locals(r80=cython.double)
+	@cython.locals(r81=cython.double)
+	@cython.locals(r82=cython.double)
+	@cython.locals(r83=cython.double)
+	@cython.locals(r84=cython.double)
+	@cython.locals(r85=cython.double)
+	@cython.locals(r86=cython.double)
+	@cython.locals(r87=cython.double)
+	@cython.locals(r88=cython.double)
+	@cython.locals(r89=cython.double)
+	@cython.locals(r90=cython.double)
+	@cython.locals(r91=cython.double)
+	@cython.locals(r92=cython.double)
+	@cython.locals(r93=cython.double)
+	@cython.locals(r94=cython.double)
+	@cython.locals(r95=cython.double)
+	@cython.locals(r96=cython.double)
+	@cython.locals(r97=cython.double)
+	@cython.locals(r98=cython.double)
+	@cython.locals(r99=cython.double)
+	@cython.locals(r100=cython.double)
+	@cython.locals(r101=cython.double)
+	@cython.locals(r102=cython.double)
+	@cython.locals(r103=cython.double)
+	@cython.locals(r104=cython.double)
+	@cython.locals(r105=cython.double)
+	@cython.locals(r106=cython.double)
+	@cython.locals(r107=cython.double)
+	@cython.locals(r108=cython.double)
+	@cython.locals(r109=cython.double)
+	@cython.locals(r110=cython.double)
+	@cython.locals(r111=cython.double)
+	@cython.locals(r112=cython.double)
+	@cython.locals(r113=cython.double)
+	@cython.locals(r114=cython.double)
+	@cython.locals(r115=cython.double)
+	@cython.locals(r116=cython.double)
+	@cython.locals(r117=cython.double)
+	@cython.locals(r118=cython.double)
+	@cython.locals(r119=cython.double)
+	@cython.locals(r120=cython.double)
+	@cython.locals(r121=cython.double)
+	@cython.locals(r122=cython.double)
+	@cython.locals(r123=cython.double)
+	@cython.locals(r124=cython.double)
+	@cython.locals(r125=cython.double)
+	@cython.locals(r126=cython.double)
+	@cython.locals(r127=cython.double)
+	@cython.locals(r128=cython.double)
+	@cython.locals(r129=cython.double)
+	@cython.locals(r130=cython.double)
+	@cython.locals(r131=cython.double)
+	@cython.locals(r132=cython.double)
+	@cython.locals(x0=cython.double, y0=cython.double)
+	@cython.locals(x1=cython.double, y1=cython.double)
+	@cython.locals(x2=cython.double, y2=cython.double)
+	@cython.locals(x3=cython.double, y3=cython.double)
 	def _curveToOne(self, p1, p2, p3):
 		x0,y0 = self._getCurrentPoint()
 		x1,y1 = p1
 		x2,y2 = p2
 		x3,y3 = p3
 
-		r0 = 6*x2
-		r1 = r0*y3
-		r2 = 6*y2
-		r3 = 10*y3
-		r4 = r3*x3
-		r5 = 3*x1
-		r6 = 3*y1
-		r7 = 6*x1
-		r8 = 3*x2
-		r9 = 6*y1
-		r10 = 3*y2
-		r11 = x2**2
-		r12 = r11*y3
-		r13 = 45*r12
-		r14 = x3**2
-		r15 = r14*y2
-		r16 = r14*y3
-		r17 = x2*x3
-		r18 = 15*r17
-		r19 = 7*y3
-		r20 = x1**2
-		r21 = 9*r20
-		r22 = x0**2
-		r23 = 21*y1
-		r24 = 9*r11
-		r25 = 9*x2
-		r26 = x2*y3
-		r27 = 15*r26
-		r28 = -r25*y1 + r27
-		r29 = r25*y2
-		r30 = r9*x3
-		r31 = 45*x1
-		r32 = x1*x3
-		r33 = 45*r20
-		r34 = 5*r14
-		r35 = x2*y2
-		r36 = 18*r35
-		r37 = 5*x3
-		r38 = r37*y3
-		r39 = r31*y1 + r36 + r38
-		r40 = x1*y0
-		r41 = x1*y3
-		r42 = x2*y0
-		r43 = x3*y1
-		r44 = r10*x3
-		r45 = x3*y2*y3
-		r46 = y2**2
-		r47 = 45*r46
-		r48 = r47*x3
-		r49 = y3**2
+		r0 = 6*y2
+		r1 = r0*x3
+		r2 = 10*y3
+		r3 = r2*x3
+		r4 = 3*y1
+		r5 = 6*x1
+		r6 = 3*x2
+		r7 = 6*y1
+		r8 = 3*y2
+		r9 = x2**2
+		r10 = 45*r9
+		r11 = r10*y3
+		r12 = x3**2
+		r13 = r12*y2
+		r14 = r12*y3
+		r15 = 7*y3
+		r16 = 15*x3
+		r17 = r16*x2
+		r18 = x1**2
+		r19 = 9*r18
+		r20 = x0**2
+		r21 = 21*y1
+		r22 = 9*r9
+		r23 = r7*x3
+		r24 = 9*y2
+		r25 = r24*x2 + r3
+		r26 = 9*x2
+		r27 = x2*y3
+		r28 = -r26*y1 + 15*r27
+		r29 = 3*x1
+		r30 = 45*x1
+		r31 = 12*x3
+		r32 = 45*r18
+		r33 = 5*r12
+		r34 = r8*x3
+		r35 = 105*y0
+		r36 = 30*y0
+		r37 = r36*x2
+		r38 = 5*x3
+		r39 = 15*y3
+		r40 = 5*y3
+		r41 = r40*x3
+		r42 = x2*y2
+		r43 = 18*r42
+		r44 = 45*y1
+		r45 = r41 + r43 + r44*x1
+		r46 = y2*y3
+		r47 = r46*x3
+		r48 = y2**2
+		r49 = 45*r48
 		r50 = r49*x3
-		r51 = y1**2
-		r52 = 9*r51
-		r53 = y0**2
-		r54 = 21*x1
-		r55 = x3*y2
-		r56 = 15*r55
-		r57 = 9*y2
-		r58 = y2*y3
-		r59 = 15*r58
-		r60 = 9*r46
-		r61 = 3*y3
-		r62 = 45*y1
-		r63 = r8*y3
-		r64 = y0*y1
-		r65 = y0*y2
-		r66 = 30*r65
-		r67 = 5*y3
-		r68 = y1*y3
-		r69 = 45*r51
-		r70 = 5*r49
-		r71 = x2**3
-		r72 = x3**3
-		r73 = 126*x3
-		r74 = x1**3
-		r75 = r14*x2
-		r76 = 63*r11
-		r77 = r76*x3
-		r78 = 15*r35
-		r79 = r19*x3
-		r80 = x1*y1
-		r81 = 63*r35
-		r82 = r38 + 378*r80 + r81
-		r83 = x1*y2
-		r84 = x2*y1
-		r85 = x3*y0
-		r86 = x2*x3*y1
-		r87 = x2*x3*y3
-		r88 = r11*y2
-		r89 = 27*r88
-		r90 = 42*y3
-		r91 = r14*r90
-		r92 = 90*x1*x2
-		r93 = 189*x2
-		r94 = 30*x1*x3
-		r95 = 14*r16 + 126*r20*y1 + 45*r88 + r94*y2
-		r96 = x1*x2
-		r97 = 252*r96
-		r98 = x1*x2*y2
-		r99 = 42*r32
-		r100 = x1*x3*y1
-		r101 = 30*r17
-		r102 = 18*r17
-		r103 = 378*r20
-		r104 = 189*y2
-		r105 = r20*y3
-		r106 = r11*y1
-		r107 = r14*y1
-		r108 = 378*r46
-		r109 = 252*y2
-		r110 = y1*y2
-		r111 = x2*x3*y2
-		r112 = y0*y3
-		r113 = 378*r51
-		r114 = 63*r46
-		r115 = 27*x2
-		r116 = r115*r46 + 42*r50
-		r117 = x2*y1*y3
-		r118 = x3*y1*y2
-		r119 = r49*x2
-		r120 = r51*x3
-		r121 = x3*y3
-		r122 = 14*x3
-		r123 = 30*r117 + r122*r49 + r47*x2 + 126*r51*x1
-		r124 = x1*y1*y3
-		r125 = x1*y2*y3
-		r126 = x2*y1*y2
-		r127 = 54*y3
-		r128 = 21*r55
-		r129 = 630*r53
-		r130 = r46*x1
-		r131 = r49*x1
-		r132 = 126*r53
-		r133 = y2**3
-		r134 = y3**3
-		r135 = 630*r49
-		r136 = y1**3
-		r137 = y0**3
-		r138 = r114*y3 + r23*r49
-		r139 = r49*y2
+		r51 = y3**2
+		r52 = r51*x3
+		r53 = y1**2
+		r54 = 9*r53
+		r55 = y0**2
+		r56 = 21*x1
+		r57 = 6*x2
+		r58 = r16*y2
+		r59 = r39*y2
+		r60 = 9*r48
+		r61 = r6*y3
+		r62 = 3*y3
+		r63 = r36*y2
+		r64 = y1*y3
+		r65 = 45*r53
+		r66 = 5*r51
+		r67 = x2**3
+		r68 = x3**3
+		r69 = 630*y2
+		r70 = 126*x3
+		r71 = x1**3
+		r72 = 126*x2
+		r73 = 63*r9
+		r74 = r73*x3
+		r75 = r15*x3 + 15*r42
+		r76 = 630*x1
+		r77 = 14*x3
+		r78 = 21*r27
+		r79 = 42*x1
+		r80 = 42*x2
+		r81 = x1*y2
+		r82 = 63*r42
+		r83 = x1*y1
+		r84 = r41 + r82 + 378*r83
+		r85 = x2*x3
+		r86 = r85*y1
+		r87 = r27*x3
+		r88 = 27*r9
+		r89 = r88*y2
+		r90 = 42*r14
+		r91 = 90*x1
+		r92 = 189*r18
+		r93 = 378*r18
+		r94 = r12*y1
+		r95 = 252*x1*x2
+		r96 = r79*x3
+		r97 = 30*r85
+		r98 = r83*x3
+		r99 = 30*x3
+		r100 = 42*x3
+		r101 = r42*x1
+		r102 = r10*y2 + 14*r14 + 126*r18*y1 + r81*r99
+		r103 = 378*r48
+		r104 = 18*y1
+		r105 = r104*y2
+		r106 = y0*y1
+		r107 = 252*y2
+		r108 = r107*y0
+		r109 = y0*y3
+		r110 = 42*r64
+		r111 = 378*r53
+		r112 = 63*r48
+		r113 = 27*x2
+		r114 = r27*y2
+		r115 = r113*r48 + 42*r52
+		r116 = x3*y3
+		r117 = 54*r42
+		r118 = r51*x1
+		r119 = r51*x2
+		r120 = r48*x1
+		r121 = 21*x3
+		r122 = r64*x1
+		r123 = r81*y3
+		r124 = 30*r27*y1 + r49*x2 + 14*r52 + 126*r53*x1
+		r125 = y2**3
+		r126 = y3**3
+		r127 = y1**3
+		r128 = y0**3
+		r129 = r51*y2
+		r130 = r112*y3 + r21*r51
+		r131 = 189*r53
+		r132 = 90*y2
 
-		self.area += r1/20 - r2*x3/20 - r4/20 + r5*(y2 + y3)/20 - r6*(x2 + x3)/20 + x0*(r10 + r9 + 10*y0 + y3)/20 - y0*(r7 + r8 + x3)/20
-		self.momentX += r13/840 - r15/8 - r16/3 - r18*(r10 - r19)/840 + r21*(r10 + 2*y3)/840 + r22*(r2 + r23 + 56*y0 + y3)/168 + r5*(r28 + r29 - r30 + r4)/840 - r6*(10*r14 + r18 + r24)/840 + x0*(12*r26 + r31*y2 - r37*y0 + r39 - 105*r40 + 15*r41 - 30*r42 - 3*r43 + r44)/840 - y0*(18*r11 + r18 + r31*x2 + 12*r32 + r33 + r34)/840
-		self.momentY += r27*(r10 + r19)/840 - r45/8 - r48/840 + r5*(10*r49 + r57*y1 + r59 + r60 + r9*y3)/840 - r50/6 - r52*(r8 + 2*x3)/840 - r53*(r0 + r54 + x3)/168 - r6*(r29 + r4 + r56)/840 + x0*(18*r46 + 140*r53 + r59 + r62*y2 + 105*r64 + r66 + r67*y0 + 12*r68 + r69 + r70)/840 - y0*(r39 + 15*r43 + 12*r55 - r61*x1 + r62*x2 + r63)/840
-		self.momentXX += -r11*r73*(-r61 + y2)/9240 + r21*(r28 - r37*y1 + r44 + r78 + r79)/9240 + r22*(21*r26 - 630*r40 + 42*r41 - 126*r42 + r57*x3 + r82 + 210*r83 + 42*r84 - 14*r85)/9240 - r5*(r11*r62 + r14*r23 + 14*r15 - r76*y3 + 54*r86 - 84*r87 - r89 - r91)/9240 - r6*(27*r71 + 42*r72 + 70*r75 + r77)/9240 + 3*r71*y3/220 - 3*r72*y2/44 - r72*y3/4 + 3*r74*(r57 + r67)/3080 - r75*(378*y2 - 630*y3)/9240 + x0**3*(r57 + r62 + 165*y0 + y3)/660 + x0*(-18*r100 - r101*y0 - r101*y1 + r102*y2 - r103*y0 + r104*r20 + 63*r105 - 27*r106 - 9*r107 + r13 - r34*y0 - r76*y0 + 42*r87 + r92*y3 + r94*y3 + r95 - r97*y0 + 162*r98 - r99*y0)/9240 - y0*(135*r11*x1 + r14*r54 + r20*r93 + r33*x3 + 45*r71 + 14*r72 + 126*r74 + 42*r75 + r77 + r92*x3)/9240
-		self.momentXY += -r108*r14/18480 + r12*(r109 + 378*y3)/18480 - r14*r49/8 - 3*r14*r58/44 - r17*(252*r46 - 1260*r49)/18480 + r21*(18*r110 + r3*y1 + 15*r46 + 7*r49 + 18*r58)/18480 + r22*(252*r110 + 28*r112 + r113 + r114 + 2310*r53 + 30*r58 + 1260*r64 + 252*r65 + 42*r68 + r70)/18480 - r52*(r102 + 15*r11 + 7*r14)/18480 - r53*(r101 + r103 + r34 + r76 + r97 + r99)/18480 + r7*(-r115*r51 + r116 + 18*r117 - 18*r118 + 42*r119 - 15*r120 + 28*r45 + r81*y3)/18480 - r9*(63*r111 + 42*r15 + 28*r87 + r89 + r91)/18480 + x0*(r1*y0 + r104*r80 + r112*r54 + 21*r119 - 9*r120 - r122*r53 + r123 + 54*r124 + 60*r125 + 54*r126 + r127*r35 + r128*y3 - r129*x1 + 81*r130 + 15*r131 - r132*x2 - r2*r85 - r23*r85 + r30*y3 + 84*r40*y2 - 84*r42*y1 + r60*x3)/9240 - y0*(54*r100 - 9*r105 + 81*r106 + 15*r107 + 54*r111 + r121*r7 + 21*r15 + r24*y3 + 60*r86 + 21*r87 + r95 + 189*r96*y1 + 54*r98)/9240
-		self.momentYY += -r108*r121/9240 - r133*r73/9240 - r134*x3/12 - r135*r55/9240 - 3*r136*(r25 + r37)/3080 - r137*(r25 + r31 + x3)/660 + r26*(r135 + 126*r46 + 378*y2*y3)/9240 + r5*(r110*r127 + 27*r133 + 42*r134 + r138 + 70*r139 + r46*r62 + 27*r51*y2 + 15*r51*y3)/9240 - r52*(r56 + r63 + r78 + r79)/9240 - r53*(r128 + r25*y3 + 42*r43 + r82 + 42*r83 + 210*r84)/9240 - r6*(r114*x3 + r116 - 14*r119 + 84*r45)/9240 + x0*(r104*r51 + r109*r64 + 90*r110*y3 + r113*y0 + r114*y0 + r129*y1 + r132*y2 + 45*r133 + 14*r134 + 126*r136 + 770*r137 + r138 + 42*r139 + 135*r46*y1 + 14*r53*y3 + r64*r90 + r66*y3 + r69*y3 + r70*y0)/9240 - y0*(90*r118 + 63*r120 + r123 - 18*r124 - 30*r125 + 162*r126 - 27*r130 - 9*r131 + r36*y3 + 30*r43*y3 + 42*r45 + r48 + r51*r93)/9240
+		self.area += -r1/20 - r3/20 - r4*(x2 + x3)/20 + x0*(r7 + r8 + 10*y0 + y3)/20 + 3*x1*(y2 + y3)/20 + 3*x2*y3/10 - y0*(r5 + r6 + x3)/20
+		self.momentX += r11/840 - r13/8 - r14/3 - r17*(-r15 + r8)/840 + r19*(r8 + 2*y3)/840 + r20*(r0 + r21 + 56*y0 + y3)/168 + r29*(-r23 + r25 + r28)/840 - r4*(10*r12 + r17 + r22)/840 + x0*(12*r27 + r30*y2 + r34 - r35*x1 - r37 - r38*y0 + r39*x1 - r4*x3 + r45)/840 - y0*(r17 + r30*x2 + r31*x1 + r32 + r33 + 18*r9)/840
+		self.momentY += -r4*(r25 + r58)/840 - r47/8 - r50/840 - r52/6 - r54*(r6 + 2*x3)/840 - r55*(r56 + r57 + x3)/168 + x0*(r35*y1 + r40*y0 + r44*y2 + 18*r48 + 140*r55 + r59 + r63 + 12*r64 + r65 + r66)/840 + x1*(r24*y1 + 10*r51 + r59 + r60 + r7*y3)/280 + x2*y3*(r15 + r8)/56 - y0*(r16*y1 + r31*y2 + r44*x2 + r45 + r61 - r62*x1)/840
+		self.momentXX += -r12*r72*(-r40 + r8)/9240 + 3*r18*(r28 + r34 - r38*y1 + r75)/3080 + r20*(r24*x3 - r72*y0 - r76*y0 - r77*y0 + r78 + r79*y3 + r80*y1 + 210*r81 + r84)/9240 - r29*(r12*r21 + 14*r13 + r44*r9 - r73*y3 + 54*r86 - 84*r87 - r89 - r90)/9240 - r4*(70*r12*x2 + 27*r67 + 42*r68 + r74)/9240 + 3*r67*y3/220 - r68*r69/9240 - r68*y3/4 - r70*r9*(-r62 + y2)/9240 + 3*r71*(r24 + r40)/3080 + x0**3*(r24 + r44 + 165*y0 + y3)/660 + x0*(r100*r27 + 162*r101 + r102 + r11 + 63*r18*y3 + r27*r91 - r33*y0 - r37*x3 + r43*x3 - r73*y0 - r88*y1 + r92*y2 - r93*y0 - 9*r94 - r95*y0 - r96*y0 - r97*y1 - 18*r98 + r99*x1*y3)/9240 - y0*(r12*r56 + r12*r80 + r32*x3 + 45*r67 + 14*r68 + 126*r71 + r74 + r85*r91 + 135*r9*x1 + r92*x2)/9240
+		self.momentXY += -r103*r12/18480 - r12*r51/8 - 3*r14*y2/44 + 3*r18*(r105 + r2*y1 + 18*r46 + 15*r48 + 7*r51)/6160 + r20*(1260*r106 + r107*y1 + r108 + 28*r109 + r110 + r111 + r112 + 30*r46 + 2310*r55 + r66)/18480 - r54*(7*r12 + 18*r85 + 15*r9)/18480 - r55*(r33 + r73 + r93 + r95 + r96 + r97)/18480 - r7*(42*r13 + r82*x3 + 28*r87 + r89 + r90)/18480 - 3*r85*(r48 - r66)/220 + 3*r9*y3*(r62 + 2*y2)/440 + x0*(-r1*y0 - 84*r106*x2 + r109*r56 + 54*r114 + r117*y1 + 15*r118 + 21*r119 + 81*r120 + r121*r46 + 54*r122 + 60*r123 + r124 - r21*x3*y0 + r23*y3 - r54*x3 - r55*r72 - r55*r76 - r55*r77 + r57*y0*y3 + r60*x3 + 84*r81*y0 + 189*r81*y1)/9240 + x1*(r104*r27 - r105*x3 - r113*r53 + 63*r114 + r115 - r16*r53 + 28*r47 + r51*r80)/3080 - y0*(54*r101 + r102 + r116*r5 + r117*x3 + 21*r13 - r19*y3 + r22*y3 + r78*x3 + 189*r83*x2 + 60*r86 + 81*r9*y1 + 15*r94 + 54*r98)/9240
+		self.momentYY += -r103*r116/9240 - r125*r70/9240 - r126*x3/12 - 3*r127*(r26 + r38)/3080 - r128*(r26 + r30 + x3)/660 - r4*(r112*x3 + r115 - 14*r119 + 84*r47)/9240 - r52*r69/9240 - r54*(r58 + r61 + r75)/9240 - r55*(r100*y1 + r121*y2 + r26*y3 + r79*y2 + r84 + 210*x2*y1)/9240 + x0*(r108*y1 + r110*y0 + r111*y0 + r112*y0 + 45*r125 + 14*r126 + 126*r127 + 770*r128 + 42*r129 + r130 + r131*y2 + r132*r64 + 135*r48*y1 + 630*r55*y1 + 126*r55*y2 + 14*r55*y3 + r63*y3 + r65*y3 + r66*y0)/9240 + x1*(27*r125 + 42*r126 + 70*r129 + r130 + r39*r53 + r44*r48 + 27*r53*y2 + 54*r64*y2)/3080 + 3*x2*y3*(r48 + r66 + r8*y3)/220 - y0*(r100*r46 + 18*r114 - 9*r118 - 27*r120 - 18*r122 - 30*r123 + r124 + r131*x2 + r132*x3*y1 + 162*r42*y1 + r50 + 63*r53*x3 + r64*r99)/9240
 
 if __name__ == '__main__':
 	from fontTools.misc.symfont import x, y, printGreenPen
diff --git a/Lib/fontTools/pens/qtPen.py b/Lib/fontTools/pens/qtPen.py
index 3473645..d08a344 100644
--- a/Lib/fontTools/pens/qtPen.py
+++ b/Lib/fontTools/pens/qtPen.py
@@ -20,10 +20,10 @@
 		self.path.lineTo(*p)
 
 	def _curveToOne(self, p1, p2, p3):
-		self.path.cubicTo(*p1+p2+p3)
+		self.path.cubicTo(*p1, *p2, *p3)
 
 	def _qCurveToOne(self, p1, p2):
-		self.path.quadTo(*p1+p2)
+		self.path.quadTo(*p1, *p2)
 
 	def _closePath(self):
 		self.path.closeSubpath()
diff --git a/Lib/fontTools/pens/statisticsPen.py b/Lib/fontTools/pens/statisticsPen.py
index abd6ff5..1583067 100644
--- a/Lib/fontTools/pens/statisticsPen.py
+++ b/Lib/fontTools/pens/statisticsPen.py
@@ -61,10 +61,13 @@
 
 		#  Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) )
 		# https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient
-		correlation = covariance / (stddevX * stddevY)
+		if stddevX * stddevY == 0:
+			correlation = float("NaN")
+		else:
+			correlation = covariance / (stddevX * stddevY)
 		self.correlation = correlation if abs(correlation) > 1e-3 else 0
 
-		slant = covariance / varianceY
+		slant = covariance / varianceY if varianceY != 0 else float("NaN")
 		self.slant = slant if abs(slant) > 1e-3 else 0
 
 
@@ -82,17 +85,16 @@
 		transformer = TransformPen(pen, Scale(1./upem))
 		glyph.draw(transformer)
 		for item in ['area', 'momentX', 'momentY', 'momentXX', 'momentYY', 'momentXY', 'meanX', 'meanY', 'varianceX', 'varianceY', 'stddevX', 'stddevY', 'covariance', 'correlation', 'slant']:
-			if item[0] == '_': continue
 			print ("%s: %g" % (item, getattr(pen, item)))
 
 def main(args):
 	if not args:
 		return
 	filename, glyphs = args[0], args[1:]
-	if not glyphs:
-		glyphs = ['e', 'o', 'I', 'slash', 'E', 'zero', 'eight', 'minus', 'equal']
 	from fontTools.ttLib import TTFont
 	font = TTFont(filename)
+	if not glyphs:
+		glyphs = font.getGlyphOrder()
 	_test(font.getGlyphSet(), font['head'].unitsPerEm, glyphs)
 
 if __name__ == '__main__':
diff --git a/Lib/fontTools/pens/svgPathPen.py b/Lib/fontTools/pens/svgPathPen.py
index e92737e..106e33b 100644
--- a/Lib/fontTools/pens/svgPathPen.py
+++ b/Lib/fontTools/pens/svgPathPen.py
@@ -23,6 +23,18 @@
             used to resolve component references in composite glyphs.
         ntos: a callable that takes a number and returns a string, to
             customize how numbers are formatted (default: str).
+
+    Note:
+        Fonts have a coordinate system where Y grows up, whereas in SVG,
+        Y grows down.  As such, rendering path data from this pen in
+        SVG typically results in upside-down glyphs.  You can fix this
+        by wrapping the data from this pen in an SVG group element with
+        transform, or wrap this pen in a transform pen.  For example:
+
+            spen = svgPathPen.SVGPathPen(glyphset)
+            pen= TransformPen(spen , (1, 0, 0, -1, 0, 0))
+            glyphset[glyphname].draw(pen)
+            print(tpen.getCommands())
     """
     def __init__(self, glyphSet, ntos: Callable[[float], str] = str):
         BasePen.__init__(self, glyphSet)
@@ -193,7 +205,70 @@
         return "".join(self._commands)
 
 
+def main(args=None):
+    """Generate per-character SVG from font and text"""
+
+    if args is None:
+        import sys
+        args = sys.argv[1:]
+
+    from fontTools.ttLib import TTFont
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        "fonttools pens.svgPathPen", description="Generate SVG from text")
+    parser.add_argument(
+        "font", metavar="font.ttf", help="Font file.")
+    parser.add_argument(
+        "text", metavar="text", help="Text string.")
+    parser.add_argument(
+        "--variations", metavar="AXIS=LOC", default='',
+        help="List of space separated locations. A location consist in "
+        "the name of a variation axis, followed by '=' and a number. E.g.: "
+        "wght=700 wdth=80. The default is the location of the base master.")
+
+    options = parser.parse_args(args)
+
+    font = TTFont(options.font)
+    text = options.text
+
+    location = {}
+    for tag_v in options.variations.split():
+        fields = tag_v.split('=')
+        tag = fields[0].strip()
+        v = int(fields[1])
+        location[tag] = v
+
+    hhea = font['hhea']
+    ascent, descent = hhea.ascent, hhea.descent
+
+    glyphset = font.getGlyphSet(location=location)
+    cmap = font['cmap'].getBestCmap()
+
+    s = ''
+    width = 0
+    for u in text:
+        g = cmap[ord(u)]
+        glyph = glyphset[g]
+
+        pen = SVGPathPen(glyphset)
+        glyph.draw(pen)
+        commands = pen.getCommands()
+
+        s += '<g transform="translate(%d %d) scale(1 -1)"><path d="%s"/></g>\n' % (width, ascent, commands)
+
+        width += glyph.width
+
+    print('<?xml version="1.0" encoding="UTF-8"?>')
+    print('<svg width="%d" height="%d" xmlns="http://www.w3.org/2000/svg">' % (width, ascent-descent))
+    print(s, end='')
+    print('</svg>')
+
+
 if __name__ == "__main__":
     import sys
-    import doctest
-    sys.exit(doctest.testmod().failed)
+    if len(sys.argv) == 1:
+        import doctest
+        sys.exit(doctest.testmod().failed)
+
+    sys.exit(main())
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 56d9c0e..b58e616 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -10,9 +10,11 @@
 from fontTools.otlLib.maxContextCalc import maxCtxFont
 from fontTools.pens.basePen import NullPen
 from fontTools.misc.loggingTools import Timer
+from fontTools.misc.cliTools import makeOutputFileName
 from fontTools.subset.util import _add_method, _uniq_sort
 from fontTools.subset.cff import *
 from fontTools.subset.svg import *
+from fontTools.varLib import varStore  # for subset_varidxes
 import sys
 import struct
 import array
@@ -636,10 +638,16 @@
 			self.Value.prune_hints()
 		self.ValueFormat = self.Value.getEffectiveFormat()
 	elif self.Format == 2:
-		if not options.hinting:
-			for v in self.Value:
-				v.prune_hints()
-		self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
+		if None in self.Value:
+			assert self.ValueFormat == 0
+			assert all(v is None for v in self.Value)
+		else:
+			if not options.hinting:
+				for v in self.Value:
+					v.prune_hints()
+			self.ValueFormat = reduce(
+				int.__or__, [v.getEffectiveFormat() for v in self.Value], 0
+			)
 
 	# Downgrade to Format 1 if all ValueRecords are the same
 	if self.Format == 2 and all(v == self.Value[0] for v in self.Value):
@@ -2608,6 +2616,9 @@
 		'vertical': ['valt', 'vert', 'vkrn', 'vpal', 'vrt2'],
 		'ltr': ['ltra', 'ltrm'],
 		'rtl': ['rtla', 'rtlm'],
+		'rand': ['rand'],
+		'justify': ['jalt'],
+		'private': ['Harf', 'HARF', 'Buzz', 'BUZZ'],
 		# Complex shapers
 		'arabic': ['init', 'medi', 'fina', 'isol', 'med2', 'fin2', 'fin3',
 			   'cswh', 'mset', 'stch'],
@@ -3180,12 +3191,7 @@
 	font = load_font(fontfile, options, dontLoadGlyphNames=dontLoadGlyphNames)
 
 	if outfile is None:
-		basename, _ = splitext(fontfile)
-		if options.flavor is not None:
-			ext = "." + options.flavor.lower()
-		else:
-			ext = ".ttf" if font.sfntVersion == "\0\1\0\0" else ".otf"
-		outfile = basename + ".subset" + ext
+		outfile = makeOutputFileName(fontfile, overWrite=True, suffix=".subset")
 
 	with timer("compile glyph list"):
 		if wildcard_glyphs:
diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py
index 0dcb797..d6872f3 100644
--- a/Lib/fontTools/subset/cff.py
+++ b/Lib/fontTools/subset/cff.py
@@ -3,7 +3,6 @@
 from fontTools.pens.basePen import NullPen
 from fontTools.misc.roundTools import otRound
 from fontTools.misc.loggingTools import deprecateFunction
-from fontTools.varLib.varStore import VarStoreInstancer
 from fontTools.subset.util import _add_method, _uniq_sort
 
 
@@ -109,15 +108,7 @@
 			del csi.file, csi.offsets
 			if hasattr(font, "FDSelect"):
 				sel = font.FDSelect
-				# XXX We want to set sel.format to None, such that the
-				# most compact format is selected. However, OTS was
-				# broken and couldn't parse a FDSelect format 0 that
-				# happened before CharStrings. As such, always force
-				# format 3 until we fix cffLib to always generate
-				# FDSelect after CharStrings.
-				# https://github.com/khaledhosny/ots/pull/31
-				#sel.format = None
-				sel.format = 3
+				sel.format = None
 				sel.gidArray = [sel.gidArray[i] for i in indices]
 			newCharStrings = {}
 			for indicesIdx, charsetIdx in enumerate(indices):
diff --git a/Lib/fontTools/subset/svg.py b/Lib/fontTools/subset/svg.py
index e25fb3e..4ed2cbd 100644
--- a/Lib/fontTools/subset/svg.py
+++ b/Lib/fontTools/subset/svg.py
@@ -7,13 +7,14 @@
 
 try:
     from lxml import etree
-except ModuleNotFoundError:
+except ImportError:
     # lxml is required for subsetting SVG, but we prefer to delay the import error
     # until subset_glyphs() is called (i.e. if font to subset has an 'SVG ' table)
     etree = None
 
 from fontTools import ttLib
 from fontTools.subset.util import _add_method
+from fontTools.ttLib.tables.S_V_G_ import SVGDocument
 
 
 __all__ = ["subset_glyphs"]
@@ -192,7 +193,7 @@
 @_add_method(ttLib.getTableClass("SVG "))
 def subset_glyphs(self, s) -> bool:
     if etree is None:
-        raise ModuleNotFoundError("No module named 'lxml', required to subset SVG")
+        raise ImportError("No module named 'lxml', required to subset SVG")
 
     # glyph names (before subsetting)
     glyph_order: List[str] = s.orig_glyph_order
@@ -201,10 +202,12 @@
     # map from original to new glyph indices (after subsetting)
     glyph_index_map: Dict[int, int] = s.glyph_index_map
 
-    new_docs: List[Tuple[bytes, int, int]] = []
-    for doc, start, end in self.docList:
+    new_docs: List[SVGDocument] = []
+    for doc in self.docList:
 
-        glyphs = {glyph_order[i] for i in range(start, end + 1)}.intersection(s.glyphs)
+        glyphs = {
+            glyph_order[i] for i in range(doc.startGlyphID, doc.endGlyphID + 1)
+        }.intersection(s.glyphs)
         if not glyphs:
             # no intersection: we can drop the whole record
             continue
@@ -212,7 +215,7 @@
         svg = etree.fromstring(
             # encode because fromstring dislikes xml encoding decl if input is str.
             # SVG xml encoding must be utf-8 as per OT spec.
-            doc.encode("utf-8"),
+            doc.data.encode("utf-8"),
             parser=etree.XMLParser(
                 # Disable libxml2 security restrictions to support very deep trees.
                 # Without this we would get an error like this:
@@ -241,7 +244,7 @@
 
         new_gids = (glyph_index_map[i] for i in gids)
         for start, end in ranges(new_gids):
-            new_docs.append((new_doc, start, end))
+            new_docs.append(SVGDocument(new_doc, start, end, doc.compressed))
 
     self.docList = new_docs
 
diff --git a/Lib/fontTools/svgLib/path/parser.py b/Lib/fontTools/svgLib/path/parser.py
index 1fcf899..e594b2b 100644
--- a/Lib/fontTools/svgLib/path/parser.py
+++ b/Lib/fontTools/svgLib/path/parser.py
@@ -16,10 +16,13 @@
 UPPERCASE = set('MZLHVCSQTA')
 
 COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
+
+# https://www.w3.org/TR/css-syntax-3/#number-token-diagram
+#   but -6.e-5 will be tokenized as "-6" then "-5" and confuse parsing
 FLOAT_RE = re.compile(
     r"[-+]?"  # optional sign
     r"(?:"
-    r"(?:0|[1-9][0-9]*)(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)?"  # int/float
+    r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?"  # int/float
     r"|"
     r"(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)"  # float with leading dot (e.g. '.42')
     r")"
@@ -278,8 +281,8 @@
             last_control = control
 
         elif command == 'A':
-            rx = float(elements.pop())
-            ry = float(elements.pop())
+            rx = abs(float(elements.pop()))
+            ry = abs(float(elements.pop()))
             rotation = float(elements.pop())
             arc_large = bool(int(elements.pop()))
             arc_sweep = bool(int(elements.pop()))
diff --git a/Lib/fontTools/ttLib/scaleUpem.py b/Lib/fontTools/ttLib/scaleUpem.py
new file mode 100644
index 0000000..9e0e0ad
--- /dev/null
+++ b/Lib/fontTools/ttLib/scaleUpem.py
@@ -0,0 +1,336 @@
+"""Change the units-per-EM of a font.
+
+AAT and Graphite tables are not supported. CFF/CFF2 fonts
+are de-subroutinized."""
+
+
+from fontTools.ttLib.ttVisitor import TTVisitor
+import fontTools.ttLib as ttLib
+import fontTools.ttLib.tables.otBase as otBase
+import fontTools.ttLib.tables.otTables as otTables
+from fontTools.cffLib import VarStoreData
+import fontTools.cffLib.specializer as cffSpecializer
+from fontTools.misc.fixedTools import otRound
+
+
+__all__ = ["scale_upem", "ScalerVisitor"]
+
+
+class ScalerVisitor(TTVisitor):
+    def __init__(self, scaleFactor):
+        self.scaleFactor = scaleFactor
+
+    def scale(self, v):
+        return otRound(v * self.scaleFactor)
+
+
+@ScalerVisitor.register_attrs(
+    (
+        (ttLib.getTableClass("head"), ("unitsPerEm", "xMin", "yMin", "xMax", "yMax")),
+        (ttLib.getTableClass("post"), ("underlinePosition", "underlineThickness")),
+        (ttLib.getTableClass("VORG"), ("defaultVertOriginY")),
+        (
+            ttLib.getTableClass("hhea"),
+            (
+                "ascent",
+                "descent",
+                "lineGap",
+                "advanceWidthMax",
+                "minLeftSideBearing",
+                "minRightSideBearing",
+                "xMaxExtent",
+                "caretOffset",
+            ),
+        ),
+        (
+            ttLib.getTableClass("vhea"),
+            (
+                "ascent",
+                "descent",
+                "lineGap",
+                "advanceHeightMax",
+                "minTopSideBearing",
+                "minBottomSideBearing",
+                "yMaxExtent",
+                "caretOffset",
+            ),
+        ),
+        (
+            ttLib.getTableClass("OS/2"),
+            (
+                "xAvgCharWidth",
+                "ySubscriptXSize",
+                "ySubscriptYSize",
+                "ySubscriptXOffset",
+                "ySubscriptYOffset",
+                "ySuperscriptXSize",
+                "ySuperscriptYSize",
+                "ySuperscriptXOffset",
+                "ySuperscriptYOffset",
+                "yStrikeoutSize",
+                "yStrikeoutPosition",
+                "sTypoAscender",
+                "sTypoDescender",
+                "sTypoLineGap",
+                "usWinAscent",
+                "usWinDescent",
+                "sxHeight",
+                "sCapHeight",
+            ),
+        ),
+        (
+            otTables.ValueRecord,
+            ("XAdvance", "YAdvance", "XPlacement", "YPlacement"),
+        ),  # GPOS
+        (otTables.Anchor, ("XCoordinate", "YCoordinate")),  # GPOS
+        (otTables.CaretValue, ("Coordinate")),  # GDEF
+        (otTables.BaseCoord, ("Coordinate")),  # BASE
+        (otTables.MathValueRecord, ("Value")),  # MATH
+        (otTables.ClipBox, ("xMin", "yMin", "xMax", "yMax")),  # COLR
+    )
+)
+def visit(visitor, obj, attr, value):
+    setattr(obj, attr, visitor.scale(value))
+
+
+@ScalerVisitor.register_attr(
+    (ttLib.getTableClass("hmtx"), ttLib.getTableClass("vmtx")), "metrics"
+)
+def visit(visitor, obj, attr, metrics):
+    for g in metrics:
+        advance, lsb = metrics[g]
+        metrics[g] = visitor.scale(advance), visitor.scale(lsb)
+
+
+@ScalerVisitor.register_attr(ttLib.getTableClass("VMTX"), "VOriginRecords")
+def visit(visitor, obj, attr, VOriginRecords):
+    for g in VOriginRecords:
+        VOriginRecords[g] = visitor.scale(VOriginRecords[g])
+
+
+@ScalerVisitor.register_attr(ttLib.getTableClass("glyf"), "glyphs")
+def visit(visitor, obj, attr, glyphs):
+    for g in glyphs.values():
+        if g.isComposite():
+            for component in g.components:
+                component.x = visitor.scale(component.x)
+                component.y = visitor.scale(component.y)
+        else:
+            for attr in ("xMin", "xMax", "yMin", "yMax"):
+                v = getattr(g, attr, None)
+                if v is not None:
+                    setattr(g, attr, visitor.scale(v))
+
+        glyf = visitor.font["glyf"]
+        coordinates = g.getCoordinates(glyf)[0]
+        for i, (x, y) in enumerate(coordinates):
+            coordinates[i] = visitor.scale(x), visitor.scale(y)
+
+
+@ScalerVisitor.register_attr(ttLib.getTableClass("gvar"), "variations")
+def visit(visitor, obj, attr, variations):
+    for varlist in variations.values():
+        for var in varlist:
+            coordinates = var.coordinates
+            for i, xy in enumerate(coordinates):
+                if xy is None:
+                    continue
+                coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1])
+
+
+@ScalerVisitor.register_attr(ttLib.getTableClass("kern"), "kernTables")
+def visit(visitor, obj, attr, kernTables):
+    for table in kernTables:
+        kernTable = table.kernTable
+        for k in kernTable.keys():
+            kernTable[k] = visitor.scale(kernTable[k])
+
+
+def _cff_scale(visitor, args):
+    for i, arg in enumerate(args):
+        if not isinstance(arg, list):
+            args[i] = visitor.scale(arg)
+        else:
+            num_blends = arg[-1]
+            _cff_scale(visitor, arg)
+            arg[-1] = num_blends
+
+
+@ScalerVisitor.register_attr(
+    (ttLib.getTableClass("CFF "), ttLib.getTableClass("CFF2")), "cff"
+)
+def visit(visitor, obj, attr, cff):
+    cff.desubroutinize()
+    topDict = cff.topDictIndex[0]
+    varStore = getattr(topDict, "VarStore", None)
+    getNumRegions = varStore.getNumRegions if varStore is not None else None
+    privates = set()
+    for fontname in cff.keys():
+        font = cff[fontname]
+        cs = font.CharStrings
+        for g in font.charset:
+            c, _ = cs.getItemAndSelector(g)
+            privates.add(c.private)
+
+            commands = cffSpecializer.programToCommands(
+                c.program, getNumRegions=getNumRegions
+            )
+            for op, args in commands:
+                _cff_scale(visitor, args)
+            c.program[:] = cffSpecializer.commandsToProgram(commands)
+
+        # Annoying business of scaling numbers that do not matter whatsoever
+
+        for attr in (
+            "UnderlinePosition",
+            "UnderlineThickness",
+            "FontBBox",
+            "StrokeWidth",
+        ):
+            value = getattr(topDict, attr, None)
+            if value is None:
+                continue
+            if isinstance(value, list):
+                _cff_scale(visitor, value)
+            else:
+                setattr(topDict, attr, visitor.scale(value))
+
+        for i in range(6):
+            topDict.FontMatrix[i] /= visitor.scaleFactor
+
+        for private in privates:
+            for attr in (
+                "BlueValues",
+                "OtherBlues",
+                "FamilyBlues",
+                "FamilyOtherBlues",
+                # "BlueScale",
+                # "BlueShift",
+                # "BlueFuzz",
+                "StdHW",
+                "StdVW",
+                "StemSnapH",
+                "StemSnapV",
+                "defaultWidthX",
+                "nominalWidthX",
+            ):
+                value = getattr(private, attr, None)
+                if value is None:
+                    continue
+                if isinstance(value, list):
+                    _cff_scale(visitor, value)
+                else:
+                    setattr(private, attr, visitor.scale(value))
+
+
+# ItemVariationStore
+
+
+@ScalerVisitor.register(otTables.VarData)
+def visit(visitor, varData):
+    for item in varData.Item:
+        for i, v in enumerate(item):
+            item[i] = visitor.scale(v)
+
+
+# COLRv1
+
+
+def _setup_scale_paint(paint, scale):
+    if -2 <= scale <= 2 - (1 >> 14):
+        paint.Format = otTables.PaintFormat.PaintScaleUniform
+        paint.scale = scale
+        return
+
+    transform = otTables.Affine2x3()
+    transform.populateDefaults()
+    transform.xy = transform.yx = transform.dx = transform.dy = 0
+    transform.xx = transform.yy = scale
+
+    paint.Format = otTables.PaintFormat.PaintTransform
+    paint.Transform = transform
+
+
+@ScalerVisitor.register(otTables.BaseGlyphPaintRecord)
+def visit(visitor, record):
+    oldPaint = record.Paint
+
+    scale = otTables.Paint()
+    _setup_scale_paint(scale, visitor.scaleFactor)
+    scale.Paint = oldPaint
+
+    record.Paint = scale
+
+    return True
+
+
+@ScalerVisitor.register(otTables.Paint)
+def visit(visitor, paint):
+    if paint.Format != otTables.PaintFormat.PaintGlyph:
+        return True
+
+    newPaint = otTables.Paint()
+    newPaint.Format = paint.Format
+    newPaint.Paint = paint.Paint
+    newPaint.Glyph = paint.Glyph
+    del paint.Paint
+    del paint.Glyph
+
+    _setup_scale_paint(paint, 1 / visitor.scaleFactor)
+    paint.Paint = newPaint
+
+    visitor.visit(newPaint.Paint)
+
+    return False
+
+
+def scale_upem(font, new_upem):
+    """Change the units-per-EM of font to the new value."""
+    upem = font["head"].unitsPerEm
+    visitor = ScalerVisitor(new_upem / upem)
+    visitor.visit(font)
+
+
+def main(args=None):
+    """Change the units-per-EM of fonts"""
+
+    if args is None:
+        import sys
+
+        args = sys.argv[1:]
+
+    from fontTools.ttLib import TTFont
+    from fontTools.misc.cliTools import makeOutputFileName
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        "fonttools ttLib.scaleUpem", description="Change the units-per-EM of fonts"
+    )
+    parser.add_argument("font", metavar="font", help="Font file.")
+    parser.add_argument(
+        "new_upem", metavar="new-upem", help="New units-per-EM integer value."
+    )
+    parser.add_argument(
+        "--output-file", metavar="path", default=None, help="Output file."
+    )
+
+    options = parser.parse_args(args)
+
+    font = TTFont(options.font)
+    new_upem = int(options.new_upem)
+    output_file = (
+        options.output_file
+        if options.output_file is not None
+        else makeOutputFileName(options.font, overWrite=True, suffix="-scaled")
+    )
+
+    scale_upem(font, new_upem)
+
+    print("Writing %s" % output_file)
+    font.save(output_file)
+
+
+if __name__ == "__main__":
+    import sys
+
+    sys.exit(main())
diff --git a/Lib/fontTools/ttLib/tables/E_B_D_T_.py b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
index 0bd2ab9..ae71651 100644
--- a/Lib/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
@@ -398,12 +398,17 @@
 		# Allow lazy decompile.
 		if attr[:2] == '__':
 			raise AttributeError(attr)
-		if not hasattr(self, "data"):
+		if attr == "data":
 			raise AttributeError(attr)
 		self.decompile()
 		del self.data
 		return getattr(self, attr)
 
+	def ensureDecompiled(self, recurse=False):
+		if hasattr(self, "data"):
+			self.decompile()
+			del self.data
+
 	# Not a fan of this but it is needed for safer safety checking.
 	def getFormat(self):
 		return safeEval(self.__class__.__name__[len(_bitmapGlyphSubclassPrefix):])
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index cfdbca7..bb3d214 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -338,11 +338,15 @@
 		# Allow lazy decompile.
 		if attr[:2] == '__':
 			raise AttributeError(attr)
-		if not hasattr(self, "data"):
+		if attr == "data":
 			raise AttributeError(attr)
 		self.decompile()
 		return getattr(self, attr)
 
+	def ensureDecompiled(self, recurse=False):
+		if hasattr(self, "data"):
+			self.decompile()
+
 	# This method just takes care of the indexSubHeader. Implementing subclasses
 	# should call it to compile the indexSubHeader and then continue compiling
 	# the remainder of their unique format.
diff --git a/Lib/fontTools/ttLib/tables/S_V_G_.py b/Lib/fontTools/ttLib/tables/S_V_G_.py
index bc0e533..49e98d0 100644
--- a/Lib/fontTools/ttLib/tables/S_V_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_V_G_.py
@@ -17,9 +17,11 @@
 	</SVG>
 """
 
-from fontTools.misc.textTools import bytesjoin, strjoin, tobytes, tostr
+from fontTools.misc.textTools import bytesjoin, safeEval, strjoin, tobytes, tostr
 from fontTools.misc import sstruct
 from . import DefaultTable
+from collections.abc import Sequence
+from dataclasses import dataclass, astuple
 from io import BytesIO
 import struct
 import logging
@@ -75,15 +77,18 @@
 				start = entry.svgDocOffset + subTableStart
 				end = start + entry.svgDocLength
 				doc = data[start:end]
+				compressed = False
 				if doc.startswith(b"\x1f\x8b"):
 					import gzip
 					bytesIO = BytesIO(doc)
 					with gzip.GzipFile(None, "r", fileobj=bytesIO) as gunzipper:
 						doc = gunzipper.read()
-					self.compressed = True
 					del bytesIO
+					compressed = True
 				doc = tostr(doc, "utf_8")
-				self.docList.append( [doc, entry.startGlyphID, entry.endGlyphID] )
+				self.docList.append(
+					SVGDocument(doc, entry.startGlyphID, entry.endGlyphID, compressed)
+				)
 
 	def compile(self, ttFont):
 		version = 0
@@ -96,12 +101,18 @@
 		entryList.append(datum)
 		curOffset = len(datum) + doc_index_entry_format_0Size*numEntries
 		seenDocs = {}
-		for doc, startGlyphID, endGlyphID in self.docList:
-			docBytes = tobytes(doc, encoding="utf_8")
-			if getattr(self, "compressed", False) and not docBytes.startswith(b"\x1f\x8b"):
+		allCompressed = getattr(self, "compressed", False)
+		for i, doc in enumerate(self.docList):
+			if isinstance(doc, (list, tuple)):
+				doc = SVGDocument(*doc)
+				self.docList[i] = doc
+			docBytes = tobytes(doc.data, encoding="utf_8")
+			if (allCompressed or doc.compressed) and not docBytes.startswith(b"\x1f\x8b"):
 				import gzip
 				bytesIO = BytesIO()
-				with gzip.GzipFile(None, "w", fileobj=bytesIO) as gzipper:
+				# mtime=0 strips the useless timestamp and makes gzip output reproducible;
+				# equivalent to `gzip -n`
+				with gzip.GzipFile(None, "w", fileobj=bytesIO, mtime=0) as gzipper:
 					gzipper.write(docBytes)
 				gzipped = bytesIO.getvalue()
 				if len(gzipped) < len(docBytes):
@@ -115,7 +126,7 @@
 				curOffset += docLength
 				seenDocs[docBytes] = docOffset
 				docList.append(docBytes)
-			entry = struct.pack(">HHLL", startGlyphID, endGlyphID, docOffset, docLength)
+			entry = struct.pack(">HHLL", doc.startGlyphID, doc.endGlyphID, docOffset, docLength)
 			entryList.append(entry)
 		entryList.extend(docList)
 		svgDocData = bytesjoin(entryList)
@@ -127,10 +138,16 @@
 		return data
 
 	def toXML(self, writer, ttFont):
-		for doc, startGID, endGID in self.docList:
-			writer.begintag("svgDoc", startGlyphID=startGID, endGlyphID=endGID)
+		for i, doc in enumerate(self.docList):
+			if isinstance(doc, (list, tuple)):
+				doc = SVGDocument(*doc)
+				self.docList[i] = doc
+			attrs = {"startGlyphID": doc.startGlyphID, "endGlyphID": doc.endGlyphID}
+			if doc.compressed:
+				attrs["compressed"] = 1
+			writer.begintag("svgDoc", **attrs)
 			writer.newline()
-			writer.writecdata(doc)
+			writer.writecdata(doc.data)
 			writer.newline()
 			writer.endtag("svgDoc")
 			writer.newline()
@@ -143,7 +160,8 @@
 			doc = doc.strip()
 			startGID = int(attrs["startGlyphID"])
 			endGID = int(attrs["endGlyphID"])
-			self.docList.append( [doc, startGID, endGID] )
+			compressed = bool(safeEval(attrs.get("compressed", "0")))
+			self.docList.append(SVGDocument(doc, startGID, endGID, compressed))
 		else:
 			log.warning("Unknown %s %s", name, content)
 
@@ -157,3 +175,23 @@
 
 	def __repr__(self):
 		return "startGlyphID: %s, endGlyphID: %s, svgDocOffset: %s, svgDocLength: %s" % (self.startGlyphID, self.endGlyphID, self.svgDocOffset, self.svgDocLength)
+
+
+@dataclass
+class SVGDocument(Sequence):
+	data: str
+	startGlyphID: int
+	endGlyphID: int
+	compressed: bool = False
+
+	# Previously, the SVG table's docList attribute contained a lists of 3 items:
+	# [doc, startGlyphID, endGlyphID]; later, we added a `compressed` attribute.
+	# For backward compatibility with code that depends of them being sequences of
+	# fixed length=3, we subclass the Sequence abstract base class and pretend only
+	# the first three items are present. 'compressed' is only accessible via named
+	# attribute lookup like regular dataclasses: i.e. `doc.compressed`, not `doc[3]`
+	def __getitem__(self, index):
+		return astuple(self)[:3][index]
+
+	def __len__(self):
+		return 3
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 9bd59a6..ef2b575 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -164,7 +164,9 @@
 		if ttFont.lazy is False:  # Be lazy for None and True
 			self.ensureDecompiled()
 
-	def ensureDecompiled(self):
+	def ensureDecompiled(self, recurse=False):
+		# The recurse argument is unused, but part of the signature of
+		# ensureDecompiled across the library.
 		for st in self.tables:
 			st.ensureDecompiled()
 
@@ -241,7 +243,9 @@
 		self.platEncID = None   #: The encoding ID of this subtable (interpretation depends on ``platformID``)
 		self.language = None    #: The language ID of this subtable (Macintosh platform only)
 
-	def ensureDecompiled(self):
+	def ensureDecompiled(self, recurse=False):
+		# The recurse argument is unused, but part of the signature of
+		# ensureDecompiled across the library.
 		if self.data is None:
 			return
 		self.decompile(None, None) # use saved data.
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 14c4519..745ef72 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -112,7 +112,9 @@
 		if ttFont.lazy is False: # Be lazy for None and True
 			self.ensureDecompiled()
 
-	def ensureDecompiled(self):
+	def ensureDecompiled(self, recurse=False):
+		# The recurse argument is unused, but part of the signature of
+		# ensureDecompiled across the library.
 		for glyph in self.glyphs.values():
 			glyph.expand(self)
 
diff --git a/Lib/fontTools/ttLib/tables/_g_v_a_r.py b/Lib/fontTools/ttLib/tables/_g_v_a_r.py
index bc283cf..dd198f4 100644
--- a/Lib/fontTools/ttLib/tables/_g_v_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_g_v_a_r.py
@@ -1,3 +1,4 @@
+from functools import partial
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
@@ -36,6 +37,46 @@
 
 GVAR_HEADER_SIZE = sstruct.calcsize(GVAR_HEADER_FORMAT)
 
+class _lazy_dict(dict):
+
+    def get(self, k, *args):
+        v = super().get(k, *args)
+        if callable(v):
+            v = v()
+            self[k] = v
+        return v
+
+    def __getitem__(self, k):
+        v = super().__getitem__(k)
+        if callable(v):
+            v = v()
+            self[k] = v
+        return v
+
+    def items(self):
+        if not hasattr(self, '_loaded'):
+            self._load()
+        return super().items()
+
+    def values(self):
+        if not hasattr(self, '_loaded'):
+            self._load()
+        return super().values()
+
+    def __eq__(self, other):
+        if not hasattr(self, '_loaded'):
+            self._load()
+        return super().__eq__(other)
+
+    def __neq__(self, other):
+        if not hasattr(self, '_loaded'):
+            self._load()
+        return super().__neq__(other)
+
+    def _load(self):
+        for k in self:
+            self[k]
+        self._loaded = True
 
 class table__g_v_a_r(DefaultTable.DefaultTable):
 	dependencies = ["fvar", "glyf"]
@@ -97,23 +138,19 @@
 		offsets = self.decompileOffsets_(data[GVAR_HEADER_SIZE:], tableFormat=(self.flags & 1), glyphCount=self.glyphCount)
 		sharedCoords = tv.decompileSharedTuples(
 			axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples)
-		self.variations = {}
+		self.variations = _lazy_dict()
 		offsetToData = self.offsetToGlyphVariationData
 		glyf = ttFont['glyf']
-		for i in range(self.glyphCount):
-			glyphName = glyphs[i]
+
+		def decompileVarGlyph(glyphName, gid):
 			glyph = glyf[glyphName]
 			numPointsInGlyph = self.getNumPoints_(glyph)
-			gvarData = data[offsetToData + offsets[i] : offsetToData + offsets[i + 1]]
-			try:
-				self.variations[glyphName] = decompileGlyph_(
-					numPointsInGlyph, sharedCoords, axisTags, gvarData)
-			except Exception:
-				log.error(
-					"Failed to decompile deltas for glyph '%s' (%d points)",
-					glyphName, numPointsInGlyph,
-				)
-				raise
+			gvarData = data[offsetToData + offsets[gid] : offsetToData + offsets[gid + 1]]
+			return decompileGlyph_(numPointsInGlyph, sharedCoords, axisTags, gvarData)
+
+		for gid in range(self.glyphCount):
+			glyphName = glyphs[gid]
+			self.variations[glyphName] = partial(decompileVarGlyph, glyphName, gid)
 
 	@staticmethod
 	def decompileOffsets_(data, tableFormat, glyphCount):
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
index f3f714b..bcad2ce 100644
--- a/Lib/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -161,9 +161,11 @@
 				len(data) - 6 * nPairs)
 
 	def compile(self, ttFont):
-		nPairs = len(self.kernTable)
+		nPairs = min(len(self.kernTable), 0xFFFF)
 		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
 		searchRange &= 0xFFFF
+		entrySelector = min(entrySelector, 0xFFFF)
+		rangeShift = min(rangeShift, 0xFFFF)
 		data = struct.pack(
 			">HHHH", nPairs, searchRange, entrySelector, rangeShift)
 
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index d30892f..1bd3198 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -1,18 +1,23 @@
 from fontTools.config import OPTIONS
 from fontTools.misc.textTools import Tag, bytesjoin
 from .DefaultTable import DefaultTable
+from enum import IntEnum
 import sys
 import array
 import struct
 import logging
-from typing import Iterator, NamedTuple, Optional
+from functools import lru_cache
+from typing import Iterator, NamedTuple, Optional, Tuple
 
 log = logging.getLogger(__name__)
 
 have_uharfbuzz = False
 try:
 	import uharfbuzz as hb
-	have_uharfbuzz = True
+	# repack method added in uharfbuzz >= 0.23; if uharfbuzz *can* be
+	# imported but repack method is missing, behave as if uharfbuzz
+	# is not available (fallback to the slower Python implementation)
+	have_uharfbuzz = callable(getattr(hb, "repack", None))
 except ImportError:
 	pass
 
@@ -36,6 +41,25 @@
 	def __str__(self):
 		return repr(self.value)
 
+class RepackerState(IntEnum):
+	# Repacking control flow is implemnted using a state machine. The state machine table:
+	#
+	# State       | Packing Success | Packing Failed | Exception Raised |
+	# ------------+-----------------+----------------+------------------+
+	# PURE_FT     | Return result   | PURE_FT        | Return failure   |
+	# HB_FT       | Return result   | HB_FT          | FT_FALLBACK      |
+	# FT_FALLBACK | HB_FT           | FT_FALLBACK    | Return failure   |
+
+	# Pack only with fontTools, don't allow sharing between extensions.
+	PURE_FT = 1
+
+	# Attempt to pack with harfbuzz (allowing sharing between extensions)
+	# use fontTools to attempt overflow resolution.
+	HB_FT = 2
+
+	# Fallback if HB/FT packing gets stuck. Pack only with fontTools, don't allow sharing between
+	# extensions.
+	FT_FALLBACK = 3
 
 class BaseTTXConverter(DefaultTable):
 
@@ -96,62 +120,98 @@
 						self.tableTag,
 					)
 
+		if (use_hb_repack in (None, True)
+				and have_uharfbuzz
+				and self.tableTag in ("GSUB", "GPOS")):
+			state = RepackerState.HB_FT
+		else:
+			state = RepackerState.PURE_FT
+
 		hb_first_error_logged = False
+		lastOverflowRecord = None
 		while True:
 			try:
 				writer = OTTableWriter(tableTag=self.tableTag)
 				self.table.compile(writer, font)
-				if (
-					use_hb_repack in (None, True)
-					and have_uharfbuzz
-					and self.tableTag in ("GSUB", "GPOS")
-				):
-					try:
-						log.debug("serializing '%s' with hb.repack", self.tableTag)
-						return writer.getAllDataUsingHarfbuzz()
-					except (ValueError, MemoryError, hb.RepackerError) as e:
-						# Only log hb repacker errors the first time they occur in
-						# the offset-overflow resolution loop, they are just noisy.
-						# Maybe we can revisit this if/when uharfbuzz actually gives
-						# us more info as to why hb.repack failed...
-						if not hb_first_error_logged:
-							error_msg = f"{type(e).__name__}"
-							if str(e) != "":
-								error_msg += f": {e}"
-							log.warning(
-								"hb.repack failed to serialize '%s', reverting to "
-								"pure-python serializer; the error message was: %s",
-								self.tableTag,
-								error_msg,
-							)
-							hb_first_error_logged = True
-						return writer.getAllData(remove_duplicate=False)
-				return writer.getAllData()
+				if state == RepackerState.HB_FT:
+					return self.tryPackingHarfbuzz(writer, hb_first_error_logged)
+				elif state == RepackerState.PURE_FT:
+					return self.tryPackingFontTools(writer)
+				elif state == RepackerState.FT_FALLBACK:
+					# Run packing with FontTools only, but don't return the result as it will
+					# not be optimally packed. Once a successful packing has been found, state is
+					# changed back to harfbuzz packing to produce the final, optimal, packing.
+					self.tryPackingFontTools(writer)
+					log.debug("Re-enabling sharing between extensions and switching back to "
+										"harfbuzz+fontTools packing.")
+					state = RepackerState.HB_FT
 
 			except OTLOffsetOverflowError as e:
+				hb_first_error_logged = True
+				ok = self.tryResolveOverflow(font, e, lastOverflowRecord)
+				lastOverflowRecord = e.value
 
-				if overflowRecord == e.value:
-					raise # Oh well...
+				if ok:
+					continue
 
-				overflowRecord = e.value
-				log.info("Attempting to fix OTLOffsetOverflowError %s", e)
-				lastItem = overflowRecord
-
-				ok = 0
-				if overflowRecord.itemName is None:
-					from .otTables import fixLookupOverFlows
-					ok = fixLookupOverFlows(font, overflowRecord)
+				if state is RepackerState.HB_FT:
+					log.debug("Harfbuzz packing out of resolutions, disabling sharing between extensions and "
+									 "switching to fontTools only packing.")
+					state = RepackerState.FT_FALLBACK
 				else:
-					from .otTables import fixSubTableOverFlows
-					ok = fixSubTableOverFlows(font, overflowRecord)
-				if not ok:
-					# Try upgrading lookup to Extension and hope
-					# that cross-lookup sharing not happening would
-					# fix overflow...
-					from .otTables import fixLookupOverFlows
-					ok = fixLookupOverFlows(font, overflowRecord)
-					if not ok:
-						raise
+					raise
+
+	def tryPackingHarfbuzz(self, writer, hb_first_error_logged):
+		try:
+			log.debug("serializing '%s' with hb.repack", self.tableTag)
+			return writer.getAllDataUsingHarfbuzz(self.tableTag)
+		except (ValueError, MemoryError, hb.RepackerError) as e:
+			# Only log hb repacker errors the first time they occur in
+			# the offset-overflow resolution loop, they are just noisy.
+			# Maybe we can revisit this if/when uharfbuzz actually gives
+			# us more info as to why hb.repack failed...
+			if not hb_first_error_logged:
+				error_msg = f"{type(e).__name__}"
+				if str(e) != "":
+					error_msg += f": {e}"
+				log.warning(
+						"hb.repack failed to serialize '%s', attempting fonttools resolutions "
+						"; the error message was: %s",
+						self.tableTag,
+						error_msg,
+				)
+				hb_first_error_logged = True
+			return writer.getAllData(remove_duplicate=False)
+
+
+	def tryPackingFontTools(self, writer):
+		return writer.getAllData()
+
+
+	def tryResolveOverflow(self, font, e, lastOverflowRecord):
+		ok = 0
+		if lastOverflowRecord == e.value:
+			# Oh well...
+			return ok
+
+		overflowRecord = e.value
+		log.info("Attempting to fix OTLOffsetOverflowError %s", e)
+
+		if overflowRecord.itemName is None:
+			from .otTables import fixLookupOverFlows
+			ok = fixLookupOverFlows(font, overflowRecord)
+		else:
+			from .otTables import fixSubTableOverFlows
+			ok = fixSubTableOverFlows(font, overflowRecord)
+
+		if ok:
+			return ok
+
+		# Try upgrading lookup to Extension and hope
+		# that cross-lookup sharing not happening would
+		# fix overflow...
+		from .otTables import fixLookupOverFlows
+		return fixLookupOverFlows(font, overflowRecord)
 
 	def toXML(self, writer, font):
 		self.table.toXML2(writer, font)
@@ -164,8 +224,8 @@
 		self.table.fromXML(name, attrs, content, font)
 		self.table.populateDefaults()
 
-	def ensureDecompiled(self):
-		self.table.ensureDecompiled(recurse=True)
+	def ensureDecompiled(self, recurse=True):
+		self.table.ensureDecompiled(recurse=recurse)
 
 
 # https://github.com/fonttools/fonttools/pull/2285#issuecomment-834652928
@@ -380,7 +440,7 @@
 			return NotImplemented
 		return self.offsetSize == other.offsetSize and self.items == other.items
 
-	def _doneWriting(self, internedTables):
+	def _doneWriting(self, internedTables, shareExtension=False):
 		# Convert CountData references to data string items
 		# collapse duplicate table references to a unique entry
 		# "tables" are OTTableWriter objects.
@@ -396,7 +456,7 @@
 		# See: https://github.com/fonttools/fonttools/issues/518
 		dontShare = hasattr(self, 'DontShare')
 
-		if isExtension:
+		if isExtension and not shareExtension:
 			internedTables = {}
 
 		items = self.items
@@ -405,7 +465,7 @@
 			if hasattr(item, "getCountData"):
 				items[i] = item.getCountData()
 			elif hasattr(item, "getData"):
-				item._doneWriting(internedTables)
+				item._doneWriting(internedTables, shareExtension=shareExtension)
 				# At this point, all subwriters are hashable based on their items.
 				# (See hash and comparison magic methods above.) So the ``setdefault``
 				# call here will return the first writer object we've seen with
@@ -510,7 +570,7 @@
 				child_idx = item_idx = item._gatherGraphForHarfbuzz(tables, obj_list, done, item_idx, virtual_edges)
 			else:
 				child_idx = done[id(item)]
-                        
+
 			real_edge = (pos, item.offsetSize, child_idx)
 			real_links.append(real_edge)
 			offset_pos += item.offsetSize
@@ -524,7 +584,7 @@
 
 		return item_idx
 
-	def getAllDataUsingHarfbuzz(self):
+	def getAllDataUsingHarfbuzz(self, tableTag):
 		"""The Whole table is represented as a Graph.
                 Assemble graph data and call Harfbuzz repacker to pack the table.
                 Harfbuzz repacker is faster and retain as much sub-table sharing as possible, see also:
@@ -533,7 +593,7 @@
                 https://github.com/harfbuzz/uharfbuzz/blob/main/src/uharfbuzz/_harfbuzz.pyx#L1149
                 """
 		internedTables = {}
-		self._doneWriting(internedTables)
+		self._doneWriting(internedTables, shareExtension=True)
 		tables = []
 		obj_list = []
 		done = {}
@@ -552,7 +612,10 @@
 			tableData = table.getDataForHarfbuzz()
 			data.append(tableData)
 
-		return hb.repack(data, obj_list)
+		if hasattr(hb, "repack_with_tag"):
+			return hb.repack_with_tag(str(tableTag), data, obj_list)
+		else:
+			return hb.repack(data, obj_list)
 
 	def getAllData(self, remove_duplicate=True):
 		"""Assemble all data, including all subtables."""
@@ -808,6 +871,9 @@
 				#elif not conv.isCount:
 				#	# Warn?
 				#	pass
+				if hasattr(conv, "DEFAULT"):
+					# OptionalValue converters (e.g. VarIndex)
+					setattr(self, conv.name, conv.DEFAULT)
 
 	def decompile(self, reader, font):
 		self.readFormat(reader)
@@ -1042,6 +1108,10 @@
 					if isinstance(v, BaseTable)
 				)
 
+	# instance (not @class)method for consistency with FormatSwitchingBaseTable
+	def getVariableAttrs(self):
+		return getVariableAttrs(self.__class__)
+
 
 class FormatSwitchingBaseTable(BaseTable):
 
@@ -1076,6 +1146,9 @@
 	def toXML(self, xmlWriter, font, attrs=None, name=None):
 		BaseTable.toXML(self, xmlWriter, font, attrs, name)
 
+	def getVariableAttrs(self):
+		return getVariableAttrs(self.__class__, self.Format)
+
 
 class UInt8FormatSwitchingBaseTable(FormatSwitchingBaseTable):
 	def readFormat(self, reader):
@@ -1097,6 +1170,33 @@
 		raise TypeError(f"Unsupported format type: {formatType!r}")
 
 
+# memoize since these are parsed from otData.py, thus stay constant
+@lru_cache()
+def getVariableAttrs(cls: BaseTable, fmt: Optional[int] = None) -> Tuple[str]:
+	"""Return sequence of variable table field names (can be empty).
+
+	Attributes are deemed "variable" when their otData.py's description contain
+	'VarIndexBase + {offset}', e.g. COLRv1 PaintVar* tables.
+	"""
+	if not issubclass(cls, BaseTable):
+		raise TypeError(cls)
+	if issubclass(cls, FormatSwitchingBaseTable):
+		if fmt is None:
+			raise TypeError(f"'fmt' is required for format-switching {cls.__name__}")
+		converters = cls.convertersByName[fmt]
+	else:
+		converters = cls.convertersByName
+	# assume if no 'VarIndexBase' field is present, table has no variable fields
+	if "VarIndexBase" not in converters:
+		return ()
+	varAttrs = {}
+	for name, conv in converters.items():
+		offset = conv.getVarIndexOffset()
+		if offset is not None:
+			varAttrs[name] = offset
+	return tuple(sorted(varAttrs, key=varAttrs.__getitem__))
+
+
 #
 # Support for ValueRecords
 #
diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py
index 44fcd0a..b08f1f1 100644
--- a/Lib/fontTools/ttLib/tables/otConverters.py
+++ b/Lib/fontTools/ttLib/tables/otConverters.py
@@ -15,10 +15,13 @@
                        ContextualMorphAction, LigatureMorphAction,
                        InsertionMorphAction, MorxSubtable,
                        ExtendMode as _ExtendMode,
-                       CompositeMode as _CompositeMode)
+                       CompositeMode as _CompositeMode,
+                       NO_VARIATION_INDEX)
 from itertools import zip_longest
 from functools import partial
+import re
 import struct
+from typing import Optional
 import logging
 
 
@@ -60,7 +63,7 @@
 			else:
 				converterClass = eval(tp, tableNamespace, converterMapping)
 
-		conv = converterClass(name, repeat, aux)
+		conv = converterClass(name, repeat, aux, description=descr)
 
 		if conv.tableClass:
 			# A "template" such as OffsetTo(AType) knowss the table class already
@@ -136,7 +139,7 @@
 	"""Base class for converter objects. Apart from the constructor, this
 	is an abstract class."""
 
-	def __init__(self, name, repeat, aux, tableClass=None):
+	def __init__(self, name, repeat, aux, tableClass=None, *, description=""):
 		self.name = name
 		self.repeat = repeat
 		self.aux = aux
@@ -159,6 +162,7 @@
 			"BaseGlyphRecordCount",
 			"LayerRecordCount",
 		]
+		self.description = description
 
 	def readArray(self, reader, font, tableDict, count):
 		"""Read an array of values from the reader."""
@@ -211,6 +215,15 @@
 		"""Write a value to XML."""
 		raise NotImplementedError(self)
 
+	varIndexBasePlusOffsetRE = re.compile(r"VarIndexBase\s*\+\s*(\d+)")
+
+	def getVarIndexOffset(self) -> Optional[int]:
+		"""If description has `VarIndexBase + {offset}`, return the offset else None."""
+		m = self.varIndexBasePlusOffsetRE.search(self.description)
+		if not m:
+			return None
+		return int(m.group(1))
+
 
 class SimpleValue(BaseConverter):
 	@staticmethod
@@ -270,7 +283,7 @@
 		return "0x%08X" % value
 
 class VarIndex(OptionalValue, ULong):
-	DEFAULT = 0xFFFFFFFF
+	DEFAULT = NO_VARIATION_INDEX
 
 class Short(IntValue):
 	staticSize = 2
@@ -402,40 +415,51 @@
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUShort(round(value * 10))
 
-class Fixed(FloatValue):
-	staticSize = 4
+class BaseFixedValue(FloatValue):
+	staticSize = NotImplemented
+	precisionBits = NotImplemented
+	readerMethod = NotImplemented
+	writerMethod = NotImplemented
 	def read(self, reader, font, tableDict):
-		return  fi2fl(reader.readLong(), 16)
+		return  self.fromInt(getattr(reader, self.readerMethod)())
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		writer.writeLong(fl2fi(value, 16))
-	@staticmethod
-	def fromString(value):
-		return str2fl(value, 16)
-	@staticmethod
-	def toString(value):
-		return fl2str(value, 16)
+		getattr(writer, self.writerMethod)(self.toInt(value))
+	@classmethod
+	def fromInt(cls, value):
+		return fi2fl(value, cls.precisionBits)
+	@classmethod
+	def toInt(cls, value):
+		return fl2fi(value, cls.precisionBits)
+	@classmethod
+	def fromString(cls, value):
+		return str2fl(value, cls.precisionBits)
+	@classmethod
+	def toString(cls, value):
+		return fl2str(value, cls.precisionBits)
 
-class F2Dot14(FloatValue):
+class Fixed(BaseFixedValue):
+	staticSize = 4
+	precisionBits = 16
+	readerMethod = "readLong"
+	writerMethod = "writeLong"
+
+class F2Dot14(BaseFixedValue):
 	staticSize = 2
-	def read(self, reader, font, tableDict):
-		return  fi2fl(reader.readShort(), 14)
-	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		writer.writeShort(fl2fi(value, 14))
-	@staticmethod
-	def fromString(value):
-		return str2fl(value, 14)
-	@staticmethod
-	def toString(value):
-		return fl2str(value, 14)
+	precisionBits = 14
+	readerMethod = "readShort"
+	writerMethod = "writeShort"
 
 class Angle(F2Dot14):
 	# angles are specified in degrees, and encoded as F2Dot14 fractions of half
 	# circle: e.g. 1.0 => 180, -0.5 => -90, -2.0 => -360, etc.
+	bias = 0.0
 	factor = 1.0/(1<<14) * 180  # 0.010986328125
-	def read(self, reader, font, tableDict):
-		return super().read(reader, font, tableDict) * 180
-	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		super().write(writer, font, tableDict, value / 180, repeatIndex=repeatIndex)
+	@classmethod
+	def fromInt(cls, value):
+		return (super().fromInt(value) + cls.bias) * 180
+	@classmethod
+	def toInt(cls, value):
+		return super().toInt((value / 180) - cls.bias)
 	@classmethod
 	def fromString(cls, value):
 		# quantize to nearest multiples of minimum fixed-precision angle
@@ -444,6 +468,11 @@
 	def toString(cls, value):
 		return nearestMultipleShortestRepr(value, cls.factor)
 
+class BiasedAngle(Angle):
+	# A bias of 1.0 is used in the representation of start and end angles
+	# of COLRv1 PaintSweepGradients to allow for encoding +360deg
+	bias = 1.0
+
 class Version(SimpleValue):
 	staticSize = 4
 	def read(self, reader, font, tableDict):
@@ -686,8 +715,10 @@
 
 class ValueFormat(IntValue):
 	staticSize = 2
-	def __init__(self, name, repeat, aux, tableClass=None):
-		BaseConverter.__init__(self, name, repeat, aux, tableClass)
+	def __init__(self, name, repeat, aux, tableClass=None, *, description=""):
+		BaseConverter.__init__(
+			self, name, repeat, aux, tableClass, description=description
+		)
 		self.which = "ValueFormat" + ("2" if name[-1] == "2" else "1")
 	def read(self, reader, font, tableDict):
 		format = reader.readUShort()
@@ -720,8 +751,10 @@
 class AATLookup(BaseConverter):
 	BIN_SEARCH_HEADER_SIZE = 10
 
-	def __init__(self, name, repeat, aux, tableClass):
-		BaseConverter.__init__(self, name, repeat, aux, tableClass)
+	def __init__(self, name, repeat, aux, tableClass, *, description=""):
+		BaseConverter.__init__(
+			self, name, repeat, aux, tableClass, description=description
+		)
 		if issubclass(self.tableClass, SimpleValue):
 			self.converter = self.tableClass(name='Value', repeat=None, aux=None)
 		else:
@@ -1019,8 +1052,10 @@
 		val: key for key, val in _PROCESSING_ORDERS.items()
 	}
 
-	def __init__(self, name, repeat, aux):
-		BaseConverter.__init__(self, name, repeat, aux)
+	def __init__(self, name, repeat, aux, tableClass=None, *, description=""):
+		BaseConverter.__init__(
+			self, name, repeat, aux, tableClass, description=description
+		)
 
 	def _setTextDirectionFromCoverageFlags(self, flags, subtable):
 		if (flags & 0x20) != 0:
@@ -1140,8 +1175,10 @@
 # https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html#ExtendedStateHeader
 # TODO: Untangle the implementation of the various lookup-specific formats.
 class STXHeader(BaseConverter):
-	def __init__(self, name, repeat, aux, tableClass):
-		BaseConverter.__init__(self, name, repeat, aux, tableClass)
+	def __init__(self, name, repeat, aux, tableClass, *, description=""):
+		BaseConverter.__init__(
+			self, name, repeat, aux, tableClass, description=description
+		)
 		assert issubclass(self.tableClass, AATAction)
 		self.classLookup = AATLookup("GlyphClasses", None, None, UShort)
 		if issubclass(self.tableClass, ContextualMorphAction):
@@ -1742,6 +1779,7 @@
 	"Fixed":	Fixed,
 	"F2Dot14":	F2Dot14,
 	"Angle":	Angle,
+	"BiasedAngle":	BiasedAngle,
 	"struct":	Struct,
 	"Offset":	Table,
 	"LOffset":	LTable,
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
index dd4033e..2e65869 100755
--- a/Lib/fontTools/ttLib/tables/otData.py
+++ b/Lib/fontTools/ttLib/tables/otData.py
@@ -1623,10 +1623,10 @@
 
 	('ClipBoxFormat2', [
 		('uint8', 'Format', None, None, 'Format for variable ClipBox: set to 2.'),
-		('int16', 'xMin', None, None, 'Minimum x of clip box.'),
-		('int16', 'yMin', None, None, 'Minimum y of clip box.'),
-		('int16', 'xMax', None, None, 'Maximum x of clip box.'),
-		('int16', 'yMax', None, None, 'Maximum y of clip box.'),
+		('int16', 'xMin', None, None, 'Minimum x of clip box. VarIndexBase + 0.'),
+		('int16', 'yMin', None, None, 'Minimum y of clip box. VarIndexBase + 1.'),
+		('int16', 'xMax', None, None, 'Maximum x of clip box. VarIndexBase + 2.'),
+		('int16', 'yMax', None, None, 'Maximum y of clip box. VarIndexBase + 3.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1648,12 +1648,12 @@
 		('Fixed', 'dy', None, None, 'Translation in y direction'),
 	]),
 	('VarAffine2x3', [
-		('Fixed', 'xx', None, None, 'x-part of x basis vector'),
-		('Fixed', 'yx', None, None, 'y-part of x basis vector'),
-		('Fixed', 'xy', None, None, 'x-part of y basis vector'),
-		('Fixed', 'yy', None, None, 'y-part of y basis vector'),
-		('Fixed', 'dx', None, None, 'Translation in x direction'),
-		('Fixed', 'dy', None, None, 'Translation in y direction'),
+		('Fixed', 'xx', None, None, 'x-part of x basis vector. VarIndexBase + 0.'),
+		('Fixed', 'yx', None, None, 'y-part of x basis vector. VarIndexBase + 1.'),
+		('Fixed', 'xy', None, None, 'x-part of y basis vector. VarIndexBase + 2.'),
+		('Fixed', 'yy', None, None, 'y-part of y basis vector. VarIndexBase + 3.'),
+		('Fixed', 'dx', None, None, 'Translation in x direction. VarIndexBase + 4.'),
+		('Fixed', 'dy', None, None, 'Translation in y direction. VarIndexBase + 5.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1663,9 +1663,9 @@
 		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'),
 	]),
 	('VarColorStop', [
-		('F2Dot14', 'StopOffset', None, None, 'VarIndexBase + 0'),
+		('F2Dot14', 'StopOffset', None, None, 'VarIndexBase + 0.'),
 		('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'),
-		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 1'),
+		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 1.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1697,7 +1697,7 @@
 	('PaintFormat3', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 3'),
 		('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'),
-		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 0'),
+		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 0.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1716,12 +1716,12 @@
 	('PaintFormat5', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 5'),
 		('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarLinearGradient table) to VarColorLine subtable.'),
-		('int16', 'x0', None, None, ''),
-		('int16', 'y0', None, None, ''),
-		('int16', 'x1', None, None, ''),
-		('int16', 'y1', None, None, ''),
-		('int16', 'x2', None, None, ''),
-		('int16', 'y2', None, None, ''),
+		('int16', 'x0', None, None, 'VarIndexBase + 0.'),
+		('int16', 'y0', None, None, 'VarIndexBase + 1.'),
+		('int16', 'x1', None, None, 'VarIndexBase + 2.'),
+		('int16', 'y1', None, None, 'VarIndexBase + 3.'),
+		('int16', 'x2', None, None, 'VarIndexBase + 4.'),
+		('int16', 'y2', None, None, 'VarIndexBase + 5.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1740,12 +1740,12 @@
 	('PaintFormat7', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 7'),
 		('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarRadialGradient table) to VarColorLine subtable.'),
-		('int16', 'x0', None, None, ''),
-		('int16', 'y0', None, None, ''),
-		('uint16', 'r0', None, None, ''),
-		('int16', 'x1', None, None, ''),
-		('int16', 'y1', None, None, ''),
-		('uint16', 'r1', None, None, ''),
+		('int16', 'x0', None, None, 'VarIndexBase + 0.'),
+		('int16', 'y0', None, None, 'VarIndexBase + 1.'),
+		('uint16', 'r0', None, None, 'VarIndexBase + 2.'),
+		('int16', 'x1', None, None, 'VarIndexBase + 3.'),
+		('int16', 'y1', None, None, 'VarIndexBase + 4.'),
+		('uint16', 'r1', None, None, 'VarIndexBase + 5.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1755,17 +1755,17 @@
 		('Offset24', 'ColorLine', None, None, 'Offset (from beginning of PaintSweepGradient table) to ColorLine subtable.'),
 		('int16', 'centerX', None, None, 'Center x coordinate.'),
 		('int16', 'centerY', None, None, 'Center y coordinate.'),
-		('Angle', 'startAngle', None, None, 'Start of the angular range of the gradient.'),
-		('Angle', 'endAngle', None, None, 'End of the angular range of the gradient.'),
+		('BiasedAngle', 'startAngle', None, None, 'Start of the angular range of the gradient.'),
+		('BiasedAngle', 'endAngle', None, None, 'End of the angular range of the gradient.'),
 	]),
 	# PaintVarSweepGradient
 	('PaintFormat9', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 9'),
 		('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarSweepGradient table) to VarColorLine subtable.'),
-		('int16', 'centerX', None, None, 'Center x coordinate.'),
-		('int16', 'centerY', None, None, 'Center y coordinate.'),
-		('Angle', 'startAngle', None, None, 'Start of the angular range of the gradient.'),
-		('Angle', 'endAngle', None, None, 'End of the angular range of the gradient.'),
+		('int16', 'centerX', None, None, 'Center x coordinate. VarIndexBase + 0.'),
+		('int16', 'centerY', None, None, 'Center y coordinate. VarIndexBase + 1.'),
+		('BiasedAngle', 'startAngle', None, None, 'Start of the angular range of the gradient. VarIndexBase + 2.'),
+		('BiasedAngle', 'endAngle', None, None, 'End of the angular range of the gradient. VarIndexBase + 3.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1806,8 +1806,8 @@
 	('PaintFormat15', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 15'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarTranslate table) to Paint subtable.'),
-		('int16', 'dx', None, None, 'Translation in x direction.'),
-		('int16', 'dy', None, None, 'Translation in y direction.'),
+		('int16', 'dx', None, None, 'Translation in x direction. VarIndexBase + 0.'),
+		('int16', 'dy', None, None, 'Translation in y direction. VarIndexBase + 1.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1822,8 +1822,8 @@
 	('PaintFormat17', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 17'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScale table) to Paint subtable.'),
-		('F2Dot14', 'scaleX', None, None, ''),
-		('F2Dot14', 'scaleY', None, None, ''),
+		('F2Dot14', 'scaleX', None, None, 'VarIndexBase + 0.'),
+		('F2Dot14', 'scaleY', None, None, 'VarIndexBase + 1.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1840,10 +1840,10 @@
 	('PaintFormat19', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 19'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleAroundCenter table) to Paint subtable.'),
-		('F2Dot14', 'scaleX', None, None, ''),
-		('F2Dot14', 'scaleY', None, None, ''),
-		('int16', 'centerX', None, None, ''),
-		('int16', 'centerY', None, None, ''),
+		('F2Dot14', 'scaleX', None, None, 'VarIndexBase + 0.'),
+		('F2Dot14', 'scaleY', None, None, 'VarIndexBase + 1.'),
+		('int16', 'centerX', None, None, 'VarIndexBase + 2.'),
+		('int16', 'centerY', None, None, 'VarIndexBase + 3.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1857,7 +1857,7 @@
 	('PaintFormat21', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 21'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleUniform table) to Paint subtable.'),
-		('F2Dot14', 'scale', None, None, ''),
+		('F2Dot14', 'scale', None, None, 'VarIndexBase + 0.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1873,9 +1873,9 @@
 	('PaintFormat23', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 23'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleUniformAroundCenter table) to Paint subtable.'),
-		('F2Dot14', 'scale', None, None, ''),
-		('int16', 'centerX', None, None, ''),
-		('int16', 'centerY', None, None, ''),
+		('F2Dot14', 'scale', None, None, 'VarIndexBase + 0'),
+		('int16', 'centerX', None, None, 'VarIndexBase + 1'),
+		('int16', 'centerY', None, None, 'VarIndexBase + 2'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1889,7 +1889,7 @@
 	('PaintFormat25', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 25'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarRotate table) to Paint subtable.'),
-		('Angle', 'angle', None, None, ''),
+		('Angle', 'angle', None, None, 'VarIndexBase + 0.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1905,9 +1905,9 @@
 	('PaintFormat27', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 27'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarRotateAroundCenter table) to Paint subtable.'),
-		('Angle', 'angle', None, None, ''),
-		('int16', 'centerX', None, None, ''),
-		('int16', 'centerY', None, None, ''),
+		('Angle', 'angle', None, None, 'VarIndexBase + 0.'),
+		('int16', 'centerX', None, None, 'VarIndexBase + 1.'),
+		('int16', 'centerY', None, None, 'VarIndexBase + 2.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1922,8 +1922,8 @@
 	('PaintFormat29', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 29'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkew table) to Paint subtable.'),
-		('Angle', 'xSkewAngle', None, None, ''),
-		('Angle', 'ySkewAngle', None, None, ''),
+		('Angle', 'xSkewAngle', None, None, 'VarIndexBase + 0.'),
+		('Angle', 'ySkewAngle', None, None, 'VarIndexBase + 1.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
@@ -1940,10 +1940,10 @@
 	('PaintFormat31', [
 		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 31'),
 		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkewAroundCenter table) to Paint subtable.'),
-		('Angle', 'xSkewAngle', None, None, ''),
-		('Angle', 'ySkewAngle', None, None, ''),
-		('int16', 'centerX', None, None, ''),
-		('int16', 'centerY', None, None, ''),
+		('Angle', 'xSkewAngle', None, None, 'VarIndexBase + 0.'),
+		('Angle', 'ySkewAngle', None, None, 'VarIndexBase + 1.'),
+		('int16', 'centerX', None, None, 'VarIndexBase + 2.'),
+		('int16', 'centerY', None, None, 'VarIndexBase + 3.'),
 		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
 	]),
 
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index fbd9db7..6e7f3df 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -600,6 +600,11 @@
 		glyphs.append(attrs["value"])
 
 
+# The special 0xFFFFFFFF delta-set index is used to indicate that there
+# is no variation data in the ItemVariationStore for a given variable field
+NO_VARIATION_INDEX = 0xFFFFFFFF
+
+
 class DeltaSetIndexMap(getFormatSwitchingBaseTableClass("uint8")):
 
 	def populateDefaults(self, propagator=None):
@@ -647,12 +652,19 @@
 		return rawTable
 
 	def toXML2(self, xmlWriter, font):
+		# Make xml dump less verbose, by omitting no-op entries like:
+		#   <Map index="..." outer="65535" inner="65535"/>
+		xmlWriter.comment(
+			"Omitted values default to 0xFFFF/0xFFFF (no variations)"
+		)
+		xmlWriter.newline()
 		for i, value in enumerate(getattr(self, "mapping", [])):
-			attrs = (
-				('index', i),
-				('outer', value >> 16),
-				('inner', value & 0xFFFF),
-			)
+			attrs = [('index', i)]
+			if value != NO_VARIATION_INDEX:
+				attrs.extend([
+					('outer', value >> 16),
+					('inner', value & 0xFFFF),
+				])
 			xmlWriter.simpletag("Map", attrs)
 			xmlWriter.newline()
 
@@ -661,8 +673,8 @@
 		if mapping is None:
 			self.mapping = mapping = []
 		index = safeEval(attrs['index'])
-		outer = safeEval(attrs['outer'])
-		inner = safeEval(attrs['inner'])
+		outer = safeEval(attrs.get('outer', '0xFFFF'))
+		inner = safeEval(attrs.get('inner', '0xFFFF'))
 		assert inner <= 0xFFFF
 		mapping.insert(index, (outer << 16) | inner)
 
@@ -1257,7 +1269,19 @@
 		return self.__dict__.copy()
 
 
+class ClipBoxFormat(IntEnum):
+	Static = 1
+	Variable = 2
+
+	def is_variable(self):
+		return self is self.Variable
+
+	def as_variable(self):
+		return self.Variable
+
+
 class ClipBox(getFormatSwitchingBaseTableClass("uint8")):
+	formatEnum = ClipBoxFormat
 
 	def as_tuple(self):
 		return tuple(getattr(self, conv.name) for conv in self.getConverters())
@@ -1492,12 +1516,24 @@
 	PaintVarSkewAroundCenter = 31
 	PaintComposite = 32
 
+	def is_variable(self):
+		return self.name.startswith("PaintVar")
+
+	def as_variable(self):
+		if self.is_variable():
+			return self
+		try:
+			return PaintFormat.__members__[f"PaintVar{self.name[5:]}"]
+		except KeyError:
+			return None
+
 
 class Paint(getFormatSwitchingBaseTableClass("uint8")):
+	formatEnum = PaintFormat
 
 	def getFormatName(self):
 		try:
-			return PaintFormat(self.Format).name
+			return self.formatEnum(self.Format).name
 		except ValueError:
 			raise NotImplementedError(f"Unknown Paint format: {self.Format}")
 
@@ -1962,6 +1998,14 @@
 				cls.DontShare = True
 			namespace[name] = cls
 
+	# link Var{Table} <-> {Table} (e.g. ColorStop <-> VarColorStop, etc.)
+	for name, _ in otData:
+		if name.startswith("Var") and len(name) > 3 and name[3:] in namespace:
+			varType = namespace[name]
+			noVarType = namespace[name[3:]]
+			varType.NoVarType = noVarType
+			noVarType.VarType = varType
+
 	for base, alts in _equivalents.items():
 		base = namespace[base]
 		for alt in alts:
diff --git a/Lib/fontTools/ttLib/tables/otTraverse.py b/Lib/fontTools/ttLib/tables/otTraverse.py
new file mode 100644
index 0000000..40b28b2
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/otTraverse.py
@@ -0,0 +1,137 @@
+"""Methods for traversing trees of otData-driven OpenType tables."""
+from collections import deque
+from typing import Callable, Deque, Iterable, List, Optional, Tuple
+from .otBase import BaseTable
+
+
+__all__ = [
+    "bfs_base_table",
+    "dfs_base_table",
+    "SubTablePath",
+]
+
+
+class SubTablePath(Tuple[BaseTable.SubTableEntry, ...]):
+
+    def __str__(self) -> str:
+        path_parts = []
+        for entry in self:
+            path_part = entry.name
+            if entry.index is not None:
+                path_part += f"[{entry.index}]"
+            path_parts.append(path_part)
+        return ".".join(path_parts)
+
+
+# Given f(current frontier, new entries) add new entries to frontier
+AddToFrontierFn = Callable[[Deque[SubTablePath], List[SubTablePath]], None]
+
+
+def dfs_base_table(
+    root: BaseTable,
+    root_accessor: Optional[str] = None,
+    skip_root: bool = False,
+    predicate: Optional[Callable[[SubTablePath], bool]] = None,
+) -> Iterable[SubTablePath]:
+    """Depth-first search tree of BaseTables.
+
+    Args:
+        root (BaseTable): the root of the tree.
+        root_accessor (Optional[str]): attribute name for the root table, if any (mostly
+            useful for debugging).
+        skip_root (Optional[bool]): if True, the root itself is not visited, only its
+            children.
+        predicate (Optional[Callable[[SubTablePath], bool]]): function to filter out
+            paths. If True, the path is yielded and its subtables are added to the
+            queue. If False, the path is skipped and its subtables are not traversed.
+
+    Yields:
+        SubTablePath: tuples of BaseTable.SubTableEntry(name, table, index) namedtuples
+        for each of the nodes in the tree. The last entry in a path is the current
+        subtable, whereas preceding ones refer to its parent tables all the way up to
+        the root.
+    """
+    yield from _traverse_ot_data(
+        root,
+        root_accessor,
+        skip_root,
+        predicate,
+        lambda frontier, new: frontier.extendleft(reversed(new)),
+    )
+
+
+def bfs_base_table(
+    root: BaseTable,
+    root_accessor: Optional[str] = None,
+    skip_root: bool = False,
+    predicate: Optional[Callable[[SubTablePath], bool]] = None,
+) -> Iterable[SubTablePath]:
+    """Breadth-first search tree of BaseTables.
+
+    Args:
+        root (BaseTable): the root of the tree.
+        root_accessor (Optional[str]): attribute name for the root table, if any (mostly
+            useful for debugging).
+        skip_root (Optional[bool]): if True, the root itself is not visited, only its
+            children.
+        predicate (Optional[Callable[[SubTablePath], bool]]): function to filter out
+            paths. If True, the path is yielded and its subtables are added to the
+            queue. If False, the path is skipped and its subtables are not traversed.
+
+    Yields:
+        SubTablePath: tuples of BaseTable.SubTableEntry(name, table, index) namedtuples
+        for each of the nodes in the tree. The last entry in a path is the current
+        subtable, whereas preceding ones refer to its parent tables all the way up to
+        the root.
+    """
+    yield from _traverse_ot_data(
+        root,
+        root_accessor,
+        skip_root,
+        predicate,
+        lambda frontier, new: frontier.extend(new),
+    )
+
+
+def _traverse_ot_data(
+    root: BaseTable,
+    root_accessor: Optional[str],
+    skip_root: bool,
+    predicate: Optional[Callable[[SubTablePath], bool]],
+    add_to_frontier_fn: AddToFrontierFn,
+) -> Iterable[SubTablePath]:
+    # no visited because general otData cannot cycle (forward-offset only)
+    if root_accessor is None:
+        root_accessor = type(root).__name__
+
+    if predicate is None:
+
+        def predicate(path):
+            return True
+
+    frontier: Deque[SubTablePath] = deque()
+
+    root_entry = BaseTable.SubTableEntry(root_accessor, root)
+    if not skip_root:
+        frontier.append((root_entry,))
+    else:
+        add_to_frontier_fn(
+            frontier,
+            [(root_entry, subtable_entry) for subtable_entry in root.iterSubTables()],
+        )
+
+    while frontier:
+        # path is (value, attr_name) tuples. attr_name is attr of parent to get value
+        path = frontier.popleft()
+        current = path[-1].value
+
+        if not predicate(path):
+            continue
+
+        yield SubTablePath(path)
+
+        new_entries = [
+            path + (subtable_entry,) for subtable_entry in current.iterSubTables()
+        ]
+
+        add_to_frontier_fn(frontier, new_entries)
diff --git a/Lib/fontTools/ttLib/ttFont.py b/Lib/fontTools/ttLib/ttFont.py
index d7f7ef8..327d113 100644
--- a/Lib/fontTools/ttLib/ttFont.py
+++ b/Lib/fontTools/ttLib/ttFont.py
@@ -4,6 +4,11 @@
 from fontTools.misc.textTools import Tag, byteord, tostr
 from fontTools.misc.loggingTools import deprecateArgument
 from fontTools.ttLib import TTLibError
+from fontTools.ttLib.ttGlyphSet import (
+    _TTGlyphSet, _TTGlyph,
+    _TTGlyphCFF, _TTGlyphGlyf,
+    _TTVarGlyphSet,
+)
 from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
 from io import BytesIO, StringIO
 import os
@@ -381,12 +386,14 @@
 		keys = sortedTagList(keys)
 		return ["GlyphOrder"] + keys
 
-	def ensureDecompiled(self):
+	def ensureDecompiled(self, recurse=None):
 		"""Decompile all the tables, even if a TTFont was opened in 'lazy' mode."""
 		for tag in self.keys():
 			table = self[tag]
-			if self.lazy is not False and hasattr(table, "ensureDecompiled"):
-				table.ensureDecompiled()
+			if recurse is None:
+				recurse = self.lazy is not False
+			if recurse and hasattr(table, "ensureDecompiled"):
+				table.ensureDecompiled(recurse=recurse)
 		self.lazy = False
 
 	def __len__(self):
@@ -673,7 +680,7 @@
 		else:
 			raise KeyError(tag)
 
-	def getGlyphSet(self, preferCFF=True):
+	def getGlyphSet(self, preferCFF=True, location=None, normalized=False):
 		"""Return a generic GlyphSet, which is a dict-like object
 		mapping glyph names to glyph objects. The returned glyph objects
 		have a .draw() method that supports the Pen protocol, and will
@@ -684,16 +691,28 @@
 		If the font contains both a 'CFF '/'CFF2' and a 'glyf' table, you can use
 		the 'preferCFF' argument to specify which one should be taken. If the
 		font contains both a 'CFF ' and a 'CFF2' table, the latter is taken.
+
+		If the 'location' parameter is set, it should be a dictionary mapping
+		four-letter variation tags to their float values, and the returned
+		glyph-set will represent an instance of a variable font at that location.
+		If the 'normalized' variable is set to True, that location is interpretted
+		as in the normalized (-1..+1) space, otherwise it is in the font's defined
+		axes space.
 		"""
 		glyphs = None
 		if (preferCFF and any(tb in self for tb in ["CFF ", "CFF2"]) or
 		   ("glyf" not in self and any(tb in self for tb in ["CFF ", "CFF2"]))):
 			table_tag = "CFF2" if "CFF2" in self else "CFF "
+			if location:
+				raise NotImplementedError # TODO
 			glyphs = _TTGlyphSet(self,
 			    list(self[table_tag].cff.values())[0].CharStrings, _TTGlyphCFF)
 
 		if glyphs is None and "glyf" in self:
-			glyphs = _TTGlyphSet(self, self["glyf"], _TTGlyphGlyf)
+			if location and 'gvar' in self:
+				glyphs = _TTVarGlyphSet(self, location=location, normalized=normalized)
+			else:
+				glyphs = _TTGlyphSet(self, self["glyf"], _TTGlyphGlyf)
 
 		if glyphs is None:
 			raise TTLibError("Font contains no outlines")
@@ -726,109 +745,6 @@
 		return self["cmap"].getBestCmap(cmapPreferences=cmapPreferences)
 
 
-class _TTGlyphSet(object):
-
-	"""Generic dict-like GlyphSet class that pulls metrics from hmtx and
-	glyph shape from TrueType or CFF.
-	"""
-
-	def __init__(self, ttFont, glyphs, glyphType):
-		"""Construct a new glyphset.
-
-		Args:
-			font (TTFont): The font object (used to get metrics).
-			glyphs (dict): A dictionary mapping glyph names to ``_TTGlyph`` objects.
-			glyphType (class): Either ``_TTGlyphCFF`` or ``_TTGlyphGlyf``.
-		"""
-		self._glyphs = glyphs
-		self._hmtx = ttFont['hmtx']
-		self._vmtx = ttFont['vmtx'] if 'vmtx' in ttFont else None
-		self._glyphType = glyphType
-
-	def keys(self):
-		return list(self._glyphs.keys())
-
-	def has_key(self, glyphName):
-		return glyphName in self._glyphs
-
-	__contains__ = has_key
-
-	def __getitem__(self, glyphName):
-		horizontalMetrics = self._hmtx[glyphName]
-		verticalMetrics = self._vmtx[glyphName] if self._vmtx else None
-		return self._glyphType(
-			self, self._glyphs[glyphName], horizontalMetrics, verticalMetrics)
-
-	def __len__(self):
-		return len(self._glyphs)
-
-	def get(self, glyphName, default=None):
-		try:
-			return self[glyphName]
-		except KeyError:
-			return default
-
-class _TTGlyph(object):
-
-	"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
-	that it has .draw() and .drawPoints() methods that take a pen object as
-	their only argument. Additionally there are 'width' and 'lsb' attributes,
-	read from the 'hmtx' table.
-
-	If the font contains a 'vmtx' table, there will also be 'height' and 'tsb'
-	attributes.
-	"""
-
-	def __init__(self, glyphset, glyph, horizontalMetrics, verticalMetrics=None):
-		"""Construct a new _TTGlyph.
-
-		Args:
-			glyphset (_TTGlyphSet): A glyphset object used to resolve components.
-			glyph (ttLib.tables._g_l_y_f.Glyph): The glyph object.
-			horizontalMetrics (int, int): The glyph's width and left sidebearing.
-		"""
-		self._glyphset = glyphset
-		self._glyph = glyph
-		self.width, self.lsb = horizontalMetrics
-		if verticalMetrics:
-			self.height, self.tsb = verticalMetrics
-		else:
-			self.height, self.tsb = None, None
-
-	def draw(self, pen):
-		"""Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
-		how that works.
-		"""
-		self._glyph.draw(pen)
-
-	def drawPoints(self, pen):
-		# drawPoints is only implemented for _TTGlyphGlyf at this time.
-		raise NotImplementedError()
-
-class _TTGlyphCFF(_TTGlyph):
-	pass
-
-class _TTGlyphGlyf(_TTGlyph):
-
-	def draw(self, pen):
-		"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
-		how that works.
-		"""
-		glyfTable = self._glyphset._glyphs
-		glyph = self._glyph
-		offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
-		glyph.draw(pen, glyfTable, offset)
-
-	def drawPoints(self, pen):
-		"""Draw the glyph onto PointPen. See fontTools.pens.pointPen
-		for details how that works.
-		"""
-		glyfTable = self._glyphset._glyphs
-		glyph = self._glyph
-		offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
-		glyph.drawPoints(pen, glyfTable, offset)
-
-
 class GlyphOrder(object):
 
 	"""A pseudo table. The glyph order isn't in the font as a separate
diff --git a/Lib/fontTools/ttLib/ttGlyphSet.py b/Lib/fontTools/ttLib/ttGlyphSet.py
new file mode 100644
index 0000000..be26215
--- /dev/null
+++ b/Lib/fontTools/ttLib/ttGlyphSet.py
@@ -0,0 +1,221 @@
+"""GlyphSets returned by a TTFont."""
+
+from fontTools.misc.fixedTools import otRound
+from copy import copy
+
+class _TTGlyphSet(object):
+
+	"""Generic dict-like GlyphSet class that pulls metrics from hmtx and
+	glyph shape from TrueType or CFF.
+	"""
+
+	def __init__(self, ttFont, glyphs, glyphType):
+		"""Construct a new glyphset.
+
+		Args:
+			font (TTFont): The font object (used to get metrics).
+			glyphs (dict): A dictionary mapping glyph names to ``_TTGlyph`` objects.
+			glyphType (class): Either ``_TTGlyphCFF`` or ``_TTGlyphGlyf``.
+		"""
+		self._glyphs = glyphs
+		self._hmtx = ttFont['hmtx']
+		self._vmtx = ttFont['vmtx'] if 'vmtx' in ttFont else None
+		self._glyphType = glyphType
+
+	def keys(self):
+		return list(self._glyphs.keys())
+
+	def has_key(self, glyphName):
+		return glyphName in self._glyphs
+
+	__contains__ = has_key
+
+	def __getitem__(self, glyphName):
+		horizontalMetrics = self._hmtx[glyphName]
+		verticalMetrics = self._vmtx[glyphName] if self._vmtx else None
+		return self._glyphType(
+			self, self._glyphs[glyphName], horizontalMetrics, verticalMetrics)
+
+	def __len__(self):
+		return len(self._glyphs)
+
+	def get(self, glyphName, default=None):
+		try:
+			return self[glyphName]
+		except KeyError:
+			return default
+
+class _TTGlyph(object):
+
+	"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
+	that it has .draw() and .drawPoints() methods that take a pen object as
+	their only argument. Additionally there are 'width' and 'lsb' attributes,
+	read from the 'hmtx' table.
+
+	If the font contains a 'vmtx' table, there will also be 'height' and 'tsb'
+	attributes.
+	"""
+
+	def __init__(self, glyphset, glyph, horizontalMetrics, verticalMetrics=None):
+		"""Construct a new _TTGlyph.
+
+		Args:
+			glyphset (_TTGlyphSet): A glyphset object used to resolve components.
+			glyph (ttLib.tables._g_l_y_f.Glyph): The glyph object.
+			horizontalMetrics (int, int): The glyph's width and left sidebearing.
+		"""
+		self._glyphset = glyphset
+		self._glyph = glyph
+		self.width, self.lsb = horizontalMetrics
+		if verticalMetrics:
+			self.height, self.tsb = verticalMetrics
+		else:
+			self.height, self.tsb = None, None
+
+	def draw(self, pen):
+		"""Draw the glyph onto ``pen``. See fontTools.pens.basePen for details
+		how that works.
+		"""
+		self._glyph.draw(pen)
+
+	def drawPoints(self, pen):
+		from fontTools.pens.pointPen import SegmentToPointPen
+		self.draw(SegmentToPointPen(pen))
+
+class _TTGlyphCFF(_TTGlyph):
+	pass
+
+class _TTGlyphGlyf(_TTGlyph):
+
+	def draw(self, pen):
+		"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
+		how that works.
+		"""
+		glyfTable = self._glyphset._glyphs
+		glyph = self._glyph
+		offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
+		glyph.draw(pen, glyfTable, offset)
+
+	def drawPoints(self, pen):
+		"""Draw the glyph onto PointPen. See fontTools.pens.pointPen
+		for details how that works.
+		"""
+		glyfTable = self._glyphset._glyphs
+		glyph = self._glyph
+		offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
+		glyph.drawPoints(pen, glyfTable, offset)
+
+
+
+class _TTVarGlyphSet(_TTGlyphSet):
+
+	def __init__(self, font, location, normalized=False):
+		self._ttFont = font
+		self._glyphs = font['glyf']
+
+		if not normalized:
+			from fontTools.varLib.models import normalizeLocation, piecewiseLinearMap
+
+			axes = {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in font['fvar'].axes}
+			location = normalizeLocation(location, axes)
+			if 'avar' in font:
+				avar = font['avar']
+				avarSegments = avar.segments
+				new_location = {}
+				for axis_tag, value in location.items():
+					avarMapping = avarSegments.get(axis_tag, None)
+					if avarMapping is not None:
+						value = piecewiseLinearMap(value, avarMapping)
+					new_location[axis_tag] = value
+				location = new_location
+				del new_location
+
+		self.location = location
+
+	def __getitem__(self, glyphName):
+		if glyphName not in self._glyphs:
+			raise KeyError(glyphName)
+		return _TTVarGlyphGlyf(self._ttFont, glyphName, self.location)
+
+
+def _setCoordinates(glyph, coord, glyfTable):
+	# Handle phantom points for (left, right, top, bottom) positions.
+	assert len(coord) >= 4
+	if not hasattr(glyph, 'xMin'):
+		glyph.recalcBounds(glyfTable)
+	leftSideX = coord[-4][0]
+	rightSideX = coord[-3][0]
+	topSideY = coord[-2][1]
+	bottomSideY = coord[-1][1]
+
+	for _ in range(4):
+		del coord[-1]
+
+	if glyph.isComposite():
+		assert len(coord) == len(glyph.components)
+		for p,comp in zip(coord, glyph.components):
+			if hasattr(comp, 'x'):
+				comp.x,comp.y = p
+	elif glyph.numberOfContours == 0:
+		assert len(coord) == 0
+	else:
+		assert len(coord) == len(glyph.coordinates)
+		glyph.coordinates = coord
+
+	glyph.recalcBounds(glyfTable)
+
+	horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
+	verticalAdvanceWidth = otRound(topSideY - bottomSideY)
+	leftSideBearing = otRound(glyph.xMin - leftSideX)
+	topSideBearing = otRound(topSideY - glyph.yMax)
+	return (
+		horizontalAdvanceWidth,
+		leftSideBearing,
+		verticalAdvanceWidth,
+		topSideBearing,
+	)
+
+
+class _TTVarGlyph(_TTGlyph):
+	def __init__(self, ttFont, glyphName, location):
+		self._ttFont = ttFont
+		self._glyphName = glyphName
+		self._location = location
+		# draw() fills these in
+		self.width = self.height = self.lsb = self.tsb = None
+
+
+class _TTVarGlyphGlyf(_TTVarGlyph):
+
+	def draw(self, pen):
+		from fontTools.varLib.iup import iup_delta
+		from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+		from fontTools.varLib.models import supportScalar
+
+		glyf = self._ttFont['glyf']
+		hMetrics = self._ttFont['hmtx'].metrics
+		vMetrics = getattr(self._ttFont.get('vmtx'), 'metrics', None)
+
+		variations = self._ttFont['gvar'].variations[self._glyphName]
+		coordinates, _ = glyf._getCoordinatesAndControls(self._glyphName, hMetrics, vMetrics)
+		origCoords, endPts = None, None
+		for var in variations:
+			scalar = supportScalar(self._location, var.axes)
+			if not scalar:
+				continue
+			delta = var.coordinates
+			if None in delta:
+				if origCoords is None:
+					origCoords,control = glyf._getCoordinatesAndControls(self._glyphName, hMetrics, vMetrics)
+					endPts = control[1] if control[0] >= 1 else list(range(len(control[1])))
+				delta = iup_delta(delta, origCoords, endPts)
+			coordinates += GlyphCoordinates(delta) * scalar
+
+		glyph = copy(glyf[self._glyphName]) # Shallow copy
+		width, lsb, height, tsb = _setCoordinates(glyph, coordinates, glyf)
+		self.width = width
+		self.lsb = lsb
+		self.height = height
+		self.tsb = tsb
+		offset = lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
+		glyph.draw(pen, glyf, offset)
diff --git a/Lib/fontTools/ttLib/ttVisitor.py b/Lib/fontTools/ttLib/ttVisitor.py
new file mode 100644
index 0000000..54db61b
--- /dev/null
+++ b/Lib/fontTools/ttLib/ttVisitor.py
@@ -0,0 +1,32 @@
+"""Specialization of fontTools.misc.visitor to work with TTFont."""
+
+from fontTools.misc.visitor import Visitor
+from fontTools.ttLib import TTFont
+
+
+class TTVisitor(Visitor):
+    def visitAttr(self, obj, attr, value, *args, **kwargs):
+        if isinstance(value, TTFont):
+            return False
+        super().visitAttr(obj, attr, value, *args, **kwargs)
+
+    def visit(self, obj, *args, **kwargs):
+        if hasattr(obj, "ensureDecompiled"):
+            obj.ensureDecompiled(recurse=False)
+        super().visit(obj, *args, **kwargs)
+
+
+@TTVisitor.register(TTFont)
+def visit(visitor, font, *args, **kwargs):
+    # Some objects have links back to TTFont; even though we
+    # have a check in visitAttr to stop them from recursing
+    # onto TTFont, sometimes they still do, for example when
+    # someone overrides visitAttr.
+    if hasattr(visitor, "font"):
+        return False
+
+    visitor.font = font
+    for tag in font.keys():
+        visitor.visit(font[tag], *args, **kwargs)
+    del visitor.font
+    return False
diff --git a/Lib/fontTools/ufoLib/__init__.py b/Lib/fontTools/ufoLib/__init__.py
index bd04dd7..fa6cb11 100755
--- a/Lib/fontTools/ufoLib/__init__.py
+++ b/Lib/fontTools/ufoLib/__init__.py
@@ -98,6 +98,11 @@
 	FORMAT_2_0 = (2, 0)
 	FORMAT_3_0 = (3, 0)
 
+# python 3.11 doesn't like when a mixin overrides a dunder method like __str__
+# for some reasons it keep using Enum.__str__, see
+# https://github.com/fonttools/fonttools/pull/2655
+UFOFormatVersion.__str__ = _VersionTupleEnumMixin.__str__
+
 
 class UFOFileStructure(enum.Enum):
 	ZIP = "zip"
diff --git a/Lib/fontTools/ufoLib/glifLib.py b/Lib/fontTools/ufoLib/glifLib.py
index 89c9176..7d28eaf 100755
--- a/Lib/fontTools/ufoLib/glifLib.py
+++ b/Lib/fontTools/ufoLib/glifLib.py
@@ -79,6 +79,9 @@
 			versions.add(cls.FORMAT_2_0)
 		return frozenset(versions)
 
+# workaround for py3.11, see https://github.com/fonttools/fonttools/pull/2655
+GLIFFormatVersion.__str__ = _VersionTupleEnumMixin.__str__
+
 
 # ------------
 # Simple Glyph
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 4029a10..f1ca99f 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -30,13 +30,15 @@
 from fontTools.ttLib.tables import otTables as ot
 from fontTools.ttLib.tables.otBase import OTTableWriter
 from fontTools.varLib import builder, models, varStore
-from fontTools.varLib.merger import VariationMerger
+from fontTools.varLib.merger import VariationMerger, COLRVariationMerger
 from fontTools.varLib.mvar import MVAR_ENTRIES
 from fontTools.varLib.iup import iup_delta_optimize
 from fontTools.varLib.featureVars import addFeatureVariations
 from fontTools.designspaceLib import DesignSpaceDocument, InstanceDescriptor
 from fontTools.designspaceLib.split import splitInterpolable, splitVariableFonts
 from fontTools.varLib.stat import buildVFStatTable
+from fontTools.colorLib.builder import buildColrV1
+from fontTools.colorLib.unbuilder import unbuildColrV1
 from functools import partial
 from collections import OrderedDict, namedtuple
 import os.path
@@ -486,7 +488,7 @@
 			vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
 
 	indirectStore = storeBuilder.finish()
-	mapping2 = indirectStore.optimize()
+	mapping2 = indirectStore.optimize(use_NO_VARIATION_INDEX=False)
 	advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
 	advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
 
@@ -606,7 +608,7 @@
 	merger.mergeTables(font, master_ttfs, ['BASE'])
 	store = merger.store_builder.finish()
 
-	if not store.VarData:
+	if not store:
 		return
 	base = font['BASE'].table
 	assert base.Version == 0x00010000
@@ -621,7 +623,7 @@
 
 	merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
 	store = merger.store_builder.finish()
-	if not store.VarData:
+	if not store:
 		return
 	try:
 		GDEF = font['GDEF'].table
@@ -711,6 +713,19 @@
 	merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder)
 
 
+def _add_COLR(font, model, master_fonts, axisTags, colr_layer_reuse=True):
+	merger = COLRVariationMerger(model, axisTags, font, allowLayerReuse=colr_layer_reuse)
+	merger.mergeTables(font, master_fonts)
+	store = merger.store_builder.finish()
+
+	colr = font["COLR"].table
+	if store:
+		mapping = store.optimize()
+		colr.VarStore = store
+		varIdxes = [mapping[v] for v in merger.varIdxes]
+		colr.VarIndexMap = builder.buildDeltaSetIndexMap(varIdxes)
+
+
 def load_designspace(designspace):
 	# TODO: remove this and always assume 'designspace' is a DesignSpaceDocument,
 	# never a file path, as that's already handled by caller
@@ -865,7 +880,14 @@
 			font["post"].italicAngle = italicAngle
 
 
-def build_many(designspace: DesignSpaceDocument, master_finder=lambda s:s, exclude=[], optimize=True, skip_vf=lambda vf_name: False):
+def build_many(
+	designspace: DesignSpaceDocument,
+	master_finder=lambda s:s,
+	exclude=[],
+	optimize=True,
+	skip_vf=lambda vf_name: False,
+	colr_layer_reuse=True,
+):
 	"""
 	Build variable fonts from a designspace file, version 5 which can define
 	several VFs, or version 4 which has implicitly one VF covering the whole doc.
@@ -890,14 +912,21 @@
 				vfDoc,
 				master_finder,
 				exclude=list(exclude) + ["STAT"],
-				optimize=optimize
+				optimize=optimize,
+				colr_layer_reuse=colr_layer_reuse,
 			)[0]
 			if "STAT" not in exclude:
 				buildVFStatTable(vf, designspace, name)
 			res[name] = vf
 	return res
 
-def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True):
+def build(
+	designspace,
+	master_finder=lambda s:s,
+	exclude=[],
+	optimize=True,
+	colr_layer_reuse=True,
+):
 	"""
 	Build variation font from a designspace file.
 
@@ -975,6 +1004,8 @@
 				post.formatType = 2.0
 				post.extraNames = []
 				post.mapping = {}
+	if 'COLR' not in exclude and 'COLR' in vf and vf['COLR'].version > 0:
+		_add_COLR(vf, model, master_fonts, axisTags, colr_layer_reuse)
 
 	set_default_weight_width_slant(
 		vf, location={axis.axisTag: axis.defaultValue for axis in vf["fvar"].axes}
@@ -1083,6 +1114,12 @@
 		help='do not perform IUP optimization'
 	)
 	parser.add_argument(
+		'--no-colr-layer-reuse',
+		dest='colr_layer_reuse',
+		action='store_false',
+		help='do not rebuild variable COLR table to optimize COLR layer reuse',
+	)
+	parser.add_argument(
 		'--master-finder',
 		default='master_ttf_interpolatable/{stem}.ttf',
 		help=(
@@ -1120,7 +1157,8 @@
 		designspace_filename,
 		finder,
 		exclude=options.exclude,
-		optimize=options.optimize
+		optimize=options.optimize,
+		colr_layer_reuse=options.colr_layer_reuse,
 	)
 
 	outfile = options.outfile
diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py
index 08ddfc4..727efa7 100644
--- a/Lib/fontTools/varLib/cff.py
+++ b/Lib/fontTools/varLib/cff.py
@@ -639,6 +639,7 @@
 						# convert to deltas
 						deltas = get_delta_func(coord)[1:]
 						coord = [coord[0]] + deltas
+						coord.append(1)
 						new_coords.append(coord)
 				cmd[1] = new_coords
 			lastOp = op
diff --git a/Lib/fontTools/varLib/errors.py b/Lib/fontTools/varLib/errors.py
index c5a149c..4f30f90 100644
--- a/Lib/fontTools/varLib/errors.py
+++ b/Lib/fontTools/varLib/errors.py
@@ -30,12 +30,8 @@
     def _master_name(self, ix):
         if self.merger is not None:
             ttf = self.merger.ttfs[ix]
-            if (
-                "name" in ttf
-                and ttf["name"].getDebugName(1)
-                and ttf["name"].getDebugName(2)
-            ):
-                return ttf["name"].getDebugName(1) + " " + ttf["name"].getDebugName(2)
+            if "name" in ttf and ttf["name"].getBestFullName():
+                return ttf["name"].getBestFullName()
             elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"):
                 return ttf.reader.file.name
         return f"master number {ix}"
@@ -46,7 +42,10 @@
             index = [x == self.cause["expected"] for x in self.cause["got"]].index(
                 False
             )
-            return index, self._master_name(index)
+            master_name = self._master_name(index)
+            if "location" in self.cause:
+                master_name = f"{master_name} ({self.cause['location']})"
+            return index, master_name
         return None, None
 
     @property
@@ -54,7 +53,7 @@
         if "expected" in self.cause and "got" in self.cause:
             offender_index, offender = self.offender
             got = self.cause["got"][offender_index]
-            return f"Expected to see {self.stack[0]}=={self.cause['expected']}, instead saw {got}\n"
+            return f"Expected to see {self.stack[0]}=={self.cause['expected']!r}, instead saw {got!r}\n"
         return ""
 
     def __str__(self):
@@ -76,11 +75,21 @@
 
     @property
     def details(self):
+        basic_message = super().details
+
         if self.stack[0] != ".FeatureCount" or self.merger is None:
-            return super().details
-        offender_index, offender = self.offender
+            return basic_message
+
+        assert self.stack[0] == ".FeatureCount"
+        offender_index, _ = self.offender
         bad_ttf = self.merger.ttfs[offender_index]
-        good_ttf = self.merger.ttfs[offender_index - 1]
+        good_ttf = next(
+            ttf
+            for ttf in self.merger.ttfs
+            if self.stack[-1] in ttf
+            and ttf[self.stack[-1]].table.FeatureList.FeatureCount
+            == self.cause["expected"]
+        )
 
         good_features = [
             x.FeatureTag
@@ -90,7 +99,7 @@
             x.FeatureTag
             for x in bad_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
         ]
-        return (
+        return basic_message + (
             "\nIncompatible features between masters.\n"
             f"Expected: {', '.join(good_features)}.\n"
             f"Got: {', '.join(bad_features)}.\n"
@@ -111,6 +120,20 @@
         return f"{stack[0]}=={cause['got']}\n"
 
 
+class NotANone(VarLibMergeError):
+    """one of the values in a list was not empty when it should have been"""
+
+    @property
+    def offender(self):
+        index = [x is not None for x in self.cause["got"]].index(True)
+        return index, self._master_name(index)
+
+    @property
+    def details(self):
+        cause, stack = self.cause, self.stack
+        return f"{stack[0]}=={cause['got']}\n"
+
+
 class MismatchedTypes(VarLibMergeError):
     """data had inconsistent types"""
 
@@ -134,12 +157,20 @@
 class UnsupportedFormat(VarLibMergeError):
     """an OpenType subtable (%s) had a format I didn't expect"""
 
+    def __init__(self, merger=None, **kwargs):
+        super().__init__(merger, **kwargs)
+        if not self.stack:
+            self.stack = [".Format"]
+
     @property
     def reason(self):
-        return self.__doc__ % self.cause["subtable"]
+        s = self.__doc__ % self.cause["subtable"]
+        if "value" in self.cause:
+            s += f" ({self.cause['value']!r})"
+        return s
 
 
-class UnsupportedFormat(UnsupportedFormat):
+class InconsistentFormats(UnsupportedFormat):
     """an OpenType subtable (%s) had inconsistent formats between masters"""
 
 
diff --git a/Lib/fontTools/varLib/featureVars.py b/Lib/fontTools/varLib/featureVars.py
index e336632..ad47ab8 100644
--- a/Lib/fontTools/varLib/featureVars.py
+++ b/Lib/fontTools/varLib/featureVars.py
@@ -44,6 +44,10 @@
     # >>> f.save(dstPath)
     """
 
+    _checkSubstitutionGlyphsExist(
+        glyphNames=set(font.getGlyphOrder()),
+        substitutions=conditionalSubstitutions,
+    )
 
     substitutions = overlayFeatureVariations(conditionalSubstitutions)
 
@@ -66,6 +70,18 @@
                             conditionsAndLookups,
                             featureTag)
 
+def _checkSubstitutionGlyphsExist(glyphNames, substitutions):
+    referencedGlyphNames = set()
+    for _, substitution in substitutions:
+        referencedGlyphNames |=  substitution.keys()
+        referencedGlyphNames |=  set(substitution.values())
+    missing = referencedGlyphNames - glyphNames
+    if missing:
+       raise VarLibValidationError(
+            "Missing glyphs are referenced in conditional substitution rules:"
+            f" {', '.join(missing)}"
+        )
+
 def overlayFeatureVariations(conditionalSubstitutions):
     """Compute overlaps between all conditional substitutions.
 
diff --git a/Lib/fontTools/varLib/instancer/__init__.py b/Lib/fontTools/varLib/instancer/__init__.py
index 6dad393..8f97612 100644
--- a/Lib/fontTools/varLib/instancer/__init__.py
+++ b/Lib/fontTools/varLib/instancer/__init__.py
@@ -90,12 +90,11 @@
 from fontTools.varLib.mvar import MVAR_ENTRIES
 from fontTools.varLib.merger import MutatorMerger
 from fontTools.varLib.instancer import names
-from contextlib import contextmanager
+from fontTools.misc.cliTools import makeOutputFileName
 import collections
 from copy import deepcopy
 from enum import IntEnum
 import logging
-from itertools import islice
 import os
 import re
 
@@ -329,7 +328,9 @@
         return [var, newVar]
 
 
-def _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=True):
+def _instantiateGvarGlyph(
+    glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=True
+):
     coordinates, ctrl = glyf._getCoordinatesAndControls(glyphname, hMetrics, vMetrics)
     endPts = ctrl.endPts
 
@@ -365,22 +366,26 @@
         for var in tupleVarStore:
             var.optimize(coordinates, endPts, isComposite)
 
+
 def instantiateGvarGlyph(varfont, glyphname, axisLimits, optimize=True):
     """Remove?
     https://github.com/fonttools/fonttools/pull/2266"""
     gvar = varfont["gvar"]
     glyf = varfont["glyf"]
-    hMetrics = varfont['hmtx'].metrics
-    vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
-    _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize)
+    hMetrics = varfont["hmtx"].metrics
+    vMetrics = getattr(varfont.get("vmtx"), "metrics", None)
+    _instantiateGvarGlyph(
+        glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize
+    )
+
 
 def instantiateGvar(varfont, axisLimits, optimize=True):
     log.info("Instantiating glyf/gvar tables")
 
     gvar = varfont["gvar"]
     glyf = varfont["glyf"]
-    hMetrics = varfont['hmtx'].metrics
-    vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
+    hMetrics = varfont["hmtx"].metrics
+    vMetrics = getattr(varfont.get("vmtx"), "metrics", None)
     # Get list of glyph names sorted by component depth.
     # If a composite glyph is processed before its base glyph, the bounds may
     # be calculated incorrectly because deltas haven't been applied to the
@@ -395,7 +400,9 @@
         ),
     )
     for glyphname in glyphnames:
-        _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize)
+        _instantiateGvarGlyph(
+            glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize
+        )
 
     if not gvar.variations:
         del varfont["gvar"]
@@ -485,7 +492,7 @@
         # or AdvHeightMap. If a direct, implicit glyphID->VariationIndex mapping is
         # used for advances, skip re-optimizing and maintain original VariationIndex.
         if getattr(vhvar, tableFields.advMapping):
-            varIndexMapping = varStore.optimize()
+            varIndexMapping = varStore.optimize(use_NO_VARIATION_INDEX=False)
             glyphOrder = varfont.getGlyphOrder()
             _remapVarIdxMap(vhvar, tableFields.advMapping, varIndexMapping, glyphOrder)
             if getattr(vhvar, tableFields.sb1):  # left or top sidebearings
@@ -633,6 +640,7 @@
         for major, deltas in enumerate(defaultDeltaArray)
         for minor, delta in enumerate(deltas)
     }
+    defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0
     return defaultDeltas
 
 
@@ -745,23 +753,7 @@
 
     values = [minValue, maxValue]
     for i, value in enumerate(values):
-        if value < 0:
-            if axisRange.minimum == 0:
-                newValue = 0
-            else:
-                newValue = value / abs(axisRange.minimum)
-                if newValue <= -1.0:
-                    newValue = -1.0
-        elif value > 0:
-            if axisRange.maximum == 0:
-                newValue = 0
-            else:
-                newValue = value / axisRange.maximum
-                if newValue >= 1.0:
-                    newValue = 1.0
-        else:
-            newValue = 0
-        values[i] = newValue
+        values[i] = normalizeValue(value, (axisRange.minimum, 0, axisRange.maximum))
 
     return AxisRange(*values)
 
@@ -806,12 +798,12 @@
     return applies, shouldKeep
 
 
-def _limitFeatureVariationRecord(record, axisRanges, fvarAxes):
+def _limitFeatureVariationRecord(record, axisRanges, axisOrder):
     newConditions = []
     for i, condition in enumerate(record.ConditionSet.ConditionTable):
         if condition.Format == 1:
             axisIdx = condition.AxisIndex
-            axisTag = fvarAxes[axisIdx].axisTag
+            axisTag = axisOrder[axisIdx]
             if axisTag in axisRanges:
                 axisRange = axisRanges[axisTag]
                 newRange = _limitFeatureVariationConditionRange(condition, axisRange)
@@ -855,7 +847,7 @@
             record, i, location, fvarAxes, axisIndexMap
         )
         if shouldKeep:
-            shouldKeep = _limitFeatureVariationRecord(record, axisRanges, fvarAxes)
+            shouldKeep = _limitFeatureVariationRecord(record, axisRanges, axisOrder)
 
         if shouldKeep and _featureVariationRecordIsUnique(record, uniqueRecords):
             newRecords.append(record)
@@ -938,24 +930,16 @@
             )
             newMapping = {}
             for fromCoord, toCoord in mapping.items():
-                if fromCoord < 0:
-                    if axisRange.minimum == 0 or fromCoord < axisRange.minimum:
-                        continue
-                    else:
-                        fromCoord /= abs(axisRange.minimum)
-                elif fromCoord > 0:
-                    if axisRange.maximum == 0 or fromCoord > axisRange.maximum:
-                        continue
-                    else:
-                        fromCoord /= axisRange.maximum
-                if toCoord < 0:
-                    assert mappedMin != 0
-                    assert toCoord >= mappedMin
-                    toCoord /= abs(mappedMin)
-                elif toCoord > 0:
-                    assert mappedMax != 0
-                    assert toCoord <= mappedMax
-                    toCoord /= mappedMax
+
+                if fromCoord < axisRange.minimum or fromCoord > axisRange.maximum:
+                    continue
+                fromCoord = normalizeValue(
+                    fromCoord, (axisRange.minimum, 0, axisRange.maximum)
+                )
+
+                assert mappedMin <= toCoord <= mappedMax
+                toCoord = normalizeValue(toCoord, (mappedMin, 0, mappedMax))
+
                 fromCoord = floatToFixedToFloat(fromCoord, 14)
                 toCoord = floatToFixedToFloat(toCoord, 14)
                 newMapping[fromCoord] = toCoord
@@ -1199,10 +1183,10 @@
             requires the skia-pathops package (available to pip install).
             The overlap parameter only has effect when generating full static instances.
         updateFontNames (bool): if True, update the instantiated font's name table using
-            the Axis Value Tables from the STAT table. The name table will be updated so
-            it conforms to the R/I/B/BI model. If the STAT table is missing or
-            an Axis Value table is missing for a given axis coordinate, a ValueError will
-            be raised.
+            the Axis Value Tables from the STAT table. The name table and the style bits
+            in the head and OS/2 table will be updated so they conform to the R/I/B/BI
+            model. If the STAT table is missing or an Axis Value table is missing for
+            a given axis coordinate, a ValueError will be raised.
     """
     # 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
     overlap = OverlapMode(int(overlap))
@@ -1272,9 +1256,51 @@
         },
     )
 
+    if updateFontNames:
+        # Set Regular/Italic/Bold/Bold Italic bits as appropriate, after the
+        # name table has been updated.
+        setRibbiBits(varfont)
+
     return varfont
 
 
+def setRibbiBits(font):
+    """Set the `head.macStyle` and `OS/2.fsSelection` style bits
+    appropriately."""
+
+    english_ribbi_style = font["name"].getName(names.NameID.SUBFAMILY_NAME, 3, 1, 0x409)
+    if english_ribbi_style is None:
+        return
+
+    styleMapStyleName = english_ribbi_style.toStr().lower()
+    if styleMapStyleName not in {"regular", "bold", "italic", "bold italic"}:
+        return
+
+    if styleMapStyleName == "bold":
+        font["head"].macStyle = 0b01
+    elif styleMapStyleName == "bold italic":
+        font["head"].macStyle = 0b11
+    elif styleMapStyleName == "italic":
+        font["head"].macStyle = 0b10
+
+    selection = font["OS/2"].fsSelection
+    # First clear...
+    selection &= ~(1 << 0)
+    selection &= ~(1 << 5)
+    selection &= ~(1 << 6)
+    # ...then re-set the bits.
+    if styleMapStyleName == "regular":
+        selection |= 1 << 6
+    elif styleMapStyleName == "bold":
+        selection |= 1 << 5
+    elif styleMapStyleName == "italic":
+        selection |= 1 << 0
+    elif styleMapStyleName == "bold italic":
+        selection |= 1 << 0
+        selection |= 1 << 5
+    font["OS/2"].fsSelection = selection
+
+
 def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
     location, axisRanges = {}, {}
     for axisTag, value in axisLimits.items():
@@ -1380,6 +1406,18 @@
         help="Update the instantiated font's `name` table. Input font must have "
         "a STAT table with Axis Value Tables",
     )
+    parser.add_argument(
+        "--no-recalc-timestamp",
+        dest="recalc_timestamp",
+        action="store_false",
+        help="Don't set the output font's timestamp to the current time.",
+    )
+    parser.add_argument(
+        "--no-recalc-bounds",
+        dest="recalc_bounds",
+        action="store_false",
+        help="Don't recalculate font bounding boxes",
+    )
     loggingGroup = parser.add_mutually_exclusive_group(required=False)
     loggingGroup.add_argument(
         "-v", "--verbose", action="store_true", help="Run more verbosely."
@@ -1417,12 +1455,16 @@
 
 
 def main(args=None):
-    """Partially instantiate a variable font."""
+    """Partially instantiate a variable font"""
     infile, axisLimits, options = parseArgs(args)
     log.info("Restricting axes: %s", axisLimits)
 
     log.info("Loading variable font")
-    varfont = TTFont(infile)
+    varfont = TTFont(
+        infile,
+        recalcTimestamp=options.recalc_timestamp,
+        recalcBBoxes=options.recalc_bounds,
+    )
 
     isFullInstance = {
         axisTag for axisTag, limit in axisLimits.items() if not isinstance(limit, tuple)
@@ -1437,9 +1479,9 @@
         updateFontNames=options.update_name_table,
     )
 
+    suffix = "-instance" if isFullInstance else "-partial"
     outfile = (
-        os.path.splitext(infile)[0]
-        + "-{}.ttf".format("instance" if isFullInstance else "partial")
+        makeOutputFileName(infile, overWrite=True, suffix=suffix)
         if not options.output
         else options.output
     )
diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py
index a9583a1..f86b6f9 100644
--- a/Lib/fontTools/varLib/interpolatable.py
+++ b/Lib/fontTools/varLib/interpolatable.py
@@ -16,12 +16,12 @@
 import sys
 
 def _rot_list(l, k):
-	"""Rotate list by k items forward.  Ie. item at position 0 will be
-	at position k in returned list.  Negative k is allowed."""
-	n = len(l)
-	k %= n
-	if not k: return l
-	return l[n-k:] + l[:n-k]
+    """Rotate list by k items forward.  Ie. item at position 0 will be
+    at position k in returned list.  Negative k is allowed."""
+    n = len(l)
+    k %= n
+    if not k: return l
+    return l[n-k:] + l[:n-k]
 
 
 class PerContourPen(BasePen):
@@ -361,20 +361,69 @@
 
     from os.path import basename
 
-    names = [basename(filename).rsplit(".", 1)[0] for filename in args.inputs]
-
     fonts = []
+    names = []
+
+    if len(args.inputs) == 1:
+        if args.inputs[0].endswith('.designspace'):
+            from fontTools.designspaceLib import DesignSpaceDocument
+            designspace = DesignSpaceDocument.fromfile(args.inputs[0])
+            args.inputs = [master.path for master in designspace.sources]
+
+        elif args.inputs[0].endswith('.glyphs'):
+            from glyphsLib import GSFont, to_ufos
+            gsfont = GSFont(args.inputs[0])
+            fonts.extend(to_ufos(gsfont))
+            names = ['%s-%s' % (f.info.familyName, f.info.styleName) for f in fonts]
+            args.inputs = []
+
+        elif args.inputs[0].endswith('.ttf'):
+            from fontTools.ttLib import TTFont
+            font = TTFont(args.inputs[0])
+            if 'gvar' in font:
+                # Is variable font
+                gvar = font['gvar']
+                # Gather all "master" locations
+                locs = set()
+                for variations in gvar.variations.values():
+                    for var in variations:
+                        loc = []
+                        for tag,val in sorted(var.axes.items()):
+                            loc.append((tag,val[1]))
+                        locs.add(tuple(loc))
+                # Rebuild locs as dictionaries
+                new_locs = [{}]
+                for loc in sorted(locs, key=lambda v: (len(v), v)):
+                    names.append(str(loc))
+                    l = {}
+                    for tag,val in loc:
+                        l[tag] = val
+                    new_locs.append(l)
+                locs = new_locs
+                del new_locs
+                # locs is all master locations now
+
+                for loc in locs:
+                    fonts.append(font.getGlyphSet(location=loc, normalized=True))
+
+                args.inputs = []
+
+
     for filename in args.inputs:
         if filename.endswith(".ufo"):
             from fontTools.ufoLib import UFOReader
-
             fonts.append(UFOReader(filename))
         else:
             from fontTools.ttLib import TTFont
-
             fonts.append(TTFont(filename))
 
-    glyphsets = [font.getGlyphSet() for font in fonts]
+        names.append(basename(filename).rsplit(".", 1)[0])
+
+    if hasattr(fonts[0], 'getGlyphSet'):
+        glyphsets = [font.getGlyphSet() for font in fonts]
+    else:
+        glyphsets = fonts
+
     problems = test(glyphsets, glyphs=glyphs, names=names)
     if args.json:
         import json
diff --git a/Lib/fontTools/varLib/iup.py b/Lib/fontTools/varLib/iup.py
index 45a7a5e..9c5bc35 100644
--- a/Lib/fontTools/varLib/iup.py
+++ b/Lib/fontTools/varLib/iup.py
@@ -1,4 +1,47 @@
-def iup_segment(coords, rc1, rd1, rc2, rd2):
+from typing import (
+	Sequence,
+	Tuple,
+	Union,
+)
+from numbers import (
+	Integral,
+	Real
+)
+
+try:
+	import cython
+except ImportError:
+	# if cython not installed, use mock module with no-op decorators and types
+	from fontTools.misc import cython
+
+if cython.compiled:
+	# Yep, I'm compiled.
+	COMPILED = True
+else:
+	# Just a lowly interpreted script.
+	COMPILED = False
+
+
+_Point = Tuple[Real, Real]
+_Delta = Tuple[Real, Real]
+_PointSegment = Sequence[_Point]
+_DeltaSegment = Sequence[_Delta]
+_DeltaOrNone = Union[_Delta, None]
+_DeltaOrNoneSegment = Sequence[_DeltaOrNone]
+_Endpoints = Sequence[Integral]
+
+
+MAX_LOOKBACK = 8
+
+def iup_segment(coords : _PointSegment,
+		rc1 : _Point,
+		rd1 : _Delta,
+		rc2 : _Point,
+		rd2 : _Delta) -> _DeltaSegment:
+	"""Given two reference coordinates `rc1` & `rc2` and their respective
+	delta vectors `rd1` & `rd2`, returns interpolated deltas for the set of
+	coordinates `coords`. """
+
 	# rc1 = reference coord 1
 	# rd1 = reference delta 1
 	out_arrays = [None, None]
@@ -6,7 +49,6 @@
 		out_arrays[j] = out = []
 		x1, x2, d1, d2 = rc1[j], rc2[j], rd1[j], rd2[j]
 
-
 		if x1 == x2:
 			n = len(coords)
 			if d1 == d2:
@@ -36,14 +78,20 @@
 
 	return zip(*out_arrays)
 
-def iup_contour(delta, coords):
-	assert len(delta) == len(coords)
-	if None not in delta:
-		return delta
+def iup_contour(deltas : _DeltaOrNoneSegment,
+		coords : _PointSegment) -> _DeltaSegment:
+	"""For the contour given in `coords`, interpolate any missing
+	delta values in delta vector `deltas`.
 
-	n = len(delta)
+	Returns fully filled-out delta vector."""
+
+	assert len(deltas) == len(coords)
+	if None not in deltas:
+		return deltas
+
+	n = len(deltas)
 	# indices of points with explicit deltas
-	indices = [i for i,v in enumerate(delta) if v is not None]
+	indices = [i for i,v in enumerate(deltas) if v is not None]
 	if not indices:
 		# All deltas are None.  Return 0,0 for all.
 		return [(0,0)]*n
@@ -54,23 +102,31 @@
 	if start != 0:
 		# Initial segment that wraps around
 		i1, i2, ri1, ri2 = 0, start, start, indices[-1]
-		out.extend(iup_segment(coords[i1:i2], coords[ri1], delta[ri1], coords[ri2], delta[ri2]))
-	out.append(delta[start])
+		out.extend(iup_segment(coords[i1:i2], coords[ri1], deltas[ri1], coords[ri2], deltas[ri2]))
+	out.append(deltas[start])
 	for end in it:
 		if end - start > 1:
 			i1, i2, ri1, ri2 = start+1, end, start, end
-			out.extend(iup_segment(coords[i1:i2], coords[ri1], delta[ri1], coords[ri2], delta[ri2]))
-		out.append(delta[end])
+			out.extend(iup_segment(coords[i1:i2], coords[ri1], deltas[ri1], coords[ri2], deltas[ri2]))
+		out.append(deltas[end])
 		start = end
 	if start != n-1:
 		# Final segment that wraps around
 		i1, i2, ri1, ri2 = start+1, n, start, indices[0]
-		out.extend(iup_segment(coords[i1:i2], coords[ri1], delta[ri1], coords[ri2], delta[ri2]))
+		out.extend(iup_segment(coords[i1:i2], coords[ri1], deltas[ri1], coords[ri2], deltas[ri2]))
 
-	assert len(delta) == len(out), (len(delta), len(out))
+	assert len(deltas) == len(out), (len(deltas), len(out))
 	return out
 
-def iup_delta(delta, coords, ends):
+def iup_delta(deltas : _DeltaOrNoneSegment,
+	      coords : _PointSegment,
+	      ends: _Endpoints) -> _DeltaSegment:
+	"""For the outline given in `coords`, with contour endpoints given
+	in sorted increasing order in `ends`, interpolate any missing
+	delta values in delta vector `deltas`.
+
+	Returns fully filled-out delta vector."""
+
 	assert sorted(ends) == ends and len(coords) == (ends[-1]+1 if ends else 0) + 4
 	n = len(coords)
 	ends = ends + [n-4, n-3, n-2, n-1]
@@ -78,7 +134,7 @@
 	start = 0
 	for end in ends:
 		end += 1
-		contour = iup_contour(delta[start:end], coords[start:end])
+		contour = iup_contour(deltas[start:end], coords[start:end])
 		out.extend(contour)
 		start = end
 
@@ -86,7 +142,15 @@
 
 # Optimizer
 
-def can_iup_in_between(deltas, coords, i, j, tolerance):
+def can_iup_in_between(deltas : _DeltaSegment,
+		       coords : _PointSegment,
+		       i : Integral,
+		       j : Integral,
+		       tolerance : Real) -> bool:
+	"""Return true if the deltas for points at `i` and `j` (`i < j`) can be
+	successfully used to interpolate deltas for points in between them within
+	provided error tolerance."""
+
 	assert j - i >= 2
 	interp = list(iup_segment(coords[i+1:j], coords[i], deltas[i], coords[j], deltas[j]))
 	deltas = deltas[i+1:j]
@@ -95,23 +159,25 @@
 
 	return all(abs(complex(x-p, y-q)) <= tolerance for (x,y),(p,q) in zip(deltas, interp))
 
-def _iup_contour_bound_forced_set(delta, coords, tolerance=0):
+def _iup_contour_bound_forced_set(deltas : _DeltaSegment,
+				  coords : _PointSegment,
+				  tolerance : Real = 0) -> set:
 	"""The forced set is a conservative set of points on the contour that must be encoded
 	explicitly (ie. cannot be interpolated).  Calculating this set allows for significantly
 	speeding up the dynamic-programming, as well as resolve circularity in DP.
 
 	The set is precise; that is, if an index is in the returned set, then there is no way
-	that IUP can generate delta for that point, given coords and delta.
+	that IUP can generate delta for that point, given `coords` and `deltas`.
 	"""
-	assert len(delta) == len(coords)
+	assert len(deltas) == len(coords)
 
+	n = len(deltas)
 	forced = set()
 	# Track "last" and "next" points on the contour as we sweep.
-	nd, nc = delta[0], coords[0]
-	ld, lc = delta[-1], coords[-1]
-	for i in range(len(delta)-1, -1, -1):
-		d, c = ld, lc
-		ld, lc = delta[i-1], coords[i-1]
+	for i in range(len(deltas)-1, -1, -1):
+		ld, lc = deltas[i-1], coords[i-1]
+		d, c = deltas[i], coords[i]
+		nd, nc = deltas[i-n+1], coords[i-n+1]
 
 		for j in (0,1): # For X and for Y
 			cj = c[j]
@@ -128,42 +194,48 @@
 				c1, c2 = ncj, lcj
 				d1, d2 = ndj, ldj
 
+			force = False
+
+			# If the two coordinates are the same, then the interpolation
+			# algorithm produces the same delta if both deltas are equal,
+			# and zero if they differ.
+			#
+			# This test has to be before the next one.
+			if c1 == c2:
+				if abs(d1 - d2) > tolerance and abs(dj) > tolerance:
+					force = True
+
 			# If coordinate for current point is between coordinate of adjacent
 			# points on the two sides, but the delta for current point is NOT
 			# between delta for those adjacent points (considering tolerance
 			# allowance), then there is no way that current point can be IUP-ed.
 			# Mark it forced.
-			force = False
-			if c1 <= cj <= c2:
+			elif c1 <= cj <= c2: # and c1 != c2
 				if not (min(d1,d2)-tolerance <= dj <= max(d1,d2)+tolerance):
 					force = True
+
+			# Otherwise, the delta should either match the closest, or have the
+			# same sign as the interpolation of the two deltas.
 			else: # cj < c1 or c2 < cj
-				if c1 == c2:
-					if d1 == d2:
-						if abs(dj - d1) > tolerance:
-							force = True
-					else:
-						if abs(dj) > tolerance:
-							# Disabled the following because the "d1 == d2" does
-							# check does not take tolerance into consideration...
-							pass # force = True
-				elif d1 != d2:
+				if d1 != d2:
 					if cj < c1:
-						if dj != d1 and ((dj-tolerance < d1) != (d1 < d2)):
+						if abs(dj) > tolerance and abs(dj - d1) > tolerance and ((dj-tolerance < d1) != (d1 < d2)):
 							force = True
 					else: # c2 < cj
-						if d2 != dj and ((d2 < dj+tolerance) != (d1 < d2)):
+						if abs(dj) > tolerance and abs(dj - d2) > tolerance and ((d2 < dj+tolerance) != (d1 < d2)):
 							force = True
 
 			if force:
 				forced.add(i)
 				break
 
-		nd, nc = d, c
-
 	return forced
 
-def _iup_contour_optimize_dp(delta, coords, forced={}, tolerance=0, lookback=None):
+def _iup_contour_optimize_dp(deltas : _DeltaSegment,
+			     coords : _PointSegment,
+			     forced={},
+			     tolerance : Real = 0,
+			     lookback : Integral =None):
 	"""Straightforward Dynamic-Programming.  For each index i, find least-costly encoding of
 	points 0 to i where i is explicitly encoded.  We find this by considering all previous
 	explicit points j and check whether interpolation can fill points between j and i.
@@ -173,9 +245,10 @@
 
 	As major speedup, we stop looking further whenever we see a "forced" point."""
 
-	n = len(delta)
+	n = len(deltas)
 	if lookback is None:
 		lookback = n
+	lookback = min(lookback, MAX_LOOKBACK)
 	costs = {-1:0}
 	chain = {-1:None}
 	for i in range(0, n):
@@ -191,7 +264,7 @@
 
 			cost = costs[j] + 1
 
-			if cost < best_cost and can_iup_in_between(delta, coords, j, i, tolerance):
+			if cost < best_cost and can_iup_in_between(deltas, coords, j, i, tolerance):
 				costs[i] = best_cost = cost
 				chain[i] = j
 
@@ -200,7 +273,7 @@
 
 	return chain, costs
 
-def _rot_list(l, k):
+def _rot_list(l : list, k : int):
 	"""Rotate list by k items forward.  Ie. item at position 0 will be
 	at position k in returned list.  Negative k is allowed."""
 	n = len(l)
@@ -208,48 +281,62 @@
 	if not k: return l
 	return l[n-k:] + l[:n-k]
 
-def _rot_set(s, k, n):
+def _rot_set(s : set, k : int, n : int):
 	k %= n
 	if not k: return s
 	return {(v + k) % n for v in s}
 
-def iup_contour_optimize(delta, coords, tolerance=0.):
-	n = len(delta)
+def iup_contour_optimize(deltas : _DeltaSegment,
+			 coords : _PointSegment,
+			 tolerance : Real = 0.) -> _DeltaOrNoneSegment:
+	"""For contour with coordinates `coords`, optimize a set of delta
+	values `deltas` within error `tolerance`.
+
+	Returns delta vector that has most number of None items instead of
+	the input delta.
+	"""
+
+	n = len(deltas)
 
 	# Get the easy cases out of the way:
 
 	# If all are within tolerance distance of 0, encode nothing:
-	if all(abs(complex(*p)) <= tolerance for p in delta):
+	if all(abs(complex(*p)) <= tolerance for p in deltas):
 		return [None] * n
 
 	# If there's exactly one point, return it:
 	if n == 1:
-		return delta
+		return deltas
 
 	# If all deltas are exactly the same, return just one (the first one):
-	d0 = delta[0]
-	if all(d0 == d for d in delta):
+	d0 = deltas[0]
+	if all(d0 == d for d in deltas):
 		return [d0] + [None] * (n-1)
 
 	# Else, solve the general problem using Dynamic Programming.
 
-	forced = _iup_contour_bound_forced_set(delta, coords, tolerance)
+	forced = _iup_contour_bound_forced_set(deltas, coords, tolerance)
 	# The _iup_contour_optimize_dp() routine returns the optimal encoding
 	# solution given the constraint that the last point is always encoded.
 	# To remove this constraint, we use two different methods, depending on
 	# whether forced set is non-empty or not:
 
+	# Debugging: Make the next if always take the second branch and observe
+	# if the font size changes (reduced); that would mean the forced-set
+	# has members it should not have.
 	if forced:
 		# Forced set is non-empty: rotate the contour start point
 		# such that the last point in the list is a forced point.
 		k = (n-1) - max(forced)
 		assert k >= 0
 
-		delta  = _rot_list(delta, k)
+		deltas  = _rot_list(deltas, k)
 		coords = _rot_list(coords, k)
 		forced = _rot_set(forced, k, n)
 
-		chain, costs = _iup_contour_optimize_dp(delta, coords, forced, tolerance)
+		# Debugging: Pass a set() instead of forced variable to the next call
+		# to exercise forced-set computation for under-counting.
+		chain, costs = _iup_contour_optimize_dp(deltas, coords, forced, tolerance)
 
 		# Assemble solution.
 		solution = set()
@@ -257,18 +344,25 @@
 		while i is not None:
 			solution.add(i)
 			i = chain[i]
-		assert forced <= solution, (forced, solution)
-		delta = [delta[i] if i in solution else None for i in range(n)]
+		solution.remove(-1)
 
-		delta = _rot_list(delta, -k)
+		#if not forced <= solution:
+		#	print("coord", coords)
+		#	print("deltas", deltas)
+		#	print("len", len(deltas))
+		assert forced <= solution, (forced, solution)
+
+		deltas = [deltas[i] if i in solution else None for i in range(n)]
+
+		deltas = _rot_list(deltas, -k)
 	else:
-		# Repeat the contour an extra time, solve the 2*n case, then look for solutions of the
-		# circular n-length problem in the solution for 2*n linear case.  I cannot prove that
+		# Repeat the contour an extra time, solve the new case, then look for solutions of the
+		# circular n-length problem in the solution for new linear case.  I cannot prove that
 		# this always produces the optimal solution...
-		chain, costs = _iup_contour_optimize_dp(delta+delta, coords+coords, forced, tolerance, n)
+		chain, costs = _iup_contour_optimize_dp(deltas+deltas, coords+coords, forced, tolerance, n)
 		best_sol, best_cost = None, n+1
 
-		for start in range(n-1, 2*n-1):
+		for start in range(n-1, len(costs) - 1):
 			# Assemble solution.
 			solution = set()
 			i = start
@@ -280,19 +374,35 @@
 				if cost <= best_cost:
 					best_sol, best_cost = solution, cost
 
-		delta = [delta[i] if i in best_sol else None for i in range(n)]
+		#if not forced <= best_sol:
+		#	print("coord", coords)
+		#	print("deltas", deltas)
+		#	print("len", len(deltas))
+		assert forced <= best_sol, (forced, best_sol)
+
+		deltas = [deltas[i] if i in best_sol else None for i in range(n)]
 
 
-	return delta
+	return deltas
 
-def iup_delta_optimize(delta, coords, ends, tolerance=0.):
+def iup_delta_optimize(deltas : _DeltaSegment,
+		       coords : _PointSegment,
+		       ends : _Endpoints,
+		       tolerance : Real = 0.) -> _DeltaOrNoneSegment:
+	"""For the outline given in `coords`, with contour endpoints given
+	in sorted increasing order in `ends`, optimize a set of delta
+	values `deltas` within error `tolerance`.
+
+	Returns delta vector that has most number of None items instead of
+	the input delta.
+	"""
 	assert sorted(ends) == ends and len(coords) == (ends[-1]+1 if ends else 0) + 4
 	n = len(coords)
 	ends = ends + [n-4, n-3, n-2, n-1]
 	out = []
 	start = 0
 	for end in ends:
-		contour = iup_contour_optimize(delta[start:end+1], coords[start:end+1], tolerance)
+		contour = iup_contour_optimize(deltas[start:end+1], coords[start:end+1], tolerance)
 		assert len(contour) == end - start + 1
 		out.extend(contour)
 		start = end+1
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py
index 3e5d2a9..c9a1d3e 100644
--- a/Lib/fontTools/varLib/merger.py
+++ b/Lib/fontTools/varLib/merger.py
@@ -3,15 +3,20 @@
 """
 import os
 import copy
+import enum
 from operator import ior
 import logging
+from fontTools.colorLib.builder import MAX_PAINT_COLR_LAYER_COUNT, LayerReuseCache
 from fontTools.misc import classifyTools
 from fontTools.misc.roundTools import otRound
+from fontTools.misc.treeTools import build_n_ary_tree
 from fontTools.ttLib.tables import otTables as ot
 from fontTools.ttLib.tables import otBase as otBase
+from fontTools.ttLib.tables.otConverters import BaseFixedValue
+from fontTools.ttLib.tables.otTraverse import dfs_base_table
 from fontTools.ttLib.tables.DefaultTable import DefaultTable
 from fontTools.varLib import builder, models, varStore
-from fontTools.varLib.models import nonNone, allNone, allEqual, allEqualTo
+from fontTools.varLib.models import nonNone, allNone, allEqual, allEqualTo, subList
 from fontTools.varLib.varStore import VarStoreInstancer
 from functools import reduce
 from fontTools.otlLib.builder import buildSinglePos
@@ -26,11 +31,12 @@
     ShouldBeConstant,
     FoundANone,
     MismatchedTypes,
+    NotANone,
     LengthsDiffer,
     KeysDiffer,
     InconsistentGlyphOrder,
     InconsistentExtensions,
-    UnsupportedFormat,
+    InconsistentFormats,
     UnsupportedFormat,
     VarLibMergeError,
 )
@@ -39,13 +45,15 @@
 
 	def __init__(self, font=None):
 		self.font = font
+		# mergeTables populates this from the parent's master ttfs
+		self.ttfs = None
 
 	@classmethod
 	def merger(celf, clazzes, attrs=(None,)):
 		assert celf != Merger, 'Subclass Merger instead.'
 		if 'mergers' not in celf.__dict__:
 			celf.mergers = {}
-		if type(clazzes) == type:
+		if type(clazzes) in (type, enum.EnumMeta):
 			clazzes = (clazzes,)
 		if type(attrs) == str:
 			attrs = (attrs,)
@@ -81,10 +89,10 @@
 
 	def mergeObjects(self, out, lst, exclude=()):
 		if hasattr(out, "ensureDecompiled"):
-			out.ensureDecompiled()
+			out.ensureDecompiled(recurse=False)
 		for item in lst:
 			if hasattr(item, "ensureDecompiled"):
-				item.ensureDecompiled()
+				item.ensureDecompiled(recurse=False)
 		keys = sorted(vars(out).keys())
 		if not all(keys == sorted(vars(v).keys()) for v in lst):
 			raise KeysDiffer(self, expected=keys,
@@ -122,6 +130,11 @@
 		mergerFunc = self.mergersFor(out).get(None, None)
 		if mergerFunc is not None:
 			mergerFunc(self, out, lst)
+		elif isinstance(out, enum.Enum):
+			# need to special-case Enums as have __dict__ but are not regular 'objects',
+			# otherwise mergeObjects/mergeThings get trapped in a RecursionError
+			if not allEqualTo(out, lst):
+				raise ShouldBeConstant(self, expected=out, got=lst)
 		elif hasattr(out, '__dict__'):
 			self.mergeObjects(out, lst)
 		elif isinstance(out, list):
@@ -134,9 +147,8 @@
 		for tag in tableTags:
 			if tag not in font: continue
 			try:
-				self.ttfs = [m for m in master_ttfs if tag in m]
-				self.mergeThings(font[tag], [m[tag] if tag in m else None
-							     for m in master_ttfs])
+				self.ttfs = master_ttfs
+				self.mergeThings(font[tag], [m.get(tag) for m in master_ttfs])
 			except VarLibMergeError as e:
 				e.stack.append(tag)
 				raise
@@ -216,6 +228,20 @@
 			  for dict_set in dict_sets]
 	return order, padded
 
+@AligningMerger.merger(otBase.ValueRecord)
+def merge(merger, self, lst):
+	# Code below sometimes calls us with self being
+	# a new object. Copy it from lst and recurse.
+	self.__dict__ = lst[0].__dict__.copy()
+	merger.mergeObjects(self, lst)
+
+@AligningMerger.merger(ot.Anchor)
+def merge(merger, self, lst):
+	# Code below sometimes calls us with self being
+	# a new object. Copy it from lst and recurse.
+	self.__dict__ = lst[0].__dict__.copy()
+	merger.mergeObjects(self, lst)
+
 def _Lookup_SinglePos_get_effective_value(merger, subtables, glyph):
 	for self in subtables:
 		if self is None or \
@@ -1036,11 +1062,19 @@
 
 	def mergeThings(self, out, lst):
 		masterModel = None
+		origTTFs = None
 		if None in lst:
 			if allNone(lst):
 				if out is not None:
 					raise FoundANone(self, got=lst)
 				return
+
+			# temporarily subset the list of master ttfs to the ones for which
+			# master values are not None
+			origTTFs = self.ttfs
+			if self.ttfs:
+				self.ttfs = subList([v is not None for v in lst], self.ttfs)
+
 			masterModel = self.model
 			model, lst = masterModel.getSubModel(lst)
 			self.setModel(model)
@@ -1049,6 +1083,8 @@
 
 		if masterModel:
 			self.setModel(masterModel)
+		if origTTFs:
+			self.ttfs = origTTFs
 
 
 def buildVarDevTable(store_builder, master_values):
@@ -1099,3 +1135,408 @@
 			setattr(self, name, value)
 			if deviceTable:
 				setattr(self, tableName, deviceTable)
+
+
+class COLRVariationMerger(VariationMerger):
+	"""A specialized VariationMerger that takes multiple master fonts containing
+	COLRv1 tables, and builds a variable COLR font.
+
+	COLR tables are special in that variable subtables can be associated with
+	multiple delta-set indices (via VarIndexBase).
+	They also contain tables that must change their type (not simply the Format)
+	as they become variable (e.g. Affine2x3 -> VarAffine2x3) so this merger takes
+	care of that too.
+	"""
+
+	def __init__(self, model, axisTags, font, allowLayerReuse=True):
+		VariationMerger.__init__(self, model, axisTags, font)
+		# maps {tuple(varIdxes): VarIndexBase} to facilitate reuse of VarIndexBase
+		# between variable tables with same varIdxes.
+		self.varIndexCache = {}
+		# flat list of all the varIdxes generated while merging
+		self.varIdxes = []
+		# set of id()s of the subtables that contain variations after merging
+		# and need to be upgraded to the associated VarType.
+		self.varTableIds = set()
+		# we keep these around for rebuilding a LayerList while merging PaintColrLayers
+		self.layers = []
+		self.layerReuseCache = None
+		if allowLayerReuse:
+			self.layerReuseCache = LayerReuseCache()
+		# flag to ensure BaseGlyphList is fully merged before LayerList gets processed
+		self._doneBaseGlyphs = False
+
+	def mergeTables(self, font, master_ttfs, tableTags=("COLR",)):
+		if "COLR" in tableTags and "COLR" in font:
+			# The merger modifies the destination COLR table in-place. If this contains
+			# multiple PaintColrLayers referencing the same layers from LayerList, it's
+			# a problem because we may risk modifying the same paint more than once, or
+			# worse, fail while attempting to do that.
+			# We don't know whether the master COLR table was built with layer reuse
+			# disabled, thus to be safe we rebuild its LayerList so that it contains only
+			# unique layers referenced from non-overlapping PaintColrLayers throughout
+			# the base paint graphs.
+			self.expandPaintColrLayers(font["COLR"].table)
+		VariationMerger.mergeTables(self, font, master_ttfs, tableTags)
+
+	def checkFormatEnum(self, out, lst, validate=lambda _: True):
+		fmt = out.Format
+		formatEnum = out.formatEnum
+		ok = False
+		try:
+			fmt = formatEnum(fmt)
+		except ValueError:
+			pass
+		else:
+			ok = validate(fmt)
+		if not ok:
+			raise UnsupportedFormat(
+				self, subtable=type(out).__name__, value=fmt
+			)
+		expected = fmt
+		got = []
+		for v in lst:
+			fmt = getattr(v, "Format", None)
+			try:
+				fmt = formatEnum(fmt)
+			except ValueError:
+				pass
+			got.append(fmt)
+		if not allEqualTo(expected, got):
+			raise InconsistentFormats(
+				self,
+				subtable=type(out).__name__,
+				expected=expected,
+				got=got,
+			)
+		return expected
+
+	def mergeSparseDict(self, out, lst):
+		for k in out.keys():
+			try:
+				self.mergeThings(out[k], [v.get(k) for v in lst])
+			except VarLibMergeError as e:
+				e.stack.append(f"[{k!r}]")
+				raise
+
+	def mergeAttrs(self, out, lst, attrs):
+		for attr in attrs:
+			value = getattr(out, attr)
+			values = [getattr(item, attr) for item in lst]
+			try:
+				self.mergeThings(value, values)
+			except VarLibMergeError as e:
+				e.stack.append(f".{attr}")
+				raise
+
+	def storeMastersForAttr(self, out, lst, attr):
+		master_values = [getattr(item, attr) for item in lst]
+
+		# VarStore treats deltas for fixed-size floats as integers, so we
+		# must convert master values to int before storing them in the builder
+		# then back to float.
+		is_fixed_size_float = False
+		conv = out.getConverterByName(attr)
+		if isinstance(conv, BaseFixedValue):
+			is_fixed_size_float = True
+			master_values = [conv.toInt(v) for v in master_values]
+
+		baseValue = master_values[0]
+		varIdx = ot.NO_VARIATION_INDEX
+		if not allEqual(master_values):
+			baseValue, varIdx = self.store_builder.storeMasters(master_values)
+
+		if is_fixed_size_float:
+			baseValue = conv.fromInt(baseValue)
+
+		return baseValue, varIdx
+
+	def storeVariationIndices(self, varIdxes) -> int:
+		# try to reuse an existing VarIndexBase for the same varIdxes, or else
+		# create a new one
+		key = tuple(varIdxes)
+		varIndexBase = self.varIndexCache.get(key)
+
+		if varIndexBase is None:
+			# scan for a full match anywhere in the self.varIdxes
+			for i in range(len(self.varIdxes) - len(varIdxes) + 1):
+				if self.varIdxes[i:i+len(varIdxes)] == varIdxes:
+					self.varIndexCache[key] = varIndexBase = i
+					break
+
+		if varIndexBase is None:
+			# try find a partial match at the end of the self.varIdxes
+			for n in range(len(varIdxes)-1, 0, -1):
+				if self.varIdxes[-n:] == varIdxes[:n]:
+					varIndexBase = len(self.varIdxes) - n
+					self.varIndexCache[key] = varIndexBase
+					self.varIdxes.extend(varIdxes[n:])
+					break
+
+		if varIndexBase is None:
+			# no match found, append at the end
+			self.varIndexCache[key] = varIndexBase = len(self.varIdxes)
+			self.varIdxes.extend(varIdxes)
+
+		return varIndexBase
+
+	def mergeVariableAttrs(self, out, lst, attrs) -> int:
+		varIndexBase = ot.NO_VARIATION_INDEX
+		varIdxes = []
+		for attr in attrs:
+			baseValue, varIdx = self.storeMastersForAttr(out, lst, attr)
+			setattr(out, attr, baseValue)
+			varIdxes.append(varIdx)
+
+		if any(v != ot.NO_VARIATION_INDEX for v in varIdxes):
+			varIndexBase = self.storeVariationIndices(varIdxes)
+
+		return varIndexBase
+
+	@classmethod
+	def convertSubTablesToVarType(cls, table):
+		for path in dfs_base_table(
+			table,
+			skip_root=True,
+			predicate=lambda path: (
+				getattr(type(path[-1].value), "VarType", None) is not None
+			)
+		):
+			st = path[-1]
+			subTable = st.value
+			varType = type(subTable).VarType
+			newSubTable = varType()
+			newSubTable.__dict__.update(subTable.__dict__)
+			newSubTable.populateDefaults()
+			parent = path[-2].value
+			if st.index is not None:
+				getattr(parent, st.name)[st.index] = newSubTable
+			else:
+				setattr(parent, st.name, newSubTable)
+
+	@staticmethod
+	def expandPaintColrLayers(colr):
+		"""Rebuild LayerList without PaintColrLayers reuse.
+
+		Each base paint graph is fully DFS-traversed (with exception of PaintColrGlyph
+		which are irrelevant for this); any layers referenced via PaintColrLayers are
+		collected into a new LayerList and duplicated when reuse is detected, to ensure
+		that all paints are distinct objects at the end of the process.
+		PaintColrLayers's FirstLayerIndex/NumLayers are updated so that no overlap
+		is left. Also, any consecutively nested PaintColrLayers are flattened.
+		The COLR table's LayerList is replaced with the new unique layers.
+		A side effect is also that any layer from the old LayerList which is not
+		referenced by any PaintColrLayers is dropped.
+		"""
+		if not colr.LayerList:
+			# if no LayerList, there's nothing to expand
+			return
+		uniqueLayerIDs = set()
+		newLayerList = []
+		for rec in colr.BaseGlyphList.BaseGlyphPaintRecord:
+			frontier = [rec.Paint]
+			while frontier:
+				paint = frontier.pop()
+				if paint.Format == ot.PaintFormat.PaintColrGlyph:
+					# don't traverse these, we treat them as constant for merging
+					continue
+				elif paint.Format == ot.PaintFormat.PaintColrLayers:
+					# de-treeify any nested PaintColrLayers, append unique copies to
+					# the new layer list and update PaintColrLayers index/count
+					children = list(_flatten_layers(paint, colr))
+					first_layer_index = len(newLayerList)
+					for layer in children:
+						if id(layer) in uniqueLayerIDs:
+							layer = copy.deepcopy(layer)
+							assert id(layer) not in uniqueLayerIDs
+						newLayerList.append(layer)
+						uniqueLayerIDs.add(id(layer))
+					paint.FirstLayerIndex = first_layer_index
+					paint.NumLayers = len(children)
+				else:
+					children = paint.getChildren(colr)
+				frontier.extend(reversed(children))
+		# sanity check all the new layers are distinct objects
+		assert len(newLayerList) == len(uniqueLayerIDs)
+		colr.LayerList.Paint = newLayerList
+		colr.LayerList.LayerCount = len(newLayerList)
+
+
+@COLRVariationMerger.merger(ot.BaseGlyphList)
+def merge(merger, self, lst):
+	# ignore BaseGlyphCount, allow sparse glyph sets across masters
+	out = {rec.BaseGlyph: rec for rec in self.BaseGlyphPaintRecord}
+	masters = [{rec.BaseGlyph: rec for rec in m.BaseGlyphPaintRecord} for m in lst]
+
+	for i, g in enumerate(out.keys()):
+		try:
+			# missing base glyphs don't participate in the merge
+			merger.mergeThings(out[g], [v.get(g) for v in masters])
+		except VarLibMergeError as e:
+			e.stack.append(f".BaseGlyphPaintRecord[{i}]")
+			e.cause["location"] = f"base glyph {g!r}"
+			raise
+
+	merger._doneBaseGlyphs = True
+
+
+@COLRVariationMerger.merger(ot.LayerList)
+def merge(merger, self, lst):
+	# nothing to merge for LayerList, assuming we have already merged all PaintColrLayers
+	# found while traversing the paint graphs rooted at BaseGlyphPaintRecords.
+	assert merger._doneBaseGlyphs, "BaseGlyphList must be merged before LayerList"
+	# Simply flush the final list of layers and go home.
+	self.LayerCount = len(merger.layers)
+	self.Paint = merger.layers
+
+
+def _flatten_layers(root, colr):
+	assert root.Format == ot.PaintFormat.PaintColrLayers
+	for paint in root.getChildren(colr):
+		if paint.Format == ot.PaintFormat.PaintColrLayers:
+			yield from _flatten_layers(paint, colr)
+		else:
+			yield paint
+
+
+def _merge_PaintColrLayers(self, out, lst):
+	# we only enforce that the (flat) number of layers is the same across all masters
+	# but we allow FirstLayerIndex to differ to acommodate for sparse glyph sets.
+
+	out_layers = list(_flatten_layers(out, self.font["COLR"].table))
+
+	# sanity check ttfs are subset to current values (see VariationMerger.mergeThings)
+	# before matching each master PaintColrLayers to its respective COLR by position
+	assert len(self.ttfs) == len(lst)
+	master_layerses = [
+		list(_flatten_layers(lst[i], self.ttfs[i]["COLR"].table))
+		for i in range(len(lst))
+	]
+
+	try:
+		self.mergeLists(out_layers, master_layerses)
+	except VarLibMergeError as e:
+		# NOTE: This attribute doesn't actually exist in PaintColrLayers but it's
+		# handy to have it in the stack trace for debugging.
+		e.stack.append(".Layers")
+		raise
+
+	# following block is very similar to LayerListBuilder._beforeBuildPaintColrLayers
+	# but I couldn't find a nice way to share the code between the two...
+
+	if self.layerReuseCache is not None:
+		# successful reuse can make the list smaller
+		out_layers = self.layerReuseCache.try_reuse(out_layers)
+
+	# if the list is still too big we need to tree-fy it
+	is_tree = len(out_layers) > MAX_PAINT_COLR_LAYER_COUNT
+	out_layers = build_n_ary_tree(out_layers, n=MAX_PAINT_COLR_LAYER_COUNT)
+
+	# We now have a tree of sequences with Paint leaves.
+	# Convert the sequences into PaintColrLayers.
+	def listToColrLayers(paint):
+		if isinstance(paint, list):
+			layers = [listToColrLayers(l) for l in paint]
+			paint = ot.Paint()
+			paint.Format = int(ot.PaintFormat.PaintColrLayers)
+			paint.NumLayers = len(layers)
+			paint.FirstLayerIndex = len(self.layers)
+			self.layers.extend(layers)
+			if self.layerReuseCache is not None:
+				self.layerReuseCache.add(layers, paint.FirstLayerIndex)
+		return paint
+
+	out_layers = [listToColrLayers(l) for l in out_layers]
+
+	if len(out_layers) == 1 and out_layers[0].Format == ot.PaintFormat.PaintColrLayers:
+		# special case when the reuse cache finds a single perfect PaintColrLayers match
+		# (it can only come from a successful reuse, _flatten_layers has gotten rid of
+		# all nested PaintColrLayers already); we assign it directly and avoid creating
+		# an extra table
+		out.NumLayers = out_layers[0].NumLayers
+		out.FirstLayerIndex = out_layers[0].FirstLayerIndex
+	else:
+		out.NumLayers = len(out_layers)
+		out.FirstLayerIndex = len(self.layers)
+
+		self.layers.extend(out_layers)
+
+		# Register our parts for reuse provided we aren't a tree
+		# If we are a tree the leaves registered for reuse and that will suffice
+		if self.layerReuseCache is not None and not is_tree:
+			self.layerReuseCache.add(out_layers, out.FirstLayerIndex)
+
+
+@COLRVariationMerger.merger((ot.Paint, ot.ClipBox))
+def merge(merger, self, lst):
+	fmt = merger.checkFormatEnum(self, lst, lambda fmt: not fmt.is_variable())
+
+	if fmt is ot.PaintFormat.PaintColrLayers:
+		_merge_PaintColrLayers(merger, self, lst)
+		return
+
+	varFormat = fmt.as_variable()
+
+	varAttrs = ()
+	if varFormat is not None:
+		varAttrs = otBase.getVariableAttrs(type(self), varFormat)
+	staticAttrs = (c.name for c in self.getConverters() if c.name not in varAttrs)
+
+	merger.mergeAttrs(self, lst, staticAttrs)
+
+	varIndexBase = merger.mergeVariableAttrs(self, lst, varAttrs)
+
+	subTables = [st.value for st in self.iterSubTables()]
+
+	# Convert table to variable if itself has variations or any subtables have
+	isVariable = (
+		varIndexBase != ot.NO_VARIATION_INDEX
+		or any(id(table) in merger.varTableIds for table in subTables)
+	)
+
+	if isVariable:
+		if varAttrs:
+			# Some PaintVar* don't have any scalar attributes that can vary,
+			# only indirect offsets to other variable subtables, thus have
+			# no VarIndexBase of their own (e.g. PaintVarTransform)
+			self.VarIndexBase = varIndexBase
+
+		if subTables:
+			# Convert Affine2x3 -> VarAffine2x3, ColorLine -> VarColorLine, etc.
+			merger.convertSubTablesToVarType(self)
+
+		assert varFormat is not None
+		self.Format = int(varFormat)
+
+
+@COLRVariationMerger.merger((ot.Affine2x3, ot.ColorStop))
+def merge(merger, self, lst):
+	varType = type(self).VarType
+
+	varAttrs = otBase.getVariableAttrs(varType)
+	staticAttrs = (c.name for c in self.getConverters() if c.name not in varAttrs)
+
+	merger.mergeAttrs(self, lst, staticAttrs)
+
+	varIndexBase = merger.mergeVariableAttrs(self, lst, varAttrs)
+
+	if varIndexBase != ot.NO_VARIATION_INDEX:
+		self.VarIndexBase = varIndexBase
+		# mark as having variations so the parent table will convert to Var{Type}
+		merger.varTableIds.add(id(self))
+
+
+@COLRVariationMerger.merger(ot.ColorLine)
+def merge(merger, self, lst):
+	merger.mergeAttrs(self, lst, (c.name for c in self.getConverters()))
+
+	if any(id(stop) in merger.varTableIds for stop in self.ColorStop):
+		merger.convertSubTablesToVarType(self)
+		merger.varTableIds.add(id(self))
+
+
+@COLRVariationMerger.merger(ot.ClipList, "clips")
+def merge(merger, self, lst):
+	# 'sparse' in that we allow non-default masters to omit ClipBox entries
+	# for some/all glyphs (i.e. they don't participate)
+	merger.mergeSparseDict(self, lst)
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index c548fbc..a7e020b 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -1,11 +1,6 @@
 """Variation fonts interpolation models."""
 
 __all__ = [
-    "nonNone",
-    "allNone",
-    "allEqual",
-    "allEqualTo",
-    "subList",
     "normalizeValue",
     "normalizeLocation",
     "supportScalar",
@@ -50,12 +45,13 @@
 
 def normalizeValue(v, triple):
     """Normalizes value based on a min/default/max triple.
-    >>> normalizeValue(400, (100, 400, 900))
-    0.0
-    >>> normalizeValue(100, (100, 400, 900))
-    -1.0
-    >>> normalizeValue(650, (100, 400, 900))
-    0.5
+
+      >>> normalizeValue(400, (100, 400, 900))
+      0.0
+      >>> normalizeValue(100, (100, 400, 900))
+      -1.0
+      >>> normalizeValue(650, (100, 400, 900))
+      0.5
     """
     lower, default, upper = triple
     if not (lower <= default <= upper):
@@ -75,41 +71,42 @@
 
 def normalizeLocation(location, axes):
     """Normalizes location based on axis min/default/max values from axes.
-    >>> axes = {"wght": (100, 400, 900)}
-    >>> normalizeLocation({"wght": 400}, axes)
-    {'wght': 0.0}
-    >>> normalizeLocation({"wght": 100}, axes)
-    {'wght': -1.0}
-    >>> normalizeLocation({"wght": 900}, axes)
-    {'wght': 1.0}
-    >>> normalizeLocation({"wght": 650}, axes)
-    {'wght': 0.5}
-    >>> normalizeLocation({"wght": 1000}, axes)
-    {'wght': 1.0}
-    >>> normalizeLocation({"wght": 0}, axes)
-    {'wght': -1.0}
-    >>> axes = {"wght": (0, 0, 1000)}
-    >>> normalizeLocation({"wght": 0}, axes)
-    {'wght': 0.0}
-    >>> normalizeLocation({"wght": -1}, axes)
-    {'wght': 0.0}
-    >>> normalizeLocation({"wght": 1000}, axes)
-    {'wght': 1.0}
-    >>> normalizeLocation({"wght": 500}, axes)
-    {'wght': 0.5}
-    >>> normalizeLocation({"wght": 1001}, axes)
-    {'wght': 1.0}
-    >>> axes = {"wght": (0, 1000, 1000)}
-    >>> normalizeLocation({"wght": 0}, axes)
-    {'wght': -1.0}
-    >>> normalizeLocation({"wght": -1}, axes)
-    {'wght': -1.0}
-    >>> normalizeLocation({"wght": 500}, axes)
-    {'wght': -0.5}
-    >>> normalizeLocation({"wght": 1000}, axes)
-    {'wght': 0.0}
-    >>> normalizeLocation({"wght": 1001}, axes)
-    {'wght': 0.0}
+
+      >>> axes = {"wght": (100, 400, 900)}
+      >>> normalizeLocation({"wght": 400}, axes)
+      {'wght': 0.0}
+      >>> normalizeLocation({"wght": 100}, axes)
+      {'wght': -1.0}
+      >>> normalizeLocation({"wght": 900}, axes)
+      {'wght': 1.0}
+      >>> normalizeLocation({"wght": 650}, axes)
+      {'wght': 0.5}
+      >>> normalizeLocation({"wght": 1000}, axes)
+      {'wght': 1.0}
+      >>> normalizeLocation({"wght": 0}, axes)
+      {'wght': -1.0}
+      >>> axes = {"wght": (0, 0, 1000)}
+      >>> normalizeLocation({"wght": 0}, axes)
+      {'wght': 0.0}
+      >>> normalizeLocation({"wght": -1}, axes)
+      {'wght': 0.0}
+      >>> normalizeLocation({"wght": 1000}, axes)
+      {'wght': 1.0}
+      >>> normalizeLocation({"wght": 500}, axes)
+      {'wght': 0.5}
+      >>> normalizeLocation({"wght": 1001}, axes)
+      {'wght': 1.0}
+      >>> axes = {"wght": (0, 1000, 1000)}
+      >>> normalizeLocation({"wght": 0}, axes)
+      {'wght': -1.0}
+      >>> normalizeLocation({"wght": -1}, axes)
+      {'wght': -1.0}
+      >>> normalizeLocation({"wght": 500}, axes)
+      {'wght': -0.5}
+      >>> normalizeLocation({"wght": 1000}, axes)
+      {'wght': 0.0}
+      >>> normalizeLocation({"wght": 1001}, axes)
+      {'wght': 0.0}
     """
     out = {}
     for tag, triple in axes.items():
@@ -118,27 +115,32 @@
     return out
 
 
-def supportScalar(location, support, ot=True):
+def supportScalar(location, support, ot=True, extrapolate=False):
     """Returns the scalar multiplier at location, for a master
     with support.  If ot is True, then a peak value of zero
     for support of an axis means "axis does not participate".  That
     is how OpenType Variation Font technology works.
-    >>> supportScalar({}, {})
-    1.0
-    >>> supportScalar({'wght':.2}, {})
-    1.0
-    >>> supportScalar({'wght':.2}, {'wght':(0,2,3)})
-    0.1
-    >>> supportScalar({'wght':2.5}, {'wght':(0,2,4)})
-    0.75
-    >>> supportScalar({'wght':2.5, 'wdth':0}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
-    0.75
-    >>> supportScalar({'wght':2.5, 'wdth':.5}, {'wght':(0,2,4), 'wdth':(-1,0,+1)}, ot=False)
-    0.375
-    >>> supportScalar({'wght':2.5, 'wdth':0}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
-    0.75
-    >>> supportScalar({'wght':2.5, 'wdth':.5}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
-    0.75
+
+      >>> supportScalar({}, {})
+      1.0
+      >>> supportScalar({'wght':.2}, {})
+      1.0
+      >>> supportScalar({'wght':.2}, {'wght':(0,2,3)})
+      0.1
+      >>> supportScalar({'wght':2.5}, {'wght':(0,2,4)})
+      0.75
+      >>> supportScalar({'wght':2.5, 'wdth':0}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
+      0.75
+      >>> supportScalar({'wght':2.5, 'wdth':.5}, {'wght':(0,2,4), 'wdth':(-1,0,+1)}, ot=False)
+      0.375
+      >>> supportScalar({'wght':2.5, 'wdth':0}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
+      0.75
+      >>> supportScalar({'wght':2.5, 'wdth':.5}, {'wght':(0,2,4), 'wdth':(-1,0,+1)})
+      0.75
+      >>> supportScalar({'wght':4}, {'wght':(0,2,3)}, extrapolate=True)
+      2.0
+      >>> supportScalar({'wght':4}, {'wght':(0,2,2)}, extrapolate=True)
+      2.0
     """
     scalar = 1.0
     for axis, (lower, peak, upper) in support.items():
@@ -156,9 +158,27 @@
             v = location[axis]
         if v == peak:
             continue
+
+        if extrapolate:
+            if v < -1 and lower <= -1:
+                if peak <= -1 and peak < upper:
+                    scalar *= (v - upper) / (peak - upper)
+                    continue
+                elif -1 < peak:
+                    scalar *= (v - lower) / (peak - lower)
+                    continue
+            elif +1 < v and +1 <= upper:
+                if +1 <= peak and lower < peak:
+                    scalar *= (v - lower) / (peak - lower)
+                    continue
+                elif peak < +1:
+                    scalar *= (v - upper) / (peak - upper)
+                    continue
+
         if v <= lower or upper <= v:
             scalar = 0.0
             break
+
         if v < peak:
             scalar *= (v - lower) / (peak - lower)
         else:  # v > peak
@@ -167,10 +187,11 @@
 
 
 class VariationModel(object):
+    """Locations must have the base master at the origin (ie. 0).
 
-    """
-  Locations must be in normalized space.  Ie. base master
-  is at origin (0)::
+    If the extrapolate argument is set to True, then location values are
+    interpretted in the normalized space, ie. in the [-1,+1] range, and
+    values are extrapolated outside this range.
 
       >>> from pprint import pprint
       >>> locations = [ \
@@ -210,14 +231,16 @@
         5: 0.6666666666666667,
         6: 0.4444444444444445,
         7: 0.6666666666666667}]
-	"""
+    """
 
-    def __init__(self, locations, axisOrder=None):
+    def __init__(self, locations, axisOrder=None, extrapolate=False):
+
         if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
             raise VariationModelError("Locations must be unique.")
 
         self.origLocations = locations
         self.axisOrder = axisOrder if axisOrder is not None else []
+        self.extrapolate = extrapolate
 
         locations = [{k: v for k, v in loc.items() if v != 0.0} for loc in locations]
         keyFunc = self.getMasterLocationsSortKeyFunc(
@@ -416,7 +439,8 @@
         return model.getDeltas(items, round=round), model.supports
 
     def getScalars(self, loc):
-        return [supportScalar(loc, support) for support in self.supports]
+        return [supportScalar(loc, support, extrapolate=self.extrapolate)
+                for support in self.supports]
 
     @staticmethod
     def interpolateFromDeltasAndScalars(deltas, scalars):
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
index 263c4e6..2e67479 100644
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -412,6 +412,9 @@
 	parser.add_argument(
 		"-o", "--output", metavar="OUTPUT.ttf", default=None,
 		help="Output instance TTF file (default: INPUT-instance.ttf).")
+	parser.add_argument(
+		"--no-recalc-timestamp", dest="recalc_timestamp", action='store_false',
+		help="Don't set the output font's timestamp to the current time.")
 	logging_group = parser.add_mutually_exclusive_group(required=False)
 	logging_group.add_argument(
 		"-v", "--verbose", action="store_true", help="Run more verbosely.")
@@ -445,7 +448,7 @@
 	log.info("Location: %s", loc)
 
 	log.info("Loading variable font")
-	varfont = TTFont(varfilename)
+	varfont = TTFont(varfilename, recalcTimestamp=options.recalc_timestamp)
 
 	instantiateVariableFont(varfont, loc, inplace=True, overlap=options.overlap)
 
diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py
index bcf81b3..2ffc6b1 100644
--- a/Lib/fontTools/varLib/varStore.py
+++ b/Lib/fontTools/varLib/varStore.py
@@ -7,6 +7,10 @@
 from collections import defaultdict
 
 
+NO_VARIATION_INDEX = ot.NO_VARIATION_INDEX
+ot.VarStore.NO_VARIATION_INDEX = NO_VARIATION_INDEX
+
+
 def _getLocationKey(loc):
 	return tuple(sorted(loc.items(), key=lambda kv: kv[0]))
 
@@ -135,6 +139,11 @@
 
 ot.VarRegion.get_support = VarRegion_get_support
 
+def VarStore___bool__(self):
+    return bool(self.VarData)
+
+ot.VarStore.__bool__ = VarStore___bool__
+
 class VarStoreInstancer(object):
 
 	def __init__(self, varstore, fvar_axes, location={}):
@@ -169,6 +178,7 @@
 
 	def __getitem__(self, varidx):
 		major, minor = varidx >> 16, varidx & 0xFFFF
+		if varidx == NO_VARIATION_INDEX: return 0.
 		varData = self._varData
 		scalars = [self._getScalar(ri) for ri in varData[major].VarRegionIndex]
 		deltas = varData[major].Item[minor]
@@ -192,6 +202,8 @@
 	# Sort out used varIdxes by major/minor.
 	used = {}
 	for varIdx in varIdxes:
+		if varIdx == NO_VARIATION_INDEX:
+			continue
 		major = varIdx >> 16
 		minor = varIdx & 0xFFFF
 		d = used.get(major)
@@ -206,7 +218,7 @@
 
 	varData = self.VarData
 	newVarData = []
-	varDataMap = {}
+	varDataMap = {NO_VARIATION_INDEX: NO_VARIATION_INDEX}
 	for major,data in enumerate(varData):
 		usedMinors = used.get(major)
 		if usedMinors is None:
@@ -431,7 +443,7 @@
 		return chars
 
 
-def VarStore_optimize(self):
+def VarStore_optimize(self, use_NO_VARIATION_INDEX=True):
 	"""Optimize storage. Returns mapping from old VarIdxes to new ones."""
 
 	# TODO
@@ -455,6 +467,10 @@
 				row[regionIdx] += v
 			row = tuple(row)
 
+			if use_NO_VARIATION_INDEX and not any(row):
+				front_mapping[(major<<16)+minor] = None
+				continue
+
 			encodings.add_row(row)
 			front_mapping[(major<<16)+minor] = row
 
@@ -537,9 +553,9 @@
 			back_mapping[item] = (major<<16)+minor
 
 	# Compile final mapping.
-	varidx_map = {}
+	varidx_map = {NO_VARIATION_INDEX:NO_VARIATION_INDEX}
 	for k,v in front_mapping.items():
-		varidx_map[k] = back_mapping[v]
+		varidx_map[k] = back_mapping[v] if v is not None else NO_VARIATION_INDEX
 
 	# Remove unused regions.
 	self.prune_regions()
diff --git a/METADATA b/METADATA
index 71fb8a6..7ab3b75 100644
--- a/METADATA
+++ b/METADATA
@@ -10,14 +10,14 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.com/fonttools/fonttools/archive/4.33.3.zip"
+    value: "https://github.com/fonttools/fonttools/archive/4.37.1.zip"
   }
-  version: "4.33.3"
+  version: "4.37.1"
   license_type: BY_EXCEPTION_ONLY
   license_note: "contains OFL fonts"
   last_upgrade_date {
     year: 2022
-    month: 5
-    day: 12
+    month: 8
+    day: 24
   }
 }
diff --git a/NEWS.rst b/NEWS.rst
index d500dd9..f022ad2 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,152 @@
+4.37.1 (released 2022-08-24)
+----------------------------
+
+- [subset] Fixed regression introduced with v4.37.0 while subsetting the VarStore of
+  ``HVAR`` and ``VVAR`` tables, whereby an ``AttributeError: subset_varidxes`` was
+  thrown because an apparently unused import statement (with the side-effect of
+  dynamically binding that ``subset_varidxes`` method to the VarStore class) had been
+  accidentally deleted in an unrelated PR (#2679, #2773).
+- [pens] Added ``cairoPen`` (#2678).
+- [gvar] Read ``gvar`` more lazily by not parsing all of the ``glyf`` table (#2771).
+- [ttGlyphSet] Make ``drawPoints(pointPen)`` method work for CFF fonts as well via
+  adapter pen (#2770).
+
+4.37.0 (released 2022-08-23)
+----------------------------
+
+- [varLib.models] Reverted PR #2717 which added support for "narrow tents" in v4.36.0,
+  as it introduced a regression (#2764, #2765). It will be restored in upcoming release
+  once we found a solution to the bug.
+- [cff.specializer] Fixed issue in charstring generalizer with the ``blend`` operator
+  (#2750, #1975).
+- [varLib.models] Added support for extrapolation (#2757).
+- [ttGlyphSet] Ensure the newly added ``_TTVarGlyphSet`` inherits from ``_TTGlyphSet``
+  to keep backward compatibility with existing API (#2762).
+- [kern] Allow compiling legacy kern tables with more than 64k entries (d21cfdede).
+- [visitor] Added new visitor API to traverse tree of objects and dispatch based
+  on the attribute type: cf. ``fontTools.misc.visitor`` and ``fontTools.ttLib.ttVisitor``. Added ``fontTools.ttLib.scaleUpem`` module that uses the latter to
+  change a font's units-per-em and scale all the related fields accordingly (#2718,
+  #2755).
+
+4.36.0 (released 2022-08-17)
+----------------------------
+
+- [varLib.models] Use a simpler model that generates narrower "tents" (regions, master
+  supports) whenever possible: specifically when any two axes that actively "cooperate"
+  (have masters at non-zero positions for both axes) have a complete set of intermediates.
+  The simpler algorithm produces fewer overlapping regions and behaves better with
+  respect to rounding at the peak positions than the generic solver, always matching
+  intermediate masters exactly, instead of maximally 0.5 units off. This may be useful
+  when 100% metrics compatibility is desired (#2218, #2717).
+- [feaLib] Remove warning when about ``GDEF`` not being built when explicitly not
+  requested; don't build one unconditonally even when not requested (#2744, also works
+  around #2747).
+- [ttFont] ``TTFont.getGlyphSet`` method now supports selecting a location that
+  represents an instance of a variable font (supports both user-scale and normalized
+  axes coordinates via the ``normalized=False`` parameter). Currently this only works
+  for TrueType-flavored variable fonts (#2738).
+
+4.35.0 (released 2022-08-15)
+----------------------------
+
+- [otData/otConverters] Added support for 'biased' PaintSweepGradient start/end angles
+  to match latest COLRv1 spec (#2743).
+- [varLib.instancer] Fixed bug in ``_instantiateFeatureVariations`` when at the same
+  time pinning one axis and restricting the range of a subsequent axis; the wrong axis
+  tag was being used in the latter step (as the records' axisIdx was updated in the
+  preceding step but looked up using the old axes order in the following step) (#2733,
+  #2734).
+- [mtiLib] Pad script tags with space when less than 4 char long (#1727).
+- [merge] Use ``'.'`` instead of ``'#'`` in duplicate glyph names (#2742).
+- [gvar] Added support for lazily loading glyph variations (#2741).
+- [varLib] In ``build_many``, we forgot to pass on ``colr_layer_reuse`` parameter to
+  the ``build`` method (#2730).
+- [svgPathPen] Add a main that prints SVG for input text (6df779fd).
+- [cffLib.width] Fixed off-by-one in optimized values; previous code didn't match the
+  code block above it (2963fa50).
+- [varLib.interpolatable] Support reading .designspace and .glyphs files (via optional
+  ``glyphsLib``).
+- Compile some modules with Cython when available and building/installing fonttools
+  from source: ``varLib.iup`` (35% faster), ``pens.momentsPen`` (makes
+  ``varLib.interpolatable`` 3x faster).
+- [feaLib] Allow features to be built for VF without also building a GDEF table (e.g.
+  only build GSUB); warn when GDEF would be needed but isn't requested (#2705, 2694).
+- [otBase] Fixed ``AttributeError`` when uharfbuzz < 0.23.0 and 'repack' method is
+  missing (32aa8eaf). Use new ``uharfbuzz.repack_with_tag`` when available (since
+  uharfbuzz>=0.30.0), enables table-specific optimizations to be performed during
+  repacking (#2724).
+- [statisticsPen] By default report all glyphs (4139d891). Avoid division-by-zero
+  (52b28f90).
+- [feaLib] Added missing required argument to FeatureLibError exception (#2693)
+- [varLib.merge] Fixed error during error reporting (#2689). Fixed undefined
+  ``NotANone`` variable (#2714).
+
+4.34.4 (released 2022-07-07)
+----------------------------
+
+- Fixed typo in varLib/merger.py that causes NameError merging COLR glyphs
+  containing more than 255 layers (#2685).
+
+4.34.3 (released 2022-07-07)
+----------------------------
+
+- [designspaceLib] Don't make up bad PS names when no STAT data (#2684)
+
+4.34.2 (released 2022-07-06)
+----------------------------
+
+- [varStore/subset] fixed KeyError exception to do with NO_VARIATION_INDEX while
+  subsetting varidxes in GPOS/GDEF (a08140d).
+
+4.34.1 (released 2022-07-06)
+----------------------------
+
+- [instancer] When optimizing HVAR/VVAR VarStore, use_NO_VARIATION_INDEX=False to avoid
+  including NO_VARIATION_INDEX in AdvWidthMap, RsbMap, LsbMap mappings, which would
+  push the VarIdx width to maximum (4bytes), which is not desirable. This also fixes
+  a hard crash when attempting to subset a varfont after it had been partially instanced
+  with use_NO_VARIATION_INDEX=True.
+
+4.34.0 (released 2022-07-06)
+----------------------------
+
+- [instancer] Set RIBBI bits in head and OS/2 table when cutting instances and the
+  subfamily nameID=2 contains strings like 'Italic' or 'Bold' (#2673).
+- [otTraverse] Addded module containing methods for traversing trees of otData tables
+  (#2660).
+- [otTables] Made DeltaSetIndexMap TTX dump less verbose by omitting no-op entries
+  (#2660).
+- [colorLib.builder] Added option to disable PaintColrLayers's reuse of layers from
+  LayerList (#2660).
+- [varLib] Added support for merging multiple master COLRv1 tables into a variable
+  COLR table (#2660, #2328). Base color glyphs of same name in different masters must have
+  identical paint graph structure (incl. number of layers, palette indices, number
+  of color line stops, corresponding paint formats at each level of the graph),
+  but can differ in the variable fields (e.g. PaintSolid.Alpha). PaintVar* tables
+  are produced when this happens and a VarStore/DeltaSetIndexMap is added to the
+  variable COLR table. It is possible for non-default masters to be 'sparse', i.e.
+  omit some of the color glyphs present in the default master.
+- [feaLib] Let the Parser set nameIDs 1 through 6 that were previously reserved (#2675).
+- [varLib.varStore] Support NO_VARIATION_INDEX in optimizer and instancer.
+- [feaLib] Show all missing glyphs at once at end of parsing (#2665).
+- [varLib.iup] Rewrite force-set conditions and limit DP loopback length (#2651).
+  For Noto Sans, IUP time drops from 23s down to 9s, with only a slight size increase
+  in the final font. This basically turns the algorithm from O(n^3) into O(n).
+- [featureVars] Report about missing glyphs in substitution rules (#2654).
+- [mutator/instancer] Added CLI flag to --no-recalc-timestamp (#2649).
+- [SVG] Allow individual SVG documents in SVG OT table to be compressed on uncompressed,
+  and remember that when roundtripping to/from ttx. The SVG.docList is now a list
+  of SVGDocument namedtuple-like dataclass containing an extra ``compressed`` field,
+  and no longer a bare 3-tuple (#2645).
+- [designspaceLib] Check for descriptor types with hasattr() to allow custom classes
+  that don't inherit the default descriptors (#2634).
+- [subset] Enable sharing across subtables of extension lookups for harfbuzz packing
+  (#2626). Updated how table packing falls back to fontTools from harfbuzz (#2668).
+- [subset] Updated default feature tags following current Harfbuzz (#2637).
+- [svgLib] Fixed regex for real number to support e.g. 1e-4 in addition to 1.0e-4.
+  Support parsing negative rx, ry on arc commands (#2596, #2611).
+- [subset] Fixed subsetting SinglePosFormat2 when ValueFormat=0 (#2603).
+
 4.33.3 (released 2022-04-26)
 ----------------------------
 
diff --git a/Snippets/print-json.py b/Snippets/print-json.py
new file mode 100644
index 0000000..bcd255e
--- /dev/null
+++ b/Snippets/print-json.py
@@ -0,0 +1,153 @@
+import fontTools.ttLib as ttLib
+from fontTools.ttLib.ttVisitor import TTVisitor
+from fontTools.misc.textTools import Tag
+from array import array
+
+
+class JsonVisitor(TTVisitor):
+    def _open(self, s):
+        print(s, file=self.file)
+        self._indent += self.indent
+        self.comma = False
+
+    def _close(self, s):
+        self._indent = self._indent[: -len(self.indent)]
+        print("\n%s%s" % (self._indent, s), end="", file=self.file)
+        self.comma = True
+
+    def __init__(self, file, indent="  "):
+        self.file = file
+        self.indent = indent
+        self._indent = ""
+
+    def visitObject(self, obj):
+        self._open("{")
+        super().visitObject(obj)
+        if self.comma:
+            print(",", end="", file=self.file)
+        print(
+            '\n%s"type": "%s"' % (self._indent, obj.__class__.__name__),
+            end="",
+            file=self.file,
+        )
+        self._close("}")
+
+    def visitAttr(self, obj, attr, value):
+        if self.comma:
+            print(",", file=self.file)
+        print('%s"%s": ' % (self._indent, attr), end="", file=self.file)
+        self.visit(value)
+        self.comma = True
+
+    def visitList(self, obj, *args, **kwargs):
+        self._open("[")
+        comma = False
+        for value in obj:
+            if comma:
+                print(",", end="", file=self.file)
+                print(file=self.file)
+            print(self._indent, end="", file=self.file)
+            self.visit(value, *args, **kwargs)
+            comma = True
+        self._close("]")
+
+    def visitDict(self, obj, *args, **kwargs):
+        self._open("{")
+        comma = False
+        for key, value in obj.items():
+            if comma:
+                print(",", end="", file=self.file)
+                print(file=self.file)
+            print('%s"%s": ' % (self._indent, key), end="", file=self.file)
+            self.visit(value, *args, **kwargs)
+            comma = True
+        self._close("}")
+
+    def visitLeaf(self, obj):
+        if isinstance(obj, tuple):
+            obj = list(obj)
+        elif isinstance(obj, bytes):
+            obj = list(obj)
+
+        if obj is None:
+            s = "null"
+        elif obj is True:
+            s = "true"
+        elif obj is False:
+            s = "false"
+        else:
+            s = repr(obj)
+
+        if s[0] == "'":
+            s = '"' + s[1:-1] + '"'
+
+        print("%s" % s, end="", file=self.file)
+
+
+@JsonVisitor.register(ttLib.TTFont)
+def visit(self, font):
+    if hasattr(visitor, "font"):
+        print("{}", end="", file=self.file)
+        return False
+    visitor.font = font
+
+    self._open("{")
+    for tag in font.keys():
+        if self.comma:
+            print(",", file=self.file)
+        print('\n%s"%s": ' % (self._indent, tag), end="", file=self.file)
+        visitor.visit(font[tag])
+    self._close("}")
+
+    del visitor.font
+    return False
+
+
+@JsonVisitor.register(ttLib.GlyphOrder)
+def visit(self, obj):
+    self.visitList(self.font.getGlyphOrder())
+    return False
+
+
+@JsonVisitor.register_attr(ttLib.getTableClass("glyf"), "glyphOrder")
+def visit(visitor, obj, attr, value):
+    return False
+
+
+@JsonVisitor.register(ttLib.getTableModule("glyf").GlyphCoordinates)
+def visit(self, obj):
+    self.visitList(obj)
+    return False
+
+
+@JsonVisitor.register(Tag)
+def visit(self, obj):
+    print('"%s"' % str(obj), end="", file=self.file)
+    return False
+
+
+@JsonVisitor.register(array)
+def visit(self, obj):
+    self.visitList(obj)
+    return False
+
+
+@JsonVisitor.register(bytearray)
+def visit(self, obj):
+    self.visitList(obj)
+    return False
+
+
+if __name__ == "__main__":
+
+    from fontTools.ttLib import TTFont
+    import sys
+
+    if len(sys.argv) != 2:
+        print("usage: print-json.py font")
+        sys.exit()
+
+    font = TTFont(sys.argv[1])
+
+    visitor = JsonVisitor(sys.stdout)
+    visitor.visit(font)
diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py
index 7259db4..3cdb2e9 100644
--- a/Tests/colorLib/builder_test.py
+++ b/Tests/colorLib/builder_test.py
@@ -3,7 +3,7 @@
 from fontTools.ttLib.tables import otTables as ot
 from fontTools.colorLib import builder
 from fontTools.colorLib.geometry import round_start_circle_stable_containment, Circle
-from fontTools.colorLib.builder import LayerListBuilder, _build_n_ary_tree
+from fontTools.colorLib.builder import LayerListBuilder
 from fontTools.colorLib.table_builder import TableBuilder
 from fontTools.colorLib.errors import ColorLibError
 import pytest
@@ -1678,7 +1678,7 @@
             clipBoxes={
                 "a": (0, 0, 1000, 1000, 0),  # optional 5th: varIndexBase
                 "c": (-100.8, -200.4, 1100.1, 1200.5),  # floats get rounded
-                "e": (0, 0, 10, 10),  # missing base glyph 'e' is ignored
+                "e": (0, 0, 10, 10),  # 'e' does _not_ get ignored despite being missing
             },
         )
 
@@ -1689,9 +1689,11 @@
         ] == [
             ("a", (0, 0, 1000, 1000, 0)),
             ("c", (-101, -201, 1101, 1201)),
+            ("e", (0, 0, 10, 10)),
         ]
         assert clipBoxes["a"].Format == 2
         assert clipBoxes["c"].Format == 1
+        assert clipBoxes["e"].Format == 1
 
     def test_duplicate_base_glyphs(self):
         # If > 1 base glyphs refer to equivalent list of layers we expect them to share
@@ -1778,81 +1780,3 @@
     )
     def test_nudge_start_circle_position(self, c0, r0, c1, r1, inside, expected):
         assert self.round_start_circle(c0, r0, c1, r1, inside) == expected
-
-
-@pytest.mark.parametrize(
-    "lst, n, expected",
-    [
-        ([0], 2, [0]),
-        ([0, 1], 2, [0, 1]),
-        ([0, 1, 2], 2, [[0, 1], 2]),
-        ([0, 1, 2], 3, [0, 1, 2]),
-        ([0, 1, 2, 3], 2, [[0, 1], [2, 3]]),
-        ([0, 1, 2, 3], 3, [[0, 1, 2], 3]),
-        ([0, 1, 2, 3, 4], 3, [[0, 1, 2], 3, 4]),
-        ([0, 1, 2, 3, 4, 5], 3, [[0, 1, 2], [3, 4, 5]]),
-        (list(range(7)), 3, [[0, 1, 2], [3, 4, 5], 6]),
-        (list(range(8)), 3, [[0, 1, 2], [3, 4, 5], [6, 7]]),
-        (list(range(9)), 3, [[0, 1, 2], [3, 4, 5], [6, 7, 8]]),
-        (list(range(10)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], 9]),
-        (list(range(11)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], 9, 10]),
-        (list(range(12)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11]]),
-        (list(range(13)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11], 12]),
-        (
-            list(range(14)),
-            3,
-            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], 12, 13]],
-        ),
-        (
-            list(range(15)),
-            3,
-            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11], [12, 13, 14]],
-        ),
-        (
-            list(range(16)),
-            3,
-            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], [12, 13, 14], 15]],
-        ),
-        (
-            list(range(23)),
-            3,
-            [
-                [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
-                [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
-                [[18, 19, 20], 21, 22],
-            ],
-        ),
-        (
-            list(range(27)),
-            3,
-            [
-                [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
-                [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
-                [[18, 19, 20], [21, 22, 23], [24, 25, 26]],
-            ],
-        ),
-        (
-            list(range(28)),
-            3,
-            [
-                [
-                    [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
-                    [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
-                    [[18, 19, 20], [21, 22, 23], [24, 25, 26]],
-                ],
-                27,
-            ],
-        ),
-        (list(range(257)), 256, [list(range(256)), 256]),
-        (list(range(258)), 256, [list(range(256)), 256, 257]),
-        (list(range(512)), 256, [list(range(256)), list(range(256, 512))]),
-        (list(range(512 + 1)), 256, [list(range(256)), list(range(256, 512)), 512]),
-        (
-            list(range(256 ** 2)),
-            256,
-            [list(range(k * 256, k * 256 + 256)) for k in range(256)],
-        ),
-    ],
-)
-def test_build_n_ary_tree(lst, n, expected):
-    assert _build_n_ary_tree(lst, n) == expected
diff --git a/Tests/colorLib/unbuilder_test.py b/Tests/colorLib/unbuilder_test.py
index 3548968..fe5dc7d 100644
--- a/Tests/colorLib/unbuilder_test.py
+++ b/Tests/colorLib/unbuilder_test.py
@@ -221,7 +221,26 @@
                 "Glyph": "glyph00012",
             },
         ],
-    }
+    },
+    # When PaintColrLayers contains more than 255 layers, we build a tree
+    # of nested PaintColrLayers of max 255 items (NumLayers field is a uint8).
+    # Below we test that unbuildColrV1 restores a flat list of layers without
+    # nested PaintColrLayers.
+    "glyph00017": {
+        "Format": int(ot.PaintFormat.PaintColrLayers),
+        "Layers": [
+            {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintSolid),
+                    "PaletteIndex": i,
+                    "Alpha": 1.0,
+                },
+                "Glyph": "glyph{str(18 + i).zfill(5)}",
+            }
+            for i in range(256)
+        ],
+    },
 }
 
 
@@ -230,7 +249,8 @@
     colorGlyphs = unbuildColrV1(layers, baseGlyphs)
     assert colorGlyphs == TEST_COLOR_GLYPHS
 
+
 def test_unbuildColrV1_noLayers():
     _, baseGlyphsV1 = buildColrV1(TEST_COLOR_GLYPHS)
     # Just looking to see we don't crash
-    unbuildColrV1(None, baseGlyphsV1)
\ No newline at end of file
+    unbuildColrV1(None, baseGlyphsV1)
diff --git a/Tests/designspaceLib/data/DS5BreakTest.designspace b/Tests/designspaceLib/data/DS5BreakTest.designspace
new file mode 100644
index 0000000..fec1633
--- /dev/null
+++ b/Tests/designspaceLib/data/DS5BreakTest.designspace
@@ -0,0 +1,56 @@
+<designspace format="4.0">
+    <axes>
+        <axis default="400" maximum="800" minimum="200" name="Weight" tag="wght">
+            <map input="200" output="0" />
+            <map input="400" output="250" />
+            <map input="800" output="1000" />
+        </axis>
+    </axes>
+
+    <sources>
+        <source familyname="DS5BreakTest" filename="DS5BreakTest-Extralight.ufo" stylename="ExtraLight Condensed">
+            <location>
+                <dimension name="Weight" xvalue="0" />
+            </location>
+        </source>
+        <source familyname="DS5BreakTest" filename="DS5BreakTest-Regular.ufo" stylename="Regular">
+            <location>
+                <dimension name="Weight" xvalue="250" />
+            </location>
+        </source>
+        <source familyname="DS5BreakTest" filename="DS5BreakTest-Extrabold.ufo" stylename="ExtraBold">
+            <location>
+                <dimension name="Weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+
+    <instances>
+        <instance familyname="DS5BreakTest" stylename="ExtraLight">
+            <location>
+                <dimension name="Weight" xvalue="0" />
+            </location>
+        </instance>
+        <instance familyname="DS5BreakTest" stylename="Regular">
+            <location>
+                <dimension name="Weight" xvalue="250" />
+            </location>
+        </instance>
+        <instance familyname="DS5BreakTest" stylename="Medium">
+            <location>
+                <dimension name="Weight" xvalue="400" />
+            </location>
+        </instance>
+        <instance familyname="DS5BreakTest" stylename="Bold">
+            <location>
+                <dimension name="Weight" xvalue="750" />
+            </location>
+        </instance>
+        <instance familyname="DS5BreakTest" stylename="ExtraBold">
+            <location>
+                <dimension name="Weight" xvalue="1000" />
+            </location>
+        </instance>
+    </instances>
+
+</designspace>
diff --git a/Tests/designspaceLib/data/test_v5.designspace b/Tests/designspaceLib/data/test_v5.designspace
index 2f611b4..d2b3cda 100644
--- a/Tests/designspaceLib/data/test_v5.designspace
+++ b/Tests/designspaceLib/data/test_v5.designspace
@@ -1,7 +1,7 @@
 <?xml version='1.0' encoding='UTF-8'?>
 <designspace format="5.0">
   <axes elidedfallbackname="Regular">
-    <axis tag="wght" name="weight" minimum="200" maximum="1000" default="200">
+    <axis tag="wght" name="Weight" minimum="200" maximum="1000" default="200">
       <labelname xml:lang="en">Wéíght</labelname>
       <labelname xml:lang="fa-IR">قطر</labelname>
       <map input="200" output="0"/>
@@ -24,7 +24,7 @@
       </labels>
     </axis>
 
-    <axis tag="wdth" name="width" minimum="50" maximum="150" default="100" hidden="1">
+    <axis tag="wdth" name="Width" minimum="50" maximum="150" default="100" hidden="1">
       <labelname xml:lang="fr">Chasse</labelname>
       <map input="50" output="10"/>
       <map input="100" output="20"/>
@@ -59,15 +59,15 @@
     <label name="Some Style">
       <labelname xml:lang="fr">Un Style</labelname>
       <location>
-        <dimension name="weight" uservalue="300"/>
-        <dimension name="width" uservalue="50"/>
+        <dimension name="Weight" uservalue="300"/>
+        <dimension name="Width" uservalue="50"/>
         <dimension name="Italic" uservalue="0"/>
       </location>
     </label>
     <label name="Other">
       <location>
-        <dimension name="weight" uservalue="700"/>
-        <dimension name="width" uservalue="100"/>
+        <dimension name="Weight" uservalue="700"/>
+        <dimension name="Width" uservalue="100"/>
         <dimension name="Italic" uservalue="1"/>
       </location>
     </label>
@@ -84,7 +84,7 @@
   </rules>
 
   <sources>
-    <source filename="masters/masterTest1.ufo" name="master.ufo1" familyname="MasterFamilyName" stylename="MasterStyleNameOne">
+    <source filename="masterTest1.ufo" name="master.ufo1" familyname="MasterFamilyName" stylename="MasterStyleNameOne">
       <familyname xml:lang="fr">Montserrat</familyname>
       <familyname xml:lang="ja">モンセラート</familyname>
       <lib copy="1"/>
@@ -93,21 +93,31 @@
       <glyph name="A" mute="1"/>
       <glyph name="Z" mute="1"/>
       <location>
-        <dimension name="weight" xvalue="0"/>
-        <dimension name="width" xvalue="20"/>
+        <dimension name="Weight" xvalue="0"/>
+        <dimension name="Width" xvalue="20"/>
+        <dimension name="Italic" xvalue="0"/>
       </location>
     </source>
-    <source filename="masters/masterTest2.ufo" name="master.ufo2" familyname="MasterFamilyName" stylename="MasterStyleNameTwo">
+    <source filename="masterTest2.ufo" name="master.ufo2" familyname="MasterFamilyName" stylename="MasterStyleNameTwo">
       <kerning mute="1"/>
       <location>
-        <dimension name="weight" xvalue="1000"/>
-        <dimension name="width" xvalue="20"/>
+        <dimension name="Weight" xvalue="1000"/>
+        <dimension name="Width" xvalue="20"/>
+        <dimension name="Italic" xvalue="0"/>
       </location>
     </source>
-    <source filename="masters/masterTest2.ufo" name="master.ufo2" familyname="MasterFamilyName" stylename="Supports" layer="supports">
+    <source filename="masterTest2.ufo" name="master.ufo2" familyname="MasterFamilyName" stylename="Supports" layer="supports">
       <location>
-        <dimension name="weight" xvalue="1000"/>
-        <dimension name="width" xvalue="20"/>
+        <dimension name="Weight" xvalue="1000"/>
+        <dimension name="Width" xvalue="20"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="masterTest2.ufo" name="master.ufo3" familyname="MasterFamilyName" stylename="FauxItalic">
+      <location>
+        <dimension name="Weight" xvalue="0"/>
+        <dimension name="Width" xvalue="100"/>
+        <dimension name="Italic" xvalue="1"/>
       </location>
     </source>
   </sources>
@@ -193,8 +203,8 @@
       <stylemapfamilyname xml:lang="de">Montserrat Halbfett</stylemapfamilyname>
       <stylemapfamilyname xml:lang="ja">モンセラート SemiBold</stylemapfamilyname>
       <location>
-        <dimension name="weight" xvalue="500"/>
-        <dimension name="width" xvalue="20"/>
+        <dimension name="Weight" xvalue="500"/>
+        <dimension name="Width" xvalue="20"/>
       </location>
 
       <!-- The following elements are deprecated in v5.0. They can still be
@@ -220,28 +230,28 @@
     </instance>
     <instance name="instance.ufo2" familyname="InstanceFamilyName" stylename="InstanceStyleName" filename="instances/instanceTest2.ufo" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName">
       <location>
-        <dimension name="weight" xvalue="500"/>
-        <dimension name="width" xvalue="400" yvalue="300"/>
+        <dimension name="Weight" xvalue="500"/>
+        <dimension name="Width" xvalue="400" yvalue="300"/>
       </location>
       <!-- ROUNDTRIP_TEST_REMOVE_ME_BEGIN -->
       <glyphs>
         <glyph unicode="0x65 0xc9 0x12d" name="arrow">
           <location>
-            <dimension name="weight" xvalue="120"/>
-            <dimension name="width" xvalue="100"/>
+            <dimension name="Weight" xvalue="120"/>
+            <dimension name="Width" xvalue="100"/>
           </location>
           <note>A note about this glyph</note>
           <masters>
             <master glyphname="BB" source="master.ufo1">
               <location>
-                <dimension name="weight" xvalue="20"/>
-                <dimension name="width" xvalue="20"/>
+                <dimension name="Weight" xvalue="20"/>
+                <dimension name="Width" xvalue="20"/>
               </location>
             </master>
             <master glyphname="CC" source="master.ufo2">
               <location>
-                <dimension name="weight" xvalue="900"/>
-                <dimension name="width" xvalue="900"/>
+                <dimension name="Weight" xvalue="900"/>
+                <dimension name="Width" xvalue="900"/>
               </location>
             </master>
           </masters>
@@ -262,24 +272,24 @@
         - with user coordinates (uservalue="")
         - with a mix of both coordinate systems
     -->
-    <instance location="asdf"/>
+    <instance location="Some Style"/>
     <instance>
       <location>
-        <dimension name="weight" xvalue="600"/>
-        <dimension name="width" xvalue="401" yvalue="420"/>
+        <dimension name="Weight" xvalue="600"/>
+        <dimension name="Width" xvalue="401" yvalue="420"/>
       </location>
     </instance>
     <instance>
       <location>
-        <dimension name="weight" xvalue="10"/>
-        <dimension name="width" uservalue="100"/>
+        <dimension name="Weight" xvalue="10"/>
+        <dimension name="Width" uservalue="100"/>
         <dimension name="Italic" xvalue="0"/>
       </location>
     </instance>
     <instance>
       <location>
-        <dimension name="weight" uservalue="300"/>
-        <dimension name="width" uservalue="130"/>
+        <dimension name="Weight" uservalue="300"/>
+        <dimension name="Width" uservalue="130"/>
         <dimension name="Italic" uservalue="1"/>
       </location>
     </instance>
diff --git a/Tests/designspaceLib/designspace_v5_test.py b/Tests/designspaceLib/designspace_v5_test.py
index 9e80334..35ad29b 100644
--- a/Tests/designspaceLib/designspace_v5_test.py
+++ b/Tests/designspaceLib/designspace_v5_test.py
@@ -34,7 +34,7 @@
         [
             AxisDescriptor(
                 tag="wght",
-                name="weight",
+                name="Weight",
                 minimum=200,
                 maximum=1000,
                 default=200,
@@ -82,7 +82,7 @@
             ),
             AxisDescriptor(
                 tag="wdth",
-                name="width",
+                name="Width",
                 minimum=50,
                 maximum=150,
                 default=100,
@@ -123,10 +123,10 @@
             LocationLabelDescriptor(
                 name="Some Style",
                 labelNames={"fr": "Un Style"},
-                userLocation={"weight": 300, "width": 50, "Italic": 0},
+                userLocation={"Weight": 300, "Width": 50, "Italic": 0},
             ),
             LocationLabelDescriptor(
-                name="Other", userLocation={"weight": 700, "width": 100, "Italic": 1}
+                name="Other", userLocation={"Weight": 700, "Width": 100, "Italic": 1}
             ),
         ],
     )
@@ -139,7 +139,7 @@
                 path=posix(str((datadir / "masters/masterTest1.ufo").resolve())),
                 name="master.ufo1",
                 layerName=None,
-                location={"weight": 0.0, "width": 20.0},
+                location={"Italic": 0.0, "Weight": 0.0, "Width": 20.0},
                 copyLib=True,
                 copyInfo=True,
                 copyGroups=False,
@@ -156,7 +156,7 @@
                 path=posix(str((datadir / "masters/masterTest2.ufo").resolve())),
                 name="master.ufo2",
                 layerName=None,
-                location={"weight": 1000.0, "width": 20.0},
+                location={"Italic": 0.0, "Weight": 1000.0, "Width": 20.0},
                 copyLib=False,
                 copyInfo=False,
                 copyGroups=False,
@@ -173,7 +173,7 @@
                 path=posix(str((datadir / "masters/masterTest2.ufo").resolve())),
                 name="master.ufo2",
                 layerName="supports",
-                location={"weight": 1000.0, "width": 20.0},
+                location={"Italic": 0.0, "Weight": 1000.0, "Width": 20.0},
                 copyLib=False,
                 copyInfo=False,
                 copyGroups=False,
@@ -185,6 +185,22 @@
                 styleName="Supports",
                 localisedFamilyName={},
             ),
+            SourceDescriptor(
+                filename="masters/masterTest2.ufo",
+                path=posix(str((datadir / "masters/masterTest2.ufo").resolve())),
+                name="master.ufo3",
+                layerName=None,
+                location={"Italic": 1.0, "Weight": 0.0, "Width": 100.0},
+                copyLib=False,
+                copyGroups=False,
+                copyFeatures=False,
+                muteKerning=False,
+                muteInfo=False,
+                mutedGlyphNames=[],
+                familyName="MasterFamilyName",
+                styleName="FauxItalic",
+                localisedFamilyName={},
+            ),
         ],
     )
 
@@ -245,7 +261,7 @@
                 filename="instances/instanceTest1.ufo",
                 path=posix(str((datadir / "instances/instanceTest1.ufo").resolve())),
                 name="instance.ufo1",
-                designLocation={"weight": 500.0, "width": 20.0},
+                designLocation={"Weight": 500.0, "Width": 20.0},
                 familyName="InstanceFamilyName",
                 styleName="InstanceStyleName",
                 postScriptFontName="InstancePostscriptName",
@@ -268,7 +284,7 @@
                 filename="instances/instanceTest2.ufo",
                 path=posix(str((datadir / "instances/instanceTest2.ufo").resolve())),
                 name="instance.ufo2",
-                designLocation={"weight": 500.0, "width": (400.0, 300.0)},
+                designLocation={"Weight": 500.0, "Width": (400.0, 300.0)},
                 familyName="InstanceFamilyName",
                 styleName="InstanceStyleName",
                 postScriptFontName="InstancePostscriptName",
@@ -278,16 +294,16 @@
                     "arrow": {
                         "unicodes": [101, 201, 301],
                         "note": "A note about this glyph",
-                        "instanceLocation": {"weight": 120.0, "width": 100.0},
+                        "instanceLocation": {"Weight": 120.0, "Width": 100.0},
                         "masters": [
                             {
                                 "font": "master.ufo1",
-                                "location": {"weight": 20.0, "width": 20.0},
+                                "location": {"Weight": 20.0, "Width": 20.0},
                                 "glyphName": "BB",
                             },
                             {
                                 "font": "master.ufo2",
-                                "location": {"weight": 900.0, "width": 900.0},
+                                "location": {"Weight": 900.0, "Width": 900.0},
                                 "glyphName": "CC",
                             },
                         ],
@@ -296,17 +312,17 @@
                 },
             ),
             InstanceDescriptor(
-                locationLabel="asdf",
+                locationLabel="Some Style",
             ),
             InstanceDescriptor(
-                designLocation={"weight": 600.0, "width": (401.0, 420.0)},
+                designLocation={"Weight": 600.0, "Width": (401.0, 420.0)},
             ),
             InstanceDescriptor(
-                designLocation={"weight": 10.0, "Italic": 0.0},
-                userLocation={"width": 100.0},
+                designLocation={"Weight": 10.0, "Italic": 0.0},
+                userLocation={"Width": 100.0},
             ),
             InstanceDescriptor(
-                userLocation={"weight": 300.0, "width": 130.0, "Italic": 1.0},
+                userLocation={"Weight": 300.0, "Width": 130.0, "Italic": 1.0},
             ),
         ],
     )
diff --git a/Tests/designspaceLib/statNames_test.py b/Tests/designspaceLib/statNames_test.py
index 076abc9..99d1c7f 100644
--- a/Tests/designspaceLib/statNames_test.py
+++ b/Tests/designspaceLib/statNames_test.py
@@ -19,7 +19,7 @@
 def test_not_all_ordering_specified_and_translations(datadir):
     doc = DesignSpaceDocument.fromfile(datadir / "test_v5.designspace")
 
-    assert getStatNames(doc, {"weight": 200, "width": 125, "Italic": 1}) == StatNames(
+    assert getStatNames(doc, {"Weight": 200, "Width": 125, "Italic": 1}) == StatNames(
         familyNames={
             "en": "MasterFamilyName",
             "fr": "Montserrat",
@@ -59,3 +59,20 @@
         styleMapFamilyNames={"en": "Aktiv Grotesk Cd"},
         styleMapStyleName="bold italic",
     )
+
+
+def test_getStatNames_on_ds4_doesnt_make_up_bad_names(datadir):
+    """See this issue on GitHub: https://github.com/googlefonts/ufo2ft/issues/630
+
+    When as in the example, there's no STAT data present, the getStatName
+    shouldn't try making up a postscript name.
+    """
+    doc = DesignSpaceDocument.fromfile(datadir / "DS5BreakTest.designspace")
+
+    assert getStatNames(doc, {"Weight": 600, "Width": 125, "Italic": 1}) == StatNames(
+        familyNames={"en": "DS5BreakTest"},
+        styleNames={},
+        postScriptFontName=None,
+        styleMapFamilyNames={},
+        styleMapStyleName=None,
+    )
diff --git a/Tests/feaLib/data/name.fea b/Tests/feaLib/data/name.fea
index 17727ed..7c94b87 100644
--- a/Tests/feaLib/data/name.fea
+++ b/Tests/feaLib/data/name.fea
@@ -1,16 +1,16 @@
 table name {
 #test-fea2fea:
-  nameid 1 "Ignored-1";
+  nameid 1 "Test1";
 #test-fea2fea:
-  nameid 2 "Ignored-2";
+  nameid 2 "Test2";
 #test-fea2fea:
-  nameid 3 "Ignored-3";
+  nameid 3 "Test3";
 #test-fea2fea:
-  nameid 4 "Ignored-4";
+  nameid 4 "Test4";
 #test-fea2fea:
-  nameid 5 "Ignored-5";
+  nameid 5 "Test5";
 #test-fea2fea:
-    nameid 6 "Ignored-6";
+    nameid 6 "Test6";
 #test-fea2fea: nameid 7 "Test7";
   nameid 7 3 "Test7";
     nameid 8 1 "Test8";
diff --git a/Tests/feaLib/data/name.ttx b/Tests/feaLib/data/name.ttx
index cdb6038..5014b25 100644
--- a/Tests/feaLib/data/name.ttx
+++ b/Tests/feaLib/data/name.ttx
@@ -2,6 +2,24 @@
 <ttFont>
 
   <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test1
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Test2
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Test3
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test4
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Test5
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Test6
+    </namerecord>
     <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
       Test7
     </namerecord>
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
index fd9dea7..b281e8a 100644
--- a/Tests/feaLib/parser_test.py
+++ b/Tests/feaLib/parser_test.py
@@ -316,7 +316,7 @@
     def test_strict_glyph_name_check(self):
         self.parse("@bad = [a b ccc];", glyphNames=("a", "b", "ccc"))
 
-        with self.assertRaisesRegex(FeatureLibError, "missing from the glyph set: ccc"):
+        with self.assertRaisesRegex(FeatureLibError, "(?s)missing from the glyph set:.*ccc"):
             self.parse("@bad = [a b ccc];", glyphNames=("a", "b"))
 
     def test_glyphclass(self):
diff --git a/Tests/merge/data/CFFFont_expected.ttx b/Tests/merge/data/CFFFont_expected.ttx
index c8870e4..2c4cd33 100644
--- a/Tests/merge/data/CFFFont_expected.ttx
+++ b/Tests/merge/data/CFFFont_expected.ttx
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<ttFont sfntVersion="OTTO" ttLibVersion="4.28">
+<ttFont sfntVersion="OTTO" ttLibVersion="4.34">
 
   <GlyphOrder>
     <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
@@ -507,7 +507,7 @@
     <GlyphID id="501" name="vabove-ar"/>
     <GlyphID id="502" name="vbelow-ar"/>
     <GlyphID id="503" name="opendammatan-ar"/>
-    <GlyphID id="504" name=".notdef#1"/>
+    <GlyphID id="504" name=".notdef.1"/>
     <GlyphID id="505" name="A"/>
     <GlyphID id="506" name="Aacute"/>
     <GlyphID id="507" name="Acircumflex"/>
@@ -701,12 +701,12 @@
     <GlyphID id="695" name="onehalf"/>
     <GlyphID id="696" name="onequarter"/>
     <GlyphID id="697" name="threequarters"/>
-    <GlyphID id="698" name="space#1"/>
+    <GlyphID id="698" name="space.1"/>
     <GlyphID id="699" name="period"/>
     <GlyphID id="700" name="comma"/>
     <GlyphID id="701" name="colon"/>
     <GlyphID id="702" name="semicolon"/>
-    <GlyphID id="703" name="exclam#1"/>
+    <GlyphID id="703" name="exclam.1"/>
     <GlyphID id="704" name="exclamdown"/>
     <GlyphID id="705" name="question"/>
     <GlyphID id="706" name="questiondown"/>
@@ -725,8 +725,8 @@
     <GlyphID id="719" name="braceright"/>
     <GlyphID id="720" name="bracketleft"/>
     <GlyphID id="721" name="bracketright"/>
-    <GlyphID id="722" name="quoteleft#1"/>
-    <GlyphID id="723" name="quoteright#1"/>
+    <GlyphID id="722" name="quoteleft.1"/>
+    <GlyphID id="723" name="quoteright.1"/>
     <GlyphID id="724" name="guillemotleft"/>
     <GlyphID id="725" name="guillemotright"/>
     <GlyphID id="726" name="guilsinglleft"/>
@@ -788,12 +788,12 @@
     <!-- Most of this table will be recalculated by the compiler -->
     <tableVersion value="1.0"/>
     <fontRevision value="1.003"/>
-    <checkSumAdjustment value="0x490fa241"/>
+    <checkSumAdjustment value="0x9a87f91"/>
     <magicNumber value="0x5f0f3cf5"/>
     <flags value="00000000 00000011"/>
     <unitsPerEm value="1000"/>
-    <created value="Thu Jan  1 00:00:00 1970"/>
-    <modified value="Thu Jan  1 00:00:00 1970"/>
+    <created value="Sun Aug 14 18:30:31 2022"/>
+    <modified value="Sun Aug 14 18:30:31 2022"/>
     <xMin value="-199"/>
     <yMin value="-364"/>
     <xMax value="1459"/>
@@ -1380,7 +1380,7 @@
           900 300 -900 -300 vlineto
           endchar
         </CharString>
-        <CharString name=".notdef#1">
+        <CharString name=".notdef.1">
           -45 50 -200 rmoveto
           400 1000 -400 -1000 hlineto
           50 50 rmoveto
@@ -4962,7 +4962,7 @@
           -4 3 -5 1 -3 -4 rrcurveto
           endchar
         </CharString>
-        <CharString name="exclam#1">
+        <CharString name="exclam.1">
           -329 112 186 rmoveto
           36 371 rlineto
           3 29 2 29 29 vvcurveto
@@ -9687,7 +9687,7 @@
           -21 14 15 -11 14 hhcurveto
           endchar
         </CharString>
-        <CharString name="quoteleft#1">
+        <CharString name="quoteleft.1">
           -345 115 532 rmoveto
           -9 14 -5 15 16 vvcurveto
           35 28 47 21 36 vhcurveto
@@ -9709,7 +9709,7 @@
           21 -14 -15 11 -14 hhcurveto
           endchar
         </CharString>
-        <CharString name="quoteright#1">
+        <CharString name="quoteright.1">
           -348 66 395 rmoveto
           35 53 54 54 62 vvcurveto
           42 -43 89 -28 -16 -32 -26 -15 -7 8 -16 6 -10 vhcurveto
@@ -11038,7 +11038,7 @@
         <CharString name="space">
           -218 endchar
         </CharString>
-        <CharString name="space#1">
+        <CharString name="space.1">
           -212 endchar
         </CharString>
         <CharString name="sterling">
@@ -29261,7 +29261,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SingleSubst index="0">
-          <Substitution in="space#1" out="space#1"/>
+          <Substitution in="space.1" out="space.1"/>
         </SingleSubst>
       </Lookup>
       <Lookup index="96">
@@ -29269,10 +29269,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SingleSubst index="0">
-          <Substitution in="exclam" out="exclam#1"/>
-          <Substitution in="quoteleft" out="quoteleft#1"/>
-          <Substitution in="quoteright" out="quoteright#1"/>
-          <Substitution in="space" out="space#1"/>
+          <Substitution in="exclam" out="exclam.1"/>
+          <Substitution in="quoteleft" out="quoteleft.1"/>
+          <Substitution in="quoteright" out="quoteright.1"/>
+          <Substitution in="space" out="space.1"/>
         </SingleSubst>
       </Lookup>
     </LookupList>
@@ -29280,7 +29280,7 @@
 
   <hmtx>
     <mtx name=".notdef" width="500" lsb="50"/>
-    <mtx name=".notdef#1" width="500" lsb="50"/>
+    <mtx name=".notdef.1" width="500" lsb="50"/>
     <mtx name="A" width="722" lsb="3"/>
     <mtx name="AE" width="797" lsb="3"/>
     <mtx name="Aacute" width="722" lsb="3"/>
@@ -29503,7 +29503,7 @@
     <mtx name="eth" width="461" lsb="28"/>
     <mtx name="euro" width="638" lsb="25"/>
     <mtx name="exclam" width="253" lsb="26"/>
-    <mtx name="exclam#1" width="216" lsb="54"/>
+    <mtx name="exclam.1" width="216" lsb="54"/>
     <mtx name="exclamdown" width="216" lsb="54"/>
     <mtx name="f" width="329" lsb="-1"/>
     <mtx name="f.alt" width="317" lsb="-1"/>
@@ -29848,9 +29848,9 @@
     <mtx name="quotedblleft" width="444" lsb="91"/>
     <mtx name="quotedblright" width="444" lsb="58"/>
     <mtx name="quoteleft" width="271" lsb="91"/>
-    <mtx name="quoteleft#1" width="200" lsb="41"/>
+    <mtx name="quoteleft.1" width="200" lsb="41"/>
     <mtx name="quoteright" width="271" lsb="58"/>
-    <mtx name="quoteright#1" width="197" lsb="36"/>
+    <mtx name="quoteright.1" width="197" lsb="36"/>
     <mtx name="quotesinglbase" width="271" lsb="58"/>
     <mtx name="quotesingle" width="210" lsb="74"/>
     <mtx name="r" width="416" lsb="0"/>
@@ -29948,7 +29948,7 @@
     <mtx name="slash" width="390" lsb="-17"/>
     <mtx name="softhyphen" width="0" lsb="0"/>
     <mtx name="space" width="146" lsb="0"/>
-    <mtx name="space#1" width="333" lsb="0"/>
+    <mtx name="space.1" width="333" lsb="0"/>
     <mtx name="sterling" width="649" lsb="22"/>
     <mtx name="sukun-ar" width="0" lsb="-98"/>
     <mtx name="sukun-ar.alt" width="0" lsb="-53"/>
diff --git a/Tests/misc/treeTools_test.py b/Tests/misc/treeTools_test.py
new file mode 100644
index 0000000..467a5c5
--- /dev/null
+++ b/Tests/misc/treeTools_test.py
@@ -0,0 +1,80 @@
+from fontTools.misc.treeTools import build_n_ary_tree
+import pytest
+
+
+@pytest.mark.parametrize(
+    "lst, n, expected",
+    [
+        ([0], 2, [0]),
+        ([0, 1], 2, [0, 1]),
+        ([0, 1, 2], 2, [[0, 1], 2]),
+        ([0, 1, 2], 3, [0, 1, 2]),
+        ([0, 1, 2, 3], 2, [[0, 1], [2, 3]]),
+        ([0, 1, 2, 3], 3, [[0, 1, 2], 3]),
+        ([0, 1, 2, 3, 4], 3, [[0, 1, 2], 3, 4]),
+        ([0, 1, 2, 3, 4, 5], 3, [[0, 1, 2], [3, 4, 5]]),
+        (list(range(7)), 3, [[0, 1, 2], [3, 4, 5], 6]),
+        (list(range(8)), 3, [[0, 1, 2], [3, 4, 5], [6, 7]]),
+        (list(range(9)), 3, [[0, 1, 2], [3, 4, 5], [6, 7, 8]]),
+        (list(range(10)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], 9]),
+        (list(range(11)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], 9, 10]),
+        (list(range(12)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11]]),
+        (list(range(13)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11], 12]),
+        (
+            list(range(14)),
+            3,
+            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], 12, 13]],
+        ),
+        (
+            list(range(15)),
+            3,
+            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11], [12, 13, 14]],
+        ),
+        (
+            list(range(16)),
+            3,
+            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], [12, 13, 14], 15]],
+        ),
+        (
+            list(range(23)),
+            3,
+            [
+                [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
+                [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
+                [[18, 19, 20], 21, 22],
+            ],
+        ),
+        (
+            list(range(27)),
+            3,
+            [
+                [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
+                [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
+                [[18, 19, 20], [21, 22, 23], [24, 25, 26]],
+            ],
+        ),
+        (
+            list(range(28)),
+            3,
+            [
+                [
+                    [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
+                    [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
+                    [[18, 19, 20], [21, 22, 23], [24, 25, 26]],
+                ],
+                27,
+            ],
+        ),
+        (list(range(257)), 256, [list(range(256)), 256]),
+        (list(range(258)), 256, [list(range(256)), 256, 257]),
+        (list(range(512)), 256, [list(range(256)), list(range(256, 512))]),
+        (list(range(512 + 1)), 256, [list(range(256)), list(range(256, 512)), 512]),
+        (
+            list(range(256 ** 2)),
+            256,
+            [list(range(k * 256, k * 256 + 256)) for k in range(256)],
+        ),
+    ],
+)
+def test_build_n_ary_tree(lst, n, expected):
+    assert build_n_ary_tree(lst, n) == expected
diff --git a/Tests/misc/visitor_test.py b/Tests/misc/visitor_test.py
new file mode 100644
index 0000000..fe71e08
--- /dev/null
+++ b/Tests/misc/visitor_test.py
@@ -0,0 +1,72 @@
+from fontTools.misc.visitor import Visitor
+import enum
+import pytest
+
+
+class E(enum.Enum):
+    E1 = 1
+    E2 = 2
+    E3 = 3
+
+class A:
+    def __init__(self):
+        self.a = 1
+        self.b = [2, 3]
+        self.c = {4: 5, 6: 7}
+        self._d = 8
+        self.e = E.E2
+        self.f = 10
+
+
+class B:
+    def __init__(self):
+        self.a = A()
+
+
+class TestVisitor(Visitor):
+    def __init__(self):
+        self.value = []
+
+    def _add(self, s):
+        self.value.append(s)
+
+    def visitLeaf(self, obj):
+        self._add(obj)
+        super().visitLeaf(obj)
+
+
+@TestVisitor.register(A)
+def visit(self, obj):
+    self._add("A")
+
+
+@TestVisitor.register_attrs([(A, "e")])
+def visit(self, obj, attr, value):
+    self._add(attr)
+    self._add(value)
+    return False
+
+
+@TestVisitor.register(B)
+def visit(self, obj):
+    self._add("B")
+    self.visitObject(obj)
+    return False
+
+
+@TestVisitor.register_attr(B, "a")
+def visit(self, obj, attr, value):
+    self._add("B a")
+
+
+class VisitorTest(object):
+    def test_visitor(self):
+        b = B()
+        visitor = TestVisitor()
+        visitor.visit(b)
+        assert visitor.value == ["B", "B a", "A", 1, 2, 3, 5, 7, "e", E.E2, 10]
+
+        visitor.value = []
+        visitor.defaultStop = True
+        visitor.visit(b)
+        assert visitor.value == ["B", "B a"]
diff --git a/Tests/pens/cu2quPen_test.py b/Tests/pens/cu2quPen_test.py
index db51787..4ce5b51 100644
--- a/Tests/pens/cu2quPen_test.py
+++ b/Tests/pens/cu2quPen_test.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import sys
 import unittest
 
 from fontTools.pens.cu2quPen import Cu2QuPen, Cu2QuPointPen
@@ -257,8 +258,12 @@
         quadpen.closePath()
 
         self.assertGreaterEqual(len(log.records), 1)
-        self.assertIn("ignore_single_points is deprecated",
-                      log.records[0].args[0])
+        if sys.version_info < (3, 11):
+            self.assertIn("ignore_single_points is deprecated",
+                          log.records[0].args[0])
+        else:
+            self.assertIn("ignore_single_points is deprecated",
+                          log.records[0].msg)
 
         # single-point contours were ignored, so the pen commands are empty
         self.assertFalse(pen.commands)
diff --git a/Tests/subset/data/expect_no_notdef_outline_cid.ttx b/Tests/subset/data/expect_no_notdef_outline_cid.ttx
index 5167c2c..990caa5 100644
--- a/Tests/subset/data/expect_no_notdef_outline_cid.ttx
+++ b/Tests/subset/data/expect_no_notdef_outline_cid.ttx
@@ -23,7 +23,7 @@
       <CIDFontType value="0"/>
       <CIDCount value="4"/>
       <!-- charset is dumped separately as the 'GlyphOrder' element -->
-      <FDSelect format="3"/>
+      <FDSelect format="0"/>
       <FDArray>
         <FontDict index="0">
           <FontName value="TestCID-Regular-One"/>
diff --git a/Tests/subset/data/expect_notdef_width_cid.ttx b/Tests/subset/data/expect_notdef_width_cid.ttx
index ccd0f65..8ea77e5 100644
--- a/Tests/subset/data/expect_notdef_width_cid.ttx
+++ b/Tests/subset/data/expect_notdef_width_cid.ttx
@@ -23,7 +23,7 @@
       <CIDFontType value="0"/>
       <CIDCount value="4"/>
       <!-- charset is dumped separately as the 'GlyphOrder' element -->
-      <FDSelect format="3"/>
+      <FDSelect format="0"/>
       <FDArray>
         <FontDict index="0">
           <FontName value="NotdefWidthCID-Regular-Space"/>
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index facafb2..7efcb69 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -819,13 +819,20 @@
             if not have_uharfbuzz:
                 pytest.skip("uharfbuzz is not installed")
             if not ok:
-                # pretend hb.repack returns an error
+                # pretend hb.repack/repack_with_tag return an error
                 import uharfbuzz as hb
 
                 def mock_repack(data, obj_list):
                     raise hb.RepackerError("mocking")
 
                 monkeypatch.setattr(hb, "repack", mock_repack)
+
+                if hasattr(hb, "repack_with_tag"):  # uharfbuzz >= 0.30.0
+
+                    def mock_repack_with_tag(tag, data, obj_list):
+                        raise hb.RepackerError("mocking")
+
+                    monkeypatch.setattr(hb, "repack_with_tag", mock_repack_with_tag)
         else:
             if have_uharfbuzz:
                 # pretend uharfbuzz is not installed
@@ -879,8 +886,8 @@
         # test we emit a log.error if hb.repack fails (and we don't if successful)
         assert (
                 (
-                "hb.repack failed to serialize 'GSUB', reverting to "
-                "pure-python serializer; the error message was: RepackerError: mocking"
+                "hb.repack failed to serialize 'GSUB', attempting fonttools resolutions "
+                "; the error message was: RepackerError: mocking"
             ) in caplog.text
         ) ^ ok
 
@@ -953,7 +960,8 @@
 # https://github.com/fonttools/fonttools/issues/1881#issuecomment-619415044
 
 
-def test_subset_single_pos_format():
+@pytest.fixture
+def singlepos2_font():
     fb = FontBuilder(unitsPerEm=1000)
     fb.setupGlyphOrder([".notdef", "a", "b", "c"])
     fb.setupCharacterMap({ord("a"): "a", ord("b"): "b", ord("c"): "c"})
@@ -971,8 +979,11 @@
     fb.save(buf)
     buf.seek(0)
 
-    font = TTFont(buf)
+    return TTFont(buf)
 
+
+def test_subset_single_pos_format(singlepos2_font):
+    font = singlepos2_font
     # The input font has a SinglePos Format 2 subtable where each glyph has
     # different ValueRecords
     assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
@@ -1018,6 +1029,46 @@
         '</Lookup>',
     ]
 
+def test_subset_single_pos_format2_all_None(singlepos2_font):
+    # https://github.com/fonttools/fonttools/issues/2602
+    font = singlepos2_font
+    gpos = font["GPOS"].table
+    subtable = gpos.LookupList.Lookup[0].SubTable[0]
+    assert subtable.Format == 2
+    # Hack a SinglePosFormat2 with ValueFormat = 0; our own buildSinglePos
+    # never makes these as a SinglePosFormat1 is more compact, but they can
+    # be found in the wild.
+    subtable.Value = [None] * subtable.ValueCount
+    subtable.ValueFormat = 0
+
+    assert getXML(subtable.toXML, font) == [
+        '<SinglePos Format="2">',
+        '  <Coverage>',
+        '    <Glyph value="a"/>',
+        '    <Glyph value="b"/>',
+        '    <Glyph value="c"/>',
+        '  </Coverage>',
+        '  <ValueFormat value="0"/>',
+        '  <!-- ValueCount=3 -->',
+        '</SinglePos>',
+    ]
+
+    options = subset.Options()
+    subsetter = subset.Subsetter(options)
+    subsetter.populate(unicodes=[ord("a"), ord("c")])
+    subsetter.subset(font)
+
+    # Check it was downgraded to Format1 after subsetting
+    assert getXML(font["GPOS"].table.LookupList.Lookup[0].SubTable[0].toXML, font) == [
+        '<SinglePos Format="1">',
+        '  <Coverage>',
+        '    <Glyph value="a"/>',
+        '    <Glyph value="c"/>',
+        '  </Coverage>',
+        '  <ValueFormat value="0"/>',
+        '</SinglePos>',
+    ]
+
 
 @pytest.fixture
 def ttf_path(tmp_path):
@@ -1426,7 +1477,7 @@
     font["SVG "].docList = [('<svg><g id="glyph1"/></svg>', 1, 1)]
     font.save(ttf_path)
 
-    with pytest.raises(ModuleNotFoundError):
+    with pytest.raises(ImportError):
         subset.main([str(ttf_path), "--gids=0,1"])
 
 
diff --git a/Tests/svgLib/path/parser_test.py b/Tests/svgLib/path/parser_test.py
index b533dd8..d33043f 100644
--- a/Tests/svgLib/path/parser_test.py
+++ b/Tests/svgLib/path/parser_test.py
@@ -280,6 +280,15 @@
 
     assert pen.value == expected
 
+    pen = RecordingPen()
+    parse_path("M-3e38 3E+38L-3E-38,3e-38", pen)
+    expected = [
+        ("moveTo", ((-3e+38, 3e+38),)),
+        ("lineTo", ((-3e-38, 3e-38),)),
+        ("endPath", ()),
+    ]
+
+    assert pen.value == expected
 
 def test_invalid_implicit_command():
     with pytest.raises(ValueError) as exc_info:
@@ -360,7 +369,7 @@
             "M1-2A3-4-1.0 01.5.7",
             [
                 ("moveTo", ((1.0, -2.0),)),
-                ("arcTo", (3.0, -4.0, -1.0, False, True, (0.5, 0.7))),
+                ("arcTo", (3.0, 4.0, -1.0, False, True, (0.5, 0.7))),
                 ("endPath", ()),
             ],
         ),
diff --git a/Tests/ttLib/data/I-512upem.ttx b/Tests/ttLib/data/I-512upem.ttx
new file mode 100644
index 0000000..34795b1
--- /dev/null
+++ b/Tests/ttLib/data/I-512upem.ttx
@@ -0,0 +1,3777 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.36">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="I"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="475"/>
+    <descent value="-125"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="227"/>
+    <minLeftSideBearing value="44"/>
+    <minRightSideBearing value="44"/>
+    <xMaxExtent value="92"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="2"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="286"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="359"/>
+    <ySubscriptYSize value="333"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="72"/>
+    <ySuperscriptXSize value="359"/>
+    <ySuperscriptYSize value="333"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="244"/>
+    <yStrikeoutSize value="26"/>
+    <yStrikeoutPosition value="128"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="73"/>
+    <usLastCharIndex value="73"/>
+    <sTypoAscender value="475"/>
+    <sTypoDescender value="-125"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="615"/>
+    <usWinDescent value="150"/>
+    <ulCodePageRange1 value="00100000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="263"/>
+    <sCapHeight value="364"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="227" lsb="25"/>
+    <mtx name="I" width="136" lsb="44"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x49" name="I"/><!-- LATIN CAPITAL LETTER I -->
+    </cmap_format_4>
+  </cmap>
+
+  <prep>
+    <assembly>
+      PUSHW[ ]	/* 1 value pushed */
+      511
+      SCANCTRL[ ]	/* ScanConversionControl */
+      PUSHB[ ]	/* 1 value pushed */
+      4
+      SCANTYPE[ ]	/* ScanType */
+    </assembly>
+  </prep>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef"/><!-- contains no outline data -->
+
+    <TTGlyph name="I" xMin="44" yMin="0" xMax="92" yMax="364">
+      <contour>
+        <pt x="44" y="0" on="1" overlap="1"/>
+        <pt x="92" y="0" on="1"/>
+        <pt x="92" y="364" on="1"/>
+        <pt x="44" y="364" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2017 The Roboto Flex Project Authors (https://github.com/TypeNetwork/Roboto-Flex)
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Roboto Flex
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Google:Roboto Regular:2017
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Roboto Flex Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 3.100
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlex-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      wght
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      wdth
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      opsz
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      GRAD
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      slnt
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      XTRA
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      XOPQ
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      YOPQ
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      YTLC
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      YTUC
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      YTAS
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      YTDE
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      YTFI
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="276" platformID="3" platEncID="1" langID="0x409">
+      ExtraBold
+    </namerecord>
+    <namerecord nameID="277" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="278" platformID="3" platEncID="1" langID="0x409">
+      ExtraBlack
+    </namerecord>
+    <namerecord nameID="279" platformID="3" platEncID="1" langID="0x409">
+      Thin Italic
+    </namerecord>
+    <namerecord nameID="280" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight Italic
+    </namerecord>
+    <namerecord nameID="281" platformID="3" platEncID="1" langID="0x409">
+      Light Italic
+    </namerecord>
+    <namerecord nameID="282" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="283" platformID="3" platEncID="1" langID="0x409">
+      Medium Italic
+    </namerecord>
+    <namerecord nameID="284" platformID="3" platEncID="1" langID="0x409">
+      SemiBold Italic
+    </namerecord>
+    <namerecord nameID="285" platformID="3" platEncID="1" langID="0x409">
+      Bold Italic
+    </namerecord>
+    <namerecord nameID="286" platformID="3" platEncID="1" langID="0x409">
+      ExtraBold Italic
+    </namerecord>
+    <namerecord nameID="287" platformID="3" platEncID="1" langID="0x409">
+      Black Italic
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      ExtraBlack Italic
+    </namerecord>
+    <namerecord nameID="289" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Thin
+    </namerecord>
+    <namerecord nameID="290" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraLight
+    </namerecord>
+    <namerecord nameID="291" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Light
+    </namerecord>
+    <namerecord nameID="292" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Regular
+    </namerecord>
+    <namerecord nameID="293" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Medium
+    </namerecord>
+    <namerecord nameID="294" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-SemiBold
+    </namerecord>
+    <namerecord nameID="295" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Bold
+    </namerecord>
+    <namerecord nameID="296" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBold
+    </namerecord>
+    <namerecord nameID="297" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Black
+    </namerecord>
+    <namerecord nameID="298" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBlack
+    </namerecord>
+    <namerecord nameID="299" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ThinItalic
+    </namerecord>
+    <namerecord nameID="300" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraLightItalic
+    </namerecord>
+    <namerecord nameID="301" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-LightItalic
+    </namerecord>
+    <namerecord nameID="302" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Italic
+    </namerecord>
+    <namerecord nameID="303" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-MediumItalic
+    </namerecord>
+    <namerecord nameID="304" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-SemiBoldItalic
+    </namerecord>
+    <namerecord nameID="305" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-BoldItalic
+    </namerecord>
+    <namerecord nameID="306" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBoldItalic
+    </namerecord>
+    <namerecord nameID="307" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-BlackItalic
+    </namerecord>
+    <namerecord nameID="308" platformID="3" platEncID="1" langID="0x409">
+      RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBlackItalic
+    </namerecord>
+    <namerecord nameID="309" platformID="3" platEncID="1" langID="0x409">
+      Grade
+    </namerecord>
+    <namerecord nameID="310" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="311" platformID="3" platEncID="1" langID="0x409">
+      X opaque
+    </namerecord>
+    <namerecord nameID="312" platformID="3" platEncID="1" langID="0x409">
+      X transparent
+    </namerecord>
+    <namerecord nameID="313" platformID="3" platEncID="1" langID="0x409">
+      Y opaque
+    </namerecord>
+    <namerecord nameID="314" platformID="3" platEncID="1" langID="0x409">
+      Y transparent ascender
+    </namerecord>
+    <namerecord nameID="315" platformID="3" platEncID="1" langID="0x409">
+      Y transparent descender
+    </namerecord>
+    <namerecord nameID="316" platformID="3" platEncID="1" langID="0x409">
+      Y transparent figures
+    </namerecord>
+    <namerecord nameID="317" platformID="3" platEncID="1" langID="0x409">
+      Y transparent lowercase
+    </namerecord>
+    <namerecord nameID="318" platformID="3" platEncID="1" langID="0x409">
+      Y transparent uppercase
+    </namerecord>
+    <namerecord nameID="319" platformID="3" platEncID="1" langID="0x409">
+      Optical size
+    </namerecord>
+    <namerecord nameID="320" platformID="3" platEncID="1" langID="0x409">
+      8pt
+    </namerecord>
+    <namerecord nameID="321" platformID="3" platEncID="1" langID="0x409">
+      9pt
+    </namerecord>
+    <namerecord nameID="322" platformID="3" platEncID="1" langID="0x409">
+      10pt
+    </namerecord>
+    <namerecord nameID="323" platformID="3" platEncID="1" langID="0x409">
+      11pt
+    </namerecord>
+    <namerecord nameID="324" platformID="3" platEncID="1" langID="0x409">
+      12pt
+    </namerecord>
+    <namerecord nameID="325" platformID="3" platEncID="1" langID="0x409">
+      14pt
+    </namerecord>
+    <namerecord nameID="326" platformID="3" platEncID="1" langID="0x409">
+      16pt
+    </namerecord>
+    <namerecord nameID="327" platformID="3" platEncID="1" langID="0x409">
+      17pt
+    </namerecord>
+    <namerecord nameID="328" platformID="3" platEncID="1" langID="0x409">
+      18pt
+    </namerecord>
+    <namerecord nameID="329" platformID="3" platEncID="1" langID="0x409">
+      20pt
+    </namerecord>
+    <namerecord nameID="330" platformID="3" platEncID="1" langID="0x409">
+      24pt
+    </namerecord>
+    <namerecord nameID="331" platformID="3" platEncID="1" langID="0x409">
+      28pt
+    </namerecord>
+    <namerecord nameID="332" platformID="3" platEncID="1" langID="0x409">
+      36pt
+    </namerecord>
+    <namerecord nameID="333" platformID="3" platEncID="1" langID="0x409">
+      48pt
+    </namerecord>
+    <namerecord nameID="334" platformID="3" platEncID="1" langID="0x409">
+      60pt
+    </namerecord>
+    <namerecord nameID="335" platformID="3" platEncID="1" langID="0x409">
+      72pt
+    </namerecord>
+    <namerecord nameID="336" platformID="3" platEncID="1" langID="0x409">
+      96pt
+    </namerecord>
+    <namerecord nameID="337" platformID="3" platEncID="1" langID="0x409">
+      120pt
+    </namerecord>
+    <namerecord nameID="338" platformID="3" platEncID="1" langID="0x409">
+      144pt
+    </namerecord>
+    <namerecord nameID="339" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="340" platformID="3" platEncID="1" langID="0x409">
+      UltraCondensed
+    </namerecord>
+    <namerecord nameID="341" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed
+    </namerecord>
+    <namerecord nameID="342" platformID="3" platEncID="1" langID="0x409">
+      Condensed
+    </namerecord>
+    <namerecord nameID="343" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed
+    </namerecord>
+    <namerecord nameID="344" platformID="3" platEncID="1" langID="0x409">
+      SemiExpanded
+    </namerecord>
+    <namerecord nameID="345" platformID="3" platEncID="1" langID="0x409">
+      Expanded
+    </namerecord>
+    <namerecord nameID="346" platformID="3" platEncID="1" langID="0x409">
+      ExtraExpanded
+    </namerecord>
+    <namerecord nameID="347" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="348" platformID="3" platEncID="1" langID="0x409">
+      Slant
+    </namerecord>
+    <namerecord nameID="349" platformID="3" platEncID="1" langID="0x409">
+      Default
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-46"/>
+    <underlineThickness value="42"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="I" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=3 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="I"/>
+          </Coverage>
+          <ValueFormat1 value="68"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="I" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=13 -->
+        <!-- RegionCount=25 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="7">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="8">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="9">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="10">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="11">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="12">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="13">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="14">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="15">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="16">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="17">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="18">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="19">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="20">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="21">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="22">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="23">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="24">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="3">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="4">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="5">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="6">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="7">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="8">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="9">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="10">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="11">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="12">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=1 -->
+        <NumShorts value="7"/>
+        <!-- VarRegionCount=25 -->
+        <VarRegionIndex index="0" value="1"/>
+        <VarRegionIndex index="1" value="5"/>
+        <VarRegionIndex index="2" value="6"/>
+        <VarRegionIndex index="3" value="7"/>
+        <VarRegionIndex index="4" value="14"/>
+        <VarRegionIndex index="5" value="15"/>
+        <VarRegionIndex index="6" value="17"/>
+        <VarRegionIndex index="7" value="0"/>
+        <VarRegionIndex index="8" value="2"/>
+        <VarRegionIndex index="9" value="3"/>
+        <VarRegionIndex index="10" value="4"/>
+        <VarRegionIndex index="11" value="9"/>
+        <VarRegionIndex index="12" value="11"/>
+        <VarRegionIndex index="13" value="12"/>
+        <VarRegionIndex index="14" value="13"/>
+        <VarRegionIndex index="15" value="16"/>
+        <VarRegionIndex index="16" value="18"/>
+        <VarRegionIndex index="17" value="19"/>
+        <VarRegionIndex index="18" value="20"/>
+        <VarRegionIndex index="19" value="21"/>
+        <VarRegionIndex index="20" value="22"/>
+        <VarRegionIndex index="21" value="23"/>
+        <VarRegionIndex index="22" value="24"/>
+        <VarRegionIndex index="23" value="8"/>
+        <VarRegionIndex index="24" value="10"/>
+        <Item index="0" value="[34, -38, -34, 40, 43, -47, 43, -24, -2, 3, 3, 6, 0, 6, 2, 22, 0, -2, 4, -12, -1, 4, -8, 0, 8]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="I" outer="1" inner="0"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=13 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="GRAD"/>
+        <AxisNameID value="309"/>  <!-- Grade -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="XOPQ"/>
+        <AxisNameID value="311"/>  <!-- X opaque -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="2">
+        <AxisTag value="XTRA"/>
+        <AxisNameID value="312"/>  <!-- X transparent -->
+        <AxisOrdering value="2"/>
+      </Axis>
+      <Axis index="3">
+        <AxisTag value="YOPQ"/>
+        <AxisNameID value="313"/>  <!-- Y opaque -->
+        <AxisOrdering value="3"/>
+      </Axis>
+      <Axis index="4">
+        <AxisTag value="YTAS"/>
+        <AxisNameID value="314"/>  <!-- Y transparent ascender -->
+        <AxisOrdering value="4"/>
+      </Axis>
+      <Axis index="5">
+        <AxisTag value="YTDE"/>
+        <AxisNameID value="315"/>  <!-- Y transparent descender -->
+        <AxisOrdering value="5"/>
+      </Axis>
+      <Axis index="6">
+        <AxisTag value="YTFI"/>
+        <AxisNameID value="316"/>  <!-- Y transparent figures -->
+        <AxisOrdering value="6"/>
+      </Axis>
+      <Axis index="7">
+        <AxisTag value="YTLC"/>
+        <AxisNameID value="317"/>  <!-- Y transparent lowercase -->
+        <AxisOrdering value="7"/>
+      </Axis>
+      <Axis index="8">
+        <AxisTag value="YTUC"/>
+        <AxisNameID value="318"/>  <!-- Y transparent uppercase -->
+        <AxisOrdering value="8"/>
+      </Axis>
+      <Axis index="9">
+        <AxisTag value="opsz"/>
+        <AxisNameID value="319"/>  <!-- Optical size -->
+        <AxisOrdering value="9"/>
+      </Axis>
+      <Axis index="10">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="339"/>  <!-- Width -->
+        <AxisOrdering value="10"/>
+      </Axis>
+      <Axis index="11">
+        <AxisTag value="wght"/>
+        <AxisNameID value="347"/>  <!-- Weight -->
+        <AxisOrdering value="11"/>
+      </Axis>
+      <Axis index="12">
+        <AxisTag value="slnt"/>
+        <AxisNameID value="348"/>  <!-- Slant -->
+        <AxisOrdering value="12"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=46 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="0.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="1">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="88.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="1">
+        <AxisIndex value="2"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="400.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="1">
+        <AxisIndex value="3"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="116.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="1">
+        <AxisIndex value="4"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="750.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="1">
+        <AxisIndex value="5"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="-250.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="1">
+        <AxisIndex value="6"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="600.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="1">
+        <AxisIndex value="7"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="500.0"/>
+      </AxisValue>
+      <AxisValue index="8" Format="1">
+        <AxisIndex value="8"/>
+        <Flags value="0"/>
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="725.0"/>
+      </AxisValue>
+      <AxisValue index="9" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="320"/>  <!-- 8pt -->
+        <Value value="8.0"/>
+      </AxisValue>
+      <AxisValue index="10" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="321"/>  <!-- 9pt -->
+        <Value value="9.0"/>
+      </AxisValue>
+      <AxisValue index="11" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="322"/>  <!-- 10pt -->
+        <Value value="10.0"/>
+      </AxisValue>
+      <AxisValue index="12" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="323"/>  <!-- 11pt -->
+        <Value value="11.0"/>
+      </AxisValue>
+      <AxisValue index="13" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="324"/>  <!-- 12pt -->
+        <Value value="12.0"/>
+      </AxisValue>
+      <AxisValue index="14" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="325"/>  <!-- 14pt -->
+        <Value value="14.0"/>
+      </AxisValue>
+      <AxisValue index="15" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="326"/>  <!-- 16pt -->
+        <Value value="16.0"/>
+      </AxisValue>
+      <AxisValue index="16" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="327"/>  <!-- 17pt -->
+        <Value value="17.0"/>
+      </AxisValue>
+      <AxisValue index="17" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="328"/>  <!-- 18pt -->
+        <Value value="18.0"/>
+      </AxisValue>
+      <AxisValue index="18" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="329"/>  <!-- 20pt -->
+        <Value value="20.0"/>
+      </AxisValue>
+      <AxisValue index="19" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="330"/>  <!-- 24pt -->
+        <Value value="24.0"/>
+      </AxisValue>
+      <AxisValue index="20" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="331"/>  <!-- 28pt -->
+        <Value value="28.0"/>
+      </AxisValue>
+      <AxisValue index="21" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="332"/>  <!-- 36pt -->
+        <Value value="36.0"/>
+      </AxisValue>
+      <AxisValue index="22" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="333"/>  <!-- 48pt -->
+        <Value value="48.0"/>
+      </AxisValue>
+      <AxisValue index="23" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="334"/>  <!-- 60pt -->
+        <Value value="60.0"/>
+      </AxisValue>
+      <AxisValue index="24" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="335"/>  <!-- 72pt -->
+        <Value value="72.0"/>
+      </AxisValue>
+      <AxisValue index="25" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="336"/>  <!-- 96pt -->
+        <Value value="96.0"/>
+      </AxisValue>
+      <AxisValue index="26" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="337"/>  <!-- 120pt -->
+        <Value value="120.0"/>
+      </AxisValue>
+      <AxisValue index="27" Format="1">
+        <AxisIndex value="9"/>
+        <Flags value="0"/>
+        <ValueNameID value="338"/>  <!-- 144pt -->
+        <Value value="144.0"/>
+      </AxisValue>
+      <AxisValue index="28" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="340"/>  <!-- UltraCondensed -->
+        <Value value="50.0"/>
+      </AxisValue>
+      <AxisValue index="29" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="341"/>  <!-- ExtraCondensed -->
+        <Value value="62.5"/>
+      </AxisValue>
+      <AxisValue index="30" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="342"/>  <!-- Condensed -->
+        <Value value="75.0"/>
+      </AxisValue>
+      <AxisValue index="31" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="343"/>  <!-- SemiCondensed -->
+        <Value value="87.5"/>
+      </AxisValue>
+      <AxisValue index="32" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="310"/>  <!-- Normal -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="33" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="344"/>  <!-- SemiExpanded -->
+        <Value value="112.5"/>
+      </AxisValue>
+      <AxisValue index="34" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="345"/>  <!-- Expanded -->
+        <Value value="125.0"/>
+      </AxisValue>
+      <AxisValue index="35" Format="1">
+        <AxisIndex value="10"/>
+        <Flags value="0"/>
+        <ValueNameID value="346"/>  <!-- ExtraExpanded -->
+        <Value value="150.0"/>
+      </AxisValue>
+      <AxisValue index="36" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="269"/>  <!-- Thin -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="37" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="270"/>  <!-- ExtraLight -->
+        <Value value="200.0"/>
+      </AxisValue>
+      <AxisValue index="38" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="271"/>  <!-- Light -->
+        <Value value="300.0"/>
+      </AxisValue>
+      <AxisValue index="39" Format="3">
+        <AxisIndex value="11"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="272"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="40" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="273"/>  <!-- Medium -->
+        <Value value="500.0"/>
+      </AxisValue>
+      <AxisValue index="41" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="274"/>  <!-- SemiBold -->
+        <Value value="600.0"/>
+      </AxisValue>
+      <AxisValue index="42" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="275"/>  <!-- Bold -->
+        <Value value="700.0"/>
+      </AxisValue>
+      <AxisValue index="43" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="276"/>  <!-- ExtraBold -->
+        <Value value="800.0"/>
+      </AxisValue>
+      <AxisValue index="44" Format="1">
+        <AxisIndex value="11"/>
+        <Flags value="0"/>
+        <ValueNameID value="277"/>  <!-- Black -->
+        <Value value="900.0"/>
+      </AxisValue>
+      <AxisValue index="45" Format="1">
+        <AxisIndex value="12"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="349"/>  <!-- Default -->
+        <Value value="0.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="wdth">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="opsz">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.16925" to="0.492"/>
+      <mapping from="0.53845" to="0.946"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="GRAD">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="slnt">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="XTRA">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="XOPQ">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="YOPQ">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="YTLC">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="YTUC">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="YTAS">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="YTDE">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="YTFI">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- wght -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>1000.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- wdth -->
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>25.0</MinValue>
+      <DefaultValue>100.0</DefaultValue>
+      <MaxValue>151.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- opsz -->
+    <Axis>
+      <AxisTag>opsz</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>8.0</MinValue>
+      <DefaultValue>14.0</DefaultValue>
+      <MaxValue>144.0</MaxValue>
+      <AxisNameID>258</AxisNameID>
+    </Axis>
+
+    <!-- GRAD -->
+    <Axis>
+      <AxisTag>GRAD</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>-200.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>150.0</MaxValue>
+      <AxisNameID>259</AxisNameID>
+    </Axis>
+
+    <!-- slnt -->
+    <Axis>
+      <AxisTag>slnt</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>-10.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>0.0</MaxValue>
+      <AxisNameID>260</AxisNameID>
+    </Axis>
+
+    <!-- XTRA -->
+    <Axis>
+      <AxisTag>XTRA</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>323.0</MinValue>
+      <DefaultValue>468.0</DefaultValue>
+      <MaxValue>603.0</MaxValue>
+      <AxisNameID>261</AxisNameID>
+    </Axis>
+
+    <!-- XOPQ -->
+    <Axis>
+      <AxisTag>XOPQ</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>27.0</MinValue>
+      <DefaultValue>96.0</DefaultValue>
+      <MaxValue>175.0</MaxValue>
+      <AxisNameID>262</AxisNameID>
+    </Axis>
+
+    <!-- YOPQ -->
+    <Axis>
+      <AxisTag>YOPQ</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>25.0</MinValue>
+      <DefaultValue>79.0</DefaultValue>
+      <MaxValue>135.0</MaxValue>
+      <AxisNameID>263</AxisNameID>
+    </Axis>
+
+    <!-- YTLC -->
+    <Axis>
+      <AxisTag>YTLC</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>416.0</MinValue>
+      <DefaultValue>514.0</DefaultValue>
+      <MaxValue>570.0</MaxValue>
+      <AxisNameID>264</AxisNameID>
+    </Axis>
+
+    <!-- YTUC -->
+    <Axis>
+      <AxisTag>YTUC</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>528.0</MinValue>
+      <DefaultValue>712.0</DefaultValue>
+      <MaxValue>760.0</MaxValue>
+      <AxisNameID>265</AxisNameID>
+    </Axis>
+
+    <!-- YTAS -->
+    <Axis>
+      <AxisTag>YTAS</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>649.0</MinValue>
+      <DefaultValue>750.0</DefaultValue>
+      <MaxValue>854.0</MaxValue>
+      <AxisNameID>266</AxisNameID>
+    </Axis>
+
+    <!-- YTDE -->
+    <Axis>
+      <AxisTag>YTDE</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>-305.0</MinValue>
+      <DefaultValue>-203.0</DefaultValue>
+      <MaxValue>-98.0</MaxValue>
+      <AxisNameID>267</AxisNameID>
+    </Axis>
+
+    <!-- YTFI -->
+    <Axis>
+      <AxisTag>YTFI</AxisTag>
+      <Flags>0x1</Flags>
+      <MinValue>560.0</MinValue>
+      <DefaultValue>738.0</DefaultValue>
+      <MaxValue>788.0</MaxValue>
+      <AxisNameID>268</AxisNameID>
+    </Axis>
+
+    <!-- Thin -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Thin -->
+    <NamedInstance flags="0x0" postscriptNameID="289" subfamilyNameID="269">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="290" subfamilyNameID="270">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="291" subfamilyNameID="271">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="292" subfamilyNameID="272">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Medium -->
+    <NamedInstance flags="0x0" postscriptNameID="293" subfamilyNameID="273">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- SemiBold -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-SemiBold -->
+    <NamedInstance flags="0x0" postscriptNameID="294" subfamilyNameID="274">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="295" subfamilyNameID="275">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- ExtraBold -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBold -->
+    <NamedInstance flags="0x0" postscriptNameID="296" subfamilyNameID="276">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="297" subfamilyNameID="277">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- ExtraBlack -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBlack -->
+    <NamedInstance flags="0x0" postscriptNameID="298" subfamilyNameID="278">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="0.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Thin Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ThinItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="299" subfamilyNameID="279">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- ExtraLight Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraLightItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="300" subfamilyNameID="280">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Light Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-LightItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="301" subfamilyNameID="281">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-Italic -->
+    <NamedInstance flags="0x0" postscriptNameID="302" subfamilyNameID="282">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Medium Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-MediumItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="303" subfamilyNameID="283">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- SemiBold Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-SemiBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="304" subfamilyNameID="284">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Bold Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-BoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="305" subfamilyNameID="285">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- ExtraBold Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="306" subfamilyNameID="286">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- Black Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-BlackItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="307" subfamilyNameID="287">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+
+    <!-- ExtraBlack Italic -->
+    <!-- PostScript: RobotoFlexNormalNormalNormalNormalNormalNormalNormalNormalNormalDefault-ExtraBlackItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="308" subfamilyNameID="288">
+      <coord axis="wght" value="1000.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="opsz" value="14.0"/>
+      <coord axis="GRAD" value="0.0"/>
+      <coord axis="slnt" value="-10.0"/>
+      <coord axis="XTRA" value="468.0"/>
+      <coord axis="XOPQ" value="96.0"/>
+      <coord axis="YOPQ" value="79.0"/>
+      <coord axis="YTLC" value="514.0"/>
+      <coord axis="YTUC" value="712.0"/>
+      <coord axis="YTAS" value="750.0"/>
+      <coord axis="YTDE" value="-203.0"/>
+      <coord axis="YTFI" value="738.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="I">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-24" y="0"/>
+        <delta pt="2" x="-24" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-24" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-12" y="0"/>
+        <delta pt="2" x="46" y="0"/>
+        <delta pt="5" x="34" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-2" y="0"/>
+        <delta pt="2" x="-2" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-2" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="3" y="0"/>
+        <delta pt="2" x="3" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="3" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="3" y="0"/>
+        <delta pt="2" x="3" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="3" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="-9" y="0"/>
+        <delta pt="1" x="-29" y="0"/>
+        <delta pt="2" x="-29" y="0"/>
+        <delta pt="3" x="-9" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-38" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="GRAD" value="-1.0"/>
+        <delta pt="0" x="8" y="0"/>
+        <delta pt="1" x="-8" y="0"/>
+        <delta pt="2" x="-8" y="0"/>
+        <delta pt="3" x="8" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="GRAD" value="1.0"/>
+        <delta pt="0" x="-8" y="0"/>
+        <delta pt="1" x="8" y="0"/>
+        <delta pt="2" x="8" y="0"/>
+        <delta pt="3" x="-8" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="-32" y="0"/>
+        <delta pt="1" x="-32" y="0"/>
+        <delta pt="2" x="32" y="0"/>
+        <delta pt="3" x="32" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="XOPQ" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="2" x="-34" y="0"/>
+        <delta pt="5" x="-34" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="XOPQ" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="2" x="40" y="0"/>
+        <delta pt="5" x="40" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="YTUC" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="-93"/>
+        <delta pt="3" x="0" y="-93"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="YTUC" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="25"/>
+        <delta pt="3" x="0" y="25"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="3" y="0"/>
+        <delta pt="5" x="6" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <delta pt="0" x="3" y="0"/>
+        <delta pt="1" x="5" y="0"/>
+        <delta pt="2" x="5" y="0"/>
+        <delta pt="3" x="3" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="8" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="4" y="0"/>
+        <delta pt="1" x="3" y="0"/>
+        <delta pt="2" x="3" y="0"/>
+        <delta pt="3" x="4" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="6" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="11" y="0"/>
+        <delta pt="1" x="-9" y="0"/>
+        <delta pt="2" x="-9" y="0"/>
+        <delta pt="3" x="11" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="2" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="-12" y="0"/>
+        <delta pt="2" x="55" y="0"/>
+        <delta pt="5" x="43" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="GRAD" value="-1.0"/>
+        <delta pt="0" x="-5" y="0"/>
+        <delta pt="1" x="6" y="0"/>
+        <delta pt="2" x="6" y="0"/>
+        <delta pt="3" x="-5" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="GRAD" value="1.0"/>
+        <delta pt="0" x="6" y="0"/>
+        <delta pt="1" x="-6" y="0"/>
+        <delta pt="2" x="-6" y="0"/>
+        <delta pt="3" x="6" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="GRAD" value="1.0"/>
+        <delta pt="0" x="6" y="0"/>
+        <delta pt="1" x="-6" y="0"/>
+        <delta pt="2" x="-6" y="0"/>
+        <delta pt="3" x="6" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="-23" y="0"/>
+        <delta pt="1" x="-24" y="0"/>
+        <delta pt="2" x="-24" y="0"/>
+        <delta pt="3" x="-23" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-47" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="11" y="0"/>
+        <delta pt="1" x="11" y="0"/>
+        <delta pt="2" x="11" y="0"/>
+        <delta pt="3" x="11" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="22" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="23" y="0"/>
+        <delta pt="1" x="20" y="0"/>
+        <delta pt="2" x="20" y="0"/>
+        <delta pt="3" x="23" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="43" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="opsz" value="-1.0"/>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="-2" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="-2" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-2" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="2" y="0"/>
+        <delta pt="1" x="2" y="0"/>
+        <delta pt="2" x="2" y="0"/>
+        <delta pt="3" x="2" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="4" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="-2" y="0"/>
+        <delta pt="1" x="-10" y="0"/>
+        <delta pt="2" x="-10" y="0"/>
+        <delta pt="3" x="-2" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-12" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="12" y="0"/>
+        <delta pt="1" x="-13" y="0"/>
+        <delta pt="2" x="-13" y="0"/>
+        <delta pt="3" x="12" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-1" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <delta pt="0" x="-5" y="0"/>
+        <delta pt="1" x="9" y="0"/>
+        <delta pt="2" x="9" y="0"/>
+        <delta pt="3" x="-5" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="4" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <delta pt="0" x="-3" y="0"/>
+        <delta pt="1" x="-5" y="0"/>
+        <delta pt="2" x="-5" y="0"/>
+        <delta pt="3" x="-3" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-8" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="-1.0"/>
+        <delta pt="0" x="-2" y="0"/>
+        <delta pt="1" x="2" y="0"/>
+        <delta pt="2" x="2" y="0"/>
+        <delta pt="3" x="-2" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="1.0"/>
+        <delta pt="0" x="1" y="0"/>
+        <delta pt="1" x="-1" y="0"/>
+        <delta pt="2" x="-1" y="0"/>
+        <delta pt="3" x="1" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="-1.0"/>
+        <delta pt="0" x="-5" y="0"/>
+        <delta pt="1" x="6" y="0"/>
+        <delta pt="2" x="6" y="0"/>
+        <delta pt="3" x="-5" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="1.0"/>
+        <delta pt="0" x="1" y="0"/>
+        <delta pt="1" x="-1" y="0"/>
+        <delta pt="2" x="-1" y="0"/>
+        <delta pt="3" x="1" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="1.0"/>
+        <coord axis="GRAD" value="1.0"/>
+        <delta pt="0" x="1" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="1" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <coord axis="wdth" value="1.0"/>
+        <coord axis="opsz" value="-1.0"/>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/ttLib/data/I.ttf b/Tests/ttLib/data/I.ttf
new file mode 100644
index 0000000..119c24c
--- /dev/null
+++ b/Tests/ttLib/data/I.ttf
Binary files differ
diff --git a/Tests/ttLib/scaleUpem_test.py b/Tests/ttLib/scaleUpem_test.py
new file mode 100644
index 0000000..dc52bf9
--- /dev/null
+++ b/Tests/ttLib/scaleUpem_test.py
@@ -0,0 +1,75 @@
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.scaleUpem import scale_upem
+import difflib
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+import pytest
+
+class ScaleUpemTest(unittest.TestCase):
+
+    def setUp(self):
+        self.tempdir = None
+        self.num_tempfiles = 0
+
+    def tearDown(self):
+        if self.tempdir:
+            shutil.rmtree(self.tempdir)
+
+    @staticmethod
+    def get_path(test_file_or_folder):
+        parent_dir = os.path.dirname(__file__)
+        return os.path.join(parent_dir, "data", test_file_or_folder)
+
+    def temp_path(self, suffix):
+        self.temp_dir()
+        self.num_tempfiles += 1
+        return os.path.join(self.tempdir,
+                            "tmp%d%s" % (self.num_tempfiles, suffix))
+
+    def temp_dir(self):
+        if not self.tempdir:
+            self.tempdir = tempfile.mkdtemp()
+
+    def read_ttx(self, path):
+        lines = []
+        with open(path, "r", encoding="utf-8") as ttx:
+            for line in ttx.readlines():
+                # Elide ttFont attributes because ttLibVersion may change.
+                if line.startswith("<ttFont "):
+                    lines.append("<ttFont>\n")
+                else:
+                    lines.append(line.rstrip() + "\n")
+        return lines
+
+    def expect_ttx(self, font, expected_ttx, tables):
+        path = self.temp_path(suffix=".ttx")
+        font.saveXML(path, tables=tables)
+        actual = self.read_ttx(path)
+        expected = self.read_ttx(expected_ttx)
+        if actual != expected:
+            for line in difflib.unified_diff(
+                    expected, actual, fromfile=expected_ttx, tofile=path):
+                sys.stdout.write(line)
+            self.fail("TTX output is different from expected")
+
+    def test_scale_upem_ttf(self):
+
+        font = TTFont(self.get_path("I.ttf"))
+        tables = [table_tag for table_tag in font.keys() if table_tag != "head"]
+
+        scale_upem(font, 512)
+
+        expected_ttx_path = self.get_path("I-512upem.ttx")
+        self.expect_ttx(font, expected_ttx_path, tables)
+
+
+    def test_scale_upem_otf(self):
+
+        # Just test that it doesn't crash
+
+        font = TTFont(self.get_path("TestVGID-Regular.otf"))
+
+        scale_upem(font, 500)
diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py
index aaf3300..132449e 100644
--- a/Tests/ttLib/tables/C_O_L_R_test.py
+++ b/Tests/ttLib/tables/C_O_L_R_test.py
@@ -400,8 +400,8 @@
     "        </ColorLine>",
     '        <centerX value="259"/>',
     '        <centerY value="300"/>',
-    '        <startAngle value="45.0"/>',
-    '        <endAngle value="135.0"/>',
+    '        <startAngle value="225.0"/>',
+    '        <endAngle value="315.0"/>',
     "      </Paint>",
     '      <Glyph value="glyph00011"/>',
     "    </Paint>",
@@ -532,17 +532,18 @@
 
 COLR_V1_VAR_XML = [
     '<VarIndexMap Format="0">',
-    '  <Map index="0" outer="1" inner="1"/>',
-    '  <Map index="1" outer="1" inner="0"/>',
-    '  <Map index="2" outer="1" inner="0"/>',
-    '  <Map index="3" outer="1" inner="1"/>',
-    '  <Map index="4" outer="1" inner="0"/>',
-    '  <Map index="5" outer="1" inner="0"/>',
+    '  <!-- Omitted values default to 0xFFFF/0xFFFF (no variations) -->',
+    '  <Map index="0" outer="1" inner="0"/>',
+    '  <Map index="1"/>',
+    '  <Map index="2"/>',
+    '  <Map index="3" outer="1" inner="0"/>',
+    '  <Map index="4"/>',
+    '  <Map index="5"/>',
     '  <Map index="6" outer="0" inner="2"/>',
     '  <Map index="7" outer="0" inner="0"/>',
     '  <Map index="8" outer="0" inner="1"/>',
-    '  <Map index="9" outer="1" inner="0"/>',
-    '  <Map index="10" outer="1" inner="0"/>',
+    '  <Map index="9"/>',
+    '  <Map index="10"/>',
     '  <Map index="11" outer="0" inner="3"/>',
     '  <Map index="12" outer="0" inner="3"/>',
     "</VarIndexMap>",
@@ -571,12 +572,11 @@
     '    <Item index="3" value="[500]"/>',
     "  </VarData>",
     '  <VarData index="1">',
-    "    <!-- ItemCount=2 -->",
+    "    <!-- ItemCount=1 -->",
     '    <NumShorts value="32769"/>',
     "    <!-- VarRegionCount=1 -->",
     '    <VarRegionIndex index="0" value="0"/>',
-    '    <Item index="0" value="[0]"/>',
-    '    <Item index="1" value="[65536]"/>',
+    '    <Item index="0" value="[65536]"/>',
     "  </VarData>",
     "</VarStore>",
 ]
diff --git a/Tests/ttLib/tables/S_V_G__test.py b/Tests/ttLib/tables/S_V_G__test.py
index 91b0f23..4c40556 100644
--- a/Tests/ttLib/tables/S_V_G__test.py
+++ b/Tests/ttLib/tables/S_V_G__test.py
@@ -1,3 +1,5 @@
+import gzip
+import io
 import struct
 
 from fontTools.misc import etree
@@ -12,6 +14,13 @@
     print("\n".join(getXML(table.toXML, ttFont)))
 
 
+def compress(data: bytes) -> bytes:
+    buf = io.BytesIO()
+    with gzip.GzipFile(None, "w", fileobj=buf, mtime=0) as gz:
+        gz.write(data)
+    return buf.getvalue()
+
+
 def strip_xml_whitespace(xml_string):
     def strip_or_none(text):
         text = text.strip() if text else None
@@ -45,7 +54,9 @@
         b"""\
         <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
           <g id="glyph3">
-            <path d="M0,0 L100,0 L50,100 Z"/>
+            <path d="M0,0 L100,0 L50,100 Z" fill="#red"/>
+            <path d="M10,10 L110,10 L60,110 Z" fill="#blue"/>
+            <path d="M20,20 L120,20 L70,120 Z" fill="#green"/>
           </g>
         </svg>""",
     )
@@ -65,25 +76,25 @@
         b"\x00\x02"  # endGlyphID (2)
         b"\x00\x00\x00\x26"  # svgDocOffset (2 + 12*3 == 38 == 0x26)
         + struct.pack(">L", len(SVG_DOCS[0]))  # svgDocLength
-        # SVGDocumentRecord[1]
+        # SVGDocumentRecord[1] (compressed)
         + b"\x00\x03"  # startGlyphID (3)
         b"\x00\x03"  # endGlyphID (3)
         + struct.pack(">L", 0x26 + len(SVG_DOCS[0]))  # svgDocOffset
-        + struct.pack(">L", len(SVG_DOCS[1]))  # svgDocLength
+        + struct.pack(">L", len(compress(SVG_DOCS[1])))  # svgDocLength
         # SVGDocumentRecord[2]
         + b"\x00\x04"  # startGlyphID (4)
         b"\x00\x04"  # endGlyphID (4)
         b"\x00\x00\x00\x26"  # svgDocOffset (38); records 0 and 2 point to same SVG doc
         + struct.pack(">L", len(SVG_DOCS[0]))  # svgDocLength
     ]
-    + SVG_DOCS
+    + [SVG_DOCS[0], compress(SVG_DOCS[1])]
 )
 
 OTSVG_TTX = [
     '<svgDoc endGlyphID="2" startGlyphID="1">',
     f"  <![CDATA[{SVG_DOCS[0].decode()}]]>",
     "</svgDoc>",
-    '<svgDoc endGlyphID="3" startGlyphID="3">',
+    '<svgDoc compressed="1" endGlyphID="3" startGlyphID="3">',
     f"  <![CDATA[{SVG_DOCS[1].decode()}]]>",
     "</svgDoc>",
     '<svgDoc endGlyphID="4" startGlyphID="4">',
@@ -129,3 +140,19 @@
     table = table_S_V_G_()
     table.decompile(compiled, font)
     assert getXML(table.toXML, font) == OTSVG_TTX
+
+
+def test_unpack_svg_doc_as_3_tuple():
+    # test that the legacy docList as list of 3-tuples interface still works
+    # even after the new SVGDocument class with extra `compressed` attribute
+    # was added
+    table = table_S_V_G_()
+    table.decompile(OTSVG_DATA, font)
+
+    for doc, compressed in zip(table.docList, (False, True, False)):
+        assert len(doc) == 3
+        data, startGID, endGID = doc
+        assert doc.data == data
+        assert doc.startGlyphID == startGID
+        assert doc.endGlyphID == endGID
+        assert doc.compressed == compressed
diff --git a/Tests/ttLib/ttGlyphSet_test.py b/Tests/ttLib/ttGlyphSet_test.py
new file mode 100644
index 0000000..bc0bf2c
--- /dev/null
+++ b/Tests/ttLib/ttGlyphSet_test.py
@@ -0,0 +1,112 @@
+from fontTools.ttLib import TTFont
+from fontTools.ttLib import ttGlyphSet
+from fontTools.pens.recordingPen import RecordingPen
+import os
+import pytest
+
+
+class TTGlyphSetTest(object):
+
+    @staticmethod
+    def getpath(testfile):
+        path = os.path.dirname(__file__)
+        return os.path.join(path, "data", testfile)
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            (
+                None,
+                [
+                 ('moveTo', ((175, 0),)),
+                 ('lineTo', ((367, 0),)),
+                 ('lineTo', ((367, 1456),)),
+                 ('lineTo', ((175, 1456),)),
+                 ('closePath', ())
+                ]
+            ),
+            (
+                {},
+                [
+                 ('moveTo', ((175, 0),)),
+                 ('lineTo', ((367, 0),)),
+                 ('lineTo', ((367, 1456),)),
+                 ('lineTo', ((175, 1456),)),
+                 ('closePath', ())
+                ]
+            ),
+            (
+                {'wght': 100},
+                [
+                 ('moveTo', ((175, 0),)),
+                 ('lineTo', ((271, 0),)),
+                 ('lineTo', ((271, 1456),)),
+                 ('lineTo', ((175, 1456),)),
+                 ('closePath', ())
+                ]
+            ),
+            (
+                {'wght': 1000},
+                [
+                 ('moveTo', ((128, 0),)),
+                 ('lineTo', ((550, 0),)),
+                 ('lineTo', ((550, 1456),)),
+                 ('lineTo', ((128, 1456),)),
+                 ('closePath', ())
+                ]
+            ),
+            (
+                {'wght': 1000, 'wdth': 25},
+                [
+                 ('moveTo', ((140, 0),)),
+                 ('lineTo', ((553, 0),)),
+                 ('lineTo', ((553, 1456),)),
+                 ('lineTo', ((140, 1456),)),
+                 ('closePath', ())
+                ]
+            ),
+            (
+                {'wght': 1000, 'wdth': 50},
+                [
+                 ('moveTo', ((136, 0),)),
+                 ('lineTo', ((552, 0),)),
+                 ('lineTo', ((552, 1456),)),
+                 ('lineTo', ((136, 1456),)),
+                 ('closePath', ())
+                ]
+            ),
+        ]
+    )
+    def test_glyphset(
+        self, location, expected
+    ):
+        # TODO: also test loading CFF-flavored fonts
+        font = TTFont(self.getpath("I.ttf"))
+        glyphset = font.getGlyphSet(location=location)
+
+        assert isinstance(glyphset, ttGlyphSet._TTGlyphSet)
+        if location:
+            assert isinstance(glyphset, ttGlyphSet._TTVarGlyphSet)
+
+        assert list(glyphset.keys()) == [".notdef", "I"]
+
+        assert "I" in glyphset
+        assert glyphset.has_key("I")  # we should really get rid of this...
+
+        assert len(glyphset) == 2
+
+        pen = RecordingPen()
+        glyph = glyphset['I']
+
+        assert glyphset.get("foobar") is None
+
+        assert isinstance(glyph, ttGlyphSet._TTGlyph)
+        if location:
+            assert isinstance(glyph, ttGlyphSet._TTVarGlyphGlyf)
+        else:
+            assert isinstance(glyph, ttGlyphSet._TTGlyphGlyf)
+
+        glyph.draw(pen)
+        actual = pen.value
+
+        assert actual == expected, (location, actual, expected)
diff --git a/Tests/ttLib/ttVisitor_test.py b/Tests/ttLib/ttVisitor_test.py
new file mode 100644
index 0000000..e84e213
--- /dev/null
+++ b/Tests/ttLib/ttVisitor_test.py
@@ -0,0 +1,39 @@
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.ttVisitor import TTVisitor
+import os
+import pytest
+
+
+class TestVisitor(TTVisitor):
+    def __init__(self):
+        self.value = []
+        self.depth = 0
+
+    def _add(self, s):
+        self.value.append(s)
+
+    def visit(self, obj, target_depth):
+        if self.depth == target_depth:
+            self._add(obj)
+        self.depth += 1
+        super().visit(obj, target_depth)
+        self.depth -= 1
+
+
+class TTVisitorTest(object):
+
+    @staticmethod
+    def getpath(testfile):
+        path = os.path.dirname(__file__)
+        return os.path.join(path, "data", testfile)
+
+    def test_ttvisitor(self):
+
+        font = TTFont(self.getpath("TestVGID-Regular.otf"))
+        visitor = TestVisitor()
+
+        # Count number of objects at depth 1:
+        # That is, number of font tables, including GlyphOrder.
+        visitor.visit(font, 1)
+
+        assert len(visitor.value) == 14
diff --git a/Tests/varLib/data/TestVariableCOLR.designspace b/Tests/varLib/data/TestVariableCOLR.designspace
new file mode 100644
index 0000000..0feddfa
--- /dev/null
+++ b/Tests/varLib/data/TestVariableCOLR.designspace
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="5.0">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="400" maximum="700" default="400"/>
+  </axes>
+  <sources>
+    <source filename="master_ttx_varcolr_ttf/TestVariableCOLR-Regular.ttx" name="Regular">
+      <location>
+        <dimension name="Weight" xvalue="400"/>
+      </location>
+    </source>
+    <source filename="master_ttx_varcolr_ttf/TestVariableCOLR-Bold.ttx" name="Bold">
+      <location>
+        <dimension name="Weight" xvalue="700"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/master_ttx_varcolr_ttf/TestVariableCOLR-Bold.ttx b/Tests/varLib/data/master_ttx_varcolr_ttf/TestVariableCOLR-Bold.ttx
new file mode 100644
index 0000000..4202dab
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_varcolr_ttf/TestVariableCOLR-Bold.ttx
@@ -0,0 +1,357 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.33">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="B"/>
+    <GlyphID id="4" name="A.0"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x92daf67f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Tue Jul  5 12:29:44 2022"/>
+    <modified value="Tue Jul  5 12:29:44 2022"/>
+    <xMin value="51"/>
+    <yMin value="-250"/>
+    <xMax value="878"/>
+    <yMax value="950"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1275"/>
+    <minLeftSideBearing value="51"/>
+    <minRightSideBearing value="397"/>
+    <xMaxExtent value="878"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="16"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="1275"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="666"/>
+    <ySubscriptYSize value="614"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="77"/>
+    <ySuperscriptXSize value="666"/>
+    <ySuperscriptYSize value="614"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="358"/>
+    <yStrikeoutSize value="51"/>
+    <yStrikeoutPosition value="307"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 10100000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="66"/>
+    <sTypoAscender value="950"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="512"/>
+    <sCapHeight value="717"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1275" lsb="51"/>
+    <mtx name=".space" width="1275" lsb="0"/>
+    <mtx name="A" width="1275" lsb="0"/>
+    <mtx name="A.0" width="1275" lsb="398"/>
+    <mtx name="B" width="1275" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name=".space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name=".space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="51" yMin="-250" xMax="461" yMax="950">
+      <contour>
+        <pt x="51" y="-250" on="1"/>
+        <pt x="51" y="950" on="1"/>
+        <pt x="461" y="950" on="1"/>
+        <pt x="461" y="-250" on="1"/>
+      </contour>
+      <contour>
+        <pt x="102" y="-199" on="1"/>
+        <pt x="410" y="-199" on="1"/>
+        <pt x="410" y="899" on="1"/>
+        <pt x="102" y="899" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name=".space"/><!-- contains no outline data -->
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="A.0" xMin="398" yMin="110" xMax="878" yMax="590">
+      <contour>
+        <pt x="878" y="350" on="1"/>
+        <pt x="878" y="416" on="0"/>
+        <pt x="813" y="525" on="0"/>
+        <pt x="704" y="590" on="0"/>
+        <pt x="638" y="590" on="1"/>
+        <pt x="571" y="590" on="0"/>
+        <pt x="462" y="525" on="0"/>
+        <pt x="398" y="416" on="0"/>
+        <pt x="398" y="350" on="1"/>
+        <pt x="398" y="284" on="0"/>
+        <pt x="462" y="175" on="0"/>
+        <pt x="571" y="110" on="0"/>
+        <pt x="638" y="110" on="1"/>
+        <pt x="704" y="110" on="0"/>
+        <pt x="813" y="175" on="0"/>
+        <pt x="878" y="284" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      An Emoji Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;AnEmojiFamily-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      An Emoji Family Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      AnEmojiFamily-Bold
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-77"/>
+    <underlineThickness value="51"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name=".space"/>
+      <psName name="A.0"/>
+    </extraNames>
+  </post>
+
+  <COLR>
+    <Version value="1"/>
+    <!-- BaseGlyphRecordCount=0 -->
+    <!-- LayerRecordCount=0 -->
+    <BaseGlyphList>
+      <!-- BaseGlyphCount=2 -->
+      <BaseGlyphPaintRecord index="0">
+        <BaseGlyph value="A"/>
+        <Paint Format="1"><!-- PaintColrLayers -->
+          <NumLayers value="3"/>
+          <FirstLayerIndex value="0"/>
+        </Paint>
+      </BaseGlyphPaintRecord>
+      <BaseGlyphPaintRecord index="1">
+        <BaseGlyph value="B"/>
+        <Paint Format="1"><!-- PaintColrLayers -->
+          <NumLayers value="2"/>
+          <FirstLayerIndex value="3"/>
+        </Paint>
+      </BaseGlyphPaintRecord>
+    </BaseGlyphList>
+    <LayerList>
+      <!-- LayerCount=5 -->
+      <Paint index="0" Format="10"><!-- PaintGlyph -->
+        <Paint Format="2"><!-- PaintSolid -->
+          <PaletteIndex value="0"/>
+          <Alpha value="1.0"/>
+        </Paint>
+        <Glyph value="A.0"/>
+      </Paint>
+      <Paint index="1" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="2"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="100"/>
+        <dy value="-120"/>
+      </Paint>
+      <Paint index="2" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="1"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-240"/>
+      </Paint>
+      <Paint index="3" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="2"/>
+            <Alpha value="0.5"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-120"/>
+      </Paint>
+      <Paint index="4" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="1"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-240"/>
+      </Paint>
+    </LayerList>
+    <ClipList Format="1">
+      <Clip>
+        <Glyph value="A"/>
+        <ClipBox Format="1">
+          <xMin value="380"/>
+          <yMin value="-140"/>
+          <xMax value="980"/>
+          <yMax value="600"/>
+        </ClipBox>
+      </Clip>
+      <Clip>
+        <Glyph value="B"/>
+        <ClipBox Format="1">
+          <xMin value="380"/>
+          <yMin value="-140"/>
+          <xMax value="880"/>
+          <yMax value="480"/>
+        </ClipBox>
+      </Clip>
+    </ClipList>
+  </COLR>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="3"/>
+    <palette index="0">
+      <color index="0" value="#0000FFFF"/>
+      <color index="1" value="#008000FF"/>
+      <color index="2" value="#FF0000FF"/>
+    </palette>
+  </CPAL>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_varcolr_ttf/TestVariableCOLR-Regular.ttx b/Tests/varLib/data/master_ttx_varcolr_ttf/TestVariableCOLR-Regular.ttx
new file mode 100644
index 0000000..195be50
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_varcolr_ttf/TestVariableCOLR-Regular.ttx
@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.33">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="B"/>
+    <GlyphID id="4" name="A.0"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x49d0234e"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Tue Jul  5 12:29:44 2022"/>
+    <modified value="Tue Jul  5 12:29:44 2022"/>
+    <xMin value="51"/>
+    <yMin value="-250"/>
+    <xMax value="878"/>
+    <yMax value="950"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1275"/>
+    <minLeftSideBearing value="51"/>
+    <minRightSideBearing value="397"/>
+    <xMaxExtent value="878"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="16"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="1275"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="666"/>
+    <ySubscriptYSize value="614"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="77"/>
+    <ySuperscriptXSize value="666"/>
+    <ySuperscriptYSize value="614"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="358"/>
+    <yStrikeoutSize value="51"/>
+    <yStrikeoutPosition value="307"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="66"/>
+    <sTypoAscender value="950"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="512"/>
+    <sCapHeight value="717"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1275" lsb="51"/>
+    <mtx name=".space" width="1275" lsb="0"/>
+    <mtx name="A" width="1275" lsb="0"/>
+    <mtx name="A.0" width="1275" lsb="398"/>
+    <mtx name="B" width="1275" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name=".space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name=".space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="51" yMin="-250" xMax="461" yMax="950">
+      <contour>
+        <pt x="51" y="-250" on="1"/>
+        <pt x="51" y="950" on="1"/>
+        <pt x="461" y="950" on="1"/>
+        <pt x="461" y="-250" on="1"/>
+      </contour>
+      <contour>
+        <pt x="102" y="-199" on="1"/>
+        <pt x="410" y="-199" on="1"/>
+        <pt x="410" y="899" on="1"/>
+        <pt x="102" y="899" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name=".space"/><!-- contains no outline data -->
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="A.0" xMin="398" yMin="110" xMax="878" yMax="590">
+      <contour>
+        <pt x="878" y="350" on="1"/>
+        <pt x="878" y="416" on="0"/>
+        <pt x="813" y="525" on="0"/>
+        <pt x="704" y="590" on="0"/>
+        <pt x="638" y="590" on="1"/>
+        <pt x="571" y="590" on="0"/>
+        <pt x="462" y="525" on="0"/>
+        <pt x="398" y="416" on="0"/>
+        <pt x="398" y="350" on="1"/>
+        <pt x="398" y="284" on="0"/>
+        <pt x="462" y="175" on="0"/>
+        <pt x="571" y="110" on="0"/>
+        <pt x="638" y="110" on="1"/>
+        <pt x="704" y="110" on="0"/>
+        <pt x="813" y="175" on="0"/>
+        <pt x="878" y="284" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      An Emoji Family
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;AnEmojiFamily-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      An Emoji Family Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      AnEmojiFamily-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-77"/>
+    <underlineThickness value="51"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name=".space"/>
+      <psName name="A.0"/>
+    </extraNames>
+  </post>
+
+  <COLR>
+    <Version value="1"/>
+    <!-- BaseGlyphRecordCount=0 -->
+    <!-- LayerRecordCount=0 -->
+    <BaseGlyphList>
+      <!-- BaseGlyphCount=2 -->
+      <BaseGlyphPaintRecord index="0">
+        <BaseGlyph value="A"/>
+        <Paint Format="1"><!-- PaintColrLayers -->
+          <NumLayers value="3"/>
+          <FirstLayerIndex value="0"/>
+        </Paint>
+      </BaseGlyphPaintRecord>
+      <BaseGlyphPaintRecord index="1">
+        <BaseGlyph value="B"/>
+        <Paint Format="1"><!-- PaintColrLayers -->
+          <NumLayers value="2"/>
+          <FirstLayerIndex value="1"/>
+        </Paint>
+      </BaseGlyphPaintRecord>
+    </BaseGlyphList>
+    <LayerList>
+      <!-- LayerCount=3 -->
+      <Paint index="0" Format="10"><!-- PaintGlyph -->
+        <Paint Format="2"><!-- PaintSolid -->
+          <PaletteIndex value="0"/>
+          <Alpha value="1.0"/>
+        </Paint>
+        <Glyph value="A.0"/>
+      </Paint>
+      <Paint index="1" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="2"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-120"/>
+      </Paint>
+      <Paint index="2" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="1"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-240"/>
+      </Paint>
+    </LayerList>
+    <ClipList Format="1">
+      <Clip>
+        <Glyph value="A"/>
+        <ClipBox Format="1">
+          <xMin value="380"/>
+          <yMin value="-140"/>
+          <xMax value="880"/>
+          <yMax value="600"/>
+        </ClipBox>
+      </Clip>
+      <Clip>
+        <Glyph value="B"/>
+        <ClipBox Format="1">
+          <xMin value="380"/>
+          <yMin value="-140"/>
+          <xMax value="880"/>
+          <yMax value="480"/>
+        </ClipBox>
+      </Clip>
+    </ClipList>
+  </COLR>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="3"/>
+    <palette index="0">
+      <color index="0" value="#0000FFFF"/>
+      <color index="1" value="#008000FF"/>
+      <color index="2" value="#FF0000FF"/>
+    </palette>
+  </CPAL>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/TestVariableCOLR-VF.ttx b/Tests/varLib/data/test_results/TestVariableCOLR-VF.ttx
new file mode 100644
index 0000000..8d0177a
--- /dev/null
+++ b/Tests/varLib/data/test_results/TestVariableCOLR-VF.ttx
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.33">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name=".space"/>
+    <GlyphID id="2" name="A"/>
+    <GlyphID id="3" name="B"/>
+    <GlyphID id="4" name="A.0"/>
+  </GlyphOrder>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>400.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>700.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="51" yMin="-250" xMax="461" yMax="950">
+      <contour>
+        <pt x="51" y="-250" on="1"/>
+        <pt x="51" y="950" on="1"/>
+        <pt x="461" y="950" on="1"/>
+        <pt x="461" y="-250" on="1"/>
+      </contour>
+      <contour>
+        <pt x="102" y="-199" on="1"/>
+        <pt x="410" y="-199" on="1"/>
+        <pt x="410" y="899" on="1"/>
+        <pt x="102" y="899" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name=".space"/><!-- contains no outline data -->
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="A.0" xMin="398" yMin="110" xMax="878" yMax="590">
+      <contour>
+        <pt x="878" y="350" on="1"/>
+        <pt x="878" y="416" on="0"/>
+        <pt x="813" y="525" on="0"/>
+        <pt x="704" y="590" on="0"/>
+        <pt x="638" y="590" on="1"/>
+        <pt x="571" y="590" on="0"/>
+        <pt x="462" y="525" on="0"/>
+        <pt x="398" y="416" on="0"/>
+        <pt x="398" y="350" on="1"/>
+        <pt x="398" y="284" on="0"/>
+        <pt x="462" y="175" on="0"/>
+        <pt x="571" y="110" on="0"/>
+        <pt x="638" y="110" on="1"/>
+        <pt x="704" y="110" on="0"/>
+        <pt x="813" y="175" on="0"/>
+        <pt x="878" y="284" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <COLR>
+    <Version value="1"/>
+    <!-- BaseGlyphRecordCount=0 -->
+    <!-- LayerRecordCount=0 -->
+    <BaseGlyphList>
+      <!-- BaseGlyphCount=2 -->
+      <BaseGlyphPaintRecord index="0">
+        <BaseGlyph value="A"/>
+        <Paint Format="1"><!-- PaintColrLayers -->
+          <NumLayers value="3"/>
+          <FirstLayerIndex value="0"/>
+        </Paint>
+      </BaseGlyphPaintRecord>
+      <BaseGlyphPaintRecord index="1">
+        <BaseGlyph value="B"/>
+        <Paint Format="1"><!-- PaintColrLayers -->
+          <NumLayers value="2"/>
+          <FirstLayerIndex value="3"/>
+        </Paint>
+      </BaseGlyphPaintRecord>
+    </BaseGlyphList>
+    <LayerList>
+      <!-- LayerCount=5 -->
+      <Paint index="0" Format="10"><!-- PaintGlyph -->
+        <Paint Format="2"><!-- PaintSolid -->
+          <PaletteIndex value="0"/>
+          <Alpha value="1.0"/>
+        </Paint>
+        <Glyph value="A.0"/>
+      </Paint>
+      <Paint index="1" Format="15"><!-- PaintVarTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="2"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-120"/>
+        <VarIndexBase value="0"/>
+      </Paint>
+      <Paint index="2" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="1"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-240"/>
+      </Paint>
+      <Paint index="3" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="3"><!-- PaintVarSolid -->
+            <PaletteIndex value="2"/>
+            <Alpha value="1.0"/>
+            <VarIndexBase value="2"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-120"/>
+      </Paint>
+      <Paint index="4" Format="14"><!-- PaintTranslate -->
+        <Paint Format="10"><!-- PaintGlyph -->
+          <Paint Format="2"><!-- PaintSolid -->
+            <PaletteIndex value="1"/>
+            <Alpha value="1.0"/>
+          </Paint>
+          <Glyph value="A.0"/>
+        </Paint>
+        <dx value="0"/>
+        <dy value="-240"/>
+      </Paint>
+    </LayerList>
+    <ClipList Format="1">
+      <Clip>
+        <Glyph value="A"/>
+        <ClipBox Format="2">
+          <xMin value="380"/>
+          <yMin value="-140"/>
+          <xMax value="880"/>
+          <yMax value="600"/>
+          <VarIndexBase value="3"/>
+        </ClipBox>
+      </Clip>
+      <Clip>
+        <Glyph value="B"/>
+        <ClipBox Format="1">
+          <xMin value="380"/>
+          <yMin value="-140"/>
+          <xMax value="880"/>
+          <yMax value="480"/>
+        </ClipBox>
+      </Clip>
+    </ClipList>
+    <VarIndexMap Format="0">
+      <!-- Omitted values default to 0xFFFF/0xFFFF (no variations) -->
+      <Map index="0" outer="0" inner="1"/>
+      <Map index="1"/>
+      <Map index="2" outer="0" inner="0"/>
+      <Map index="3"/>
+      <Map index="4"/>
+      <Map index="5" outer="0" inner="1"/>
+      <Map index="6"/>
+    </VarIndexMap>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=1 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[-8192]"/>
+        <Item index="1" value="[100]"/>
+      </VarData>
+    </VarStore>
+  </COLR>
+
+  <CPAL>
+    <version value="0"/>
+    <numPaletteEntries value="3"/>
+    <palette index="0">
+      <color index="0" value="#0000FFFF"/>
+      <color index="1" value="#008000FF"/>
+      <color index="2" value="#FF0000FF"/>
+    </palette>
+  </CPAL>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/STATInstancerTest.ttx b/Tests/varLib/instancer/data/STATInstancerTest.ttx
new file mode 100644
index 0000000..eee24d8
--- /dev/null
+++ b/Tests/varLib/instancer/data/STATInstancerTest.ttx
@@ -0,0 +1,1830 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.33">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0xbc466984"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Jul  5 13:33:16 2022"/>
+    <modified value="Tue Jul  5 13:33:42 2022"/>
+    <xMin value="50"/>
+    <yMin value="-200"/>
+    <xMax value="450"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="500"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <xMaxExtent value="450"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="1"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="500"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65535"/>
+    <usLastCharIndex value="65535"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Italic
+    </namerecord>
+    <namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Hair
+    </namerecord>
+    <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdHair
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Hair Italic
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdHairItalic
+    </namerecord>
+    <namerecord nameID="263" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Hair
+    </namerecord>
+    <namerecord nameID="264" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Hair
+    </namerecord>
+    <namerecord nameID="265" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Hair Italic
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-HairItalic
+    </namerecord>
+    <namerecord nameID="267" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Hair
+    </namerecord>
+    <namerecord nameID="268" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExHair
+    </namerecord>
+    <namerecord nameID="269" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Hair Italic
+    </namerecord>
+    <namerecord nameID="270" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExHairItalic
+    </namerecord>
+    <namerecord nameID="271" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Thin
+    </namerecord>
+    <namerecord nameID="272" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdThin
+    </namerecord>
+    <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Thin Italic
+    </namerecord>
+    <namerecord nameID="274" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdThinItalic
+    </namerecord>
+    <namerecord nameID="275" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Thin
+    </namerecord>
+    <namerecord nameID="276" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Thin
+    </namerecord>
+    <namerecord nameID="277" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Thin Italic
+    </namerecord>
+    <namerecord nameID="278" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ThinItalic
+    </namerecord>
+    <namerecord nameID="279" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Thin
+    </namerecord>
+    <namerecord nameID="280" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExThin
+    </namerecord>
+    <namerecord nameID="281" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Thin Italic
+    </namerecord>
+    <namerecord nameID="282" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExThinItalic
+    </namerecord>
+    <namerecord nameID="283" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Light
+    </namerecord>
+    <namerecord nameID="284" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdLight
+    </namerecord>
+    <namerecord nameID="285" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Light Italic
+    </namerecord>
+    <namerecord nameID="286" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdLightItalic
+    </namerecord>
+    <namerecord nameID="287" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Light
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Light
+    </namerecord>
+    <namerecord nameID="289" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Light Italic
+    </namerecord>
+    <namerecord nameID="290" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-LightItalic
+    </namerecord>
+    <namerecord nameID="291" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Light
+    </namerecord>
+    <namerecord nameID="292" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExLight
+    </namerecord>
+    <namerecord nameID="293" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Light Italic
+    </namerecord>
+    <namerecord nameID="294" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExLightItalic
+    </namerecord>
+    <namerecord nameID="295" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd
+    </namerecord>
+    <namerecord nameID="296" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Cd
+    </namerecord>
+    <namerecord nameID="297" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Italic
+    </namerecord>
+    <namerecord nameID="298" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdItalic
+    </namerecord>
+    <namerecord nameID="299" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      
+    </namerecord>
+    <namerecord nameID="300" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-
+    </namerecord>
+    <namerecord nameID="301" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Italic
+    </namerecord>
+    <namerecord nameID="302" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex
+    </namerecord>
+    <namerecord nameID="303" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Ex
+    </namerecord>
+    <namerecord nameID="304" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Italic
+    </namerecord>
+    <namerecord nameID="305" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExItalic
+    </namerecord>
+    <namerecord nameID="306" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Medium
+    </namerecord>
+    <namerecord nameID="307" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdMedium
+    </namerecord>
+    <namerecord nameID="308" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Medium Italic
+    </namerecord>
+    <namerecord nameID="309" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdMediumItalic
+    </namerecord>
+    <namerecord nameID="310" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Medium
+    </namerecord>
+    <namerecord nameID="311" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Medium
+    </namerecord>
+    <namerecord nameID="312" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Medium Italic
+    </namerecord>
+    <namerecord nameID="313" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-MediumItalic
+    </namerecord>
+    <namerecord nameID="314" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Medium
+    </namerecord>
+    <namerecord nameID="315" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExMedium
+    </namerecord>
+    <namerecord nameID="316" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Medium Italic
+    </namerecord>
+    <namerecord nameID="317" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExMediumItalic
+    </namerecord>
+    <namerecord nameID="318" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd SemiBold
+    </namerecord>
+    <namerecord nameID="319" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdSemiBold
+    </namerecord>
+    <namerecord nameID="320" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd SemiBold Italic
+    </namerecord>
+    <namerecord nameID="321" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdSemiBoldItalic
+    </namerecord>
+    <namerecord nameID="322" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="323" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-SemiBold
+    </namerecord>
+    <namerecord nameID="324" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiBold Italic
+    </namerecord>
+    <namerecord nameID="325" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-SemiBoldItalic
+    </namerecord>
+    <namerecord nameID="326" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex SemiBold
+    </namerecord>
+    <namerecord nameID="327" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExSemiBold
+    </namerecord>
+    <namerecord nameID="328" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex SemiBold Italic
+    </namerecord>
+    <namerecord nameID="329" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExSemiBoldItalic
+    </namerecord>
+    <namerecord nameID="330" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Bold
+    </namerecord>
+    <namerecord nameID="331" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdBold
+    </namerecord>
+    <namerecord nameID="332" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Bold Italic
+    </namerecord>
+    <namerecord nameID="333" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdBoldItalic
+    </namerecord>
+    <namerecord nameID="334" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="335" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Bold
+    </namerecord>
+    <namerecord nameID="336" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold Italic
+    </namerecord>
+    <namerecord nameID="337" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-BoldItalic
+    </namerecord>
+    <namerecord nameID="338" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Bold
+    </namerecord>
+    <namerecord nameID="339" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExBold
+    </namerecord>
+    <namerecord nameID="340" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Bold Italic
+    </namerecord>
+    <namerecord nameID="341" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExBoldItalic
+    </namerecord>
+    <namerecord nameID="342" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd XBold
+    </namerecord>
+    <namerecord nameID="343" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdXBold
+    </namerecord>
+    <namerecord nameID="344" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd XBold Italic
+    </namerecord>
+    <namerecord nameID="345" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdXBoldItalic
+    </namerecord>
+    <namerecord nameID="346" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      XBold
+    </namerecord>
+    <namerecord nameID="347" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-XBold
+    </namerecord>
+    <namerecord nameID="348" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      XBold Italic
+    </namerecord>
+    <namerecord nameID="349" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-XBoldItalic
+    </namerecord>
+    <namerecord nameID="350" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex XBold
+    </namerecord>
+    <namerecord nameID="351" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExXBold
+    </namerecord>
+    <namerecord nameID="352" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex XBold Italic
+    </namerecord>
+    <namerecord nameID="353" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExXBoldItalic
+    </namerecord>
+    <namerecord nameID="354" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Black
+    </namerecord>
+    <namerecord nameID="355" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdBlack
+    </namerecord>
+    <namerecord nameID="356" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Cd Black Italic
+    </namerecord>
+    <namerecord nameID="357" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-CdBlackItalic
+    </namerecord>
+    <namerecord nameID="358" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="359" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-Black
+    </namerecord>
+    <namerecord nameID="360" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black Italic
+    </namerecord>
+    <namerecord nameID="361" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-BlackItalic
+    </namerecord>
+    <namerecord nameID="362" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Black
+    </namerecord>
+    <namerecord nameID="363" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExBlack
+    </namerecord>
+    <namerecord nameID="364" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ex Black Italic
+    </namerecord>
+    <namerecord nameID="365" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      NewFont-ExBlackItalic
+    </namerecord>
+    <namerecord nameID="366" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="367" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Normal
+    </namerecord>
+    <namerecord nameID="368" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Upright
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      New Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;NewFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      New Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Cd Hair
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdHair
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Cd Hair Italic
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdHairItalic
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      Hair
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Hair
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      Hair Italic
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      NewFont-HairItalic
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      Ex Hair
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExHair
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      Ex Hair Italic
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExHairItalic
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      Cd Thin
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdThin
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      Cd Thin Italic
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdThinItalic
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="276" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Thin
+    </namerecord>
+    <namerecord nameID="277" platformID="3" platEncID="1" langID="0x409">
+      Thin Italic
+    </namerecord>
+    <namerecord nameID="278" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ThinItalic
+    </namerecord>
+    <namerecord nameID="279" platformID="3" platEncID="1" langID="0x409">
+      Ex Thin
+    </namerecord>
+    <namerecord nameID="280" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExThin
+    </namerecord>
+    <namerecord nameID="281" platformID="3" platEncID="1" langID="0x409">
+      Ex Thin Italic
+    </namerecord>
+    <namerecord nameID="282" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExThinItalic
+    </namerecord>
+    <namerecord nameID="283" platformID="3" platEncID="1" langID="0x409">
+      Cd Light
+    </namerecord>
+    <namerecord nameID="284" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdLight
+    </namerecord>
+    <namerecord nameID="285" platformID="3" platEncID="1" langID="0x409">
+      Cd Light Italic
+    </namerecord>
+    <namerecord nameID="286" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdLightItalic
+    </namerecord>
+    <namerecord nameID="287" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Light
+    </namerecord>
+    <namerecord nameID="289" platformID="3" platEncID="1" langID="0x409">
+      Light Italic
+    </namerecord>
+    <namerecord nameID="290" platformID="3" platEncID="1" langID="0x409">
+      NewFont-LightItalic
+    </namerecord>
+    <namerecord nameID="291" platformID="3" platEncID="1" langID="0x409">
+      Ex Light
+    </namerecord>
+    <namerecord nameID="292" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExLight
+    </namerecord>
+    <namerecord nameID="293" platformID="3" platEncID="1" langID="0x409">
+      Ex Light Italic
+    </namerecord>
+    <namerecord nameID="294" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExLightItalic
+    </namerecord>
+    <namerecord nameID="295" platformID="3" platEncID="1" langID="0x409">
+      Cd
+    </namerecord>
+    <namerecord nameID="296" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Cd
+    </namerecord>
+    <namerecord nameID="297" platformID="3" platEncID="1" langID="0x409">
+      Cd Italic
+    </namerecord>
+    <namerecord nameID="298" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdItalic
+    </namerecord>
+    <namerecord nameID="299" platformID="3" platEncID="1" langID="0x409">
+      
+    </namerecord>
+    <namerecord nameID="300" platformID="3" platEncID="1" langID="0x409">
+      NewFont-
+    </namerecord>
+    <namerecord nameID="301" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Italic
+    </namerecord>
+    <namerecord nameID="302" platformID="3" platEncID="1" langID="0x409">
+      Ex
+    </namerecord>
+    <namerecord nameID="303" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Ex
+    </namerecord>
+    <namerecord nameID="304" platformID="3" platEncID="1" langID="0x409">
+      Ex Italic
+    </namerecord>
+    <namerecord nameID="305" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExItalic
+    </namerecord>
+    <namerecord nameID="306" platformID="3" platEncID="1" langID="0x409">
+      Cd Medium
+    </namerecord>
+    <namerecord nameID="307" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdMedium
+    </namerecord>
+    <namerecord nameID="308" platformID="3" platEncID="1" langID="0x409">
+      Cd Medium Italic
+    </namerecord>
+    <namerecord nameID="309" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdMediumItalic
+    </namerecord>
+    <namerecord nameID="310" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="311" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Medium
+    </namerecord>
+    <namerecord nameID="312" platformID="3" platEncID="1" langID="0x409">
+      Medium Italic
+    </namerecord>
+    <namerecord nameID="313" platformID="3" platEncID="1" langID="0x409">
+      NewFont-MediumItalic
+    </namerecord>
+    <namerecord nameID="314" platformID="3" platEncID="1" langID="0x409">
+      Ex Medium
+    </namerecord>
+    <namerecord nameID="315" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExMedium
+    </namerecord>
+    <namerecord nameID="316" platformID="3" platEncID="1" langID="0x409">
+      Ex Medium Italic
+    </namerecord>
+    <namerecord nameID="317" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExMediumItalic
+    </namerecord>
+    <namerecord nameID="318" platformID="3" platEncID="1" langID="0x409">
+      Cd SemiBold
+    </namerecord>
+    <namerecord nameID="319" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdSemiBold
+    </namerecord>
+    <namerecord nameID="320" platformID="3" platEncID="1" langID="0x409">
+      Cd SemiBold Italic
+    </namerecord>
+    <namerecord nameID="321" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdSemiBoldItalic
+    </namerecord>
+    <namerecord nameID="322" platformID="3" platEncID="1" langID="0x409">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="323" platformID="3" platEncID="1" langID="0x409">
+      NewFont-SemiBold
+    </namerecord>
+    <namerecord nameID="324" platformID="3" platEncID="1" langID="0x409">
+      SemiBold Italic
+    </namerecord>
+    <namerecord nameID="325" platformID="3" platEncID="1" langID="0x409">
+      NewFont-SemiBoldItalic
+    </namerecord>
+    <namerecord nameID="326" platformID="3" platEncID="1" langID="0x409">
+      Ex SemiBold
+    </namerecord>
+    <namerecord nameID="327" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExSemiBold
+    </namerecord>
+    <namerecord nameID="328" platformID="3" platEncID="1" langID="0x409">
+      Ex SemiBold Italic
+    </namerecord>
+    <namerecord nameID="329" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExSemiBoldItalic
+    </namerecord>
+    <namerecord nameID="330" platformID="3" platEncID="1" langID="0x409">
+      Cd Bold
+    </namerecord>
+    <namerecord nameID="331" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdBold
+    </namerecord>
+    <namerecord nameID="332" platformID="3" platEncID="1" langID="0x409">
+      Cd Bold Italic
+    </namerecord>
+    <namerecord nameID="333" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdBoldItalic
+    </namerecord>
+    <namerecord nameID="334" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="335" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Bold
+    </namerecord>
+    <namerecord nameID="336" platformID="3" platEncID="1" langID="0x409">
+      Bold Italic
+    </namerecord>
+    <namerecord nameID="337" platformID="3" platEncID="1" langID="0x409">
+      NewFont-BoldItalic
+    </namerecord>
+    <namerecord nameID="338" platformID="3" platEncID="1" langID="0x409">
+      Ex Bold
+    </namerecord>
+    <namerecord nameID="339" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExBold
+    </namerecord>
+    <namerecord nameID="340" platformID="3" platEncID="1" langID="0x409">
+      Ex Bold Italic
+    </namerecord>
+    <namerecord nameID="341" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExBoldItalic
+    </namerecord>
+    <namerecord nameID="342" platformID="3" platEncID="1" langID="0x409">
+      Cd XBold
+    </namerecord>
+    <namerecord nameID="343" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdXBold
+    </namerecord>
+    <namerecord nameID="344" platformID="3" platEncID="1" langID="0x409">
+      Cd XBold Italic
+    </namerecord>
+    <namerecord nameID="345" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdXBoldItalic
+    </namerecord>
+    <namerecord nameID="346" platformID="3" platEncID="1" langID="0x409">
+      XBold
+    </namerecord>
+    <namerecord nameID="347" platformID="3" platEncID="1" langID="0x409">
+      NewFont-XBold
+    </namerecord>
+    <namerecord nameID="348" platformID="3" platEncID="1" langID="0x409">
+      XBold Italic
+    </namerecord>
+    <namerecord nameID="349" platformID="3" platEncID="1" langID="0x409">
+      NewFont-XBoldItalic
+    </namerecord>
+    <namerecord nameID="350" platformID="3" platEncID="1" langID="0x409">
+      Ex XBold
+    </namerecord>
+    <namerecord nameID="351" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExXBold
+    </namerecord>
+    <namerecord nameID="352" platformID="3" platEncID="1" langID="0x409">
+      Ex XBold Italic
+    </namerecord>
+    <namerecord nameID="353" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExXBoldItalic
+    </namerecord>
+    <namerecord nameID="354" platformID="3" platEncID="1" langID="0x409">
+      Cd Black
+    </namerecord>
+    <namerecord nameID="355" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdBlack
+    </namerecord>
+    <namerecord nameID="356" platformID="3" platEncID="1" langID="0x409">
+      Cd Black Italic
+    </namerecord>
+    <namerecord nameID="357" platformID="3" platEncID="1" langID="0x409">
+      NewFont-CdBlackItalic
+    </namerecord>
+    <namerecord nameID="358" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="359" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Black
+    </namerecord>
+    <namerecord nameID="360" platformID="3" platEncID="1" langID="0x409">
+      Black Italic
+    </namerecord>
+    <namerecord nameID="361" platformID="3" platEncID="1" langID="0x409">
+      NewFont-BlackItalic
+    </namerecord>
+    <namerecord nameID="362" platformID="3" platEncID="1" langID="0x409">
+      Ex Black
+    </namerecord>
+    <namerecord nameID="363" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExBlack
+    </namerecord>
+    <namerecord nameID="364" platformID="3" platEncID="1" langID="0x409">
+      Ex Black Italic
+    </namerecord>
+    <namerecord nameID="365" platformID="3" platEncID="1" langID="0x409">
+      NewFont-ExBlackItalic
+    </namerecord>
+    <namerecord nameID="366" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="367" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="368" platformID="3" platEncID="1" langID="0x409">
+      Upright
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=3 -->
+        <!-- RegionCount=19 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.48517"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.48517"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="7">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="8">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="9">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="10">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="11">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.48517"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="12">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.48517"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="13">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="14">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="15">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="16">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="17">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="18">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="2">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=3 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="2">
+        <AxisTag value="ital"/>
+        <AxisNameID value="258"/>  <!-- Italic -->
+        <AxisOrdering value="2"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=14 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="263"/>  <!-- Hair -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="275"/>  <!-- Thin -->
+        <Value value="200.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="287"/>  <!-- Light -->
+        <Value value="300.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="3">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="366"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="310"/>  <!-- Medium -->
+        <Value value="500.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="322"/>  <!-- SemiBold -->
+        <Value value="600.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="334"/>  <!-- Bold -->
+        <Value value="700.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="346"/>  <!-- XBold -->
+        <Value value="800.0"/>
+      </AxisValue>
+      <AxisValue index="8" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="358"/>  <!-- Black -->
+        <Value value="900.0"/>
+      </AxisValue>
+      <AxisValue index="9" Format="1">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="295"/>  <!-- Cd -->
+        <Value value="75.0"/>
+      </AxisValue>
+      <AxisValue index="10" Format="1">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="367"/>  <!-- Normal -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="11" Format="1">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="302"/>  <!-- Ex -->
+        <Value value="125.0"/>
+      </AxisValue>
+      <AxisValue index="12" Format="3">
+        <AxisIndex value="2"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="368"/>  <!-- Upright -->
+        <Value value="0.0"/>
+        <LinkedValue value="1.0"/>
+      </AxisValue>
+      <AxisValue index="13" Format="1">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="258"/>  <!-- Italic -->
+        <Value value="1.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="-0.6667" to="-0.74194"/>
+      <mapping from="-0.3333" to="-0.4355"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.2" to="0.1386"/>
+      <mapping from="0.4" to="0.30695"/>
+      <mapping from="0.6" to="0.48517"/>
+      <mapping from="0.8" to="0.73267"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="wdth">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+    <segment axis="ital">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Width -->
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>75.0</MinValue>
+      <DefaultValue>100.0</DefaultValue>
+      <MaxValue>125.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Italic -->
+    <Axis>
+      <AxisTag>ital</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>1.0</MaxValue>
+      <AxisNameID>258</AxisNameID>
+    </Axis>
+
+    <!-- Cd Hair -->
+    <!-- PostScript: NewFont-CdHair -->
+    <NamedInstance flags="0x0" postscriptNameID="260" subfamilyNameID="259">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Hair Italic -->
+    <!-- PostScript: NewFont-CdHairItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="262" subfamilyNameID="261">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Hair -->
+    <!-- PostScript: NewFont-Hair -->
+    <NamedInstance flags="0x0" postscriptNameID="264" subfamilyNameID="263">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Hair Italic -->
+    <!-- PostScript: NewFont-HairItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="266" subfamilyNameID="265">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex Hair -->
+    <!-- PostScript: NewFont-ExHair -->
+    <NamedInstance flags="0x0" postscriptNameID="268" subfamilyNameID="267">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Hair Italic -->
+    <!-- PostScript: NewFont-ExHairItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="270" subfamilyNameID="269">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd Thin -->
+    <!-- PostScript: NewFont-CdThin -->
+    <NamedInstance flags="0x0" postscriptNameID="272" subfamilyNameID="271">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Thin Italic -->
+    <!-- PostScript: NewFont-CdThinItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="274" subfamilyNameID="273">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Thin -->
+    <!-- PostScript: NewFont-Thin -->
+    <NamedInstance flags="0x0" postscriptNameID="276" subfamilyNameID="275">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Thin Italic -->
+    <!-- PostScript: NewFont-ThinItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="278" subfamilyNameID="277">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex Thin -->
+    <!-- PostScript: NewFont-ExThin -->
+    <NamedInstance flags="0x0" postscriptNameID="280" subfamilyNameID="279">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Thin Italic -->
+    <!-- PostScript: NewFont-ExThinItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="282" subfamilyNameID="281">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd Light -->
+    <!-- PostScript: NewFont-CdLight -->
+    <NamedInstance flags="0x0" postscriptNameID="284" subfamilyNameID="283">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Light Italic -->
+    <!-- PostScript: NewFont-CdLightItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="286" subfamilyNameID="285">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: NewFont-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="288" subfamilyNameID="287">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Light Italic -->
+    <!-- PostScript: NewFont-LightItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="290" subfamilyNameID="289">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex Light -->
+    <!-- PostScript: NewFont-ExLight -->
+    <NamedInstance flags="0x0" postscriptNameID="292" subfamilyNameID="291">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Light Italic -->
+    <!-- PostScript: NewFont-ExLightItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="294" subfamilyNameID="293">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd -->
+    <!-- PostScript: NewFont-Cd -->
+    <NamedInstance flags="0x0" postscriptNameID="296" subfamilyNameID="295">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Italic -->
+    <!-- PostScript: NewFont-CdItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="298" subfamilyNameID="297">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+    <!-- PostScript: NewFont- -->
+    <NamedInstance flags="0x0" postscriptNameID="300" subfamilyNameID="299">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Italic -->
+    <!-- PostScript: NewFont-Italic -->
+    <NamedInstance flags="0x0" postscriptNameID="301" subfamilyNameID="258">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex -->
+    <!-- PostScript: NewFont-Ex -->
+    <NamedInstance flags="0x0" postscriptNameID="303" subfamilyNameID="302">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Italic -->
+    <!-- PostScript: NewFont-ExItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="305" subfamilyNameID="304">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd Medium -->
+    <!-- PostScript: NewFont-CdMedium -->
+    <NamedInstance flags="0x0" postscriptNameID="307" subfamilyNameID="306">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Medium Italic -->
+    <!-- PostScript: NewFont-CdMediumItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="309" subfamilyNameID="308">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <!-- PostScript: NewFont-Medium -->
+    <NamedInstance flags="0x0" postscriptNameID="311" subfamilyNameID="310">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Medium Italic -->
+    <!-- PostScript: NewFont-MediumItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="313" subfamilyNameID="312">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex Medium -->
+    <!-- PostScript: NewFont-ExMedium -->
+    <NamedInstance flags="0x0" postscriptNameID="315" subfamilyNameID="314">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Medium Italic -->
+    <!-- PostScript: NewFont-ExMediumItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="317" subfamilyNameID="316">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd SemiBold -->
+    <!-- PostScript: NewFont-CdSemiBold -->
+    <NamedInstance flags="0x0" postscriptNameID="319" subfamilyNameID="318">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd SemiBold Italic -->
+    <!-- PostScript: NewFont-CdSemiBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="321" subfamilyNameID="320">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- SemiBold -->
+    <!-- PostScript: NewFont-SemiBold -->
+    <NamedInstance flags="0x0" postscriptNameID="323" subfamilyNameID="322">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- SemiBold Italic -->
+    <!-- PostScript: NewFont-SemiBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="325" subfamilyNameID="324">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex SemiBold -->
+    <!-- PostScript: NewFont-ExSemiBold -->
+    <NamedInstance flags="0x0" postscriptNameID="327" subfamilyNameID="326">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex SemiBold Italic -->
+    <!-- PostScript: NewFont-ExSemiBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="329" subfamilyNameID="328">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd Bold -->
+    <!-- PostScript: NewFont-CdBold -->
+    <NamedInstance flags="0x0" postscriptNameID="331" subfamilyNameID="330">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Bold Italic -->
+    <!-- PostScript: NewFont-CdBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="333" subfamilyNameID="332">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: NewFont-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="335" subfamilyNameID="334">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Bold Italic -->
+    <!-- PostScript: NewFont-BoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="337" subfamilyNameID="336">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex Bold -->
+    <!-- PostScript: NewFont-ExBold -->
+    <NamedInstance flags="0x0" postscriptNameID="339" subfamilyNameID="338">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Bold Italic -->
+    <!-- PostScript: NewFont-ExBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="341" subfamilyNameID="340">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd XBold -->
+    <!-- PostScript: NewFont-CdXBold -->
+    <NamedInstance flags="0x0" postscriptNameID="343" subfamilyNameID="342">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd XBold Italic -->
+    <!-- PostScript: NewFont-CdXBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="345" subfamilyNameID="344">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- XBold -->
+    <!-- PostScript: NewFont-XBold -->
+    <NamedInstance flags="0x0" postscriptNameID="347" subfamilyNameID="346">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- XBold Italic -->
+    <!-- PostScript: NewFont-XBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="349" subfamilyNameID="348">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex XBold -->
+    <!-- PostScript: NewFont-ExXBold -->
+    <NamedInstance flags="0x0" postscriptNameID="351" subfamilyNameID="350">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex XBold Italic -->
+    <!-- PostScript: NewFont-ExXBoldItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="353" subfamilyNameID="352">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Cd Black -->
+    <!-- PostScript: NewFont-CdBlack -->
+    <NamedInstance flags="0x0" postscriptNameID="355" subfamilyNameID="354">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Cd Black Italic -->
+    <!-- PostScript: NewFont-CdBlackItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="357" subfamilyNameID="356">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="75.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: NewFont-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="359" subfamilyNameID="358">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Black Italic -->
+    <!-- PostScript: NewFont-BlackItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="361" subfamilyNameID="360">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="100.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+
+    <!-- Ex Black -->
+    <!-- PostScript: NewFont-ExBlack -->
+    <NamedInstance flags="0x0" postscriptNameID="363" subfamilyNameID="362">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Ex Black Italic -->
+    <!-- PostScript: NewFont-ExBlackItalic -->
+    <NamedInstance flags="0x0" postscriptNameID="365" subfamilyNameID="364">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="125.0"/>
+      <coord axis="ital" value="1.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/instancer_test.py b/Tests/varLib/instancer/instancer_test.py
index b9d4ffe..db224cc 100644
--- a/Tests/varLib/instancer/instancer_test.py
+++ b/Tests/varLib/instancer/instancer_test.py
@@ -458,6 +458,8 @@
 
         defaultDeltaArray = []
         for varidx, delta in sorted(defaultDeltas.items()):
+            if varidx == varStore.NO_VARIATION_INDEX:
+                continue
             major, minor = varidx >> 16, varidx & 0xFFFF
             if major == len(defaultDeltaArray):
                 defaultDeltaArray.append([])
@@ -1975,3 +1977,35 @@
     captured = capsys.readouterr()
 
     assert "Specified multiple limits for the same axis" in captured.err
+
+
+def test_set_ribbi_bits():
+    varfont = ttLib.TTFont()
+    varfont.importXML(os.path.join(TESTDATA, "STATInstancerTest.ttx"))
+
+    for location in [instance.coordinates for instance in varfont["fvar"].instances]:
+        instance = instancer.instantiateVariableFont(
+            varfont, location, updateFontNames=True
+        )
+        name_id_2 = instance["name"].getDebugName(2)
+        mac_style = instance["head"].macStyle
+        fs_selection = instance["OS/2"].fsSelection & 0b1100001  # Just bits 0, 5, 6
+
+        if location["ital"] == 0:
+            if location["wght"] == 700:
+                assert name_id_2 == "Bold", location
+                assert mac_style == 0b01, location
+                assert fs_selection == 0b0100000, location
+            else:
+                assert name_id_2 == "Regular", location
+                assert mac_style == 0b00, location
+                assert fs_selection == 0b1000000, location
+        else:
+            if location["wght"] == 700:
+                assert name_id_2 == "Bold Italic", location
+                assert mac_style == 0b11, location
+                assert fs_selection == 0b0100001, location
+            else:
+                assert name_id_2 == "Italic", location
+                assert mac_style == 0b10, location
+                assert fs_selection == 0b0000001, location
diff --git a/Tests/varLib/iup_test.py b/Tests/varLib/iup_test.py
new file mode 100644
index 0000000..76b2af5
--- /dev/null
+++ b/Tests/varLib/iup_test.py
@@ -0,0 +1,53 @@
+import fontTools.varLib.iup as iup
+import sys
+import pytest
+
+
+class IupTest:
+
+# -----
+# Tests
+# -----
+
+    @pytest.mark.parametrize(
+        "delta, coords, forced",
+        [
+            (
+                [(0, 0)],
+                [(1, 2)],
+                set()
+            ),
+            (
+                [(0, 0), (0, 0), (0, 0)],
+                [(1, 2), (3, 2), (2, 3)],
+                set()
+            ),
+            (
+                [(1, 1), (-1, 1), (-1, -1), (1, -1)],
+                [(0, 0), (2, 0), (2, 2), (0, 2)],
+                set()
+            ),
+            (
+                [(-1, 0), (-1, 0), (-1, 0), (-1, 0), (-1, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (-1, 0)],
+                [(-35, -152), (-86, -101), (-50, -65), (0, -116), (51, -65), (86, -99), (35, -151), (87, -202), (51, -238), (-1, -187), (-53, -239), (-88, -205)],
+                {11}
+            ),
+            (
+                [(0, 0), (1, 0), (2, 0), (2, 0), (0, 0), (1, 0), (3, 0), (3, 0), (2, 0), (2, 0), (0, 0), (0, 0), (-1, 0), (-1, 0), (-1, 0), (-3, 0), (-1, 0), (0, 0), (0, 0), (-2, 0), (-2, 0), (-1, 0), (-1, 0), (-1, 0), (-4, 0)],
+                [(330, 65), (401, 65), (499, 117), (549, 225), (549, 308), (549, 422), (549, 500), (497, 600), (397, 648), (324, 648), (271, 648), (200, 620), (165, 570), (165, 536), (165, 473), (252, 407), (355, 407), (396, 407), (396, 333), (354, 333), (249, 333), (141, 268), (141, 203), (141, 131), (247, 65)],
+                {5, 15, 24}
+            ),
+        ]
+    )
+    def test_forced_set(self, delta, coords, forced):
+        f = iup._iup_contour_bound_forced_set(delta, coords)
+        assert forced == f
+
+        chain1, costs1 = iup._iup_contour_optimize_dp(delta, coords, f)
+        chain2, costs2 = iup._iup_contour_optimize_dp(delta, coords, set())
+
+        assert chain1 == chain2, f
+        assert costs1 == costs2, f
+
+if __name__ == "__main__":
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/varLib/merger_test.py b/Tests/varLib/merger_test.py
new file mode 100644
index 0000000..aa7a699
--- /dev/null
+++ b/Tests/varLib/merger_test.py
@@ -0,0 +1,1844 @@
+from copy import deepcopy
+import string
+from fontTools.colorLib.builder import LayerListBuilder, buildCOLR, buildClipList
+from fontTools.misc.testTools import getXML
+from fontTools.varLib.merger import COLRVariationMerger
+from fontTools.varLib.models import VariationModel
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
+import pytest
+
+
+NO_VARIATION_INDEX = ot.NO_VARIATION_INDEX
+
+
+def dump_xml(table, ttFont=None):
+    xml = getXML(table.toXML, ttFont)
+    print("[")
+    for line in xml:
+        print(f"  {line!r},")
+    print("]")
+    return xml
+
+
+def compile_decompile(table, ttFont):
+    writer = OTTableWriter(tableTag="COLR")
+    # compile itself may modify a table, safer to copy it first
+    table = deepcopy(table)
+    table.compile(writer, ttFont)
+    data = writer.getAllData()
+
+    reader = OTTableReader(data, tableTag="COLR")
+    table2 = table.__class__()
+    table2.decompile(reader, ttFont)
+
+    return table2
+
+
+@pytest.fixture
+def ttFont():
+    font = TTFont()
+    font.setGlyphOrder([".notdef"] + list(string.ascii_letters))
+    return font
+
+
+def build_paint(data):
+    return LayerListBuilder().buildPaint(data)
+
+
+class COLRVariationMergerTest:
+    @pytest.mark.parametrize(
+        "paints, expected_xml, expected_varIdxes",
+        [
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintSolid),
+                        "PaletteIndex": 0,
+                        "Alpha": 1.0,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintSolid),
+                        "PaletteIndex": 0,
+                        "Alpha": 1.0,
+                    },
+                ],
+                [
+                    '<Paint Format="2"><!-- PaintSolid -->',
+                    '  <PaletteIndex value="0"/>',
+                    '  <Alpha value="1.0"/>',
+                    "</Paint>",
+                ],
+                [],
+                id="solid-same",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintSolid),
+                        "PaletteIndex": 0,
+                        "Alpha": 1.0,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintSolid),
+                        "PaletteIndex": 0,
+                        "Alpha": 0.5,
+                    },
+                ],
+                [
+                    '<Paint Format="3"><!-- PaintVarSolid -->',
+                    '  <PaletteIndex value="0"/>',
+                    '  <Alpha value="1.0"/>',
+                    '  <VarIndexBase value="0"/>',
+                    "</Paint>",
+                ],
+                [0],
+                id="solid-alpha",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintLinearGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "x2": 2,
+                        "y2": 2,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintLinearGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.1, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 0.9, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "x2": 2,
+                        "y2": 2,
+                    },
+                ],
+                [
+                    '<Paint Format="5"><!-- PaintVarLinearGradient -->',
+                    "  <ColorLine>",
+                    '    <Extend value="pad"/>',
+                    "    <!-- StopCount=2 -->",
+                    '    <ColorStop index="0">',
+                    '      <StopOffset value="0.0"/>',
+                    '      <PaletteIndex value="0"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="0"/>',
+                    "    </ColorStop>",
+                    '    <ColorStop index="1">',
+                    '      <StopOffset value="1.0"/>',
+                    '      <PaletteIndex value="1"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="2"/>',
+                    "    </ColorStop>",
+                    "  </ColorLine>",
+                    '  <x0 value="0"/>',
+                    '  <y0 value="0"/>',
+                    '  <x1 value="1"/>',
+                    '  <y1 value="1"/>',
+                    '  <x2 value="2"/>',
+                    '  <y2 value="2"/>',
+                    "  <VarIndexBase/>",
+                    "</Paint>",
+                ],
+                [0, NO_VARIATION_INDEX, 1, NO_VARIATION_INDEX],
+                id="linear_grad-stop-offsets",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintLinearGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "x2": 2,
+                        "y2": 2,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintLinearGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 0.5},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "x2": 2,
+                        "y2": 2,
+                    },
+                ],
+                [
+                    '<Paint Format="5"><!-- PaintVarLinearGradient -->',
+                    "  <ColorLine>",
+                    '    <Extend value="pad"/>',
+                    "    <!-- StopCount=2 -->",
+                    '    <ColorStop index="0">',
+                    '      <StopOffset value="0.0"/>',
+                    '      <PaletteIndex value="0"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="0"/>',
+                    "    </ColorStop>",
+                    '    <ColorStop index="1">',
+                    '      <StopOffset value="1.0"/>',
+                    '      <PaletteIndex value="1"/>',
+                    '      <Alpha value="1.0"/>',
+                    "      <VarIndexBase/>",
+                    "    </ColorStop>",
+                    "  </ColorLine>",
+                    '  <x0 value="0"/>',
+                    '  <y0 value="0"/>',
+                    '  <x1 value="1"/>',
+                    '  <y1 value="1"/>',
+                    '  <x2 value="2"/>',
+                    '  <y2 value="2"/>',
+                    "  <VarIndexBase/>",
+                    "</Paint>",
+                ],
+                [NO_VARIATION_INDEX, 0],
+                id="linear_grad-stop[0].alpha",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintLinearGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "x2": 2,
+                        "y2": 2,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintLinearGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": -0.5, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "x2": 2,
+                        "y2": -200,
+                    },
+                ],
+                [
+                    '<Paint Format="5"><!-- PaintVarLinearGradient -->',
+                    "  <ColorLine>",
+                    '    <Extend value="pad"/>',
+                    "    <!-- StopCount=2 -->",
+                    '    <ColorStop index="0">',
+                    '      <StopOffset value="0.0"/>',
+                    '      <PaletteIndex value="0"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="0"/>',
+                    "    </ColorStop>",
+                    '    <ColorStop index="1">',
+                    '      <StopOffset value="1.0"/>',
+                    '      <PaletteIndex value="1"/>',
+                    '      <Alpha value="1.0"/>',
+                    "      <VarIndexBase/>",
+                    "    </ColorStop>",
+                    "  </ColorLine>",
+                    '  <x0 value="0"/>',
+                    '  <y0 value="0"/>',
+                    '  <x1 value="1"/>',
+                    '  <y1 value="1"/>',
+                    '  <x2 value="2"/>',
+                    '  <y2 value="2"/>',
+                    '  <VarIndexBase value="1"/>',
+                    "</Paint>",
+                ],
+                [
+                    0,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    1,
+                ],
+                id="linear_grad-stop[0].offset-y2",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintRadialGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "r0": 0,
+                        "x1": 1,
+                        "y1": 1,
+                        "r1": 1,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintRadialGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.1, "PaletteIndex": 0, "Alpha": 0.6},
+                                {"StopOffset": 0.9, "PaletteIndex": 1, "Alpha": 0.7},
+                            ],
+                        },
+                        "x0": -1,
+                        "y0": -2,
+                        "r0": 3,
+                        "x1": -4,
+                        "y1": -5,
+                        "r1": 6,
+                    },
+                ],
+                [
+                    '<Paint Format="7"><!-- PaintVarRadialGradient -->',
+                    "  <ColorLine>",
+                    '    <Extend value="pad"/>',
+                    "    <!-- StopCount=2 -->",
+                    '    <ColorStop index="0">',
+                    '      <StopOffset value="0.0"/>',
+                    '      <PaletteIndex value="0"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="0"/>',
+                    "    </ColorStop>",
+                    '    <ColorStop index="1">',
+                    '      <StopOffset value="1.0"/>',
+                    '      <PaletteIndex value="1"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="2"/>',
+                    "    </ColorStop>",
+                    "  </ColorLine>",
+                    '  <x0 value="0"/>',
+                    '  <y0 value="0"/>',
+                    '  <r0 value="0"/>',
+                    '  <x1 value="1"/>',
+                    '  <y1 value="1"/>',
+                    '  <r1 value="1"/>',
+                    '  <VarIndexBase value="4"/>',
+                    "</Paint>",
+                ],
+                [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+                id="radial_grad-all-different",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintSweepGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.REPEAT),
+                            "ColorStop": [
+                                {"StopOffset": 0.4, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 0.6, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "centerX": 0,
+                        "centerY": 0,
+                        "startAngle": 0,
+                        "endAngle": 180.0,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintSweepGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.REPEAT),
+                            "ColorStop": [
+                                {"StopOffset": 0.4, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 0.6, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "centerX": 0,
+                        "centerY": 0,
+                        "startAngle": 90.0,
+                        "endAngle": 180.0,
+                    },
+                ],
+                [
+                    '<Paint Format="9"><!-- PaintVarSweepGradient -->',
+                    "  <ColorLine>",
+                    '    <Extend value="repeat"/>',
+                    "    <!-- StopCount=2 -->",
+                    '    <ColorStop index="0">',
+                    '      <StopOffset value="0.4"/>',
+                    '      <PaletteIndex value="0"/>',
+                    '      <Alpha value="1.0"/>',
+                    "      <VarIndexBase/>",
+                    "    </ColorStop>",
+                    '    <ColorStop index="1">',
+                    '      <StopOffset value="0.6"/>',
+                    '      <PaletteIndex value="1"/>',
+                    '      <Alpha value="1.0"/>',
+                    "      <VarIndexBase/>",
+                    "    </ColorStop>",
+                    "  </ColorLine>",
+                    '  <centerX value="0"/>',
+                    '  <centerY value="0"/>',
+                    '  <startAngle value="0.0"/>',
+                    '  <endAngle value="180.0"/>',
+                    '  <VarIndexBase value="0"/>',
+                    "</Paint>",
+                ],
+                [NO_VARIATION_INDEX, NO_VARIATION_INDEX, 0, NO_VARIATION_INDEX],
+                id="sweep_grad-startAngle",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintSweepGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 1.0},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 1.0},
+                            ],
+                        },
+                        "centerX": 0,
+                        "centerY": 0,
+                        "startAngle": 0.0,
+                        "endAngle": 180.0,
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintSweepGradient),
+                        "ColorLine": {
+                            "Extend": int(ot.ExtendMode.PAD),
+                            "ColorStop": [
+                                {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 0.5},
+                                {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 0.5},
+                            ],
+                        },
+                        "centerX": 0,
+                        "centerY": 0,
+                        "startAngle": 0.0,
+                        "endAngle": 180.0,
+                    },
+                ],
+                [
+                    '<Paint Format="9"><!-- PaintVarSweepGradient -->',
+                    "  <ColorLine>",
+                    '    <Extend value="pad"/>',
+                    "    <!-- StopCount=2 -->",
+                    '    <ColorStop index="0">',
+                    '      <StopOffset value="0.0"/>',
+                    '      <PaletteIndex value="0"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="0"/>',
+                    "    </ColorStop>",
+                    '    <ColorStop index="1">',
+                    '      <StopOffset value="1.0"/>',
+                    '      <PaletteIndex value="1"/>',
+                    '      <Alpha value="1.0"/>',
+                    '      <VarIndexBase value="0"/>',
+                    "    </ColorStop>",
+                    "  </ColorLine>",
+                    '  <centerX value="0"/>',
+                    '  <centerY value="0"/>',
+                    '  <startAngle value="0.0"/>',
+                    '  <endAngle value="180.0"/>',
+                    "  <VarIndexBase/>",
+                    "</Paint>",
+                ],
+                [NO_VARIATION_INDEX, 0],
+                id="sweep_grad-stops-alpha-reuse-varidxbase",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": int(ot.PaintFormat.PaintTransform),
+                        "Paint": {
+                            "Format": int(ot.PaintFormat.PaintRadialGradient),
+                            "ColorLine": {
+                                "Extend": int(ot.ExtendMode.PAD),
+                                "ColorStop": [
+                                    {
+                                        "StopOffset": 0.0,
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    {
+                                        "StopOffset": 1.0,
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                ],
+                            },
+                            "x0": 0,
+                            "y0": 0,
+                            "r0": 0,
+                            "x1": 1,
+                            "y1": 1,
+                            "r1": 1,
+                        },
+                        "Transform": {
+                            "xx": 1.0,
+                            "xy": 0.0,
+                            "yx": 0.0,
+                            "yy": 1.0,
+                            "dx": 0.0,
+                            "dy": 0.0,
+                        },
+                    },
+                    {
+                        "Format": int(ot.PaintFormat.PaintTransform),
+                        "Paint": {
+                            "Format": int(ot.PaintFormat.PaintRadialGradient),
+                            "ColorLine": {
+                                "Extend": int(ot.ExtendMode.PAD),
+                                "ColorStop": [
+                                    {
+                                        "StopOffset": 0.0,
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    {
+                                        "StopOffset": 1.0,
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                ],
+                            },
+                            "x0": 0,
+                            "y0": 0,
+                            "r0": 0,
+                            "x1": 1,
+                            "y1": 1,
+                            "r1": 1,
+                        },
+                        "Transform": {
+                            "xx": 1.0,
+                            "xy": 0.0,
+                            "yx": 0.0,
+                            "yy": 0.5,
+                            "dx": 0.0,
+                            "dy": -100.0,
+                        },
+                    },
+                ],
+                [
+                    '<Paint Format="13"><!-- PaintVarTransform -->',
+                    '  <Paint Format="6"><!-- PaintRadialGradient -->',
+                    "    <ColorLine>",
+                    '      <Extend value="pad"/>',
+                    "      <!-- StopCount=2 -->",
+                    '      <ColorStop index="0">',
+                    '        <StopOffset value="0.0"/>',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </ColorStop>",
+                    '      <ColorStop index="1">',
+                    '        <StopOffset value="1.0"/>',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </ColorStop>",
+                    "    </ColorLine>",
+                    '    <x0 value="0"/>',
+                    '    <y0 value="0"/>',
+                    '    <r0 value="0"/>',
+                    '    <x1 value="1"/>',
+                    '    <y1 value="1"/>',
+                    '    <r1 value="1"/>',
+                    "  </Paint>",
+                    "  <Transform>",
+                    '    <xx value="1.0"/>',
+                    '    <yx value="0.0"/>',
+                    '    <xy value="0.0"/>',
+                    '    <yy value="1.0"/>',
+                    '    <dx value="0.0"/>',
+                    '    <dy value="0.0"/>',
+                    '    <VarIndexBase value="0"/>',
+                    "  </Transform>",
+                    "</Paint>",
+                ],
+                [
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    0,
+                    NO_VARIATION_INDEX,
+                    1,
+                ],
+                id="transform-yy-dy",
+            ),
+            pytest.param(
+                [
+                    {
+                        "Format": ot.PaintFormat.PaintTransform,
+                        "Paint": {
+                            "Format": ot.PaintFormat.PaintSweepGradient,
+                            "ColorLine": {
+                                "Extend": ot.ExtendMode.PAD,
+                                "ColorStop": [
+                                    {"StopOffset": 0.0, "PaletteIndex": 0},
+                                    {
+                                        "StopOffset": 1.0,
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                ],
+                            },
+                            "centerX": 0,
+                            "centerY": 0,
+                            "startAngle": 0,
+                            "endAngle": 360,
+                        },
+                        "Transform": (1.0, 0, 0, 1.0, 0, 0),
+                    },
+                    {
+                        "Format": ot.PaintFormat.PaintTransform,
+                        "Paint": {
+                            "Format": ot.PaintFormat.PaintSweepGradient,
+                            "ColorLine": {
+                                "Extend": ot.ExtendMode.PAD,
+                                "ColorStop": [
+                                    {"StopOffset": 0.0, "PaletteIndex": 0},
+                                    {
+                                        "StopOffset": 1.0,
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                ],
+                            },
+                            "centerX": 256,
+                            "centerY": 0,
+                            "startAngle": 0,
+                            "endAngle": 360,
+                        },
+                        # Transform.xx below produces the same VarStore delta as the
+                        # above PaintSweepGradient's centerX because, when Fixed16.16
+                        # is converted to integer, it becomes:
+                        # floatToFixed(1.00390625, 16) == 256
+                        # Because there is overlap between the varIdxes of the
+                        # PaintVarTransform's Affine2x3 and the PaintSweepGradient's
+                        # the VarIndexBase is reused (0 for both)
+                        "Transform": (1.00390625, 0, 0, 1.0, 10, 0),
+                    },
+                ],
+                [
+                    '<Paint Format="13"><!-- PaintVarTransform -->',
+                    '  <Paint Format="9"><!-- PaintVarSweepGradient -->',
+                    "    <ColorLine>",
+                    '      <Extend value="pad"/>',
+                    "      <!-- StopCount=2 -->",
+                    '      <ColorStop index="0">',
+                    '        <StopOffset value="0.0"/>',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "        <VarIndexBase/>",
+                    "      </ColorStop>",
+                    '      <ColorStop index="1">',
+                    '        <StopOffset value="1.0"/>',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "        <VarIndexBase/>",
+                    "      </ColorStop>",
+                    "    </ColorLine>",
+                    '    <centerX value="0"/>',
+                    '    <centerY value="0"/>',
+                    '    <startAngle value="0.0"/>',
+                    '    <endAngle value="360.0"/>',
+                    '    <VarIndexBase value="0"/>',
+                    "  </Paint>",
+                    "  <Transform>",
+                    '    <xx value="1.0"/>',
+                    '    <yx value="0.0"/>',
+                    '    <xy value="0.0"/>',
+                    '    <yy value="1.0"/>',
+                    '    <dx value="0.0"/>',
+                    '    <dy value="0.0"/>',
+                    '    <VarIndexBase value="0"/>',
+                    "  </Transform>",
+                    "</Paint>",
+                ],
+                [
+                    0,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    NO_VARIATION_INDEX,
+                    1,
+                    NO_VARIATION_INDEX,
+                ],
+                id="transform-xx-sweep_grad-centerx-same-varidxbase",
+            ),
+        ],
+    )
+    def test_merge_Paint(self, paints, ttFont, expected_xml, expected_varIdxes):
+        paints = [build_paint(p) for p in paints]
+        out = deepcopy(paints[0])
+
+        model = VariationModel([{}, {"ZZZZ": 1.0}])
+        merger = COLRVariationMerger(model, ["ZZZZ"], ttFont)
+
+        merger.mergeThings(out, paints)
+
+        assert compile_decompile(out, ttFont) == out
+        assert dump_xml(out, ttFont) == expected_xml
+        assert merger.varIdxes == expected_varIdxes
+
+    def test_merge_ClipList(self, ttFont):
+        clipLists = [
+            buildClipList(clips)
+            for clips in [
+                {
+                    "A": (0, 0, 1000, 1000),
+                    "B": (0, 0, 1000, 1000),
+                    "C": (0, 0, 1000, 1000),
+                    "D": (0, 0, 1000, 1000),
+                },
+                {
+                    # non-default masters' clip boxes can be 'sparse'
+                    # (i.e. can omit explicit clip box for some glyphs)
+                    # "A": (0, 0, 1000, 1000),
+                    "B": (10, 0, 1000, 1000),
+                    "C": (20, 20, 1020, 1020),
+                    "D": (20, 20, 1020, 1020),
+                },
+            ]
+        ]
+        out = deepcopy(clipLists[0])
+
+        model = VariationModel([{}, {"ZZZZ": 1.0}])
+        merger = COLRVariationMerger(model, ["ZZZZ"], ttFont)
+
+        merger.mergeThings(out, clipLists)
+
+        assert compile_decompile(out, ttFont) == out
+        assert dump_xml(out, ttFont) == [
+            '<ClipList Format="1">',
+            "  <Clip>",
+            '    <Glyph value="A"/>',
+            '    <ClipBox Format="1">',
+            '      <xMin value="0"/>',
+            '      <yMin value="0"/>',
+            '      <xMax value="1000"/>',
+            '      <yMax value="1000"/>',
+            "    </ClipBox>",
+            "  </Clip>",
+            "  <Clip>",
+            '    <Glyph value="B"/>',
+            '    <ClipBox Format="2">',
+            '      <xMin value="0"/>',
+            '      <yMin value="0"/>',
+            '      <xMax value="1000"/>',
+            '      <yMax value="1000"/>',
+            '      <VarIndexBase value="0"/>',
+            "    </ClipBox>",
+            "  </Clip>",
+            "  <Clip>",
+            '    <Glyph value="C"/>',
+            '    <Glyph value="D"/>',
+            '    <ClipBox Format="2">',
+            '      <xMin value="0"/>',
+            '      <yMin value="0"/>',
+            '      <xMax value="1000"/>',
+            '      <yMax value="1000"/>',
+            '      <VarIndexBase value="4"/>',
+            "    </ClipBox>",
+            "  </Clip>",
+            "</ClipList>",
+        ]
+        assert merger.varIdxes == [
+            0,
+            NO_VARIATION_INDEX,
+            NO_VARIATION_INDEX,
+            NO_VARIATION_INDEX,
+            1,
+            1,
+            1,
+            1,
+        ]
+
+    @pytest.mark.parametrize(
+        "master_layer_reuse",
+        [
+            pytest.param(False, id="no-reuse"),
+            pytest.param(True, id="with-reuse"),
+        ],
+    )
+    @pytest.mark.parametrize(
+        "color_glyphs, output_layer_reuse, expected_xml, expected_varIdxes",
+        [
+            pytest.param(
+                [
+                    {
+                        "A": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                    {
+                        "A": {
+                            "Format": ot.PaintFormat.PaintColrLayers,
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                ],
+                False,
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=1 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="0"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "  <LayerList>",
+                    "    <!-- LayerCount=2 -->",
+                    '    <Paint index="0" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="1" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    "  </LayerList>",
+                    "</COLR>",
+                ],
+                [],
+                id="no-variation",
+            ),
+            pytest.param(
+                [
+                    {
+                        "A": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "C": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 3,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                    {
+                        # NOTE: 'A' is missing from non-default master
+                        "C": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 3,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                ],
+                False,
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=2 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="0"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="1">',
+                    '      <BaseGlyph value="C"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="2"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "  <LayerList>",
+                    "    <!-- LayerCount=4 -->",
+                    '    <Paint index="0" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="1" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="2" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="3"><!-- PaintVarSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    '        <VarIndexBase value="0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="3" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="3"><!-- PaintVarSolid -->',
+                    '        <PaletteIndex value="3"/>',
+                    '        <Alpha value="1.0"/>',
+                    '        <VarIndexBase value="0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    "  </LayerList>",
+                    "</COLR>",
+                ],
+                [0],
+                id="sparse-masters",
+            ),
+            pytest.param(
+                [
+                    {
+                        "A": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "C": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                # 'C' reuses layers 1-3 from 'A'
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "D": {  # identical to 'C'
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "E": {  # superset of 'C' or 'D'
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 3,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                    {
+                        # NOTE: 'A' is missing from non-default master
+                        "C": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "D": {  # same as 'C'
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "E": {  # first two layers vary the same way as 'C' or 'D'
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 3,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                ],
+                True,  # reuse
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=4 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="3"/>',
+                    '        <FirstLayerIndex value="0"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="1">',
+                    '      <BaseGlyph value="C"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="3"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="2">',
+                    '      <BaseGlyph value="D"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="3"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="3">',
+                    '      <BaseGlyph value="E"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="5"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "  <LayerList>",
+                    "    <!-- LayerCount=7 -->",
+                    '    <Paint index="0" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="1" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="2" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="3" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="3"><!-- PaintVarSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    '        <VarIndexBase value="0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="4" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="3"><!-- PaintVarSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    '        <VarIndexBase value="0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="5" Format="1"><!-- PaintColrLayers -->',
+                    '      <NumLayers value="2"/>',
+                    '      <FirstLayerIndex value="3"/>',
+                    "    </Paint>",
+                    '    <Paint index="6" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="3"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    "  </LayerList>",
+                    "</COLR>",
+                ],
+                [0],
+                id="sparse-masters-with-reuse",
+            ),
+            pytest.param(
+                [
+                    {
+                        "A": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "C": {  # 'C' shares layer 1 and 2 with 'A'
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                    {
+                        "A": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 0,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 0.9,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                        "C": {
+                            "Format": int(ot.PaintFormat.PaintColrLayers),
+                            "Layers": [
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 1,
+                                        "Alpha": 0.5,
+                                    },
+                                    "Glyph": "B",
+                                },
+                                {
+                                    "Format": int(ot.PaintFormat.PaintGlyph),
+                                    "Paint": {
+                                        "Format": int(ot.PaintFormat.PaintSolid),
+                                        "PaletteIndex": 2,
+                                        "Alpha": 1.0,
+                                    },
+                                    "Glyph": "B",
+                                },
+                            ],
+                        },
+                    },
+                ],
+                True,
+                [
+                    # a different Alpha variation is applied to a shared layer between
+                    # 'A' and 'C' and thus they are no longer shared.
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=2 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="3"/>',
+                    '        <FirstLayerIndex value="0"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="1">',
+                    '      <BaseGlyph value="C"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="3"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "  <LayerList>",
+                    "    <!-- LayerCount=5 -->",
+                    '    <Paint index="0" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="1" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="3"><!-- PaintVarSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    '        <VarIndexBase value="0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="2" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="3" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="3"><!-- PaintVarSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    '        <VarIndexBase value="1"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="4" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    "  </LayerList>",
+                    "</COLR>",
+                ],
+                [0, 1],
+                id="shared-master-layers-different-variations",
+            ),
+        ],
+    )
+    def test_merge_full_table(
+        self,
+        color_glyphs,
+        ttFont,
+        expected_xml,
+        expected_varIdxes,
+        master_layer_reuse,
+        output_layer_reuse,
+    ):
+        master_ttfs = [deepcopy(ttFont) for _ in range(len(color_glyphs))]
+        for ttf, glyphs in zip(master_ttfs, color_glyphs):
+            # merge algorithm is expected to work the same even if the master COLRs
+            # may differ as to the layer reuse, hence we try both ways
+            ttf["COLR"] = buildCOLR(glyphs, allowLayerReuse=master_layer_reuse)
+        vf = deepcopy(master_ttfs[0])
+
+        model = VariationModel([{}, {"ZZZZ": 1.0}])
+        merger = COLRVariationMerger(
+            model, ["ZZZZ"], vf, allowLayerReuse=output_layer_reuse
+        )
+
+        merger.mergeTables(vf, master_ttfs)
+
+        out = vf["COLR"].table
+
+        assert compile_decompile(out, vf) == out
+        assert dump_xml(out, vf) == expected_xml
+        assert merger.varIdxes == expected_varIdxes
+
+    @pytest.mark.parametrize(
+        "color_glyphs, before_xml, expected_xml",
+        [
+            pytest.param(
+                {
+                    "A": {
+                        "Format": int(ot.PaintFormat.PaintColrLayers),
+                        "Layers": [
+                            {
+                                "Format": int(ot.PaintFormat.PaintGlyph),
+                                "Paint": {
+                                    "Format": int(ot.PaintFormat.PaintSolid),
+                                    "PaletteIndex": 0,
+                                    "Alpha": 1.0,
+                                },
+                                "Glyph": "B",
+                            },
+                            {
+                                "Format": int(ot.PaintFormat.PaintGlyph),
+                                "Paint": {
+                                    "Format": int(ot.PaintFormat.PaintSolid),
+                                    "PaletteIndex": 1,
+                                    "Alpha": 1.0,
+                                },
+                                "Glyph": "C",
+                            },
+                            {
+                                "Format": int(ot.PaintFormat.PaintGlyph),
+                                "Paint": {
+                                    "Format": int(ot.PaintFormat.PaintSolid),
+                                    "PaletteIndex": 2,
+                                    "Alpha": 1.0,
+                                },
+                                "Glyph": "D",
+                            },
+                        ],
+                    },
+                    "E": {
+                        "Format": int(ot.PaintFormat.PaintColrLayers),
+                        "Layers": [
+                            {
+                                "Format": int(ot.PaintFormat.PaintGlyph),
+                                "Paint": {
+                                    "Format": int(ot.PaintFormat.PaintSolid),
+                                    "PaletteIndex": 1,
+                                    "Alpha": 1.0,
+                                },
+                                "Glyph": "C",
+                            },
+                            {
+                                "Format": int(ot.PaintFormat.PaintGlyph),
+                                "Paint": {
+                                    "Format": int(ot.PaintFormat.PaintSolid),
+                                    "PaletteIndex": 2,
+                                    "Alpha": 1.0,
+                                },
+                                "Glyph": "D",
+                            },
+                            {
+                                "Format": int(ot.PaintFormat.PaintGlyph),
+                                "Paint": {
+                                    "Format": int(ot.PaintFormat.PaintSolid),
+                                    "PaletteIndex": 3,
+                                    "Alpha": 1.0,
+                                },
+                                "Glyph": "F",
+                            },
+                        ],
+                    },
+                    "G": {
+                        "Format": int(ot.PaintFormat.PaintColrGlyph),
+                        "Glyph": "E",
+                    },
+                },
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=3 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="3"/>',
+                    '        <FirstLayerIndex value="0"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="1">',
+                    '      <BaseGlyph value="E"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="2"/>',
+                    '        <FirstLayerIndex value="3"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="2">',
+                    '      <BaseGlyph value="G"/>',
+                    '      <Paint Format="11"><!-- PaintColrGlyph -->',
+                    '        <Glyph value="E"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "  <LayerList>",
+                    "    <!-- LayerCount=5 -->",
+                    '    <Paint index="0" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="1" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="C"/>',
+                    "    </Paint>",
+                    '    <Paint index="2" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="D"/>',
+                    "    </Paint>",
+                    '    <Paint index="3" Format="1"><!-- PaintColrLayers -->',
+                    '      <NumLayers value="2"/>',
+                    '      <FirstLayerIndex value="1"/>',
+                    "    </Paint>",
+                    '    <Paint index="4" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="3"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="F"/>',
+                    "    </Paint>",
+                    "  </LayerList>",
+                    "</COLR>",
+                ],
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=3 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="3"/>',
+                    '        <FirstLayerIndex value="0"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="1">',
+                    '      <BaseGlyph value="E"/>',
+                    '      <Paint Format="1"><!-- PaintColrLayers -->',
+                    '        <NumLayers value="3"/>',
+                    '        <FirstLayerIndex value="3"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    '    <BaseGlyphPaintRecord index="2">',
+                    '      <BaseGlyph value="G"/>',
+                    '      <Paint Format="11"><!-- PaintColrGlyph -->',
+                    '        <Glyph value="E"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "  <LayerList>",
+                    "    <!-- LayerCount=6 -->",
+                    '    <Paint index="0" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="0"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="B"/>',
+                    "    </Paint>",
+                    '    <Paint index="1" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="C"/>',
+                    "    </Paint>",
+                    '    <Paint index="2" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="D"/>',
+                    "    </Paint>",
+                    '    <Paint index="3" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="1"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="C"/>',
+                    "    </Paint>",
+                    '    <Paint index="4" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="2"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="D"/>',
+                    "    </Paint>",
+                    '    <Paint index="5" Format="10"><!-- PaintGlyph -->',
+                    '      <Paint Format="2"><!-- PaintSolid -->',
+                    '        <PaletteIndex value="3"/>',
+                    '        <Alpha value="1.0"/>',
+                    "      </Paint>",
+                    '      <Glyph value="F"/>',
+                    "    </Paint>",
+                    "  </LayerList>",
+                    "</COLR>",
+                ],
+                id="simple-reuse",
+            ),
+            pytest.param(
+                {
+                    "A": {
+                        "Format": int(ot.PaintFormat.PaintGlyph),
+                        "Paint": {
+                            "Format": int(ot.PaintFormat.PaintSolid),
+                            "PaletteIndex": 0,
+                            "Alpha": 1.0,
+                        },
+                        "Glyph": "B",
+                    },
+                },
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=1 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="10"><!-- PaintGlyph -->',
+                    '        <Paint Format="2"><!-- PaintSolid -->',
+                    '          <PaletteIndex value="0"/>',
+                    '          <Alpha value="1.0"/>',
+                    "        </Paint>",
+                    '        <Glyph value="B"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "</COLR>",
+                ],
+                [
+                    "<COLR>",
+                    '  <Version value="1"/>',
+                    "  <!-- BaseGlyphRecordCount=0 -->",
+                    "  <!-- LayerRecordCount=0 -->",
+                    "  <BaseGlyphList>",
+                    "    <!-- BaseGlyphCount=1 -->",
+                    '    <BaseGlyphPaintRecord index="0">',
+                    '      <BaseGlyph value="A"/>',
+                    '      <Paint Format="10"><!-- PaintGlyph -->',
+                    '        <Paint Format="2"><!-- PaintSolid -->',
+                    '          <PaletteIndex value="0"/>',
+                    '          <Alpha value="1.0"/>',
+                    "        </Paint>",
+                    '        <Glyph value="B"/>',
+                    "      </Paint>",
+                    "    </BaseGlyphPaintRecord>",
+                    "  </BaseGlyphList>",
+                    "</COLR>",
+                ],
+                id="no-layer-list",
+            ),
+        ],
+    )
+    def test_expandPaintColrLayers(
+        self, color_glyphs, ttFont, before_xml, expected_xml
+    ):
+        colr = buildCOLR(color_glyphs, allowLayerReuse=True)
+
+        assert dump_xml(colr.table, ttFont) == before_xml
+
+        before_layer_count = 0
+        reuses_colr_layers = False
+        if colr.table.LayerList:
+            before_layer_count = len(colr.table.LayerList.Paint)
+            reuses_colr_layers = any(
+                p.Format == ot.PaintFormat.PaintColrLayers
+                for p in colr.table.LayerList.Paint
+            )
+
+        COLRVariationMerger.expandPaintColrLayers(colr.table)
+
+        assert dump_xml(colr.table, ttFont) == expected_xml
+
+        after_layer_count = (
+            0 if not colr.table.LayerList else len(colr.table.LayerList.Paint)
+        )
+
+        if reuses_colr_layers:
+            assert not any(
+                p.Format == ot.PaintFormat.PaintColrLayers
+                for p in colr.table.LayerList.Paint
+            )
+            assert after_layer_count > before_layer_count
+        else:
+            assert after_layer_count == before_layer_count
+
+        if colr.table.LayerList:
+            assert len({id(p) for p in colr.table.LayerList.Paint}) == after_layer_count
diff --git a/Tests/varLib/models_test.py b/Tests/varLib/models_test.py
index c220d3d..e008012 100644
--- a/Tests/varLib/models_test.py
+++ b/Tests/varLib/models_test.py
@@ -1,78 +1,120 @@
 from fontTools.varLib.models import (
-    normalizeLocation, supportScalar, VariationModel, VariationModelError)
+    normalizeLocation,
+    supportScalar,
+    VariationModel,
+    VariationModelError,
+)
 import pytest
 
 
 def test_normalizeLocation():
     axes = {"wght": (100, 400, 900)}
-    assert normalizeLocation({"wght": 400}, axes) == {'wght': 0.0}
-    assert normalizeLocation({"wght": 100}, axes) == {'wght': -1.0}
-    assert normalizeLocation({"wght": 900}, axes) == {'wght': 1.0}
-    assert normalizeLocation({"wght": 650}, axes) == {'wght': 0.5}
-    assert normalizeLocation({"wght": 1000}, axes) == {'wght': 1.0}
-    assert normalizeLocation({"wght": 0}, axes) == {'wght': -1.0}
+    assert normalizeLocation({"wght": 400}, axes) == {"wght": 0.0}
+    assert normalizeLocation({"wght": 100}, axes) == {"wght": -1.0}
+    assert normalizeLocation({"wght": 900}, axes) == {"wght": 1.0}
+    assert normalizeLocation({"wght": 650}, axes) == {"wght": 0.5}
+    assert normalizeLocation({"wght": 1000}, axes) == {"wght": 1.0}
+    assert normalizeLocation({"wght": 0}, axes) == {"wght": -1.0}
 
     axes = {"wght": (0, 0, 1000)}
-    assert normalizeLocation({"wght": 0}, axes) == {'wght': 0.0}
-    assert normalizeLocation({"wght": -1}, axes) == {'wght': 0.0}
-    assert normalizeLocation({"wght": 1000}, axes) == {'wght': 1.0}
-    assert normalizeLocation({"wght": 500}, axes) == {'wght': 0.5}
-    assert normalizeLocation({"wght": 1001}, axes) == {'wght': 1.0}
+    assert normalizeLocation({"wght": 0}, axes) == {"wght": 0.0}
+    assert normalizeLocation({"wght": -1}, axes) == {"wght": 0.0}
+    assert normalizeLocation({"wght": 1000}, axes) == {"wght": 1.0}
+    assert normalizeLocation({"wght": 500}, axes) == {"wght": 0.5}
+    assert normalizeLocation({"wght": 1001}, axes) == {"wght": 1.0}
 
     axes = {"wght": (0, 1000, 1000)}
-    assert normalizeLocation({"wght": 0}, axes) == {'wght': -1.0}
-    assert normalizeLocation({"wght": -1}, axes) == {'wght': -1.0}
-    assert normalizeLocation({"wght": 500}, axes) == {'wght': -0.5}
-    assert normalizeLocation({"wght": 1000}, axes) == {'wght': 0.0}
-    assert normalizeLocation({"wght": 1001}, axes) == {'wght': 0.0}
+    assert normalizeLocation({"wght": 0}, axes) == {"wght": -1.0}
+    assert normalizeLocation({"wght": -1}, axes) == {"wght": -1.0}
+    assert normalizeLocation({"wght": 500}, axes) == {"wght": -0.5}
+    assert normalizeLocation({"wght": 1000}, axes) == {"wght": 0.0}
+    assert normalizeLocation({"wght": 1001}, axes) == {"wght": 0.0}
 
 
 def test_supportScalar():
     assert supportScalar({}, {}) == 1.0
-    assert supportScalar({'wght':.2}, {}) == 1.0
-    assert supportScalar({'wght':.2}, {'wght':(0,2,3)}) == 0.1
-    assert supportScalar({'wght':2.5}, {'wght':(0,2,4)}) == 0.75
+    assert supportScalar({"wght": 0.2}, {}) == 1.0
+    assert supportScalar({"wght": 0.2}, {"wght": (0, 2, 3)}) == 0.1
+    assert supportScalar({"wght": 2.5}, {"wght": (0, 2, 4)}) == 0.75
+    assert supportScalar({"wght": 4}, {"wght": (0, 2, 2)}) == 0.0
+    assert supportScalar({"wght": 4}, {"wght": (0, 2, 2)}, extrapolate=True) == 2.0
+    assert supportScalar({"wght": 4}, {"wght": (0, 2, 3)}, extrapolate=True) == 2.0
+    assert supportScalar({"wght": 2}, {"wght": (0, 0.75, 1)}, extrapolate=True) == -4.0
+
+
+@pytest.mark.parametrize(
+    "numLocations, numSamples",
+    [
+        pytest.param(127, 509, marks=pytest.mark.slow),
+        (31, 251),
+    ],
+)
+def test_modeling_error(numLocations, numSamples):
+    # https://github.com/fonttools/fonttools/issues/2213
+    locations = [{"axis": float(i) / numLocations} for i in range(numLocations)]
+    masterValues = [100.0 if i else 0.0 for i in range(numLocations)]
+
+    model = VariationModel(locations)
+
+    for i in range(numSamples):
+        loc = {"axis": float(i) / numSamples}
+        scalars = model.getScalars(loc)
+
+        deltas_float = model.getDeltas(masterValues)
+        deltas_round = model.getDeltas(masterValues, round=round)
+
+        expected = model.interpolateFromDeltasAndScalars(deltas_float, scalars)
+        actual = model.interpolateFromDeltasAndScalars(deltas_round, scalars)
+
+        err = abs(actual - expected)
+        assert err <= 0.5, (i, err)
+
+        # This is how NOT to round deltas.
+        # deltas_late_round = [round(d) for d in deltas_float]
+        # bad = model.interpolateFromDeltasAndScalars(deltas_late_round, scalars)
+        # err_bad = abs(bad - expected)
+        # if err != err_bad:
+        #    print("{:d}	{:.2}	{:.2}".format(i, err, err_bad))
 
 
 class VariationModelTest(object):
-
     @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": 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": -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)}
+                    {"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)},
                 ],
                 [
                     {},
@@ -81,47 +123,49 @@
                     {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}
-                ]
+                    {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.43560000000000004,
+                        7: 0.66,
+                    },
+                ],
             ),
             (
                 [
                     {},
-                    {'bar': 0.5},
-                    {'bar': 1.0},
-                    {'foo': 1.0},
-                    {'bar': 0.5, 'foo': 1.0},
-                    {'bar': 1.0, 'foo': 1.0},
+                    {"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.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)},
+                    {"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)},
                 ],
                 [
                     {},
@@ -131,12 +175,96 @@
                     {0: 1.0, 1: 1.0, 3: 1.0},
                     {0: 1.0, 2: 1.0, 3: 1.0},
                 ],
-            )
-        ]
+            ),
+            (
+                [
+                    {},
+                    {"foo": 0.25},
+                    {"foo": 0.5},
+                    {"foo": 0.75},
+                    {"foo": 1.0},
+                    {"bar": 0.25},
+                    {"bar": 0.75},
+                    {"bar": 1.0},
+                ],
+                None,
+                [
+                    {},
+                    {"bar": 0.25},
+                    {"bar": 0.75},
+                    {"bar": 1.0},
+                    {"foo": 0.25},
+                    {"foo": 0.5},
+                    {"foo": 0.75},
+                    {"foo": 1.0},
+                ],
+                [
+                    {},
+                    {"bar": (0.0, 0.25, 1.0)},
+                    {"bar": (0.25, 0.75, 1.0)},
+                    {"bar": (0.75, 1.0, 1.0)},
+                    {"foo": (0.0, 0.25, 1.0)},
+                    {"foo": (0.25, 0.5, 1.0)},
+                    {"foo": (0.5, 0.75, 1.0)},
+                    {"foo": (0.75, 1.0, 1.0)},
+                ],
+                [
+                    {},
+                    {0: 1.0},
+                    {0: 1.0, 1: 0.3333333333333333},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0, 4: 0.6666666666666666},
+                    {0: 1.0, 4: 0.3333333333333333, 5: 0.5},
+                    {0: 1.0},
+                ],
+            ),
+            (
+                [
+                    {},
+                    {"foo": 0.25},
+                    {"foo": 0.5},
+                    {"foo": 0.75},
+                    {"foo": 1.0},
+                    {"bar": 0.25},
+                    {"bar": 0.75},
+                    {"bar": 1.0},
+                ],
+                None,
+                [
+                    {},
+                    {"bar": 0.25},
+                    {"bar": 0.75},
+                    {"bar": 1.0},
+                    {"foo": 0.25},
+                    {"foo": 0.5},
+                    {"foo": 0.75},
+                    {"foo": 1.0},
+                ],
+                [
+                    {},
+                    {"bar": (0, 0.25, 1.0)},
+                    {"bar": (0.25, 0.75, 1.0)},
+                    {"bar": (0.75, 1.0, 1.0)},
+                    {"foo": (0, 0.25, 1.0)},
+                    {"foo": (0.25, 0.5, 1.0)},
+                    {"foo": (0.5, 0.75, 1.0)},
+                    {"foo": (0.75, 1.0, 1.0)},
+                ],
+                [
+                    {},
+                    {0: 1.0},
+                    {0: 1.0, 1: 0.3333333333333333},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0, 4: 0.6666666666666666},
+                    {0: 1.0, 4: 0.3333333333333333, 5: 0.5},
+                    {0: 1.0},
+                ],
+            ),
+        ],
     )
-    def test_init(
-        self, locations, axisOrder, sortedLocs, supports, deltaWeights
-    ):
+    def test_init(self, locations, axisOrder, sortedLocs, supports, deltaWeights):
         model = VariationModel(locations, axisOrder=axisOrder)
 
         assert model.locations == sortedLocs
@@ -152,3 +280,45 @@
                     {"bar": 1.0, "foo": 1.0},
                 ]
             )
+
+    @pytest.mark.parametrize(
+        "locations, axisOrder, masterValues, instanceLocation, expectedValue",
+        [
+            (
+                [
+                    {},
+                    {"axis_A": 1.0},
+                    {"axis_B": 1.0},
+                    {"axis_A": 1.0, "axis_B": 1.0},
+                    {"axis_A": 0.5, "axis_B": 1.0},
+                    {"axis_A": 1.0, "axis_B": 0.5},
+                ],
+                ["axis_A", "axis_B"],
+                [
+                    0,
+                    10,
+                    20,
+                    70,
+                    50,
+                    60,
+                ],
+                {
+                    "axis_A": 0.5,
+                    "axis_B": 0.5,
+                },
+                37.5,
+            ),
+        ],
+    )
+    def test_interpolation(
+        self,
+        locations,
+        axisOrder,
+        masterValues,
+        instanceLocation,
+        expectedValue,
+    ):
+        model = VariationModel(locations, axisOrder=axisOrder)
+        interpolatedValue = model.interpolateFromMasters(instanceLocation, masterValues)
+
+        assert interpolatedValue == expectedValue
diff --git a/Tests/varLib/stat_test.py b/Tests/varLib/stat_test.py
index 6aa88a0..6def990 100644
--- a/Tests/varLib/stat_test.py
+++ b/Tests/varLib/stat_test.py
@@ -15,7 +15,7 @@
     doc = DesignSpaceDocument.fromfile(datadir / "test_v5.designspace")
 
     assert getStatAxes(
-        doc, {"Italic": 0, "width": Range(50, 150), "weight": Range(200, 900)}
+        doc, {"Italic": 0, "Width": Range(50, 150), "Weight": Range(200, 900)}
     ) == [
         {
             "values": [
@@ -82,7 +82,7 @@
                     "rangeMinValue": 150.0,
                 },
             ],
-            "name": {"en": "width", "fr": "Chasse"},
+            "name": {"en": "Width", "fr": "Chasse"},
             "ordering": 1,
             "tag": "wdth",
         },
@@ -96,7 +96,7 @@
         },
     ]
 
-    assert getStatAxes(doc, {"Italic": 1, "width": 100, "weight": Range(400, 700)}) == [
+    assert getStatAxes(doc, {"Italic": 1, "Width": 100, "Weight": Range(400, 700)}) == [
         {
             "values": [
                 {
@@ -129,7 +129,7 @@
             "values": [
                 {"flags": 3, "name": {"en": "Normal"}, "value": 100.0},
             ],
-            "name": {"en": "width", "fr": "Chasse"},
+            "name": {"en": "Width", "fr": "Chasse"},
             "ordering": 1,
             "tag": "wdth",
         },
@@ -148,7 +148,7 @@
     doc = DesignSpaceDocument.fromfile(datadir / "test_v5.designspace")
 
     assert getStatLocations(
-        doc, {"Italic": 0, "width": Range(50, 150), "weight": Range(200, 900)}
+        doc, {"Italic": 0, "Width": Range(50, 150), "Weight": Range(200, 900)}
     ) == [
         {
             "flags": 0,
@@ -157,7 +157,7 @@
         },
     ]
     assert getStatLocations(
-        doc, {"Italic": 1, "width": Range(50, 150), "weight": Range(200, 900)}
+        doc, {"Italic": 1, "Width": Range(50, 150), "Weight": Range(200, 900)}
     ) == [
         {
             "flags": 0,
diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py
index 484a2e2..29f909a 100644
--- a/Tests/varLib/varLib_test.py
+++ b/Tests/varLib/varLib_test.py
@@ -823,6 +823,7 @@
 GPOS.table.FeatureList.FeatureCount
 
 The problem is likely to be in Simple Two Axis Bold:
+Expected to see .FeatureCount==2, instead saw 1
 
 Incompatible features between masters.
 Expected: kern, mark.
@@ -840,7 +841,7 @@
     def test_varlib_build_incompatible_lookup_types(self):
         with pytest.raises(
             varLibErrors.MismatchedTypes,
-            match = r"MarkBasePos, instead saw PairPos"
+            match = r"'MarkBasePos', instead saw 'PairPos'"
         ):
             self._run_varlib_build_test(
                 designspace_name="IncompatibleLookupTypes",
@@ -870,6 +871,15 @@
                 save_before_dump=True,
             )
 
+    def test_varlib_build_variable_colr(self):
+        self._run_varlib_build_test(
+            designspace_name='TestVariableCOLR',
+            font_name='TestVariableCOLR',
+            tables=["GlyphOrder", "fvar", "glyf", "COLR", "CPAL"],
+            expected_ttx_name='TestVariableCOLR-VF',
+            save_before_dump=True,
+        )
+
 def test_load_masters_layerName_without_required_font():
     ds = DesignSpaceDocument()
     s = SourceDescriptor()
diff --git a/Tests/varLib/varStore_test.py b/Tests/varLib/varStore_test.py
index a1bdd12..cad8ac7 100644
--- a/Tests/varLib/varStore_test.py
+++ b/Tests/varLib/varStore_test.py
@@ -13,7 +13,7 @@
         (
             [{}, {"a": 1}],
             [
-                [10, 20],
+                [10, 10], # Test NO_VARIATION_INDEX
                 [100, 2000],
                 [100, 22000],
             ],
diff --git a/requirements.txt b/requirements.txt
index 008bc8c..1bab6d7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,13 +3,14 @@
 brotli==1.0.9; platform_python_implementation != "PyPy"
 brotlicffi==1.0.9.2; platform_python_implementation == "PyPy"
 unicodedata2==14.0.0; python_version < '3.11'
-scipy==1.7.3; platform_python_implementation != "PyPy"
+scipy==1.7.3; platform_python_implementation != "PyPy" and python_version <= '3.7'  # pyup: ignore
+scipy==1.9.0; platform_python_implementation != "PyPy" and python_version > '3.7'
 munkres==1.1.4; platform_python_implementation == "PyPy"
-zopfli==0.1.9
-fs==2.4.14
+zopfli==0.2.1
+fs==2.4.16
 skia-pathops==0.7.2; platform_python_implementation != "PyPy"
 # this is only required to run Tests/cu2qu/{ufo,cli}_test.py
-ufoLib2==0.13.0
-pyobjc==8.1; sys_platform == "darwin"
-freetype-py==2.2.0
-uharfbuzz==0.24.1
+ufoLib2==0.13.1
+pyobjc==8.5; sys_platform == "darwin"
+freetype-py==2.3.0
+uharfbuzz==0.30.0
diff --git a/setup.cfg b/setup.cfg
index e184ce3..69bfde1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 4.33.3
+current_version = 4.37.1
 commit = True
 tag = False
 tag_name = {new_version}
@@ -52,6 +52,8 @@
 	ignore:writePlist:DeprecationWarning:plistlib_test
 	ignore:some_function:DeprecationWarning:fontTools.ufoLib.utils
 	ignore::DeprecationWarning:fontTools.varLib.designspace
+markers = 
+	slow: marks tests as slow (deselect with '-m "not slow"')
 
 [tool:interrogate]
 ignore-semiprivate = true
diff --git a/setup.py b/setup.py
index 6e6d391..03395ac 100755
--- a/setup.py
+++ b/setup.py
@@ -68,6 +68,12 @@
 	ext_modules.append(
 		Extension("fontTools.cu2qu.cu2qu", ["Lib/fontTools/cu2qu/cu2qu.py"]),
 	)
+	ext_modules.append(
+		Extension("fontTools.pens.momentsPen", ["Lib/fontTools/pens/momentsPen.py"]),
+	)
+	ext_modules.append(
+		Extension("fontTools.varLib.iup", ["Lib/fontTools/varLib/iup.py"]),
+	)
 
 extras_require = {
 	# for fontTools.ufoLib: to read/write UFO fonts
@@ -443,7 +449,7 @@
 
 setup_params = dict(
 	name="fonttools",
-	version="4.33.3",
+	version="4.37.1",
 	description="Tools to manipulate font files",
 	author="Just van Rossum",
 	author_email="just@letterror.com",