Merge branch 'master' into remove-gvar-zero-workaround

* master: (70 commits)
  Bump version: 4.4.0 → 4.4.1.dev0
  Release 4.4.0
  Update changelog [skip ci]
  fontBuilder: allow to build v1 from setupCPAL method
  colorLib_test: add tests for buildCPAL v1
  colorLib: allow to build CPAL version=1
  [CPAL] the absence of a color palette label nameID is 0xFFFF, not 0
  colorLib: add type annotations
  add tests for buildCORL and buildCPAL
  fontBuilder: add setupCOLR and setupCPAL methods
  colorLib: add buildCOLR and buildCPAL
  ufoLib: don't crash if UFO2 has openTypeHheaCaretOffset=0.0
  _g_l_y_f_test: add tests for Glyph.getCoordinates and GlyphComponent.to/fromXML
  [glyf] if comp uses anchors compute firstPt-secondPt offset after applying transform
  Update NEWS.rst
  merger: Convert input checking asserts into proper exceptions
  featureVars: Use new exceptions
  cff: Use new exceptions
  models: Use new exceptions where input is checked
  init: Convert input checking asserts into proper exceptions
  ...
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
index 69d6af1..d563e85 100644
--- a/Doc/source/designspaceLib/readme.rst
+++ b/Doc/source/designspaceLib/readme.rst
@@ -30,6 +30,10 @@
 different objects, as long as they have the same attributes. Reader and
 Writer objects can be subclassed as well.
 
+**Note:** Python attribute names are usually camelCased, the
+corresponding `XML <#document-xml-structure>`_ attributes are usually
+all lowercase.
+
 .. example-1:
 
 .. code:: python
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index 0db40fc..089ac7b 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -4,6 +4,6 @@
 
 log = logging.getLogger(__name__)
 
-version = __version__ = "4.2.3.dev0"
+version = __version__ = "4.4.1.dev0"
 
 __all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/colorLib/__init__.py b/Lib/fontTools/colorLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/fontTools/colorLib/__init__.py
diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py
new file mode 100644
index 0000000..486909e
--- /dev/null
+++ b/Lib/fontTools/colorLib/builder.py
@@ -0,0 +1,147 @@
+import enum
+from typing import Dict, Iterable, List, Optional, Tuple, Union
+from fontTools.ttLib.tables.C_O_L_R_ import LayerRecord, table_C_O_L_R_
+from fontTools.ttLib.tables.C_P_A_L_ import Color, table_C_P_A_L_
+from fontTools.ttLib.tables._n_a_m_e import table__n_a_m_e
+from .errors import ColorLibError
+
+
+def buildCOLR(colorLayers: Dict[str, List[Tuple[str, int]]]) -> table_C_O_L_R_:
+    """Build COLR table from color layers mapping.
+
+    Args:
+        colorLayers: : map of base glyph names to lists of (layer glyph names,
+            palette indices) tuples.
+
+    Return:
+        A new COLRv0 table.
+    """
+    colorLayerLists = {}
+    for baseGlyphName, layers in colorLayers.items():
+        colorLayerLists[baseGlyphName] = [
+            LayerRecord(layerGlyphName, colorID) for layerGlyphName, colorID in layers
+        ]
+
+    colr = table_C_O_L_R_()
+    colr.version = 0
+    colr.ColorLayers = colorLayerLists
+    return colr
+
+
+class ColorPaletteType(enum.IntFlag):
+    USABLE_WITH_LIGHT_BACKGROUND = 0x0001
+    USABLE_WITH_DARK_BACKGROUND = 0x0002
+
+    @classmethod
+    def _missing_(cls, value):
+        # enforce reserved bits
+        if isinstance(value, int) and (value < 0 or value & 0xFFFC != 0):
+            raise ValueError(f"{value} is not a valid {cls.__name__}")
+        return super()._missing_(value)
+
+
+# None, 'abc' or {'en': 'abc', 'de': 'xyz'}
+_OptionalLocalizedString = Union[None, str, Dict[str, str]]
+
+
+def buildPaletteLabels(
+    labels: List[_OptionalLocalizedString], nameTable: table__n_a_m_e
+) -> List[Optional[int]]:
+    return [
+        nameTable.addMultilingualName(l, mac=False)
+        if isinstance(l, dict)
+        else table_C_P_A_L_.NO_NAME_ID
+        if l is None
+        else nameTable.addMultilingualName({"en": l}, mac=False)
+        for l in labels
+    ]
+
+
+def buildCPAL(
+    palettes: List[List[Tuple[float, float, float, float]]],
+    paletteTypes: Optional[List[ColorPaletteType]] = None,
+    paletteLabels: Optional[List[_OptionalLocalizedString]] = None,
+    paletteEntryLabels: Optional[List[_OptionalLocalizedString]] = None,
+    nameTable: Optional[table__n_a_m_e] = None,
+) -> table_C_P_A_L_:
+    """Build CPAL table from list of color palettes.
+
+    Args:
+        palettes: list of lists of colors encoded as tuples of (R, G, B, A) floats
+            in the range [0..1].
+        paletteTypes: optional list of ColorPaletteType, one for each palette.
+        paletteLabels: optional list of palette labels. Each lable can be either:
+            None (no label), a string (for for default English labels), or a
+            localized string (as a dict keyed with BCP47 language codes).
+        paletteEntryLabels: optional list of palette entry labels, one for each
+            palette entry (see paletteLabels).
+        nameTable: optional name table where to store palette and palette entry
+            labels. Required if either paletteLabels or paletteEntryLabels is set.
+
+    Return:
+        A new CPAL v0 or v1 table, if custom palette types or labels are specified.
+    """
+    if len({len(p) for p in palettes}) != 1:
+        raise ColorLibError("color palettes have different lengths")
+
+    if (paletteLabels or paletteEntryLabels) and not nameTable:
+        raise TypeError(
+            "nameTable is required if palette or palette entries have labels"
+        )
+
+    cpal = table_C_P_A_L_()
+    cpal.numPaletteEntries = len(palettes[0])
+
+    cpal.palettes = []
+    for i, palette in enumerate(palettes):
+        colors = []
+        for j, color in enumerate(palette):
+            if not isinstance(color, tuple) or len(color) != 4:
+                raise ColorLibError(
+                    f"In palette[{i}][{j}]: expected (R, G, B, A) tuple, got {color!r}"
+                )
+            if any(v > 1 or v < 0 for v in color):
+                raise ColorLibError(
+                    f"palette[{i}][{j}] has invalid out-of-range [0..1] color: {color!r}"
+                )
+            # input colors are RGBA, CPAL encodes them as BGRA
+            red, green, blue, alpha = color
+            colors.append(Color(*(round(v * 255) for v in (blue, green, red, alpha))))
+        cpal.palettes.append(colors)
+
+    if any(v is not None for v in (paletteTypes, paletteLabels, paletteEntryLabels)):
+        cpal.version = 1
+
+        if paletteTypes is not None:
+            if len(paletteTypes) != len(palettes):
+                raise ColorLibError(
+                    f"Expected {len(palettes)} paletteTypes, got {len(paletteTypes)}"
+                )
+            cpal.paletteTypes = [ColorPaletteType(t).value for t in paletteTypes]
+        else:
+            cpal.paletteTypes = [table_C_P_A_L_.DEFAULT_PALETTE_TYPE] * len(palettes)
+
+        if paletteLabels is not None:
+            if len(paletteLabels) != len(palettes):
+                raise ColorLibError(
+                    f"Expected {len(palettes)} paletteLabels, got {len(paletteLabels)}"
+                )
+            cpal.paletteLabels = buildPaletteLabels(paletteLabels, nameTable)
+        else:
+            cpal.paletteLabels = [table_C_P_A_L_.NO_NAME_ID] * len(palettes)
+
+        if paletteEntryLabels is not None:
+            if len(paletteEntryLabels) != cpal.numPaletteEntries:
+                raise ColorLibError(
+                    f"Expected {cpal.numPaletteEntries} paletteEntryLabels, "
+                    f"got {len(paletteEntryLabels)}"
+                )
+            cpal.paletteEntryLabels = buildPaletteLabels(paletteEntryLabels, nameTable)
+        else:
+            cpal.paletteEntryLabels = [
+                table_C_P_A_L_.NO_NAME_ID
+            ] * cpal.numPaletteEntries
+    else:
+        cpal.version = 0
+
+    return cpal
diff --git a/Lib/fontTools/colorLib/errors.py b/Lib/fontTools/colorLib/errors.py
new file mode 100644
index 0000000..a0bdda1
--- /dev/null
+++ b/Lib/fontTools/colorLib/errors.py
@@ -0,0 +1,3 @@
+
+class ColorLibError(Exception):
+    pass
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
index 1836ebb..6e65437 100644
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -679,15 +679,17 @@
         self.cur_lookup_name_ = name
         self.named_lookups_[name] = None
         self.cur_lookup_ = None
-        self.lookupflag_ = 0
-        self.lookupflag_markFilterSet_ = None
+        if self.cur_feature_name_ is None:
+            self.lookupflag_ = 0
+            self.lookupflag_markFilterSet_ = None
 
     def end_lookup_block(self):
         assert self.cur_lookup_name_ is not None
         self.cur_lookup_name_ = None
         self.cur_lookup_ = None
-        self.lookupflag_ = 0
-        self.lookupflag_markFilterSet_ = None
+        if self.cur_feature_name_ is None:
+            self.lookupflag_ = 0
+            self.lookupflag_markFilterSet_ = None
 
     def add_lookup_call(self, lookup_name):
         assert lookup_name in self.named_lookups_, lookup_name
@@ -892,9 +894,17 @@
             return
         lookup = self.get_lookup_(location, MultipleSubstBuilder)
         if glyph in lookup.mapping:
-            raise FeatureLibError(
-                'Already defined substitution for glyph "%s"' % glyph,
-                location)
+            if replacements == lookup.mapping[glyph]:
+                log.info(
+                    'Removing duplicate multiple substitution from glyph'
+                    ' "%s" to %s%s',
+                    glyph, replacements,
+                    ' at {}:{}:{}'.format(*location) if location else '',
+                )
+            else:
+                raise FeatureLibError(
+                    'Already defined substitution for glyph "%s"' % glyph,
+                    location)
         lookup.mapping[glyph] = replacements
 
     def add_reverse_chain_single_subst(self, location, old_prefix,
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
index d8670d6..bda34e2 100644
--- a/Lib/fontTools/feaLib/parser.py
+++ b/Lib/fontTools/feaLib/parser.py
@@ -19,6 +19,14 @@
 
     def __init__(self, featurefile, glyphNames=(), followIncludes=True,
                  **kwargs):
+        """Initializes a Parser object.
+
+        Note: the `glyphNames` iterable serves a double role to help distinguish
+        glyph names from ranges in the presence of hyphens and to ensure that glyph
+        names referenced in a feature file are actually part of a font's glyph set.
+        If the iterable is left empty, no glyph name in glyph set checking takes
+        place.
+        """
         if "glyphMap" in kwargs:
             from fontTools.misc.loggingTools import deprecateArgument
             deprecateArgument("glyphMap", "use 'glyphNames' (iterable) instead")
@@ -268,6 +276,7 @@
         if (accept_glyphname and
                 self.next_token_type_ in (Lexer.NAME, Lexer.CID)):
             glyph = self.expect_glyph_()
+            self.check_glyph_name_in_glyph_set(glyph)
             return self.ast.GlyphName(glyph, location=self.cur_token_location_)
         if self.next_token_type_ is Lexer.GLYPHCLASS:
             self.advance_lexer_()
@@ -292,6 +301,7 @@
                 location = self.cur_token_location_
                 if '-' in glyph and glyph not in self.glyphNames_:
                     start, limit = self.split_glyph_range_(glyph, location)
+                    self.check_glyph_name_in_glyph_set(start, limit)
                     glyphs.add_range(
                         start, limit,
                         self.make_glyph_range_(location, start, limit))
@@ -299,10 +309,12 @@
                     start = glyph
                     self.expect_symbol_("-")
                     limit = self.expect_glyph_()
+                    self.check_glyph_name_in_glyph_set(start, limit)
                     glyphs.add_range(
                         start, limit,
                         self.make_glyph_range_(location, start, limit))
                 else:
+                    self.check_glyph_name_in_glyph_set(glyph)
                     glyphs.append(glyph)
             elif self.next_token_type_ is Lexer.CID:
                 glyph = self.expect_glyph_()
@@ -311,11 +323,17 @@
                     range_start = self.cur_token_
                     self.expect_symbol_("-")
                     range_end = self.expect_cid_()
+                    self.check_glyph_name_in_glyph_set(
+                        f"cid{range_start:05d}",
+                        f"cid{range_end:05d}",
+                    )
                     glyphs.add_cid_range(range_start, range_end,
                                          self.make_cid_range_(range_location,
                                                               range_start, range_end))
                 else:
-                    glyphs.append("cid%05d" % self.cur_token_)
+                    glyph_name = f"cid{self.cur_token_:05d}"
+                    self.check_glyph_name_in_glyph_set(glyph_name)
+                    glyphs.append(glyph_name)
             elif self.next_token_type_ is Lexer.GLYPHCLASS:
                 self.advance_lexer_()
                 gc = self.glyphclasses_.resolve(self.cur_token_)
@@ -705,7 +723,7 @@
         else:
             keyword = None
         self.expect_symbol_(";")
-        if len(new) is 0 and not any(lookups):
+        if len(new) == 0 and not any(lookups):
             raise FeatureLibError(
                 'Expected "by", "from" or explicit lookup references',
                 self.cur_token_location_)
@@ -1452,12 +1470,21 @@
 
         # Upgrade all single substitutions to multiple substitutions.
         if has_single and has_multiple:
-            for i, s in enumerate(statements):
+            statements = []
+            for s in block.statements:
                 if isinstance(s, self.ast.SingleSubstStatement):
-                    statements[i] = self.ast.MultipleSubstStatement(
-                        s.prefix, s.glyphs[0].glyphSet()[0], s.suffix,
-                        [r.glyphSet()[0] for r in s.replacements],
-                        s.forceChain, location=s.location)
+                    glyphs = s.glyphs[0].glyphSet()
+                    replacements = s.replacements[0].glyphSet()
+                    if len(replacements) == 1:
+                        replacements *= len(glyphs)
+                    for i, glyph in enumerate(glyphs):
+                        statements.append(
+                            self.ast.MultipleSubstStatement(
+                                s.prefix, glyph, s.suffix, [replacements[i]],
+                                s.forceChain, location=s.location))
+                else:
+                    statements.append(s)
+            block.statements = statements
 
     def is_cur_keyword_(self, k):
         if self.cur_token_type_ is Lexer.NAME:
@@ -1500,6 +1527,21 @@
         raise FeatureLibError("Expected a glyph name or CID",
                               self.cur_token_location_)
 
+    def check_glyph_name_in_glyph_set(self, *names):
+        """Raises if glyph name (just `start`) or glyph names of a
+        range (`start` and `end`) are not in the glyph set.
+
+        If no glyph set is present, does nothing.
+        """
+        if self.glyphNames_:
+            missing = [name for name in names if name not in self.glyphNames_]
+            if missing:
+                raise FeatureLibError(
+                    "The following glyph names are referenced but are missing from the "
+                    f"glyph set: {', '.join(missing)}",
+                    self.cur_token_location_
+                )
+
     def expect_markClass_reference_(self):
         name = self.expect_class_name_()
         mc = self.glyphclasses_.resolve(name)
diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py
index 29d58fb..a08442a 100644
--- a/Lib/fontTools/fontBuilder.py
+++ b/Lib/fontTools/fontBuilder.py
@@ -602,7 +602,10 @@
         varData = buildVarData(list(range(len(regions))), None, optimize=False)
         varStore = buildVarStore(varRegionList, [varData])
         vstore = VarStoreData(otVarStore=varStore)
-        self.font["CFF2"].cff.topDictIndex[0].VarStore = vstore
+        topDict = self.font["CFF2"].cff.topDictIndex[0]
+        topDict.VarStore = vstore
+        for fontDict in topDict.FDArray:
+            fontDict.Private.vstore = vstore
 
     def setupGlyf(self, glyphs, calcGlyphBounds=True):
         """Create the `glyf` table from a dict, that maps glyph names
@@ -765,6 +768,39 @@
             self.font, conditionalSubstitutions, featureTag=featureTag
         )
 
+    def setupCOLR(self, colorLayers):
+        """Build new COLR table using color layers dictionary.
+
+        Cf. `fontTools.colorLib.builder.buildCOLR`.
+        """
+        from fontTools.colorLib.builder import buildCOLR
+
+        self.font["COLR"] = buildCOLR(colorLayers)
+
+    def setupCPAL(
+        self,
+        palettes,
+        paletteTypes=None,
+        paletteLabels=None,
+        paletteEntryLabels=None,
+    ):
+        """Build new CPAL table using list of palettes.
+
+        Optionally build CPAL v1 table using paletteTypes, paletteLabels and
+        paletteEntryLabels.
+
+        Cf. `fontTools.colorLib.builder.buildCPAL`.
+        """
+        from fontTools.colorLib.builder import buildCPAL
+
+        self.font["CPAL"] = buildCPAL(
+            palettes,
+            paletteTypes=paletteTypes,
+            paletteLabels=paletteLabels,
+            paletteEntryLabels=paletteEntryLabels,
+            nameTable=self.font.get("name")
+        )
+
 
 def buildCmapSubTable(cmapping, format, platformID, platEncID):
     subTable = cmap_classes[format](format)
diff --git a/Lib/fontTools/mtiLib/__init__.py b/Lib/fontTools/mtiLib/__init__.py
index 67e99d7..f0f0a13 100644
--- a/Lib/fontTools/mtiLib/__init__.py
+++ b/Lib/fontTools/mtiLib/__init__.py
@@ -854,7 +854,7 @@
 
 	lookup.SubTable = subtables
 	lookup.SubTableCount = len(lookup.SubTable)
-	if lookup.SubTableCount is 0:
+	if lookup.SubTableCount == 0:
 		# Remove this return when following is fixed:
 		# https://github.com/fonttools/fonttools/issues/789
 		return None
diff --git a/Lib/fontTools/pens/ttGlyphPen.py b/Lib/fontTools/pens/ttGlyphPen.py
index f7b1483..0b64cb3 100644
--- a/Lib/fontTools/pens/ttGlyphPen.py
+++ b/Lib/fontTools/pens/ttGlyphPen.py
@@ -1,6 +1,6 @@
 from fontTools.misc.py23 import *
 from array import array
-from fontTools.misc.fixedTools import MAX_F2DOT14, otRound
+from fontTools.misc.fixedTools import MAX_F2DOT14, otRound, floatToFixedToFloat
 from fontTools.pens.basePen import LoggingPen
 from fontTools.pens.transformPen import TransformPen
 from fontTools.ttLib.tables import ttProgram
@@ -119,7 +119,11 @@
             component = GlyphComponent()
             component.glyphName = glyphName
             component.x, component.y = (otRound(v) for v in transformation[4:])
-            transformation = transformation[:4]
+            # quantize floats to F2Dot14 so we get same values as when decompiled
+            # from a binary glyf table
+            transformation = tuple(
+                floatToFixedToFloat(v, 14) for v in transformation[:4]
+            )
             if transformation != (1, 0, 0, 1):
                 if (self.handleOverflowingTransforms and
                         any(MAX_F2DOT14 < s <= 2 for s in transformation)):
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 7205c34..fadda34 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -546,6 +546,11 @@
 	if not options.hinting:
 		# Drop device tables
 		self.ValueFormat &= ~0x00F0
+	# Downgrade to Format 1 if all ValueRecords are the same
+	if self.Format == 2 and all(v == self.Value[0] for v in self.Value):
+		self.Format = 1
+		self.Value = self.Value[0] if self.ValueFormat != 0 else None
+		del self.ValueCount
 	return True
 
 @_add_method(otTables.PairPos)
diff --git a/Lib/fontTools/ttLib/tables/C_P_A_L_.py b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
index f041560..a7b4ad2 100644
--- a/Lib/fontTools/ttLib/tables/C_P_A_L_.py
+++ b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
@@ -13,6 +13,9 @@
 
 class table_C_P_A_L_(DefaultTable.DefaultTable):
 
+	NO_NAME_ID = 0xFFFF
+	DEFAULT_PALETTE_TYPE = 0
+
 	def __init__(self, tag=None):
 		DefaultTable.DefaultTable.__init__(self, tag)
 		self.palettes = []
@@ -45,24 +48,25 @@
 			offsetToPaletteEntryLabelArray) = (
 				struct.unpack(">LLL", data[pos:pos+12]))
 		self.paletteTypes = self._decompileUInt32Array(
-			data, offsetToPaletteTypeArray, numPalettes)
+			data, offsetToPaletteTypeArray, numPalettes,
+			default=self.DEFAULT_PALETTE_TYPE)
 		self.paletteLabels = self._decompileUInt16Array(
-			data, offsetToPaletteLabelArray, numPalettes)
+			data, offsetToPaletteLabelArray, numPalettes, default=self.NO_NAME_ID)
 		self.paletteEntryLabels = self._decompileUInt16Array(
 			data, offsetToPaletteEntryLabelArray,
-			self.numPaletteEntries)
+			self.numPaletteEntries, default=self.NO_NAME_ID)
 
-	def _decompileUInt16Array(self, data, offset, numElements):
+	def _decompileUInt16Array(self, data, offset, numElements, default=0):
 		if offset == 0:
-			return [0] * numElements
+			return [default] * numElements
 		result = array.array("H", data[offset : offset + 2 * numElements])
 		if sys.byteorder != "big": result.byteswap()
 		assert len(result) == numElements, result
 		return result.tolist()
 
-	def _decompileUInt32Array(self, data, offset, numElements):
+	def _decompileUInt32Array(self, data, offset, numElements, default=0):
 		if offset == 0:
-			return [0] * numElements
+			return [default] * numElements
 		result = array.array("I", data[offset : offset + 4 * numElements])
 		if sys.byteorder != "big": result.byteswap()
 		assert len(result) == numElements, result
@@ -136,7 +140,7 @@
 		return result
 
 	def _compilePaletteLabels(self):
-		if self.version == 0 or not any(self.paletteLabels):
+		if self.version == 0 or all(l == self.NO_NAME_ID for l in self.paletteLabels):
 			return b''
 		assert len(self.paletteLabels) == len(self.palettes)
 		result = bytesjoin([struct.pack(">H", label)
@@ -145,7 +149,7 @@
 		return result
 
 	def _compilePaletteEntryLabels(self):
-		if self.version == 0 or not any(self.paletteEntryLabels):
+		if self.version == 0 or all(l == self.NO_NAME_ID for l in self.paletteEntryLabels):
 			return b''
 		assert len(self.paletteEntryLabels) == self.numPaletteEntries
 		result = bytesjoin([struct.pack(">H", label)
@@ -165,15 +169,15 @@
 		writer.newline()
 		for index, palette in enumerate(self.palettes):
 			attrs = {"index": index}
-			paletteType = paletteTypes.get(index)
-			paletteLabel = paletteLabels.get(index)
-			if self.version > 0 and paletteLabel is not None:
+			paletteType = paletteTypes.get(index, self.DEFAULT_PALETTE_TYPE)
+			paletteLabel = paletteLabels.get(index, self.NO_NAME_ID)
+			if self.version > 0 and paletteLabel != self.NO_NAME_ID:
 				attrs["label"] = paletteLabel
-			if self.version > 0 and paletteType is not None:
+			if self.version > 0 and paletteType != self.DEFAULT_PALETTE_TYPE:
 				attrs["type"] = paletteType
 			writer.begintag("palette", **attrs)
 			writer.newline()
-			if (self.version > 0 and paletteLabel and
+			if (self.version > 0 and paletteLabel != self.NO_NAME_ID and
 			    ttFont and "name" in ttFont):
 				name = ttFont["name"].getDebugName(paletteLabel)
 				if name is not None:
@@ -184,11 +188,11 @@
 				color.toXML(writer, ttFont, cindex)
 			writer.endtag("palette")
 			writer.newline()
-		if self.version > 0 and any(self.paletteEntryLabels):
+		if self.version > 0 and not all(l == self.NO_NAME_ID for l in self.paletteEntryLabels):
 			writer.begintag("paletteEntryLabels")
 			writer.newline()
 			for index, label in enumerate(self.paletteEntryLabels):
-				if label:
+				if label != self.NO_NAME_ID:
 					writer.simpletag("label", index=index, value=label)
 					if (self.version > 0 and label and ttFont and "name" in ttFont):
 						name = ttFont["name"].getDebugName(label)
@@ -200,8 +204,8 @@
 
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "palette":
-			self.paletteLabels.append(int(attrs.get("label", "0")))
-			self.paletteTypes.append(int(attrs.get("type", "0")))
+			self.paletteLabels.append(int(attrs.get("label", self.NO_NAME_ID)))
+			self.paletteTypes.append(int(attrs.get("type", self.DEFAULT_PALETTE_TYPE)))
 			palette = []
 			for element in content:
 				if isinstance(element, basestring):
@@ -221,13 +225,13 @@
 					nameID = safeEval(elementAttr["value"])
 					colorLabels[labelIndex] = nameID
 			self.paletteEntryLabels = [
-				colorLabels.get(i, 0)
+				colorLabels.get(i, self.NO_NAME_ID)
 				for i in range(self.numPaletteEntries)]
 		elif "value" in attrs:
 			value = safeEval(attrs["value"])
 			setattr(self, name, value)
 			if name == "numPaletteEntries":
-				self.paletteEntryLabels = [0] * self.numPaletteEntries
+				self.paletteEntryLabels = [self.NO_NAME_ID] * self.numPaletteEntries
 
 
 class Color(namedtuple("Color", "blue green red alpha")):
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 224042d..f71ae95 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -482,7 +482,7 @@
 			dataList = [EblcIndexSubTable.compile(self, ttFont)]
 			dataList += [struct.pack(dataFormat, offsetValue) for offsetValue in offsetArray]
 			# Take care of any padding issues. Only occurs in format 3.
-			if offsetDataSize * len(dataList) % 4 != 0:
+			if offsetDataSize * len(offsetArray) % 4 != 0:
 				dataList.append(struct.pack(dataFormat, 0))
 			return bytesjoin(dataList)
 
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 2fdc8c3..f7f9266 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -18,7 +18,7 @@
 	cmap = {}
 	glyphOrder = font.getGlyphOrder()
 	for char,gid in zip(chars,gids):
-		if gid is 0:
+		if gid == 0:
 			continue
 		try:
 			name = glyphOrder[gid]
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index cc22ad0..a6cd1fc 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -122,6 +122,12 @@
 			ttFont['loca'].set(locations)
 		if 'maxp' in ttFont:
 			ttFont['maxp'].numGlyphs = len(self.glyphs)
+		if not data:
+		# As a special case when all glyph in the font are empty, add a zero byte
+		# to the table, so that OTS doesn’t reject it, and to make the table work
+		# on Windows as well.
+		# See https://github.com/khaledhosny/ots/issues/52
+			data = b"\0"
 		return data
 
 	def toXML(self, writer, ttFont, splitGlyphs=False):
@@ -1006,33 +1012,37 @@
 					coordinates, endPts, flags = g.getCoordinates(glyfTable)
 				except RecursionError:
 					raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName)
+				coordinates = GlyphCoordinates(coordinates)
 				if hasattr(compo, "firstPt"):
-					# move according to two reference points
+					# component uses two reference points: we apply the transform _before_
+					# computing the offset between the points
+					if hasattr(compo, "transform"):
+						coordinates.transform(compo.transform)
 					x1,y1 = allCoords[compo.firstPt]
 					x2,y2 = coordinates[compo.secondPt]
 					move = x1-x2, y1-y2
-				else:
-					move = compo.x, compo.y
-
-				coordinates = GlyphCoordinates(coordinates)
-				if not hasattr(compo, "transform"):
 					coordinates.translate(move)
 				else:
-					apple_way = compo.flags & SCALED_COMPONENT_OFFSET
-					ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
-					assert not (apple_way and ms_way)
-					if not (apple_way or ms_way):
-						scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT  # see top of this file
-					else:
-						scale_component_offset = apple_way
-					if scale_component_offset:
-						# the Apple way: first move, then scale (ie. scale the component offset)
+					# component uses XY offsets
+					move = compo.x, compo.y
+					if not hasattr(compo, "transform"):
 						coordinates.translate(move)
-						coordinates.transform(compo.transform)
 					else:
-						# the MS way: first scale, then move
-						coordinates.transform(compo.transform)
-						coordinates.translate(move)
+						apple_way = compo.flags & SCALED_COMPONENT_OFFSET
+						ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
+						assert not (apple_way and ms_way)
+						if not (apple_way or ms_way):
+							scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT  # see top of this file
+						else:
+							scale_component_offset = apple_way
+						if scale_component_offset:
+							# the Apple way: first move, then scale (ie. scale the component offset)
+							coordinates.translate(move)
+							coordinates.transform(compo.transform)
+						else:
+							# the MS way: first scale, then move
+							coordinates.transform(compo.transform)
+							coordinates.translate(move)
 				offset = len(allCoords)
 				allEndPts.extend(e + offset for e in endPts)
 				allCoords.extend(coordinates)
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
index 5afc925..154669a 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -4,7 +4,7 @@
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
 from fontTools.misc.timeTools import timestampFromString, timestampToString, timestampNow
 from fontTools.misc.timeTools import epoch_diff as mac_epoch_diff # For backward compat
-from fontTools.misc.arrayTools import intRect
+from fontTools.misc.arrayTools import intRect, unionRect
 from . import DefaultTable
 import logging
 
@@ -34,14 +34,14 @@
 
 class table__h_e_a_d(DefaultTable.DefaultTable):
 
-	dependencies = ['maxp', 'loca', 'CFF ']
+	dependencies = ['maxp', 'loca', 'CFF ', 'CFF2']
 
 	def decompile(self, data, ttFont):
 		dummy, rest = sstruct.unpack2(headFormat, data, self)
 		if rest:
 			# this is quite illegal, but there seem to be fonts out there that do this
 			log.warning("extra bytes at the end of 'head' table")
-			assert rest == "\0\0"
+			assert rest == b"\0\0"
 
 		# For timestamp fields, ignore the top four bytes.  Some fonts have
 		# bogus values there.  Since till 2038 those bytes only can be zero,
@@ -65,6 +65,19 @@
 			if 'CFF ' in ttFont:
 				topDict = ttFont['CFF '].cff.topDictIndex[0]
 				self.xMin, self.yMin, self.xMax, self.yMax = intRect(topDict.FontBBox)
+			elif 'CFF2' in ttFont:
+				topDict = ttFont['CFF2'].cff.topDictIndex[0]
+				charStrings = topDict.CharStrings
+				fontBBox = None
+				for charString in charStrings.values():
+					bounds = charString.calcBounds(charStrings)
+					if bounds is not None:
+						if fontBBox is not None:
+							fontBBox = unionRect(fontBBox, bounds)
+						else:
+							fontBBox = bounds
+				if fontBBox is not None:
+					self.xMin, self.yMin, self.xMax, self.yMax = intRect(fontBBox)
 		if ttFont.recalcTimestamp:
 			self.modified = timestampNow()
 		data = sstruct.pack(headFormat, self)
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
index 0f5ec51..4d93b28 100644
--- a/Lib/fontTools/ttLib/tables/_h_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -33,7 +33,7 @@
 
 	# Note: Keep in sync with table__v_h_e_a
 
-	dependencies = ['hmtx', 'glyf', 'CFF ']
+	dependencies = ['hmtx', 'glyf', 'CFF ', 'CFF2']
 
 	# OpenType spec renamed these, add aliases for compatibility
 	@property
@@ -52,7 +52,7 @@
 		sstruct.unpack(hheaFormat, data, self)
 
 	def compile(self, ttFont):
-		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
+		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ') or ttFont.isLoaded('CFF2')):
 			self.recalc(ttFont)
 		self.tableVersion = fi2ve(self.tableVersion)
 		return sstruct.pack(hheaFormat, self)
@@ -74,8 +74,11 @@
 					# Calculate those.
 					g.recalcBounds(glyfTable)
 				boundsWidthDict[name] = g.xMax - g.xMin
-		elif 'CFF ' in ttFont:
-			topDict = ttFont['CFF '].cff.topDictIndex[0]
+		elif 'CFF ' in ttFont or 'CFF2' in ttFont:
+			if 'CFF ' in ttFont:
+				topDict = ttFont['CFF '].cff.topDictIndex[0]
+			else:
+				topDict = ttFont['CFF2'].cff.topDictIndex[0]
 			charStrings = topDict.CharStrings
 			for name in ttFont.getGlyphOrder():
 				cs = charStrings[name]
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
index 2fc4b01..55ba45a 100644
--- a/Lib/fontTools/ttLib/tables/_v_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -32,13 +32,13 @@
 
 	# Note: Keep in sync with table__h_h_e_a
 
-	dependencies = ['vmtx', 'glyf', 'CFF ']
+	dependencies = ['vmtx', 'glyf', 'CFF ', 'CFF2']
 
 	def decompile(self, data, ttFont):
 		sstruct.unpack(vheaFormat, data, self)
 
 	def compile(self, ttFont):
-		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
+		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ') or ttFont.isLoaded('CFF2')):
 			self.recalc(ttFont)
 		self.tableVersion = fi2ve(self.tableVersion)
 		return sstruct.pack(vheaFormat, self)
@@ -60,8 +60,11 @@
 					# Calculate those.
 					g.recalcBounds(glyfTable)
 				boundsHeightDict[name] = g.yMax - g.yMin
-		elif 'CFF ' in ttFont:
-			topDict = ttFont['CFF '].cff.topDictIndex[0]
+		elif 'CFF ' in ttFont or 'CFF2' in ttFont:
+			if 'CFF ' in ttFont:
+				topDict = ttFont['CFF '].cff.topDictIndex[0]
+			else:
+				topDict = ttFont['CFF2'].cff.topDictIndex[0]
 			charStrings = topDict.CharStrings
 			for name in ttFont.getGlyphOrder():
 				cs = charStrings[name]
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index fa00ca0..302c9b2 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -1415,6 +1415,7 @@
 			oldMarkCoverage.append(glyphName)
 			oldMarkRecords.append(markRecord)
 		else:
+			markRecord.Class -= oldClassCount
 			newMarkCoverage.append(glyphName)
 			newMarkRecords.append(markRecord)
 
diff --git a/Lib/fontTools/ufoLib/__init__.py b/Lib/fontTools/ufoLib/__init__.py
index fb09bf6..4b86437 100755
--- a/Lib/fontTools/ufoLib/__init__.py
+++ b/Lib/fontTools/ufoLib/__init__.py
@@ -2146,18 +2146,14 @@
 	"""
 	if attr in _ufo2To3FloatToInt:
 		try:
-			v = int(round(value))
+			value = round(value)
 		except (ValueError, TypeError):
 			raise UFOLibError("Could not convert value for %s." % attr)
-		if v != value:
-			value = v
 	if attr in _ufo2To3NonNegativeInt:
 		try:
-			v = int(abs(value))
+			value = int(abs(value))
 		except (ValueError, TypeError):
 			raise UFOLibError("Could not convert value for %s." % attr)
-		if v != value:
-			value = v
 	elif attr in _ufo2To3NonNegativeIntOrFloat:
 		try:
 			v = float(abs(value))
diff --git a/Lib/fontTools/unicodedata/__init__.py b/Lib/fontTools/unicodedata/__init__.py
index 89528d7..8c2b58f 100644
--- a/Lib/fontTools/unicodedata/__init__.py
+++ b/Lib/fontTools/unicodedata/__init__.py
@@ -190,6 +190,14 @@
 
     # Unicode-9.0 additions
     'Adlm',  # Adlam
+
+    # Unicode-11.0 additions
+    'Rohg',  # Hanifi Rohingya
+    'Sogo',  # Old Sogdian
+    'Sogd',  # Sogdian
+
+    # Unicode-12.0 additions
+    'Elym',  # Elymaic
 }
 
 def script_horizontal_direction(script_code, default=KeyError):
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 1e8e986..6df39fa 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -39,13 +39,11 @@
 import logging
 from copy import deepcopy
 from pprint import pformat
+from .errors import VarLibError, VarLibValidationError
 
 log = logging.getLogger("fontTools.varLib")
 
 
-class VarLibError(Exception):
-	pass
-
 #
 # Creation routines
 #
@@ -81,7 +79,12 @@
 		coordinates = instance.location
 
 		if "en" not in instance.localisedStyleName:
-			assert instance.styleName
+			if not instance.styleName:
+				raise VarLibValidationError(
+					f"Instance at location '{coordinates}' must have a default English "
+					"style name ('stylename' attribute on the instance element or a "
+					"stylename element with an 'xml:lang=\"en\"' attribute)."
+				)
 			localisedStyleName = dict(instance.localisedStyleName)
 			localisedStyleName["en"] = tounicode(instance.styleName)
 		else:
@@ -137,20 +140,32 @@
 		# Current avar requirements.  We don't have to enforce
 		# these on the designer and can deduce some ourselves,
 		# but for now just enforce them.
-		assert axis.minimum == min(keys)
-		assert axis.maximum == max(keys)
-		assert axis.default in keys
-		# No duplicates
-		assert len(set(keys)) == len(keys), (
-			f"{axis.tag} axis: All axis mapping input='...' "
-			"values must be unique, but we found duplicates."
-		)
-		assert len(set(vals)) == len(vals), (
-			f"{axis.tag} axis: All axis mapping output='...' "
-			"values must be unique, but we found duplicates."
-		)
+		if axis.minimum != min(keys):
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': there must be a mapping for the axis minimum "
+				f"value {axis.minimum} and it must be the lowest input mapping value."
+			)
+		if axis.maximum != max(keys):
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': there must be a mapping for the axis maximum "
+				f"value {axis.maximum} and it must be the highest input mapping value."
+			)
+		if axis.default not in keys:
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': there must be a mapping for the axis default "
+				f"value {axis.default}."
+			)
+		# No duplicate input values (output values can be >= their preceeding value).
+		if len(set(keys)) != len(keys):
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': All axis mapping input='...' values must be "
+				"unique, but we found duplicates."
+			)
 		# Ascending values
-		assert sorted(vals) == vals
+		if sorted(vals) != vals:
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': mapping output values must be in ascending order."
+			)
 
 		keys_triple = (axis.minimum, axis.default, axis.maximum)
 		vals_triple = tuple(axis.map_forward(v) for v in keys_triple)
@@ -214,8 +229,8 @@
 
 
 def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
-
-	assert tolerance >= 0
+	if tolerance < 0:
+		raise ValueError("`tolerance` must be a positive number.")
 
 	log.info("Generating gvar")
 	assert "gvar" not in font
@@ -669,9 +684,11 @@
 
 
 def _add_CFF2(varFont, model, master_fonts):
-	from .cff import (convertCFFtoCFF2, merge_region_fonts)
+	from .cff import merge_region_fonts
 	glyphOrder = varFont.getGlyphOrder()
-	convertCFFtoCFF2(varFont)
+	if "CFF2" not in varFont:
+		from .cff import convertCFFtoCFF2
+		convertCFFtoCFF2(varFont)
 	ordered_fonts_list = model.reorderMasters(master_fonts, model.reverseMapping)
 	# re-ordering the master list simplifies building the CFF2 data item lists.
 	merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder)
@@ -687,9 +704,10 @@
 
 	masters = ds.sources
 	if not masters:
-		raise VarLibError("no sources found in .designspace")
+		raise VarLibValidationError("Designspace must have at least one source.")
 	instances = ds.instances
 
+	# TODO: Use fontTools.designspaceLib.tagForAxisName instead.
 	standard_axis_map = OrderedDict([
 		('weight',  ('wght', {'en': u'Weight'})),
 		('width',   ('wdth', {'en': u'Width'})),
@@ -699,11 +717,15 @@
 		])
 
 	# Setup axes
+	if not ds.axes:
+		raise VarLibValidationError(f"Designspace must have at least one axis.")
+
 	axes = OrderedDict()
-	for axis in ds.axes:
+	for axis_index, axis in enumerate(ds.axes):
 		axis_name = axis.name
 		if not axis_name:
-			assert axis.tag is not None
+			if not axis.tag:
+				raise VarLibValidationError(f"Axis at index {axis_index} needs a tag.")
 			axis_name = axis.name = axis.tag
 
 		if axis_name in standard_axis_map:
@@ -712,7 +734,8 @@
 			if not axis.labelNames:
 				axis.labelNames.update(standard_axis_map[axis_name][1])
 		else:
-			assert axis.tag is not None
+			if not axis.tag:
+				raise VarLibValidationError(f"Axis at index {axis_index} needs a tag.")
 			if not axis.labelNames:
 				axis.labelNames["en"] = tounicode(axis_name)
 
@@ -723,14 +746,28 @@
 	for obj in masters+instances:
 		obj_name = obj.name or obj.styleName or ''
 		loc = obj.location
+		if loc is None:
+			raise VarLibValidationError(
+				f"Source or instance '{obj_name}' has no location."
+			)
 		for axis_name in loc.keys():
-			assert axis_name in axes, "Location axis '%s' unknown for '%s'." % (axis_name, obj_name)
+			if axis_name not in axes:
+				raise VarLibValidationError(
+					f"Location axis '{axis_name}' unknown for '{obj_name}'."
+				)
 		for axis_name,axis in axes.items():
 			if axis_name not in loc:
-				loc[axis_name] = axis.default
+				# NOTE: `axis.default` is always user-space, but `obj.location` always design-space.
+				loc[axis_name] = axis.map_forward(axis.default)
 			else:
 				v = axis.map_backward(loc[axis_name])
-				assert axis.minimum <= v <= axis.maximum, "Location for axis '%s' (mapped to %s) out of range for '%s' [%s..%s]" % (axis_name, v, obj_name, axis.minimum, axis.maximum)
+				if not (axis.minimum <= v <= axis.maximum):
+					raise VarLibValidationError(
+						f"Source or instance '{obj_name}' has out-of-range location "
+						f"for axis '{axis_name}': is mapped to {v} but must be in "
+						f"mapped range [{axis.minimum}..{axis.maximum}] (NOTE: all "
+						"values are in user-space)."
+					)
 
 	# Normalize master locations
 
@@ -751,9 +788,15 @@
 	base_idx = None
 	for i,m in enumerate(normalized_master_locs):
 		if all(v == 0 for v in m.values()):
-			assert base_idx is None
+			if base_idx is not None:
+				raise VarLibValidationError(
+					"More than one base master found in Designspace."
+				)
 			base_idx = i
-	assert base_idx is not None, "Base master not found; no master at default location?"
+	if base_idx is None:
+		raise VarLibValidationError(
+			"Base master not found; no master at default location?"
+		)
 	log.info("Index of base master: %s", base_idx)
 
 	return _DesignSpaceData(
@@ -870,7 +913,7 @@
 		_merge_TTHinting(vf, model, master_fonts)
 	if 'GSUB' not in exclude and ds.rules:
 		_add_GSUB_feature_variations(vf, ds.axes, ds.internal_axis_supports, ds.rules, ds.rulesProcessingLast)
-	if 'CFF2' not in exclude and 'CFF ' in vf:
+	if 'CFF2' not in exclude and ('CFF ' in vf or 'CFF2' in vf):
 		_add_CFF2(vf, model, master_fonts)
 		if "post" in vf:
 			# set 'post' to format 2 to keep the glyph names dropped from CFF2
@@ -910,7 +953,7 @@
 	elif tp in ("TTF", "OTF", "WOFF", "WOFF2"):
 		font = TTFont(master_path)
 	else:
-		raise VarLibError("Invalid master path: %r" % master_path)
+		raise VarLibValidationError("Invalid master path: %r" % master_path)
 	return font
 
 
@@ -930,10 +973,10 @@
 		# If a SourceDescriptor has a layer name, demand that the compiled TTFont
 		# be supplied by the caller. This spares us from modifying MasterFinder.
 		if master.layerName and master.font is None:
-			raise AttributeError(
-				"Designspace source '%s' specified a layer name but lacks the "
-				"required TTFont object in the 'font' attribute."
-				% (master.name or "<Unknown>")
+			raise VarLibValidationError(
+				f"Designspace source '{master.name or '<Unknown>'}' specified a "
+				"layer name but lacks the required TTFont object in the 'font' "
+				"attribute."
 			)
 
 	return designspace.loadSourceFonts(_open_font, master_finder=master_finder)
diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py
index 967fde7..000e1b3 100644
--- a/Lib/fontTools/varLib/cff.py
+++ b/Lib/fontTools/varLib/cff.py
@@ -1,5 +1,4 @@
 from collections import namedtuple
-import os
 from fontTools.cffLib import (
 	maxStackLimit,
 	TopDictIndex,
@@ -21,6 +20,13 @@
 from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor
 from fontTools.pens.t2CharStringPen import T2CharStringPen, t2c_round
 
+from .errors import VarLibCFFDictMergeError, VarLibCFFPointTypeMergeError, VarLibMergeError
+
+
+# Backwards compatibility
+MergeDictError = VarLibCFFDictMergeError
+MergeTypeError = VarLibCFFPointTypeMergeError
+
 
 def addCFFVarStore(varFont, varModel, varDataList, masterSupports):
 	fvarTable = varFont['fvar']
@@ -30,6 +36,11 @@
 
 	topDict = varFont['CFF2'].cff.topDictIndex[0]
 	topDict.VarStore = VarStoreData(otVarStore=varStoreCFFV)
+	if topDict.FDArray[0].vstore is None:
+		fdArray = topDict.FDArray
+		for fontDict in fdArray:
+			if hasattr(fontDict, "Private"):
+				fontDict.Private.vstore = topDict.VarStore
 
 
 def lib_convertCFFToCFF2(cff, otFont):
@@ -121,16 +132,6 @@
 	del varFont['CFF ']
 
 
-class MergeDictError(TypeError):
-	def __init__(self, key, value, values):
-		error_msg = ["For the Private Dict key '{}', ".format(key),
-					 "the default font value list:",
-					 "\t{}".format(value),
-					 "had a different number of values than a region font:"]
-		error_msg += ["\t{}".format(region_value) for region_value in values]
-		error_msg = os.linesep.join(error_msg)
-
-
 def conv_to_int(num):
 	if isinstance(num, float) and num.is_integer():
 		return int(num)
@@ -214,7 +215,7 @@
 				try:
 					values = zip(*values)
 				except IndexError:
-					raise MergeDictError(key, value, values)
+					raise VarLibCFFDictMergeError(key, value, values)
 				"""
 				Row 0 contains the first  value from each master.
 				Convert each row from absolute values to relative
@@ -266,6 +267,12 @@
 			private_dict.rawDict[key] = dataList
 
 
+def _cff_or_cff2(font):
+	if "CFF " in font:
+		return font["CFF "]
+	return font["CFF2"]
+
+
 def getfd_map(varFont, fonts_list):
 	""" Since a subset source font may have fewer FontDicts in their
 	FDArray than the default font, we have to match up the FontDicts in
@@ -278,7 +285,7 @@
 	default_font = fonts_list[0]
 	region_fonts = fonts_list[1:]
 	num_regions = len(region_fonts)
-	topDict = default_font['CFF '].cff.topDictIndex[0]
+	topDict = _cff_or_cff2(default_font).cff.topDictIndex[0]
 	if not hasattr(topDict, 'FDSelect'):
 		# All glyphs reference only one FontDict.
 		# Map the FD index for regions to index 0.
@@ -294,7 +301,7 @@
 			fd_map[fdIndex] = {}
 	for ri, region_font in enumerate(region_fonts):
 		region_glyphOrder = region_font.getGlyphOrder()
-		region_topDict = region_font['CFF '].cff.topDictIndex[0]
+		region_topDict = _cff_or_cff2(region_font).cff.topDictIndex[0]
 		if not hasattr(region_topDict, 'FDSelect'):
 			# All the glyphs share the same FontDict. Pick any glyph.
 			default_fdIndex = gname_mapping[region_glyphOrder[0]]
@@ -313,7 +320,7 @@
 def merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder):
 	topDict = varFont['CFF2'].cff.topDictIndex[0]
 	top_dicts = [topDict] + [
-					ttFont['CFF '].cff.topDictIndex[0]
+					_cff_or_cff2(ttFont).cff.topDictIndex[0]
 					for ttFont in ordered_fonts_list[1:]
 					]
 	num_masters = len(model.mapping)
@@ -415,21 +422,6 @@
 	return cvData
 
 
-class MergeTypeError(TypeError):
-	def __init__(self, point_type, pt_index, m_index, default_type, glyphName):
-			self.error_msg = [
-						"In glyph '{gname}' "
-						"'{point_type}' at point index {pt_index} in master "
-						"index {m_index} differs from the default font point "
-						"type '{default_type}'"
-						"".format(
-								gname=glyphName,
-								point_type=point_type, pt_index=pt_index,
-								m_index=m_index, default_type=default_type)
-							][0]
-			super(MergeTypeError, self).__init__(self.error_msg)
-
-
 def makeRoundNumberFunc(tolerance):
 	if tolerance < 0:
 		raise ValueError("Rounding tolerance must be positive")
@@ -536,7 +528,7 @@
 		else:
 			cmd = self._commands[self.pt_index]
 			if cmd[0] != point_type:
-				raise MergeTypeError(
+				raise VarLibCFFPointTypeMergeError(
 									point_type,
 									self.pt_index, len(cmd[1]),
 									cmd[0], self.glyphName)
@@ -549,7 +541,7 @@
 		else:
 			cmd = self._commands[self.pt_index]
 			if cmd[0] != hint_type:
-				raise MergeTypeError(hint_type, self.pt_index, len(cmd[1]),
+				raise VarLibCFFPointTypeMergeError(hint_type, self.pt_index, len(cmd[1]),
 					cmd[0], self.glyphName)
 			cmd[1].append(args)
 		self.pt_index += 1
@@ -565,7 +557,7 @@
 		else:
 			cmd = self._commands[self.pt_index]
 			if cmd[0] != hint_type:
-				raise MergeTypeError(hint_type, self.pt_index, len(cmd[1]),
+				raise VarLibCFFPointTypeMergeError(hint_type, self.pt_index, len(cmd[1]),
 					cmd[0], self.glyphName)
 			self.pt_index += 1
 			cmd = self._commands[self.pt_index]
@@ -635,8 +627,8 @@
 			# second has only args.
 			if lastOp in ['hintmask', 'cntrmask']:
 				coord = list(cmd[1])
-				assert allEqual(coord), (
-					"hintmask values cannot differ between source fonts.")
+				if not allEqual(coord):
+					raise VarLibMergeError("Hintmask values cannot differ between source fonts.")
 				cmd[1] = [coord[0][0]]
 			else:
 				coords = cmd[1]
diff --git a/Lib/fontTools/varLib/errors.py b/Lib/fontTools/varLib/errors.py
new file mode 100644
index 0000000..b73f188
--- /dev/null
+++ b/Lib/fontTools/varLib/errors.py
@@ -0,0 +1,39 @@
+class VarLibError(Exception):
+    """Base exception for the varLib module."""
+
+
+class VarLibValidationError(VarLibError):
+    """Raised when input data is invalid from varLib's point of view."""
+
+
+class VarLibMergeError(VarLibError):
+    """Raised when input data cannot be merged into a variable font."""
+
+
+class VarLibCFFDictMergeError(VarLibMergeError):
+    """Raised when a CFF PrivateDict cannot be merged."""
+
+    def __init__(self, key, value, values):
+        error_msg = (
+            f"For the Private Dict key '{key}', the default font value list:"
+            f"\n\t{value}\nhad a different number of values than a region font:"
+        )
+        for region_value in values:
+            error_msg += f"\n\t{region_value}"
+        self.args = (error_msg,)
+
+
+class VarLibCFFPointTypeMergeError(VarLibMergeError):
+    """Raised when a CFF glyph cannot be merged."""
+
+    def __init__(self, point_type, pt_index, m_index, default_type, glyph_name):
+        error_msg = (
+            f"Glyph '{glyph_name}': '{point_type}' at point index {pt_index} in "
+            f"master index {m_index} differs from the default font point type "
+            f"'{default_type}'"
+        )
+        self.args = (error_msg,)
+
+
+class VariationModelError(VarLibError):
+    """Raised when a variation model is faulty."""
diff --git a/Lib/fontTools/varLib/featureVars.py b/Lib/fontTools/varLib/featureVars.py
index 287a885..dab9a0b 100644
--- a/Lib/fontTools/varLib/featureVars.py
+++ b/Lib/fontTools/varLib/featureVars.py
@@ -10,6 +10,8 @@
 from fontTools.otlLib.builder import buildLookup, buildSingleSubstSubtable
 from collections import OrderedDict
 
+from .errors import VarLibValidationError
+
 
 def addFeatureVariations(font, conditionalSubstitutions, featureTag='rvrn'):
     """Add conditional substitutions to a Variable Font.
@@ -312,7 +314,10 @@
     for conditionSet, substitutions in conditionalSubstitutions:
         conditionTable = []
         for axisTag, (minValue, maxValue) in sorted(conditionSet.items()):
-            assert minValue < maxValue
+            if minValue > maxValue:
+                raise VarLibValidationError(
+                    "A condition set has a minimum value above the maximum value."
+                )
             ct = buildConditionTable(axisIndices[axisTag], minValue, maxValue)
             conditionTable.append(ct)
 
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py
index 2ac64fd..3f04364 100644
--- a/Lib/fontTools/varLib/merger.py
+++ b/Lib/fontTools/varLib/merger.py
@@ -14,6 +14,8 @@
 from functools import reduce
 from fontTools.otlLib.builder import buildSinglePos
 
+from .errors import VarLibMergeError
+
 
 class Merger(object):
 
@@ -60,9 +62,14 @@
 		return _default
 
 	def mergeObjects(self, out, lst, exclude=()):
+		if hasattr(out, "ensureDecompiled"):
+			out.ensureDecompiled()
+		for item in lst:
+			if hasattr(item, "ensureDecompiled"):
+				item.ensureDecompiled()
 		keys = sorted(vars(out).keys())
-		assert all(keys == sorted(vars(v).keys()) for v in lst), \
-			(keys, [sorted(vars(v).keys()) for v in lst])
+		if not all(keys == sorted(vars(v).keys()) for v in lst):
+			raise VarLibMergeError((keys, [sorted(vars(v).keys()) for v in lst]))
 		mergers = self.mergersFor(out)
 		defaultMerger = mergers.get('*', self.__class__.mergeThings)
 		try:
@@ -77,7 +84,8 @@
 			raise
 
 	def mergeLists(self, out, lst):
-		assert allEqualTo(out, lst, len), (len(out), [len(v) for v in lst])
+		if not allEqualTo(out, lst, len):
+			raise VarLibMergeError((len(out), [len(v) for v in lst]))
 		for i,(value,values) in enumerate(zip(out, zip(*lst))):
 			try:
 				self.mergeThings(value, values)
@@ -87,7 +95,8 @@
 
 	def mergeThings(self, out, lst):
 		try:
-			assert allEqualTo(out, lst, type), (out, lst)
+			if not allEqualTo(out, lst, type):
+				raise VarLibMergeError((out, lst))
 			mergerFunc = self.mergersFor(out).get(None, None)
 			if mergerFunc is not None:
 				mergerFunc(self, out, lst)
@@ -96,7 +105,8 @@
 			elif isinstance(out, list):
 				self.mergeLists(out, lst)
 			else:
-				assert allEqualTo(out, lst), (out, lst)
+				if not allEqualTo(out, lst):
+					raise VarLibMergeError((out, lst))
 		except Exception as e:
 			e.args = e.args + (type(out).__name__,)
 			raise
@@ -117,7 +127,8 @@
 @AligningMerger.merger(ot.GDEF, "GlyphClassDef")
 def merge(merger, self, lst):
 	if self is None:
-		assert allNone(lst), (lst)
+		if not allNone(lst):
+			raise VarLibMergeError(lst)
 		return
 
 	lst = [l.classDefs for l in lst]
@@ -129,7 +140,8 @@
 	allKeys.update(*[l.keys() for l in lst])
 	for k in allKeys:
 		allValues = nonNone(l.get(k) for l in lst)
-		assert allEqual(allValues), allValues
+		if not allEqual(allValues):
+			raise VarLibMergeError(allValues)
 		if not allValues:
 			self[k] = None
 		else:
@@ -165,7 +177,8 @@
 	sortKey = font.getReverseGlyphMap().__getitem__
 	order = sorted(combined, key=sortKey)
 	# Make sure all input glyphsets were in proper order
-	assert all(sorted(vs, key=sortKey) == vs for vs in lst), "glyph orders are not consistent across masters"
+	if not all(sorted(vs, key=sortKey) == vs for vs in lst):
+		raise VarLibMergeError("Glyph order inconsistent across masters.")
 	del combined
 
 	paddedValues = None
@@ -192,7 +205,10 @@
 		elif self.Format == 2:
 			return self.Value[self.Coverage.glyphs.index(glyph)]
 		else:
-			assert 0
+			raise VarLibMergeError(
+				"Cannot retrieve effective value for SinglePos lookup, unsupported "
+				f"format {self.Format}."
+			)
 	return None
 
 def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph):
@@ -214,13 +230,17 @@
 			klass2 = self.ClassDef2.classDefs.get(secondGlyph, 0)
 			return self.Class1Record[klass1].Class2Record[klass2]
 		else:
-			assert 0
+			raise VarLibMergeError(
+				"Cannot retrieve effective value pair for PairPos lookup, unsupported "
+				f"format {self.Format}."
+			)
 	return None
 
 @AligningMerger.merger(ot.SinglePos)
 def merge(merger, self, lst):
 	self.ValueFormat = valueFormat = reduce(int.__or__, [l.ValueFormat for l in lst], 0)
-	assert len(lst) == 1 or (valueFormat & ~0xF == 0), valueFormat
+	if not (len(lst) == 1 or (valueFormat & ~0xF == 0)):
+		raise VarLibMergeError(f"SinglePos format {valueFormat} is unsupported.")
 
 	# If all have same coverage table and all are format 1,
 	coverageGlyphs = self.Coverage.glyphs
@@ -506,7 +526,9 @@
 	elif self.Format == 2:
 		_PairPosFormat2_merge(self, lst, merger)
 	else:
-		assert False
+		raise VarLibMergeError(
+			f"Cannot merge PairPos lookup, unsupported format {self.Format}."
+		)
 
 	del merger.valueFormat1, merger.valueFormat2
 
@@ -571,7 +593,8 @@
 		# failures in that case will probably signify mistakes in the
 		# input masters.
 
-		assert allEqual(allClasses), allClasses
+		if not allEqual(allClasses):
+			raise VarLibMergeError(allClasses)
 		if not allClasses:
 			rec = None
 		else:
@@ -620,19 +643,31 @@
 
 @AligningMerger.merger(ot.MarkBasePos)
 def merge(merger, self, lst):
-	assert allEqualTo(self.Format, (l.Format for l in lst))
+	if not allEqualTo(self.Format, (l.Format for l in lst)):
+		raise VarLibMergeError(
+			f"MarkBasePos formats inconsistent across masters, "
+			f"expected {self.Format} but got {[l.Format for l in lst]}."
+		)
 	if self.Format == 1:
 		_MarkBasePosFormat1_merge(self, lst, merger)
 	else:
-		assert False
+		raise VarLibMergeError(
+			f"Cannot merge MarkBasePos lookup, unsupported format {self.Format}."
+		)
 
 @AligningMerger.merger(ot.MarkMarkPos)
 def merge(merger, self, lst):
-	assert allEqualTo(self.Format, (l.Format for l in lst))
+	if not allEqualTo(self.Format, (l.Format for l in lst)):
+		raise VarLibMergeError(
+			f"MarkMarkPos formats inconsistent across masters, "
+			f"expected {self.Format} but got {[l.Format for l in lst]}."
+		)
 	if self.Format == 1:
 		_MarkBasePosFormat1_merge(self, lst, merger, 'Mark1', 'Mark2')
 	else:
-		assert False
+		raise VarLibMergeError(
+			f"Cannot merge MarkMarkPos lookup, unsupported format {self.Format}."
+		)
 
 
 def _PairSet_flatten(lst, font):
@@ -761,8 +796,16 @@
 		if not sts:
 			continue
 		if sts[0].__class__.__name__.startswith('Extension'):
-			assert allEqual([st.__class__ for st in sts])
-			assert allEqual([st.ExtensionLookupType for st in sts])
+			if not allEqual([st.__class__ for st in sts]):
+				raise VarLibMergeError(
+					"Use of extensions inconsistent between masters: "
+					f"{[st.__class__.__name__ for st in sts]}."
+				)
+			if not allEqual([st.ExtensionLookupType for st in sts]):
+				raise VarLibMergeError(
+					"Extension lookup type differs between masters: "
+					f"{[st.ExtensionLookupType for st in sts]}."
+				)
 			l.LookupType = sts[0].ExtensionLookupType
 			new_sts = [st.ExtSubTable for st in sts]
 			del sts[:]
@@ -990,7 +1033,8 @@
 		masterModel = None
 		if None in lst:
 			if allNone(lst):
-				assert out is None, (out, lst)
+				if out is not None:
+					raise VarLibMergeError((out, lst))
 				return
 			masterModel = self.model
 			model, lst = masterModel.getSubModel(lst)
@@ -1010,7 +1054,8 @@
 
 @VariationMerger.merger(ot.CaretValue)
 def merge(merger, self, lst):
-	assert self.Format == 1
+	if self.Format != 1:
+		raise VarLibMergeError(f"CaretValue format {self.Format} unsupported.")
 	self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
 	if DeviceTable:
 		self.Format = 3
@@ -1018,7 +1063,8 @@
 
 @VariationMerger.merger(ot.Anchor)
 def merge(merger, self, lst):
-	assert self.Format == 1
+	if self.Format != 1:
+		raise VarLibMergeError(f"Anchor format {self.Format} unsupported.")
 	self.XCoordinate, XDeviceTable = buildVarDevTable(merger.store_builder, [a.XCoordinate for a in lst])
 	self.YCoordinate, YDeviceTable = buildVarDevTable(merger.store_builder, [a.YCoordinate for a in lst])
 	if XDeviceTable or YDeviceTable:
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index 2ffe33e..d6837ee 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -5,6 +5,8 @@
 	   'supportScalar',
 	   'VariationModel']
 
+from .errors import VariationModelError
+
 
 def nonNone(lst):
 	return [l for l in lst if l is not None]
@@ -43,7 +45,11 @@
 	0.5
 	"""
 	lower, default, upper = triple
-	assert lower <= default <= upper, "invalid axis values: %3.3f, %3.3f %3.3f"%(lower, default, upper)
+	if not (lower <= default <= upper):
+		raise ValueError(
+			f"Invalid axis values, must be minimum, default, maximum: "
+			f"{lower:3.3f}, {default:3.3f}, {upper:3.3f}"
+		)
 	v = max(min(v, upper), lower)
 	if v == default:
 		v = 0.
@@ -192,7 +198,7 @@
 
 	def __init__(self, locations, axisOrder=None):
 		if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
-			raise ValueError("locations must be unique")
+			raise VariationModelError("Locations must be unique.")
 
 		self.origLocations = locations
 		self.axisOrder = axisOrder if axisOrder is not None else []
@@ -220,7 +226,8 @@
 
 	@staticmethod
 	def getMasterLocationsSortKeyFunc(locations, axisOrder=[]):
-		assert {} in locations, "Base master not found."
+		if {} not in locations:
+			raise VariationModelError("Base master not found.")
 		axisPoints = {}
 		for loc in locations:
 			if len(loc) != 1:
diff --git a/NEWS.rst b/NEWS.rst
index 1a0698a..6cca080 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,63 @@
+4.4.0 (released 2020-02-18)
+---------------------------
+
+- [colorLib] Added ``fontTools.colorLib.builder`` module, initially with ``buildCOLR``
+  and ``buildCPAL`` public functions. More color font formats will follow (#1827).
+- [fontBuilder] Added ``setupCOLR`` and ``setupCPAL`` methods (#1826).
+- [ttGlyphPen] Quantize ``GlyphComponent.transform`` floats to ``F2Dot14`` to fix
+  round-trip issue when computing bounding boxes of transformed components (#1830).
+- [glyf] If a component uses reference points (``firstPt`` and ``secondPt``) for
+  alignment (instead of X and Y offsets), compute the effective translation offset
+  *after* having applied any transform (#1831).
+- [glyf] When all glyphs have zero contours, compile ``glyf`` table data as a single
+  null byte in order to pass validation by OTS and Windows (#1829).
+- [feaLib] Parsing feature code now ensures that referenced glyph names are part of
+  the known glyph set, unless a glyph set was not provided.
+- [varLib] When filling in the default axis value for a missing location of a source or
+  instance, correctly map the value forward.
+- [varLib] The avar table can now contain mapping output values that are greater than
+  OR EQUAL to the preceeding value, as the avar specification allows this.
+- [varLib] The errors of the module are now ordered hierarchically below VarLibError. 
+  See #1821.
+
+4.3.0 (released 2020-02-03)
+---------------------------
+
+- [EBLC/CBLC] Fixed incorrect padding length calculation for Format 3 IndexSubTable
+  (#1817, #1818).
+- [varLib] Fixed error when merging OTL tables and TTFonts were loaded as ``lazy=True``
+  (#1808, #1809).
+- [varLib] Allow to use master fonts containing ``CFF2`` table when building VF (#1816).
+- [ttLib] Make ``recalcBBoxes`` option work also with ``CFF2`` table (#1816).
+- [feaLib] Don't reset ``lookupflag`` in lookups defined inside feature blocks.
+  They will now inherit the current ``lookupflag`` of the feature. This is what
+  Adobe ``makeotf`` also does in this case (#1815).
+- [feaLib] Fixed bug with mixed single/multiple substitutions. If a single substitution
+  involved a glyph class, we were incorrectly using only the first glyph in the class
+  (#1814).
+
+4.2.5 (released 2020-01-29)
+---------------------------
+
+- [feaLib] Do not fail on duplicate multiple substitutions, only warn (#1811).
+- [subset] Optimize SinglePos subtables to Format 1 if all ValueRecords are the same
+  (#1802).
+
+4.2.4 (released 2020-01-09)
+---------------------------
+
+- [unicodedata] Update RTL_SCRIPTS for Unicode 11 and 12.
+
+4.2.3 (released 2020-01-07)
+---------------------------
+
+- [otTables] Fixed bug when splitting `MarkBasePos` subtables as offsets overflow.
+  The mark class values in the split subtable were not being updated, leading to
+  invalid mark-base attachments (#1797, googlefonts/noto-source#145).
+- [feaLib] Only log a warning instead of error when features contain duplicate
+  substitutions (#1767).
+- [glifLib] Strip XML comments when parsing with lxml (#1784, #1785).
+
 4.2.2 (released 2019-12-12)
 ---------------------------
 
diff --git a/Tests/colorLib/__init__.py b/Tests/colorLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/colorLib/__init__.py
diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py
new file mode 100644
index 0000000..c517187
--- /dev/null
+++ b/Tests/colorLib/builder_test.py
@@ -0,0 +1,187 @@
+from fontTools.ttLib import newTable
+from fontTools.colorLib import builder
+from fontTools.colorLib.errors import ColorLibError
+import pytest
+
+
+def test_buildCOLR_v0():
+    color_layer_lists = {
+        "a": [("a.color0", 0), ("a.color1", 1)],
+        "b": [("b.color1", 1), ("b.color0", 0)],
+    }
+
+    colr = builder.buildCOLR(color_layer_lists)
+
+    assert colr.tableTag == "COLR"
+    assert colr.version == 0
+    assert colr.ColorLayers["a"][0].name == "a.color0"
+    assert colr.ColorLayers["a"][0].colorID == 0
+    assert colr.ColorLayers["a"][1].name == "a.color1"
+    assert colr.ColorLayers["a"][1].colorID == 1
+    assert colr.ColorLayers["b"][0].name == "b.color1"
+    assert colr.ColorLayers["b"][0].colorID == 1
+    assert colr.ColorLayers["b"][1].name == "b.color0"
+    assert colr.ColorLayers["b"][1].colorID == 0
+
+
+def test_buildCPAL_v0():
+    palettes = [
+        [(0.68, 0.20, 0.32, 1.0), (0.45, 0.68, 0.21, 1.0)],
+        [(0.68, 0.20, 0.32, 0.6), (0.45, 0.68, 0.21, 0.6)],
+        [(0.68, 0.20, 0.32, 0.3), (0.45, 0.68, 0.21, 0.3)],
+    ]
+
+    cpal = builder.buildCPAL(palettes)
+
+    assert cpal.tableTag == "CPAL"
+    assert cpal.version == 0
+    assert cpal.numPaletteEntries == 2
+
+    assert len(cpal.palettes) == 3
+    assert [tuple(c) for c in cpal.palettes[0]] == [
+        (82, 51, 173, 255),
+        (54, 173, 115, 255),
+    ]
+    assert [tuple(c) for c in cpal.palettes[1]] == [
+        (82, 51, 173, 153),
+        (54, 173, 115, 153),
+    ]
+    assert [tuple(c) for c in cpal.palettes[2]] == [
+        (82, 51, 173, 76),
+        (54, 173, 115, 76),
+    ]
+
+
+def test_buildCPAL_palettes_different_lengths():
+    with pytest.raises(ColorLibError, match="have different lengths"):
+        builder.buildCPAL([[(1, 1, 1, 1)], [(0, 0, 0, 1), (0.5, 0.5, 0.5, 1)]])
+
+
+def test_buildPaletteLabels():
+    name_table = newTable("name")
+    name_table.names = []
+
+    name_ids = builder.buildPaletteLabels(
+        [None, "hi", {"en": "hello", "de": "hallo"}], name_table
+    )
+
+    assert name_ids == [0xFFFF, 256, 257]
+
+    assert len(name_table.names) == 3
+    assert str(name_table.names[0]) == "hi"
+    assert name_table.names[0].nameID == 256
+
+    assert str(name_table.names[1]) == "hallo"
+    assert name_table.names[1].nameID == 257
+
+    assert str(name_table.names[2]) == "hello"
+    assert name_table.names[2].nameID == 257
+
+
+def test_build_CPAL_v1_types_no_labels():
+    palettes = [
+        [(0.1, 0.2, 0.3, 1.0), (0.4, 0.5, 0.6, 1.0)],
+        [(0.1, 0.2, 0.3, 0.6), (0.4, 0.5, 0.6, 0.6)],
+        [(0.1, 0.2, 0.3, 0.3), (0.4, 0.5, 0.6, 0.3)],
+    ]
+    paletteTypes = [
+        builder.ColorPaletteType.USABLE_WITH_LIGHT_BACKGROUND,
+        builder.ColorPaletteType.USABLE_WITH_DARK_BACKGROUND,
+        builder.ColorPaletteType.USABLE_WITH_LIGHT_BACKGROUND
+        | builder.ColorPaletteType.USABLE_WITH_DARK_BACKGROUND,
+    ]
+
+    cpal = builder.buildCPAL(palettes, paletteTypes=paletteTypes)
+
+    assert cpal.tableTag == "CPAL"
+    assert cpal.version == 1
+    assert cpal.numPaletteEntries == 2
+    assert len(cpal.palettes) == 3
+
+    assert cpal.paletteTypes == paletteTypes
+    assert cpal.paletteLabels == [cpal.NO_NAME_ID] * len(palettes)
+    assert cpal.paletteEntryLabels == [cpal.NO_NAME_ID] * cpal.numPaletteEntries
+
+
+def test_build_CPAL_v1_labels():
+    palettes = [
+        [(0.1, 0.2, 0.3, 1.0), (0.4, 0.5, 0.6, 1.0)],
+        [(0.1, 0.2, 0.3, 0.6), (0.4, 0.5, 0.6, 0.6)],
+        [(0.1, 0.2, 0.3, 0.3), (0.4, 0.5, 0.6, 0.3)],
+    ]
+    paletteLabels = ["First", {"en": "Second", "it": "Seconda"}, None]
+    paletteEntryLabels = ["Foo", "Bar"]
+
+    with pytest.raises(TypeError, match="nameTable is required"):
+        builder.buildCPAL(palettes, paletteLabels=paletteLabels)
+    with pytest.raises(TypeError, match="nameTable is required"):
+        builder.buildCPAL(palettes, paletteEntryLabels=paletteEntryLabels)
+
+    name_table = newTable("name")
+    name_table.names = []
+
+    cpal = builder.buildCPAL(
+        palettes,
+        paletteLabels=paletteLabels,
+        paletteEntryLabels=paletteEntryLabels,
+        nameTable=name_table,
+    )
+
+    assert cpal.tableTag == "CPAL"
+    assert cpal.version == 1
+    assert cpal.numPaletteEntries == 2
+    assert len(cpal.palettes) == 3
+
+    assert cpal.paletteTypes == [cpal.DEFAULT_PALETTE_TYPE] * len(palettes)
+    assert cpal.paletteLabels == [256, 257, cpal.NO_NAME_ID]
+    assert cpal.paletteEntryLabels == [258, 259]
+
+    assert name_table.getDebugName(256) == "First"
+    assert name_table.getDebugName(257) == "Second"
+    assert name_table.getDebugName(258) == "Foo"
+    assert name_table.getDebugName(259) == "Bar"
+
+
+def test_invalid_ColorPaletteType():
+    with pytest.raises(ValueError, match="not a valid ColorPaletteType"):
+        builder.ColorPaletteType(-1)
+    with pytest.raises(ValueError, match="not a valid ColorPaletteType"):
+        builder.ColorPaletteType(4)
+    with pytest.raises(ValueError, match="not a valid ColorPaletteType"):
+        builder.ColorPaletteType("abc")
+
+
+def test_buildCPAL_v1_invalid_args_length():
+    with pytest.raises(ColorLibError, match="Expected 2 paletteTypes, got 1"):
+        builder.buildCPAL([[(0, 0, 0, 0)], [(1, 1, 1, 1)]], paletteTypes=[1])
+
+    with pytest.raises(ColorLibError, match="Expected 2 paletteLabels, got 1"):
+        builder.buildCPAL(
+            [[(0, 0, 0, 0)], [(1, 1, 1, 1)]],
+            paletteLabels=["foo"],
+            nameTable=newTable("name"),
+        )
+
+    with pytest.raises(ColorLibError, match="Expected 1 paletteEntryLabels, got 0"):
+        cpal = builder.buildCPAL(
+            [[(0, 0, 0, 0)], [(1, 1, 1, 1)]],
+            paletteEntryLabels=[],
+            nameTable=newTable("name"),
+        )
+
+
+def test_buildCPAL_invalid_color():
+    with pytest.raises(
+        ColorLibError,
+        match=r"In palette\[0\]\[1\]: expected \(R, G, B, A\) tuple, got \(1, 1, 1\)",
+    ):
+        builder.buildCPAL([[(1, 1, 1, 1), (1, 1, 1)]])
+
+    with pytest.raises(
+        ColorLibError,
+        match=(
+            r"palette\[1\]\[0\] has invalid out-of-range "
+            r"\[0..1\] color: \(1, 1, -1, 2\)"
+        ),
+    ):
+        builder.buildCPAL([[(0, 0, 0, 0)], [(1, 1, -1, 2)]])
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
index 329b3fd..0400210 100644
--- a/Tests/feaLib/builder_test.py
+++ b/Tests/feaLib/builder_test.py
@@ -41,7 +41,7 @@
         a_n_d T_h T_h.swash germandbls ydieresis yacute breve
         grave acute dieresis macron circumflex cedilla umlaut ogonek caron
         damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial
-        by feature lookup sub table
+        by feature lookup sub table uni0327 uni0328 e.fina
     """.split()
     font = TTFont()
     font.setGlyphOrder(glyphs)
@@ -206,9 +206,20 @@
             "feature test {"
             "    sub f_f_i by f f i;"
             "    sub c_t by c t;"
-            "    sub f_f_i by f f i;"
+            "    sub f_f_i by f_f i;"
             "} test;")
 
+    def test_multipleSubst_multipleIdenticalSubstitutionsForSameGlyph_info(self):
+        logger = logging.getLogger("fontTools.feaLib.builder")
+        with CapturingLogHandler(logger, "INFO") as captor:
+            self.build(
+                "feature test {"
+                "    sub f_f_i by f f i;"
+                "    sub c_t by c t;"
+                "    sub f_f_i by f f i;"
+                "} test;")
+        captor.assertRegex(r"Removing duplicate multiple substitution from glyph \"f_f_i\" to \('f', 'f', 'i'\)")
+
     def test_pairPos_redefinition_warning(self):
         # https://github.com/fonttools/fonttools/issues/1147
         logger = logging.getLogger("fontTools.feaLib.builder")
diff --git a/Tests/feaLib/data/lookupflag.fea b/Tests/feaLib/data/lookupflag.fea
index 651dcd0..ced046b 100644
--- a/Tests/feaLib/data/lookupflag.fea
+++ b/Tests/feaLib/data/lookupflag.fea
@@ -95,3 +95,42 @@
     lookup M;
     lookup N;
 } test;
+
+feature test {
+    lookupflag IgnoreMarks;
+    lookup O {
+        pos one 1;
+    } O;
+    lookup P {
+       pos one 1;
+    } P;
+} test;
+
+feature test {
+    lookup Q {
+         pos one 1;
+    } Q;
+    lookup R {
+         pos one 1;
+    } R;
+} test;
+
+feature test {
+    lookup S {
+        lookupflag IgnoreMarks;
+        pos one 1;
+    } S;
+    lookup T {
+        pos one 1;
+    } T;
+} test;
+
+feature test {
+    lookup U {
+        pos one 1;
+    } U;
+    lookup V {
+        lookupflag IgnoreMarks;
+        pos one 1;
+    } V;
+} test;
diff --git a/Tests/feaLib/data/lookupflag.ttx b/Tests/feaLib/data/lookupflag.ttx
index bb05b9a..52a09b8 100644
--- a/Tests/feaLib/data/lookupflag.ttx
+++ b/Tests/feaLib/data/lookupflag.ttx
@@ -61,7 +61,7 @@
       <FeatureRecord index="0">
         <FeatureTag value="test"/>
         <Feature>
-          <!-- LookupCount=14 -->
+          <!-- LookupCount=22 -->
           <LookupListIndex index="0" value="0"/>
           <LookupListIndex index="1" value="1"/>
           <LookupListIndex index="2" value="2"/>
@@ -76,11 +76,19 @@
           <LookupListIndex index="11" value="11"/>
           <LookupListIndex index="12" value="12"/>
           <LookupListIndex index="13" value="13"/>
+          <LookupListIndex index="14" value="14"/>
+          <LookupListIndex index="15" value="15"/>
+          <LookupListIndex index="16" value="16"/>
+          <LookupListIndex index="17" value="17"/>
+          <LookupListIndex index="18" value="18"/>
+          <LookupListIndex index="19" value="19"/>
+          <LookupListIndex index="20" value="20"/>
+          <LookupListIndex index="21" value="21"/>
         </Feature>
       </FeatureRecord>
     </FeatureList>
     <LookupList>
-      <!-- LookupCount=14 -->
+      <!-- LookupCount=22 -->
       <Lookup index="0">
         <LookupType value="1"/>
         <LookupFlag value="1"/>
@@ -253,6 +261,102 @@
           <Value XAdvance="1"/>
         </SinglePos>
       </Lookup>
+      <Lookup index="14">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="15">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="16">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="17">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="18">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="19">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="20">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="21">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
     </LookupList>
   </GPOS>
 
diff --git a/Tests/feaLib/lexer_test.py b/Tests/feaLib/lexer_test.py
index f8f8e75..fa3f0ea 100644
--- a/Tests/feaLib/lexer_test.py
+++ b/Tests/feaLib/lexer_test.py
@@ -223,7 +223,7 @@
             # an in-memory stream, so it will use the current working
             # directory to resolve relative include statements
             lexer = IncludingLexer(UnicodeIO("include(included.fea);"))
-            files = set(loc[0] for _, _, loc in lexer)
+            files = set(os.path.realpath(loc[0]) for _, _, loc in lexer)
             expected = os.path.realpath(included.name)
             self.assertIn(expected, files)
         finally:
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
index a5b9f69..cb4e689 100644
--- a/Tests/feaLib/parser_test.py
+++ b/Tests/feaLib/parser_test.py
@@ -39,6 +39,14 @@
     n.sc o.sc p.sc q.sc r.sc s.sc t.sc u.sc v.sc w.sc x.sc y.sc z.sc
     a.swash b.swash x.swash y.swash z.swash
     foobar foo.09 foo.1234 foo.9876
+    one two five six acute grave dieresis umlaut cedilla ogonek macron
+    a_f_f_i o_f_f_i f_i f_f_i one.fitted one.oldstyle a.1 a.2 a.3 c_t
+    PRE SUF FIX BACK TRACK LOOK AHEAD ampersand ampersand.1 ampersand.2
+    cid00001 cid00002 cid00003 cid00004 cid00005 cid00006 cid00007
+    cid12345 cid78987 cid00999 cid01000 cid01001 cid00998 cid00995
+    cid00111 cid00222
+    comma endash emdash figuredash damma hamza
+    c_d d.alt n.end s.end f_f
 """).split() + ["foo.%d" % i for i in range(1, 200)]
 
 
@@ -260,6 +268,12 @@
             FeatureLibError, "Font revision numbers must be positive",
             self.parse, "table head {FontRevision -17.2;} head;")
 
+    def test_strict_glyph_name_check(self):
+        self.parse("@bad = [a b ccc];", glyphNames=("a", "b", "ccc"))
+
+        with self.assertRaisesRegex(FeatureLibError, "missing from the glyph set: ccc"):
+            self.parse("@bad = [a b ccc];", glyphNames=("a", "b"))
+
     def test_glyphclass(self):
         [gc] = self.parse("@dash = [endash emdash figuredash];").statements
         self.assertEqual(gc.name, "dash")
@@ -1396,12 +1410,22 @@
                          "  sub f_f   by f f;"
                          "  sub f     by f;"
                          "  sub f_f_i by f f i;"
+                         "  sub [a a.sc] by a;"
+                         "  sub [a a.sc] by [b b.sc];"
                          "} Look;")
         statements = doc.statements[0].statements
         for sub in statements:
             self.assertIsInstance(sub, ast.MultipleSubstStatement)
         self.assertEqual(statements[1].glyph, "f")
         self.assertEqual(statements[1].replacement, ["f"])
+        self.assertEqual(statements[3].glyph, "a")
+        self.assertEqual(statements[3].replacement, ["a"])
+        self.assertEqual(statements[4].glyph, "a.sc")
+        self.assertEqual(statements[4].replacement, ["a"])
+        self.assertEqual(statements[5].glyph, "a")
+        self.assertEqual(statements[5].replacement, ["b"])
+        self.assertEqual(statements[6].glyph, "a.sc")
+        self.assertEqual(statements[6].replacement, ["b.sc"])
 
     def test_substitute_from(self):  # GSUB LookupType 3
         doc = self.parse("feature test {"
diff --git a/Tests/fontBuilder/data/test_var.otf.ttx b/Tests/fontBuilder/data/test_var.otf.ttx
index e00d33c..ccf64dc 100644
--- a/Tests/fontBuilder/data/test_var.otf.ttx
+++ b/Tests/fontBuilder/data/test_var.otf.ttx
@@ -19,10 +19,10 @@
     <unitsPerEm value="1000"/>
     <created value="Wed Mar 27 00:23:21 2019"/>
     <modified value="Wed Mar 27 00:23:21 2019"/>
-    <xMin value="0"/>
-    <yMin value="0"/>
-    <xMax value="0"/>
-    <yMax value="0"/>
+    <xMin value="100"/>
+    <yMin value="100"/>
+    <xMax value="600"/>
+    <yMax value="1000"/>
     <macStyle value="00000000 00000000"/>
     <lowestRecPPEM value="3"/>
     <fontDirectionHint value="2"/>
@@ -35,10 +35,10 @@
     <ascent value="824"/>
     <descent value="200"/>
     <lineGap value="0"/>
-    <advanceWidthMax value="0"/>
+    <advanceWidthMax value="600"/>
     <minLeftSideBearing value="0"/>
-    <minRightSideBearing value="0"/>
-    <xMaxExtent value="0"/>
+    <minRightSideBearing value="200"/>
+    <xMaxExtent value="400"/>
     <caretSlopeRise value="1"/>
     <caretSlopeRun value="0"/>
     <caretOffset value="0"/>
diff --git a/Tests/pens/ttGlyphPen_test.py b/Tests/pens/ttGlyphPen_test.py
index 961738d..f6ad848 100644
--- a/Tests/pens/ttGlyphPen_test.py
+++ b/Tests/pens/ttGlyphPen_test.py
@@ -264,6 +264,33 @@
         compositeGlyph.recalcBounds(glyphSet)
         self.assertGlyphBoundsEqual(compositeGlyph, (-86, 0, 282, 1))
 
+    def test_scaled_component_bounds(self):
+        glyphSet = {}
+
+        pen = TTGlyphPen(glyphSet)
+        pen.moveTo((-231, 939))
+        pen.lineTo((-55, 939))
+        pen.lineTo((-55, 745))
+        pen.lineTo((-231, 745))
+        pen.closePath()
+        glyphSet["gravecomb"] = gravecomb = pen.glyph()
+
+        pen = TTGlyphPen(glyphSet)
+        pen.moveTo((-278, 939))
+        pen.lineTo((8, 939))
+        pen.lineTo((8, 745))
+        pen.lineTo((-278, 745))
+        pen.closePath()
+        glyphSet["circumflexcomb"] = circumflexcomb = pen.glyph()
+
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("circumflexcomb", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("gravecomb", (0.9, 0, 0, 0.9, 198, 180))
+        glyphSet["uni0302_uni0300"] = uni0302_uni0300 = pen.glyph()
+
+        uni0302_uni0300.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(uni0302_uni0300, (-278, 745, 148, 1025))
+
 
 class _TestGlyph(object):
     def __init__(self, glyph):
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index 3d8c27e..14ca313 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -1,5 +1,6 @@
 import io
 from fontTools.misc.py23 import *
+from fontTools.misc.testTools import getXML
 from fontTools import subset
 from fontTools.fontBuilder import FontBuilder
 from fontTools.ttLib import TTFont, newTable
@@ -11,6 +12,8 @@
 import sys
 import tempfile
 import unittest
+import pathlib
+import pytest
 
 
 class SubsetTest(unittest.TestCase):
@@ -768,5 +771,106 @@
     assert "dollar.rvrn" in font.getGlyphOrder()
 
 
+def test_subset_single_pos_format():
+    fb = FontBuilder(unitsPerEm=1000)
+    fb.setupGlyphOrder([".notdef", "a", "b", "c"])
+    fb.setupCharacterMap({ord("a"): "a", ord("b"): "b", ord("c"): "c"})
+    fb.setupNameTable({"familyName": "TestSingePosFormat", "styleName": "Regular"})
+    fb.setupPost()
+    fb.addOpenTypeFeatures("""
+        feature kern {
+            pos a -50;
+            pos b -40;
+            pos c -50;
+        } kern;
+    """)
+
+    buf = io.BytesIO()
+    fb.save(buf)
+    buf.seek(0)
+
+    font = TTFont(buf)
+
+    # The input font has a SinglePos Format 2 subtable where each glyph has
+    # different ValueRecords
+    assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
+        '<Lookup>',
+        '  <LookupType value="1"/>',
+        '  <LookupFlag value="0"/>',
+        '  <!-- SubTableCount=1 -->',
+        '  <SinglePos index="0" Format="2">',
+        '    <Coverage Format="1">',
+        '      <Glyph value="a"/>',
+        '      <Glyph value="b"/>',
+        '      <Glyph value="c"/>',
+        '    </Coverage>',
+        '    <ValueFormat value="4"/>',
+        '    <!-- ValueCount=3 -->',
+        '    <Value index="0" XAdvance="-50"/>',
+        '    <Value index="1" XAdvance="-40"/>',
+        '    <Value index="2" XAdvance="-50"/>',
+        '  </SinglePos>',
+        '</Lookup>',
+    ]
+
+    options = subset.Options()
+    subsetter = subset.Subsetter(options)
+    subsetter.populate(unicodes=[ord("a"), ord("c")])
+    subsetter.subset(font)
+
+    # All the subsetted glyphs from the original SinglePos Format2 subtable
+    # now have the same ValueRecord, so we use a more compact Format 1 subtable.
+    assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
+        '<Lookup>',
+        '  <LookupType value="1"/>',
+        '  <LookupFlag value="0"/>',
+        '  <!-- SubTableCount=1 -->',
+        '  <SinglePos index="0" Format="1">',
+        '    <Coverage Format="1">',
+        '      <Glyph value="a"/>',
+        '      <Glyph value="c"/>',
+        '    </Coverage>',
+        '    <ValueFormat value="4"/>',
+        '    <Value XAdvance="-50"/>',
+        '  </SinglePos>',
+        '</Lookup>',
+    ]
+
+
+@pytest.fixture
+def ttf_path(tmp_path):
+    # $(dirname $0)/../ttLib/data
+    ttLib_data = pathlib.Path(__file__).parent.parent / "ttLib" / "data"
+    font = TTFont()
+    font.importXML(ttLib_data / "TestTTF-Regular.ttx")
+    font_path = tmp_path / "TestTTF-Regular.ttf"
+    font.save(font_path)
+    return font_path
+
+
+def test_subset_empty_glyf(tmp_path, ttf_path):
+    subset_path = tmp_path / (ttf_path.name + ".subset")
+    # only keep empty .notdef and space glyph, resulting in an empty glyf table
+    subset.main(
+        [
+            str(ttf_path),
+            "--no-notdef-outline",
+            "--glyph-names",
+            f"--output-file={subset_path}",
+            "--glyphs=.notdef space",
+        ]
+    )
+    subset_font = TTFont(subset_path)
+
+    assert subset_font.getGlyphOrder() == [".notdef", "space"]
+    assert subset_font.reader['glyf'] == b"\x00"
+
+    glyf = subset_font["glyf"]
+    assert all(glyf[g].numberOfContours == 0 for g in subset_font.getGlyphOrder())
+
+    loca = subset_font["loca"]
+    assert all(loc == 0 for loc in loca)
+
+
 if __name__ == "__main__":
     sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/C_B_L_C_test.py b/Tests/ttLib/tables/C_B_L_C_test.py
new file mode 100644
index 0000000..fed25f9
--- /dev/null
+++ b/Tests/ttLib/tables/C_B_L_C_test.py
@@ -0,0 +1,80 @@
+import base64
+import io
+import os
+
+from fontTools.misc.testTools import getXML
+from fontTools.ttLib import TTFont
+
+
+DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data")
+
+# This is a subset from NotoColorEmoji.ttf which contains an IndexTable format=3
+INDEX_FORMAT_3_TTX = os.path.join(DATA_DIR, "NotoColorEmoji.subset.index_format_3.ttx")
+# The CLBC table was compiled with Harfbuzz' hb-subset and contains the correct padding
+CBLC_INDEX_FORMAT_3 = base64.b64decode(
+    "AAMAAAAAAAEAAAA4AAAALAAAAAIAAAAAZeWIAAAAAAAAAAAAZeWIAAAAAAAAAAAAAAEAA"
+    "21tIAEAAQACAAAAEAADAAMAAAAgAAMAEQAAAAQAAAOmEQ0AAAADABEAABERAAAIUg=="
+)
+
+
+def test_compile_decompile_index_table_format_3():
+    font = TTFont()
+    font.importXML(INDEX_FORMAT_3_TTX)
+    buf = io.BytesIO()
+    font.save(buf)
+    buf.seek(0)
+    font = TTFont(buf)
+
+    assert font.reader["CBLC"] == CBLC_INDEX_FORMAT_3
+
+    assert getXML(font["CBLC"].toXML, font) == [
+        '<header version="3.0"/>',
+        '<strike index="0">',
+        "  <bitmapSizeTable>",
+        '    <sbitLineMetrics direction="hori">',
+        '      <ascender value="101"/>',
+        '      <descender value="-27"/>',
+        '      <widthMax value="136"/>',
+        '      <caretSlopeNumerator value="0"/>',
+        '      <caretSlopeDenominator value="0"/>',
+        '      <caretOffset value="0"/>',
+        '      <minOriginSB value="0"/>',
+        '      <minAdvanceSB value="0"/>',
+        '      <maxBeforeBL value="0"/>',
+        '      <minAfterBL value="0"/>',
+        '      <pad1 value="0"/>',
+        '      <pad2 value="0"/>',
+        "    </sbitLineMetrics>",
+        '    <sbitLineMetrics direction="vert">',
+        '      <ascender value="101"/>',
+        '      <descender value="-27"/>',
+        '      <widthMax value="136"/>',
+        '      <caretSlopeNumerator value="0"/>',
+        '      <caretSlopeDenominator value="0"/>',
+        '      <caretOffset value="0"/>',
+        '      <minOriginSB value="0"/>',
+        '      <minAdvanceSB value="0"/>',
+        '      <maxBeforeBL value="0"/>',
+        '      <minAfterBL value="0"/>',
+        '      <pad1 value="0"/>',
+        '      <pad2 value="0"/>',
+        "    </sbitLineMetrics>",
+        '    <colorRef value="0"/>',
+        '    <startGlyphIndex value="1"/>',
+        '    <endGlyphIndex value="3"/>',
+        '    <ppemX value="109"/>',
+        '    <ppemY value="109"/>',
+        '    <bitDepth value="32"/>',
+        '    <flags value="1"/>',
+        "  </bitmapSizeTable>",
+        "  <!-- GlyphIds are written but not read. The firstGlyphIndex and",
+        "       lastGlyphIndex values will be recalculated by the compiler. -->",
+        '  <eblc_index_sub_table_3 imageFormat="17" firstGlyphIndex="1" lastGlyphIndex="2">',
+        '    <glyphLoc id="1" name="eight"/>',
+        '    <glyphLoc id="2" name="registered"/>',
+        "  </eblc_index_sub_table_3>",
+        '  <eblc_index_sub_table_3 imageFormat="17" firstGlyphIndex="3" lastGlyphIndex="3">',
+        '    <glyphLoc id="3" name="uni2049"/>',
+        "  </eblc_index_sub_table_3>",
+        "</strike>",
+    ]
diff --git a/Tests/ttLib/tables/C_P_A_L_test.py b/Tests/ttLib/tables/C_P_A_L_test.py
index 6800987..b018a52 100644
--- a/Tests/ttLib/tables/C_P_A_L_test.py
+++ b/Tests/ttLib/tables/C_P_A_L_test.py
@@ -66,9 +66,6 @@
         self.assertEqual(cpal.numPaletteEntries, 2)
         self.assertEqual(repr(cpal.palettes),
                          '[[#000000FF, #66CCFFFF], [#000000FF, #800000FF]]')
-        self.assertEqual(cpal.paletteLabels, [0, 0])
-        self.assertEqual(cpal.paletteTypes, [0, 0])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 0])
 
     def test_decompile_v0_sharingColors(self):
         cpal = newTable('CPAL')
@@ -80,9 +77,6 @@
             '[#223344FF, #99887711, #55555555]',
             '[#223344FF, #99887711, #FFFFFFFF]',
             '[#223344FF, #99887711, #55555555]'])
-        self.assertEqual(cpal.paletteLabels, [0, 0, 0, 0])
-        self.assertEqual(cpal.paletteTypes, [0, 0, 0, 0])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 0, 0])
 
     def test_decompile_v1_noLabelsNoTypes(self):
         cpal = newTable('CPAL')
@@ -92,9 +86,10 @@
         self.assertEqual([repr(p) for p in cpal.palettes], [
             '[#CAFECAFE, #22110033, #66554477]',  # RGBA
             '[#59413127, #42424242, #13330037]'])
-        self.assertEqual(cpal.paletteLabels, [0, 0])
+        self.assertEqual(cpal.paletteLabels, [cpal.NO_NAME_ID] * len(cpal.palettes))
         self.assertEqual(cpal.paletteTypes, [0, 0])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 0, 0])
+        self.assertEqual(cpal.paletteEntryLabels,
+                        [cpal.NO_NAME_ID] * cpal.numPaletteEntries)
 
     def test_decompile_v1(self):
         cpal = newTable('CPAL')
@@ -194,9 +189,6 @@
         self.assertEqual(cpal.version, 0)
         self.assertEqual(cpal.numPaletteEntries, 2)
         self.assertEqual(repr(cpal.palettes), '[[#12345678, #FEDCBA98]]')
-        self.assertEqual(cpal.paletteLabels, [0])
-        self.assertEqual(cpal.paletteTypes, [0])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 0])
 
     def test_fromXML_v1(self):
         cpal = newTable('CPAL')
@@ -218,7 +210,8 @@
                          '[[#12345678, #FEDCBA98, #CAFECAFE]]')
         self.assertEqual(cpal.paletteLabels, [259])
         self.assertEqual(cpal.paletteTypes, [2])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 262, 0])
+        self.assertEqual(cpal.paletteEntryLabels,
+                        [cpal.NO_NAME_ID, 262, cpal.NO_NAME_ID])
 
 
 if __name__ == "__main__":
diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py
index 9e2b355..cec15cc 100644
--- a/Tests/ttLib/tables/_g_l_y_f_test.py
+++ b/Tests/ttLib/tables/_g_l_y_f_test.py
@@ -6,9 +6,12 @@
 from fontTools.pens.pointPen import PointToSegmentPen
 from fontTools.ttLib import TTFont, newTable, TTLibError
 from fontTools.ttLib.tables._g_l_y_f import (
+    Glyph,
     GlyphCoordinates,
     GlyphComponent,
     ARGS_ARE_XY_VALUES,
+    SCALED_COMPONENT_OFFSET,
+    UNSCALED_COMPONENT_OFFSET,
     WE_HAVE_A_SCALE,
     WE_HAVE_A_TWO_BY_TWO,
     WE_HAVE_AN_X_AND_Y_SCALE,
@@ -190,7 +193,7 @@
     return re.sub(' ttLibVersion=".*"', '', string)
 
 
-class glyfTableTest(unittest.TestCase):
+class GlyfTableTest(unittest.TestCase):
 
     def __init__(self, methodName):
         unittest.TestCase.__init__(self, methodName)
@@ -338,6 +341,136 @@
         glyfTable["glyph00003"].drawPoints(PointToSegmentPen(pen2), glyfTable)
         self.assertEqual(pen1.value, pen2.value)
 
+    def test_compile_empty_table(self):
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        font.importXML(GLYF_TTX)
+        glyfTable = font['glyf']
+        # set all glyphs to zero contours
+        glyfTable.glyphs = {glyphName: Glyph() for glyphName in font.getGlyphOrder()}
+        glyfData = glyfTable.compile(font)
+        self.assertEqual(glyfData, b"\x00")
+        self.assertEqual(list(font["loca"]), [0] * (font["maxp"].numGlyphs+1))
+
+    def test_decompile_empty_table(self):
+        font = TTFont()
+        glyphNames = [".notdef", "space"]
+        font.setGlyphOrder(glyphNames)
+        font["loca"] = newTable("loca")
+        font["loca"].locations = [0] * (len(glyphNames) + 1)
+        font["glyf"] = newTable("glyf")
+        font["glyf"].decompile(b"\x00", font)
+        self.assertEqual(len(font["glyf"]), 2)
+        self.assertEqual(font["glyf"][".notdef"].numberOfContours, 0)
+        self.assertEqual(font["glyf"]["space"].numberOfContours, 0)
+
+
+class GlyphTest:
+
+    def test_getCoordinates(self):
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+        pen.moveTo((0, 0))
+        pen.lineTo((100, 0))
+        pen.lineTo((100, 100))
+        pen.lineTo((0, 100))
+        pen.closePath()
+        # simple contour glyph
+        glyphSet["a"] = a = pen.glyph()
+
+        assert a.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(0, 0), (100, 0), (100, 100), (0, 100)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with only XY offset
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (1, 0, 0, 1, 10, 20))
+        glyphSet["b"] = b = pen.glyph()
+
+        assert b.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(10, 20), (110, 20), (110, 120), (10, 120)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with a scale (and referencing another composite glyph)
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("b", (0.5, 0, 0, 0.5, 0, 0))
+        glyphSet["c"] = c = pen.glyph()
+
+        assert c.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with unscaled offset (MS-style)
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20))
+        glyphSet["d"] = d = pen.glyph()
+        d.components[0].flags |= UNSCALED_COMPONENT_OFFSET
+
+        assert d.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(10, 20), (60, 20), (60, 70), (10, 70)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with a scaled offset (Apple-style)
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20))
+        glyphSet["e"] = e = pen.glyph()
+        e.components[0].flags |= SCALED_COMPONENT_OFFSET
+
+        assert e.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph where the 2nd and 3rd components use anchor points
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (1, 0, 0, 1, 0, 0))
+        glyphSet["f"] = f = pen.glyph()
+
+        comp1 = GlyphComponent()
+        comp1.glyphName = "a"
+        # aling the new component's pt 0 to pt 2 of contour points added so far
+        comp1.firstPt = 2
+        comp1.secondPt = 0
+        comp1.flags = 0
+        f.components.append(comp1)
+
+        comp2 = GlyphComponent()
+        comp2.glyphName = "a"
+        # aling the new component's pt 0 to pt 6 of contour points added so far
+        comp2.firstPt = 6
+        comp2.secondPt = 0
+        comp2.transform = [[0.707107, 0.707107], [-0.707107, 0.707107]]  # rotate 45 deg
+        comp2.flags = WE_HAVE_A_TWO_BY_TWO
+        f.components.append(comp2)
+
+        coords, end_pts, flags = f.getCoordinates(glyphSet)
+        assert end_pts == [3, 7, 11]
+        assert flags == array.array("B", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
+        assert list(sum(coords, ())) == pytest.approx(
+            [
+                0, 0,
+                100, 0,
+                100, 100,
+                0, 100,
+                100, 100,
+                200, 100,
+                200, 200,
+                100, 200,
+                200, 200,
+                270.7107, 270.7107,
+                200.0, 341.4214,
+                129.2893, 270.7107,
+            ]
+        )
+
 
 class GlyphComponentTest:
 
@@ -456,6 +589,29 @@
         ):
             assert value == pytest.approx(expected)
 
+    def test_toXML_reference_points(self):
+        comp = GlyphComponent()
+        comp.glyphName = "a"
+        comp.flags = 0
+        comp.firstPt = 1
+        comp.secondPt = 2
+
+        assert getXML(comp.toXML) == [
+            '<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>'
+        ]
+
+    def test_fromXML_reference_points(self):
+        comp = GlyphComponent()
+        for name, attrs, content in parseXML(
+            ['<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>']
+        ):
+            comp.fromXML(name, attrs, content, ttFont=None)
+
+        assert comp.glyphName == "a"
+        assert comp.flags == 0
+        assert (comp.firstPt, comp.secondPt) == (1, 2)
+        assert not hasattr(comp, "transform")
+
 
 if __name__ == "__main__":
     import sys
diff --git a/Tests/ttLib/tables/data/NotoColorEmoji.subset.index_format_3.ttx b/Tests/ttLib/tables/data/NotoColorEmoji.subset.index_format_3.ttx
new file mode 100644
index 0000000..6a1728e
--- /dev/null
+++ b/Tests/ttLib/tables/data/NotoColorEmoji.subset.index_format_3.ttx
@@ -0,0 +1,705 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.44">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="eight"/>
+    <GlyphID id="2" name="registered"/>
+    <GlyphID id="3" name="uni2049"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.011"/>
+    <checkSumAdjustment value="0x73631d70"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="2048"/>
+    <created value="Wed May 22 20:00:43 2013"/>
+    <modified value="Thu Jan 30 14:31:14 2020"/>
+    <xMin value="0"/>
+    <yMin value="-500"/>
+    <xMax value="2550"/>
+    <yMax value="1900"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1900"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="2550"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="2550"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="1"/>
+    <maxFunctionDefs value="1"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="64"/>
+    <maxSizeOfInstructions value="46"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="2550"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="1331"/>
+    <ySubscriptYSize value="1433"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="286"/>
+    <ySuperscriptXSize value="1331"/>
+    <ySuperscriptYSize value="1433"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="983"/>
+    <yStrikeoutSize value="102"/>
+    <yStrikeoutPosition value="530"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="6"/>
+      <bProportion value="9"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="56"/>
+    <usLastCharIndex value="8265"/>
+    <sTypoAscender value="1900"/>
+    <sTypoDescender value="-500"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1900"/>
+    <usWinDescent value="500"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="1900"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="2550" lsb="0"/>
+    <mtx name="eight" width="2550" lsb="0"/>
+    <mtx name="registered" width="2550" lsb="0"/>
+    <mtx name="uni2049" width="2550" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x38" uvs="0xfe0f"/>
+      <map uv="0xae" uvs="0xfe0f"/>
+      <map uv="0x2049" uvs="0xfe0f"/>
+    </cmap_format_14>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="52" language="0" nGroups="3">
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0xae" name="registered"/><!-- REGISTERED SIGN -->
+      <map code="0x2049" name="uni2049"/><!-- EXCLAMATION QUESTION MARK -->
+    </cmap_format_12>
+  </cmap>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2013 Google Inc.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Noto Color Emoji
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Noto Color Emoji
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Color Emoji
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.011;GOOG;noto-emoji:20180424; pistol
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoColorEmoji
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-1244"/>
+    <underlineThickness value="131"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CBDT>
+    <header version="3.0"/>
+    <strikedata index="0">
+      <cbdt_bitmap_format_17 name="registered">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="136"/>
+          <BearingX value="0"/>
+          <BearingY value="101"/>
+          <Advance value="136"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000088 00000080 08030000 00e737d1
+          0d000000 ff504c54 454c6971 74747475
+          75757878 78777777 7b7b7b7e 7e7e6868
+          68787878 79797977 77777777 77787878
+          6969697b 7b7b7777 77737373 6f6f6f6c
+          6c6c6767 67787878 6a6a6a64 64646161
+          617d7d7d 7a7a7a60 60607575 75797979
+          6666665e 5e5e7171 716e6e6e 6565655c
+          5c5c5a5a 5a6c6c6c 58585857 57577373
+          73555555 53535352 52526e6e 6e505050
+          4e4e4e6b 6b6b4c4c 4c666666 4b4b4b66
+          66666565 65494949 65656547 47474646
+          465b5b5b 4444445a 5a5a4242 42606060
+          5757573f 3f3f6060 60575757 3d3d3d58
+          58583a3a 3a5c5c5c 5d5d5d39 39393737
+          37353535 33333353 53535252 52525252
+          2f2f2f52 52524f4f 4f2b2b2b 28282825
+          25252323 23212121 f9766a48 00000050
+          74524e53 00406180 bfefff20 9fcf508e
+          df30ffff ffffffff 10ffffff ffffffff
+          afffffff ffffffff 70ffffef ffffff80
+          ffffc0ff 8fffefa4 ffdfffff 50ff10ff
+          c162ff80 40ffa7ff d6efffff ffff8fdb
+          7fffefbf 08ff47de 00000bbe 49444154
+          7801ecd5 d77eab30 0c067033 dcb0629b
+          8d11a381 aef77fc2 23b70c93 09745c9d
+          ff5533ac 7e96941f e4bfff7e 8d615ab6
+          4d9f26d4 b62dd320 7fea603a eed30dae
+          631ec89f f0fce0e9 81c0f7c8 2f3bfaf3
+          30181761 144fa250 70360fca 3f925f93
+          98d34078 9866f915 591af269 4866f23b
+          312c5a7c 9222cdcb 3bf254c8 e213b592
+          5f8bc144 5cae100b f63b5186 181c7bb1
+          529ef221 0af93946 50281097 9bc45028
+          81f15353 a976c5d0 a2543f32 1f8f1648
+          d6cd2eb5 2c10f5be df0ebf40 2cbaf63f
+          a210a098 0084d1b5 b4112b90 ffcda61c
+          dc0241d6 9c89435e 5cc5c38b 301914c8
+          3d7c6b4b e9b576c4 2d2bee60 6d7cad29
+          d420bb99 0cf1653b f267c906 d4f62dcf
+          1878966f 533690cf f9b2299c 2193ec64
+          31d4968d e624d897 407fc8ea 8fe5807d
+          11a74653 b60c5964 1787a168 39ed2185
+          7fbcf75c 1cb240b6 1c0f7276 e788bb59
+          13b24fce c3611bea 2c0a9b6e 16ef4ca2
+          6ac9ac9b a592216a 252b1f4d 0cc9b49b
+          65724f12 4b95c9bb 492ef418 eba308bd
+          86dcbe27 e6598e5a 32541dc8 06878a21
+          59eb49b6 fe760c89 b4b94412 05c6f687
+          a544913e 1db4a1cc 814a29e3 794b8544
+          4e42364b 1c89c4bc b331bea4 87d5c75d
+          29b5356b 409d36c9 2ea6ba13 cc49527c
+          e9aebd92 affad98f 4ad58fe0 88350379
+          07b52bcb bb72d7a3 3a25ca7e a4a6ec93
+          553c75b2 9b72f0af 3b24b65c 21f0bdab
+          fde55392 4eddcb5b 35187a71 cec65e3a
+          7225ea1f ce0ada17 37a36b86 53a9455d
+          e4705487 d5a8b3fc b6380d85 1c386751
+          9c4512b5 b0d5ba5f 6edb8f5a d50f822c
+          2979d33f d0652948 85be9005 7b5974d5
+          6f38e01c bad741ca 397793a1 54d4af91
+          4752b193 e59e60a1 f475d001 e70179c0
+          c213f578 22e77822 19ef94f6 eb349144
+          f4b84812 60a9fc75 50e30beb d1a6722e
+          f4e4586f 631094c3 459223d5 3b2db070
+          f2b021e5 db20c417 e63865ce e3d7d5fa
+          673cba4c 62e23be1 58b97cd4 92e45dfb
+          f609bfed 903d4150 346cd7cc c1774eda
+          1ddf937b 0d0180e6 e3cbab00 08123dc8
+          db166acf fde59a00 88d7a178 0300d6bd
+          86003c7f 0c520030 c8ad20fd 29d4a531
+          0e74a9c5 2406d118 00908ed5 9f01eeb4
+          c4d41ad2 01404526 3640fd31 eb045c88
+          f0a8a607 009be82a 00e8b496 98e41657
+          6bc83f52 ca43c759 1c8ac2c0 163205c9
+          258e2524 c7941448 4fa6effc efff5c7b
+          b35c6106 dfb059f6 53733807 fbb3cd8c
+          00e59814 c16b23c8 601987c1 236d89d7
+          60db3992 87e00609 bca9dd81 e025b622
+          b95b23bf eef6b70e bf3f372a ba72acc0
+          76e27d82 05a61ac6 4940f304 5f535537
+          28bc435a 4474ae0d 897f7bee 9b4898c3
+          fb06152e 5081e653 40033589 b5a53b10
+          5ac4a56e 99eb8659 f9f32b79 f48e6489
+          4ba026c1 2363acc0 9681f1b4 2f52b7a0
+          08f5b18b da61fb9b 9ec2b406 c302c68f
+          f4cd30c6 db29189b 043d91d9 90084e01
+          2685ab49 f777834c 18b335c2 19a3efe6
+          196c370d 57dbf0a7 08631d11 c5182932
+          5d33265d 4d33d63b fdf07aea b8089cfa
+          73401013 9d6111fa 54adab95 30e5f06e
+          63ea82e1 798d1d0b a7e68b60 0880c816
+          037fc755 5babf060 7ba698d6 904681cf
+          c45ab56b 28adb589 27a20745 1098bc74
+          3d5f2481 a94b5c46 593ba1fe ad5aabb1
+          31b3b67f 7bbfb729 4e714364 6d6dea7a
+          b06ad8bf 1b6b6798 6a6ba97f ae84ea08
+          11ec1122 f4c113b7 0b4feb7d 030ca3c1
+          05e47891 089ee132 b51f030b 6b332cac
+          a010fb0b a4840879 ae832231 3c5bb90d
+          2f823e5b ce25e605 e7eb8010 d9b7d022
+          6e197247 c89af302 73c9b93f cd81f319
+          e69af303 11cf3126 674022ce f9aead2d
+          e117b590 c67c060b f97996cd 8f0d26cb
+          4e2345a0 a65c6d4e 2d74ca32 830bcdb3
+          cccfd759 563891ed 8027d540 1eb37616
+          ac4dfc6f c0891459 b6f67221 448d390c
+          1f478924 b01bb16f 5b7bd08a 7c59981e
+          0b350cff 4524f444 84e86c35 17624b79
+          c09b956b 95f033f6 5ae11d22 e7867122
+          d39300f4 d1a18438 04a4082e 3456e4dc
+          a285b874 2bd370f1 22aea467 c70a7e47
+          ff592486 679bd77f 38c230a1 445e5b36
+          82022bc8 f540de02 5a042b3b fa44ba22
+          2129e248 0541ba7f eda0f19b 1f10d950
+          224aa91d e630f445 942a5f3b 94aac76c
+          797ced17 0e012502 01767630 1c2be2d8
+          7539bff6 a86092cb 74acc87b c32d91f7
+          fb291590 04b744b0 754b64ff d100c34f
+          4264f971 2faf7305 4401c927 44d8db53
+          2217a52a ccb552db ff23b2cf 490f64ab
+          94c66205 d7e7e507 29575f0d a9942732
+          be8f4202 9724b8c1 49ca149b 2b290f94
+          48817949 e41057f7 79ec2470 f82ba0c0
+          994aa7ec 8b6c9d68 0d1b1a2f a2e1ed45
+          709b8b94 b53bfaad 972fa4cc bf1b8eb0
+          a59810f9 be0b7839 1af08821 3f623597
+          d2570ea1 f04ece45 8bec2ac7 7b4f241c
+          108920c7 e63b5d35 c6ecb191 1af3d217
+          31a6fe76 bc42a5c3 a613cddc cb142fc6
+          a4d8dcc3 9b44e397 5baa32e6 ad9fe679
+          5744e73f d9bb6809 3f07be91 37632a6c
+          d6c6fca2 54f3bcc0 c619e64a 86447650
+          787b796a 8028d75f 3f242f71 708304ca
+          672c1679 4e9ddddf 8456c792 eb2010f4
+          4da77785 2a0e1c98 924b72ce 56708e9b
+          feff7b5e d72e3028 d9fddeee 4a9aeea6
+          67c0612f 84280f7f 9042cc6a 4184181c
+          3c8c1041 2f199486 8b53c1d5 06664248
+          cb2bc1db b71d673c 9f5a4e22 c4fa5510
+          12e2180e 13d23957 53c1e53a d6422441
+          e0e80d67 0ecea91e 64d419a4 58a0cfd2
+          574b0d79 fb3beb89 2373b72d 53d307de
+          9b6523c8 d98383f0 e6f4b9bc e9dc9c25
+          efcc4157 f7bfeab6 b15629ae 8b4a1029
+          8320b194 d5d95fa4 942bae8f 24134214
+          b04d83b4 59c79baf 948e55b2 930f327e
+          11a48056 1d9840d0 9f7a0d1c f1b874bd
+          4ab9e83a d1815902 5ad119a4 df6838c3
+          12092a41 236bafe7 b84c3a28 29675daf
+          f160be79 6d243ba5 c6578fbe 52f5c95f
+          94525b66 8c713b6b 1b486e17 58e1baf3
+          abc24d29 0ad6ba47 9520535e 26690629
+          ee4ae973 85a26a9b 1381d277 7552ead6
+          ebc21ee2 dc124b5c 5faa411e 1e1c8491
+          419032e5 a0d149d1 185a69ed 735cefb9
+          d4d255e2 9c069596 765a4f9f 1e89d61c
+          c4e1a2b5 ce99b3c2 ed25ac9f 6038e056
+          38660b8e 1097d6e8 6cb4be15 4190392f
+          928641b8 0dadcdb5 42d25950 bea17cb6
+          b552571c 5abd92b0 a5e5fb20 8c0c8211
+          931ea475 70cc96a8 ae8291f2 405e8c84
+          5bdabb20 c6ac3e3c 5263dafa b918634a
+          6695b8dd f9f307b3 f4f97620 3c126362
+          67742663 eeffda82 0c10a443 4d4fa68d
+          90e464df 1a50a2b3 2bc4c6f0 40da7184
+          761bb674 2b7c904f 8f6a1046 06c18869
+          cfc4982f 7b40c261 6d0deb3b 01053d9c
+          62ee9320 c8865718 358d7873 0ecc5b61
+          6f7c8eb9 737d105c 7bef7082 6410b46e
+          ad2e4463 5e8088f6 adeae84e 147f78de
+          98e862db a8999e7a 6f812529 779a8f94
+          febcf6f8 fb704f4b dcb4bd3d 5bdedcf1
+          1e84c0d6 32f5f172 6bf90e05 9aea5f2b
+          4976452f 8ae338b1 5f1256b8 feee92ef
+          501c1f7e 799b04d7 51afd855 725cfb44
+          7c525f21 838ee7f8 808e704e 7ee20a3a
+          471bdd2b bc1f9c0f 42670fde 18dcfe6f
+          af5e7694 85a1008e 9f1d2b5e 83585342
+          4c8cdc02 2975a2f4 4a19bff7 7f96ef94
+          4174eee0 e8ace647 5c68da93 bf674306
+          b3481cb0 356747ea 1710828c 2e6c059f
+          0a6d7421 2154789d 1ecd995f a784798a
+          273c5c1a 235e1ee1 4b6c058c ab68f024
+          832fafeb 2e1a28ce a0b2be43 4cc372fc
+          8afb9d29 f07faa36 67228e10 2f60b182
+          472816e6 acf6eb0c 603616a1 8399e411
+          520c1662 2a42b999 1c22b468 4c851748
+          6b267b12 211ec002 018f10d9 9b49eba7
+          54b0885e ad56a475 1391ac90 d505cc54
+          68bb4289 709396e0 0f1a16e2 bee4e8cc
+          f8385793 29657606 a99d9b46 1cfd000e
+          8b71821a 77617232 e00cbec1 3819e4c6
+          5d34c35d 801b4b6a 77456cc9 40c9103e
+          154a4506 5be1aed4 b776204d 50eeaeb5
+          2979a178 15c03b41 c5157991 b6ee5a4e
+          90861b55 0425a2bf 664a4a46 b6933a63
+          28c44fa6 6567c988 96a6bf26 12822ab8
+          19b3ebf5 9aeefbd7 da92aebf 40cbb67f
+          6defcf5b 063f10a8 35dab9fe 0d516fd7
+          1fdad6a2 7fc3edd6 4805f023 85a4283e
+          f4ef8943 9da67492 a6f541f4 ef1d628a
+          64013f95 598a36e6 f9266643 91cde00e
+          0a4ebddc dc909153 8f17701f 4cc55e6e
+          4e8b983c f61483fb d136f652 f17c9ae9
+          59a4b167 35dc5531 a6248dfb 37836b92
+          31a3807b c39464b0 6bcce9ab 88936976
+          c9e01119 5e51a964 541e4dff 51446f8e
+          65325255 010f134a 9b9c6dcb a615ceeb
+          1d126d53 26132b43 78b04caa cd3794cc
+          e0570415 ff346678 2dff2aa6 75d7d9cd
+          c4769dd6 0cfefc79 94ff183d 2a93948d
+          64820000 00004945 4e44ae42 6082
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+      <cbdt_bitmap_format_17 name="eight">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="136"/>
+          <BearingX value="0"/>
+          <BearingY value="101"/>
+          <Advance value="136"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000088 00000080 08030000 00e737d1
+          0d000000 36504c54 454c6971 40404040
+          40404040 40404040 40404071 7171c3c3
+          c3dfdfdf efefeff4 f4f4fafa fad0d0d0
+          f8f8f8b6 b6b6e7e7 e7909090 a6a6a6ad
+          4143d100 00001274 524e5300 0a131e29
+          3340739e c8dfff84 f266b34d 59e71538
+          bc000003 04494441 547801ed 9ac1b29d
+          200c860b f02902a2 beffcb76 3a3d73ab
+          a15e4f17 c959946f ed8cbf49 0249cc8f
+          c1603018 0c0683c1 6030b0c7 f910232f
+          620cde7d 44458874 c460adc5 456e88ce
+          5ec634a7 25971779 49f3642c 25005097
+          d2b15400 828d8e08 5073f92b 6b028856
+          3a5a2eb7 e406441b 1da97c4b 02a2858e
+          ad3cb04d ea4a82d0 71c3a21d b1eece2f
+          920d70ba 8e99cb5b ccaacef1 30e5f216
+          eb045ed3 20a93b37 e61df639 ad7dea44
+          c50899d6 ee752fa6 d49bc4e9 a54cbdbe
+          ece0c47c 155921e8 79662967 1a10bdfb
+          559944a4 caace71b a0f38b3f 994b0490
+          9a6f1c34 190527e3 0719414d 2b6fbc30
+          fe266c1f c5a19bb4 822408db cfe085d0
+          6623447c f10e4e9e ffe2c289 26490374
+          c1bc7e5e 887ce07f 102282f5 d9355659
+          b3839342 4cb2c6c3 fc2fe95b c1ab5dbe
+          0f075a6f 311522e4 72e27ac4 7b71c467
+          c0a80c48 9c947820 c932c0aa 30aad732
+          e0d02b8c 04519864 9d3971ac 7aa5a2c0
+          21a2a4a4 e9a654cc 80576d27 f6b59c59
+          b7b9c121 8be7f580 a8dc60b5 37db1a54
+          c7241ea8 e5910a78 fde6b7ad 0fcd55b3
+          18d60460 5fca372c bba20ea1 847a6b94
+          5cb1d1e1 7930c9b6 03380b1d d31b1323
+          bcbe8e23 9707f204 78751d6b 79643d74
+          bde3a48e 4f2989bd 8e9c5a83 36a72c94
+          68cef37c 77e72d8d 2f5a36bb f5ba7e3f
+          7161331a 1979d8bb 1b85f0bb 300a208b
+          955dcb24 7db70fd1 5dfe586c b2b8b628
+          e2f3a576 160312cd 625196ce b5fbe0d8
+          3f11f45b df1570bd cd54e612 cf438767
+          ad2a21b2 f79ded73 77ac3eca 9bc13f75
+          c74d5f88 7c897c66 083116f2 744878a8
+          e642b6e7 ac5113b2 cb6b5ebe 05c8327d
+          f57f09b4 ce24415e cf80c174 7301bcb0
+          199b3ce2 2d7e1b55 e4e88a26 1f081675
+          d17afcd9 18713ed2 8d2cb42a 2360e99a
+          6d313292 bed36a7a 5bdf5e22 4746fad3
+          3c072cfd c6480338 eaf63287 c86f2d93
+          1ce56d0e d5f126b5 bc49d26c f5bc3c29
+          eed980f0 a9ad0da1 23daefb1 98e9104a
+          ead330af 9aecf6bc 37cc8b46 c3bc762b
+          6569669b 681e803d e5d291d3 0180375d
+          cd63afd7 15c1ba83 b239242e 708bf502
+          a70ff410 fca7166b f9e2338b b583c160
+          30180c06 83c160f0 13e55b78 1b5bd5fd
+          5a000000 0049454e 44ae4260 82
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+      <cbdt_bitmap_format_17 name="uni2049">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="136"/>
+          <BearingX value="0"/>
+          <BearingY value="101"/>
+          <Advance value="136"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000088 00000080 08030000 00e737d1
+          0d000001 3b504c54 454c6971 d73b3bd9
+          4141da44 44d94141 d94242d5 3737d83f
+          3fd84040 d94040d9 4141d941 41d53838
+          d13030d8 3f3fee4a 4af74d4d e34545eb
+          4747ff50 50fe5050 fe4e4eda 4242fe4f
+          4ffb4747 d94040d3 3434fd4c 4ce14040
+          f34747fc 4b4bd83e 3eeb4242 fb4949fa
+          4545d639 39d02e2e dc3a3aef 3f3fd333
+          33f94343 d43737f8 4141f740 40ed3939
+          e13434d3 3434cf2b 2bf63e3e f53c3cd2
+          3131f439 39d02c2c cc2626d0 2d2df337
+          37f23535 e83030cf 2b2bf132 32ce2929
+          dd2b2bcb 2222f030 30cb2323 d42525cd
+          2626ef2d 2dc81d1d c31313cb 2222eb25
+          25ee2929 c81b1bdb 2121c91f 1fec2525
+          ea2222eb 2424c71a 1ac10c0c c61919c0
+          0a0ac00a 0ac20c0c c10c0cd1 0b0bda0c
+          0cc00a0a e10c0cc1 0808be06 06df0909
+          cb0202de 0606d403 03bc0202 dc0303b9
+          0000b800 00da0101 b80000b9 0000b800
+          00b70000 fbff4a1d 00000068 74524e53
+          0040cfff df583080 afbfef8f 10209fff
+          ffffffff ffffffff ffff70ff ffffffff
+          ffffffff 60ffffef ffffffff ffffffbf
+          ffffffff 9fd5ffff ffffffff ffff80ff
+          8fffffff ef40ffff ffbfffff ffffffff
+          a0ff50df ff8fffff 8fffffff ffffffff
+          ffffdf9f ff50ff8f 9e461cce 00000655
+          49444154 7801ec96 e9e2e22a 0c476ff7
+          55812254 dcfabfef ff929320 636b6c71
+          c3d98fdf f0977808 a8fdef1f ff788128
+          4e6e48b3 7b557956 94553d56 c4555344
+          ed3b1e59 328bcf64 95954e81 1237d9cb
+          2269324b ba946fb3 2af151af f3d744a0
+          96710283 c5d5bc46 719985e8 189b9430
+          7171a9b2 1745b824 7058cc7d 1a827179
+          0be79d70 2a513891 b96b9d3a 8b8d5c04
+          a78934ed c744dae6 ac31492b 3e833c1f
+          529a7f48 64156b00 34b81f6c c6120d3c
+          7953a0e2 56041649 2caf612d 614b6320
+          6c843509 2f821ebe 7110140c c59a0417
+          b11e5dbf d928fe20 ea691314 d910a848
+          6b3dfc16 0437933c ac486c3d 36923f07
+          54a56d48 9102ef47 3f734b7b c63a21ec
+          4f6ccf29 b2c79934 0f8ba45a b39e702d
+          92a387ea a949dfe9 291da32e 4a8a670e
+          a79a1161 30d3ab84 e654830b 4d49ae55
+          94b41baa de1419eb 236d1357 26dbef1a
+          55d514f8 60a22d82 13153c9c 28944865
+          8c564a49 b5e5eea5 943640dd 8c9fb1da
+          c506d05c f131266d 70fda888 319d2274
+          c65c4472 e3025272 47af5123 bb79d23b
+          9b8c833b 77326d18 9106baef ad08771b
+          e5e851ce b45f1b9b e5e34014 87a52c8c
+          486a8c50 167749e4 c14ce74d 4d84cd21
+          0a398073 10113c99 e3dea2e0 fced9bcb
+          e75eda34 de26f4c6 224c3f2e b2274c44
+          0a9cb643 49789da0 73bcd4ac ad8d1198
+          430d2b82 67138510 29b1f3c5 449db798
+          2f35436f c3210749 87366617 4224a56f
+          fbbf906d 4df3c298 e62191c6 18e111b1
+          873ee1e8 ff1ae07d 3d2cf5f2 53cc8888
+          cbae231c 35e95bfb da655040 45d27744
+          0a27320c 43779c70 1806ef06 5750703c
+          4d38c2c2 6322c370 3811e0d3 5004a987
+          1bfc77ef 5322d940 895b6fbf 7818bec2
+          8ba0493c 4ca9d7c4 8352bd23 f23fc18a
+          bc088a4c 7bfd13f9 65446a10 d94ef87a
+          46644b78 43a4c5af 2f11a9be 114717ba
+          11c34010 40b74cee 89a10ca6 630ef9ff
+          3fac7315 d99b8241 ca8d78a5 993c3951
+          cd0b340b 217cee93 6d8da29a 2752ca4f
+          96372915 e5e54e4a cdb71eb3 21389d50
+          5ecea57c f6a70cb6 2e8f0051 a81a7fea
+          594a4b47 80dc48a9 c77eb494 a378c898
+          251ba2d0 7cf7970c 9f1a0622 ec0f0f72
+          4e9190c9 64c22138 6541ae51 34c183e0
+          f0383ce4 113d3df5 a327132b 12205396
+          3c889a20 337fc7e0 70454343 2e2d6ac6
+          9f99c984 0721c1ea 6910e698 07330b5c
+          144567b9 5c9a5910 83934876 a034efcd
+          aca81492 e358042b eb094e97 89907590
+          68087384 2b1aa72b 4a83ccc3 89394ea5
+          8ed922f1 c7144098 6313e4b0 614532a4
+          3f52ec40 2e694808 1cdbedb6 e7c04d51
+          5a50e110 9cca1c5b e4913220 bb202910
+          f1ede07d 644f8342 4455e060 59fdb814
+          5baeeb7a d104996f 71cb7060 8b4f2dea
+          ba8aebee f1cd2d73 d4dc5104 594555d5
+          c1d1963b 4a21f716 1f9db77e 921d0cd2
+          068985a0 597741b3 2b70d0ca 390e712e
+          06f2e878 b373c89e 068608eb 5cdd777c
+          1143c74a 0ac25014 865359b1 2f604d6f
+          2305eb05 f50a27c8 d1d57dff 9759ab4d
+          120b329b ccf20fed c97c97ad 290639a4
+          41f622d2 1ebcda4f 11a9cddf 2122dd21
+          a8135986 5412efd2 1d4521bd 881ca395
+          9cccff43 9ad7ece8 d5cad22a e1b4f331
+          2805b251 d53618a9 ea471e44 35829c55
+          fb8495fa 9b8bba51 06e4e297 04d9a976
+          fea8531d aa321057 0a442388 aaeecd0a
+          90118075 8ba905b0 c9850031 045882d4
+          0026af0e 684c01c8 14e420cb 2337d917
+          825cdc97 00d94510 00f52a7f e40accde
+          c202d864 43c81842 2e411af2 176227db
+          91340520 b8f9d904 08c9bb37 99c9abc9
+          6d24790b 421ac4ba 8a416c10 c9716134
+          909c5d24 4f45205f 410990fe 1137e643
+          5eafdc83 129ead9a c8d19b22 90d977cc
+          49f7d5cf abeb399a fcaae1f1 5e6556a8
+          7f77f466 95b6c377 d090e9f8 698f0e76
+          1c048130 8e73da5b 1f8b3b11 4b5b953a
+          8222efff 043b241b 56d9c334 0e7be377
+          2bc9ff8b 50869b3c b889a669 9a0b3aa5
+          7ba455c7 6f18a4ee 332d990d c3bd3f51
+          179abba8 40f58979 20437c09 aba13c7b
+          645e3fcc 47abcf61 188e0dfe 7c0a2699
+          361fafec 91563ba2 19c77138 36031e48
+          c1a30f9b 7955d76f 281ddec5 bc4e0c1e
+          7564339d 900d49e1 e5a6025e 4f5d69b8
+          ff8c9d0a 16df996a de05aa21 e193ceef
+          c28c87b5 1b1200fc 1dc5c3af 0bcdbf7c
+          c88d6896 02e34388 d1ca0dc9 01d8a560
+          015ced86 e401c6a5 300278aa 590b5443
+          da420876 3db178b4 d56e682e 0498d783
+          194270f5 1b920c79 356f86ed 422305d3
+          9e566d7e e3b4e9c9 26c6187e 9b803f77
+          c1e62302 3b230b11 f98f9b24 377c7b3c
+          d9990d83 74317392 df306cde 45e4fc56
+          af691a86 6f38d2f4 13a054ea b3000000
+          0049454e 44ae4260 82 
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+    </strikedata>
+  </CBDT>
+
+  <CBLC>
+    <header version="3.0"/>
+    <strike index="0">
+      <bitmapSizeTable>
+        <sbitLineMetrics direction="hori">
+          <ascender value="101"/>
+          <descender value="-27"/>
+          <widthMax value="136"/>
+          <caretSlopeNumerator value="0"/>
+          <caretSlopeDenominator value="0"/>
+          <caretOffset value="0"/>
+          <minOriginSB value="0"/>
+          <minAdvanceSB value="0"/>
+          <maxBeforeBL value="0"/>
+          <minAfterBL value="0"/>
+          <pad1 value="0"/>
+          <pad2 value="0"/>
+        </sbitLineMetrics>
+        <sbitLineMetrics direction="vert">
+          <ascender value="101"/>
+          <descender value="-27"/>
+          <widthMax value="136"/>
+          <caretSlopeNumerator value="0"/>
+          <caretSlopeDenominator value="0"/>
+          <caretOffset value="0"/>
+          <minOriginSB value="0"/>
+          <minAdvanceSB value="0"/>
+          <maxBeforeBL value="0"/>
+          <minAfterBL value="0"/>
+          <pad1 value="0"/>
+          <pad2 value="0"/>
+        </sbitLineMetrics>
+        <colorRef value="0"/>
+        <startGlyphIndex value="1"/>
+        <endGlyphIndex value="3"/>
+        <ppemX value="109"/>
+        <ppemY value="109"/>
+        <bitDepth value="32"/>
+        <flags value="1"/>
+      </bitmapSizeTable>
+      <!-- GlyphIds are written but not read. The firstGlyphIndex and
+           lastGlyphIndex values will be recalculated by the compiler. -->
+      <eblc_index_sub_table_3 imageFormat="17" firstGlyphIndex="1" lastGlyphIndex="2">
+        <glyphLoc id="1" name="eight"/>
+        <glyphLoc id="2" name="registered"/>
+      </eblc_index_sub_table_3>
+      <eblc_index_sub_table_3 imageFormat="17" firstGlyphIndex="3" lastGlyphIndex="3">
+        <glyphLoc id="3" name="uni2049"/>
+      </eblc_index_sub_table_3>
+    </strike>
+  </CBLC>
+
+  <vhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1275"/>
+    <descent value="-1275"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="2500"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="2400"/>
+    <caretSlopeRise value="0"/>
+    <caretSlopeRun value="1"/>
+    <caretOffset value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <reserved4 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfVMetrics value="1"/>
+  </vhea>
+
+  <vmtx>
+    <mtx name=".notdef" height="2500" tsb="0"/>
+    <mtx name="eight" height="2500" tsb="0"/>
+    <mtx name="registered" height="2500" tsb="0"/>
+    <mtx name="uni2049" height="2500" tsb="0"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/otTables_test.py b/Tests/ttLib/tables/otTables_test.py
index 4afe2db..bd4daeb 100644
--- a/Tests/ttLib/tables/otTables_test.py
+++ b/Tests/ttLib/tables/otTables_test.py
@@ -605,20 +605,87 @@
 	ok = otTables.splitMarkBasePos(oldSubTable, newSubTable, overflowRecord=None)
 
 	assert ok
-	assert oldSubTable.Format == newSubTable.Format
-	assert oldSubTable.MarkCoverage.glyphs == [
-		"acutecomb", "gravecomb"
+
+	assert getXML(oldSubTable.toXML) == [
+		'<MarkBasePos Format="1">',
+		'  <MarkCoverage Format="1">',
+		'    <Glyph value="acutecomb"/>',
+		'    <Glyph value="gravecomb"/>',
+		'  </MarkCoverage>',
+		'  <BaseCoverage Format="1">',
+		'    <Glyph value="a"/>',
+		'    <Glyph value="c"/>',
+		'  </BaseCoverage>',
+		'  <!-- ClassCount=1 -->',
+		'  <MarkArray>',
+		'    <!-- MarkCount=2 -->',
+		'    <MarkRecord index="0">',
+		'      <Class value="0"/>',
+		'      <MarkAnchor Format="1">',
+		'        <XCoordinate value="0"/>',
+		'        <YCoordinate value="600"/>',
+		'      </MarkAnchor>',
+		'    </MarkRecord>',
+		'    <MarkRecord index="1">',
+		'      <Class value="0"/>',
+		'      <MarkAnchor Format="1">',
+		'        <XCoordinate value="0"/>',
+		'        <YCoordinate value="590"/>',
+		'      </MarkAnchor>',
+		'    </MarkRecord>',
+		'  </MarkArray>',
+		'  <BaseArray>',
+		'    <!-- BaseCount=2 -->',
+		'    <BaseRecord index="0">',
+		'      <BaseAnchor index="0" Format="1">',
+		'        <XCoordinate value="350"/>',
+		'        <YCoordinate value="500"/>',
+		'      </BaseAnchor>',
+		'    </BaseRecord>',
+		'    <BaseRecord index="1">',
+		'      <BaseAnchor index="0" Format="1">',
+		'        <XCoordinate value="300"/>',
+		'        <YCoordinate value="700"/>',
+		'      </BaseAnchor>',
+		'    </BaseRecord>',
+		'  </BaseArray>',
+		'</MarkBasePos>',
 	]
-	assert newSubTable.MarkCoverage.glyphs == ["cedillacomb"]
-	assert newSubTable.MarkCoverage.Format == 1
-	assert oldSubTable.BaseCoverage.glyphs == newSubTable.BaseCoverage.glyphs
-	assert newSubTable.BaseCoverage.Format == 1
-	assert oldSubTable.ClassCount == newSubTable.ClassCount == 1
-	assert oldSubTable.MarkArray.MarkCount == 2
-	assert newSubTable.MarkArray.MarkCount == 1
-	assert oldSubTable.BaseArray.BaseCount == newSubTable.BaseArray.BaseCount
-	assert newSubTable.BaseArray.BaseRecord[0].BaseAnchor[0] is None
-	assert newSubTable.BaseArray.BaseRecord[1].BaseAnchor[0] == buildAnchor(300, 0)
+
+	assert getXML(newSubTable.toXML) == [
+		'<MarkBasePos Format="1">',
+		'  <MarkCoverage Format="1">',
+		'    <Glyph value="cedillacomb"/>',
+		'  </MarkCoverage>',
+		'  <BaseCoverage Format="1">',
+		'    <Glyph value="a"/>',
+		'    <Glyph value="c"/>',
+		'  </BaseCoverage>',
+		'  <!-- ClassCount=1 -->',
+		'  <MarkArray>',
+		'    <!-- MarkCount=1 -->',
+		'    <MarkRecord index="0">',
+		'      <Class value="0"/>',
+		'      <MarkAnchor Format="1">',
+		'        <XCoordinate value="0"/>',
+		'        <YCoordinate value="0"/>',
+		'      </MarkAnchor>',
+		'    </MarkRecord>',
+		'  </MarkArray>',
+		'  <BaseArray>',
+		'    <!-- BaseCount=2 -->',
+		'    <BaseRecord index="0">',
+		'      <BaseAnchor index="0" empty="1"/>',
+		'    </BaseRecord>',
+		'    <BaseRecord index="1">',
+		'      <BaseAnchor index="0" Format="1">',
+		'        <XCoordinate value="300"/>',
+		'        <YCoordinate value="0"/>',
+		'      </BaseAnchor>',
+		'    </BaseRecord>',
+		'  </BaseArray>',
+		'</MarkBasePos>',
+	]
 
 
 if __name__ == "__main__":
diff --git a/Tests/varLib/data/TestCFF2Input.designspace b/Tests/varLib/data/TestCFF2Input.designspace
new file mode 100644
index 0000000..ebbf14a
--- /dev/null
+++ b/Tests/varLib/data/TestCFF2Input.designspace
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<designspace format="3">
+    <axes>
+        <axis default="400.0" maximum="900.0" minimum="200.0" name="weight" tag="wght">
+            <map input="200" output="0" />   <!-- ExtraLight -->
+            <map input="300" output="100" /> <!-- Light -->
+            <map input="400" output="368" /> <!-- Regular -->
+            <map input="500" output="486" /> <!-- Medium -->
+            <map input="600" output="600" /> <!-- Semibold -->
+            <map input="700" output="824" /> <!-- Bold -->
+            <map input="900" output="1000" /><!-- Black -->
+        </axis>
+    </axes>
+    <rules>
+        <rule name="named.rule.1">
+            <conditionset>
+                <condition maximum="600" minimum="0" name="weight" />
+            </conditionset>
+            <sub name="dollar" with="dollar.a" />
+        </rule>
+    </rules>
+    <sources>
+        <source filename="master_cff2_input/TestCFF2_ExtraLight.ufo" name="master_0">
+            <lib copy="1" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source filename="master_cff2_input/TestCFF2_Regular.ufo" name="master_1">
+            <glyph mute="1" name="T" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+        </source>
+        <source filename="master_cff2_input/TestCFF2_Black.ufo" name="master_2">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="100" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Medium" stylename="Medium">
+            <location>
+                <dimension name="weight" xvalue="486" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/VarLibLocationTest.designspace b/Tests/varLib/data/VarLibLocationTest.designspace
new file mode 100644
index 0000000..b73e1b5
--- /dev/null
+++ b/Tests/varLib/data/VarLibLocationTest.designspace
@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="4.0">
+    <axes>
+        <axis default="100" maximum="900" minimum="100" name="weight" tag="wght">
+            <map input="500" output="105"/>
+            <map input="300" output="57"/>
+            <map input="900" output="158"/>
+            <map input="100" output="0"/>
+        </axis>
+        <axis default="50" maximum="100" minimum="0" name="width" tag="wdth" />
+    </axes>
+    <sources>
+        <source filename="A.ufo">
+            <location>
+                <dimension name="weight" xvalue="0" />
+                <dimension name="width" xvalue="50" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance filename="C.ufo" familyname="C" stylename="CCC">
+            <location>
+            </location>
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/master_cff2_input/TestCFF2_Black.ttx b/Tests/varLib/data/master_cff2_input/TestCFF2_Black.ttx
new file mode 100644
index 0000000..22f8275
--- /dev/null
+++ b/Tests/varLib/data/master_cff2_input/TestCFF2_Black.ttx
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.2">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x26378952"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="0"/>
+    <yMin value="-116"/>
+    <xMax value="600"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="600"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="592"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="8"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.010;ADBO;SourceCode_Black
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_Black
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 2
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_Black
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_Black
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 2
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.a"/>
+    </extraNames>
+  </post>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueValues value="-12 0 500 512 580 592 634 646 650 662 696 708"/>
+            <OtherBlues value="-188 -176"/>
+            <FamilyBlues value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+            <FamilyOtherBlues value="-217 -205"/>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="134"/>
+            <StdVW value="172"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          24 0 rmoveto
+          552 0 rlineto
+          0 660 rlineto
+          -552 0 rlineto
+          0 -660 rlineto
+          212 104 rmoveto
+          26 56 rlineto
+          36 96 rlineto
+          4 0 rlineto
+          36 -96 rlineto
+          26 -56 rlineto
+          -128 0 rlineto
+          -100 68 rmoveto
+          0 336 rlineto
+          82 -168 rlineto
+          -82 -168 rlineto
+          162 252 rmoveto
+          -40 96 rlineto
+          -18 36 rlineto
+          120 0 rlineto
+          -18 -36 rlineto
+          -40 -96 rlineto
+          -4 0 rlineto
+          84 -84 rmoveto
+          82 168 rlineto
+          0 -336 rlineto
+          -82 168 rlineto
+        </CharString>
+        <CharString name="A">
+          0 0 rmoveto
+          176 0 rlineto
+          73 316 rlineto
+          14 62 17 78 14 66 rrcurveto
+          4 0 rlineto
+          14 -66 19 -78 14 -62 rrcurveto
+          73 -316 rlineto
+          182 0 rlineto
+          -196 650 rlineto
+          -208 0 rlineto
+          -196 -650 rlineto
+          141 138 rmoveto
+          316 0 rlineto
+          0 133 rlineto
+          -316 0 rlineto
+          0 -133 rlineto
+        </CharString>
+        <CharString name="T">
+          214 0 rmoveto
+          172 0 rlineto
+          0 506 rlineto
+          187 0 rlineto
+          0 144 rlineto
+          -546 0 rlineto
+          0 -144 rlineto
+          187 0 rlineto
+          0 -506 rlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 260 39 rmoveto
+          -65 0 -28 11 -49 24 rrcurveto
+          89 -53 rlineto
+          -15 89 rlineto
+          -9 52 -22 18 -43 0 rrcurveto
+          -26 0 -27 -14 -14 -38 rrcurveto
+          0 -90 71 -54 139 0 rrcurveto
+          163 0 99 84 0 117 rrcurveto
+          0 98 -58 68 -142 45 rrcurveto
+          -33 10 rlineto
+          -72 22 -24 24 0 49 rrcurveto
+          0 61 47 23 67 0 rrcurveto
+          42 0 27 -4 52 -24 rrcurveto
+          -85 47 rlineto
+          10 -67 rlineto
+          11 -75 37 -14 39 0 rrcurveto
+          26 0 29 15 5 41 rrcurveto
+          -8 88 -76 48 -121 0 rrcurveto
+          -158 0 -85 -80 0 -115 rrcurveto
+          0 -93 66 -69 121 -39 rrcurveto
+          32 -11 rlineto
+          80 -28 23 -19 0 -53 rrcurveto
+          0 -55 -43 -39 -72 0 rrcurveto
+          64 275 rmoveto
+          0 417 rlineto
+          -71 0 rlineto
+          0 -417 rlineto
+          71 0 rlineto
+          -79 -429 rmoveto
+          71 0 rlineto
+          0 429 rlineto
+          -71 0 rlineto
+          0 -429 rlineto
+        </CharString>
+        <CharString name="dollar.a">
+          292 34 rmoveto
+          163 0 83 80 0 100 rrcurveto
+          0 182 -302 -4 0 56 rrcurveto
+          0 21 18 11 36 0 rrcurveto
+          55 0 39 -16 52 -32 rrcurveto
+          84 98 rlineto
+          -53 52 -69 36 -97 0 rrcurveto
+          -141 0 -88 -68 0 -104 rrcurveto
+          0 -188 302 12 0 -68 rrcurveto
+          0 -20 -19 -10 -37 0 rrcurveto
+          -61 0 -55 20 -72 40 rrcurveto
+          -74 -116 rlineto
+          65 -54 101 -28 70 0 rrcurveto
+          -19 -150 rmoveto
+          160 854 rlineto
+          -100 12 rlineto
+          -160 -854 rlineto
+          100 -12 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="0"/>
+            <SubfamilyNameID value="0"/>
+            <RangeStart value="0.0"/>
+            <RangeEnd value="0.0"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="24"/>
+    <mtx name="A" width="600" lsb="0"/>
+    <mtx name="T" width="600" lsb="27"/>
+    <mtx name="dollar" width="560" lsb="51"/>
+    <mtx name="dollar.a" width="600" lsb="56"/>
+  </hmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_cff2_input/TestCFF2_ExtraLight.ttx b/Tests/varLib/data/master_cff2_input/TestCFF2_ExtraLight.ttx
new file mode 100644
index 0000000..e3a35f0
--- /dev/null
+++ b/Tests/varLib/data/master_cff2_input/TestCFF2_ExtraLight.ttx
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.2">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0xeb345d38"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="50"/>
+    <yMin value="-115"/>
+    <xMax value="550"/>
+    <yMax value="762"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <xMaxExtent value="550"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="578"/>
+    <usWeightClass value="200"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="286"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 0
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 0
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.a"/>
+    </extraNames>
+  </post>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueValues value="-12 0 478 490 570 582 640 652 660 672 722 734"/>
+            <OtherBlues value="-234 -222"/>
+            <FamilyBlues value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+            <FamilyOtherBlues value="-217 -205"/>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="28"/>
+            <StdVW value="34"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          84 0 rmoveto
+          432 0 rlineto
+          0 660 rlineto
+          -432 0 rlineto
+          0 -660 rlineto
+          48 32 rmoveto
+          102 176 rlineto
+          64 106 rlineto
+          4 0 rlineto
+          62 -106 rlineto
+          100 -176 rlineto
+          -332 0 rlineto
+          -10 42 rmoveto
+          0 536 rlineto
+          154 -270 rlineto
+          -154 -266 rlineto
+          176 292 rmoveto
+          -56 92 rlineto
+          -94 168 rlineto
+          302 0 rlineto
+          -94 -168 rlineto
+          -54 -92 rlineto
+          -4 0 rlineto
+          26 -26 rmoveto
+          152 270 rlineto
+          0 -536 rlineto
+          -152 266 rlineto
+        </CharString>
+        <CharString name="A">
+          50 0 rmoveto
+          32 0 rlineto
+          140 396 rlineto
+          28 80 24 68 24 82 rrcurveto
+          4 0 rlineto
+          24 -82 24 -68 28 -80 rrcurveto
+          138 -396 rlineto
+          34 0 rlineto
+          -236 660 rlineto
+          -28 0 rlineto
+          -236 -660 rlineto
+          102 236 rmoveto
+          293 0 rlineto
+          0 28 rlineto
+          -293 0 rlineto
+          0 -28 rlineto
+        </CharString>
+        <CharString name="T">
+          284 0 rmoveto
+          32 0 rlineto
+          0 632 rlineto
+          234 0 rlineto
+          0 28 rlineto
+          -500 0 rlineto
+          0 -28 rlineto
+          234 0 rlineto
+          0 -632 rlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 245 7 rmoveto
+          -65 0 -39 15 -46 50 rrcurveto
+          36 -48 rlineto
+          -28 100 rlineto
+          -4 16 -12 4 -11 0 rrcurveto
+          -14 0 -8 -7 -1 -14 rrcurveto
+          24 -85 61 -51 107 0 rrcurveto
+          91 0 90 53 0 111 rrcurveto
+          0 70 -26 66 -134 57 rrcurveto
+          -19 8 rlineto
+          -93 39 -42 49 0 68 rrcurveto
+          0 91 60 48 88 0 rrcurveto
+          56 0 35 -14 44 -50 rrcurveto
+          -38 47 rlineto
+          28 -100 rlineto
+          6 -15 10 -5 11 0 rrcurveto
+          14 0 8 7 1 14 rrcurveto
+          -24 88 -67 48 -84 0 rrcurveto
+          -92 0 -82 -51 0 -108 rrcurveto
+          0 -80 45 -53 92 -42 rrcurveto
+          37 -17 rlineto
+          114 -52 26 -46 0 -65 rrcurveto
+          0 -92 -65 -54 -90 0 rrcurveto
+          18 327 rmoveto
+          0 428 rlineto
+          -22 0 rlineto
+          0 -428 rlineto
+          22 0 rlineto
+          -22 -449 rmoveto
+          22 0 rlineto
+          0 449 rlineto
+          -22 0 rlineto
+          0 -449 rlineto
+        </CharString>
+        <CharString name="dollar.a">
+          311 34 rmoveto
+          103 0 88 56 0 94 rrcurveto
+          0 184 -338 -32 0 142 rrcurveto
+          0 68 57 44 85 0 rrcurveto
+          76 0 34 -24 44 -38 rrcurveto
+          20 20 rlineto
+          -41 38 -45 32 -85 0 rrcurveto
+          -99 0 -78 -54 0 -88 rrcurveto
+          0 -166 338 28 0 -156 rrcurveto
+          0 -70 -56 -50 -103 0 rrcurveto
+          -85 0 -66 38 -40 34 rrcurveto
+          -18 -22 rlineto
+          45 -38 73 -40 91 0 rrcurveto
+          -70 -146 rmoveto
+          158 860 rlineto
+          -30 4 rlineto
+          -158 -860 rlineto
+          30 -4 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="0"/>
+            <SubfamilyNameID value="0"/>
+            <RangeStart value="0.0"/>
+            <RangeEnd value="0.0"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="84"/>
+    <mtx name="A" width="600" lsb="50"/>
+    <mtx name="T" width="600" lsb="50"/>
+    <mtx name="dollar" width="490" lsb="53"/>
+    <mtx name="dollar.a" width="600" lsb="102"/>
+  </hmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_cff2_input/TestCFF2_Regular.ttx b/Tests/varLib/data/master_cff2_input/TestCFF2_Regular.ttx
new file mode 100644
index 0000000..bf0a962
--- /dev/null
+++ b/Tests/varLib/data/master_cff2_input/TestCFF2_Regular.ttx
@@ -0,0 +1,508 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.2">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+    <GlyphID id="2" name="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x60d07155"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="31"/>
+    <yMin value="-115"/>
+    <xMax value="569"/>
+    <yMax value="751"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="984"/>
+    <descent value="-273"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="31"/>
+    <minRightSideBearing value="31"/>
+    <xMaxExtent value="569"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="579"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="291"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="486"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.a"/>
+    </extraNames>
+  </post>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueValues value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+            <OtherBlues value="-217 -205"/>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="67"/>
+            <StdVW value="85"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          62 0 rmoveto
+          476 0 rlineto
+          0 660 rlineto
+          -476 0 rlineto
+          0 -660 rlineto
+          109 59 rmoveto
+          73 131 rlineto
+          54 102 rlineto
+          4 0 rlineto
+          52 -102 rlineto
+          73 -131 rlineto
+          -256 0 rlineto
+          -44 52 rmoveto
+          0 461 rlineto
+          127 -232 rlineto
+          -127 -229 rlineto
+          171 277 rmoveto
+          -50 93 rlineto
+          -66 119 rlineto
+          234 0 rlineto
+          -65 -119 rlineto
+          -49 -93 rlineto
+          -4 0 rlineto
+          48 -48 rmoveto
+          126 232 rlineto
+          0 -461 rlineto
+          -126 229 rlineto
+        </CharString>
+        <CharString name="A">
+          31 0 rmoveto
+          86 0 rlineto
+          115 366 rlineto
+          23 73 21 72 21 76 rrcurveto
+          4 0 rlineto
+          20 -76 22 -72 23 -73 rrcurveto
+          113 -366 rlineto
+          90 0 rlineto
+          -221 656 rlineto
+          -96 0 rlineto
+          -221 -656 rlineto
+          117 199 rmoveto
+          301 0 rlineto
+          0 68 rlineto
+          -301 0 rlineto
+          0 -68 rlineto
+        </CharString>
+        <CharString name="T">
+          258 0 rmoveto
+          84 0 rlineto
+          0 585 rlineto
+          217 0 rlineto
+          0 71 rlineto
+          -518 0 rlineto
+          0 -71 rlineto
+          217 0 rlineto
+          0 -585 rlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 248 35 rmoveto
+          -39 0 -45 5 -46 18 rrcurveto
+          53 -36 rlineto
+          -17 76 rlineto
+          -12 53 -22 13 -24 0 rrcurveto
+          -22 0 -14 -11 -9 -20 rrcurveto
+          4 -87 81 -59 107 0 rrcurveto
+          136 0 82 76 0 107 rrcurveto
+          0 82 -41 65 -135 47 rrcurveto
+          -38 13 rlineto
+          -71 23 -40 35 0 64 rrcurveto
+          0 75 57 37 74 0 rrcurveto
+          30 0 36 -5 42 -17 rrcurveto
+          -52 36 rlineto
+          17 -76 rlineto
+          12 -52 25 -14 22 0 rrcurveto
+          19 0 17 10 8 21 rrcurveto
+          -6 86 -80 60 -101 0 rrcurveto
+          -115 0 -83 -80 0 -102 rrcurveto
+          0 -100 62 -54 105 -37 rrcurveto
+          37 -13 rlineto
+          85 -30 36 -30 0 -63 rrcurveto
+          0 -74 -53 -42 -82 0 rrcurveto
+          31 287 rmoveto
+          0 428 rlineto
+          -40 0 rlineto
+          0 -428 rlineto
+          40 0 rlineto
+          -41 -437 rmoveto
+          40 0 rlineto
+          0 437 rlineto
+          -40 0 rlineto
+          0 -437 rlineto
+        </CharString>
+        <CharString name="dollar.a">
+          304 34 rmoveto
+          125 0 86 65 0 96 rrcurveto
+          0 183 -324 -21 0 110 rrcurveto
+          0 50 42 32 67 0 rrcurveto
+          68 0 36 -21 47 -36 rrcurveto
+          44 49 rlineto
+          -46 44 -54 33 -89 0 rrcurveto
+          -115 0 -81 -59 0 -94 rrcurveto
+          0 -174 324 22 0 -124 rrcurveto
+          0 -51 -42 -35 -78 0 rrcurveto
+          -76 0 -62 31 -52 37 rrcurveto
+          -39 -58 rlineto
+          52 -43 84 -36 83 0 rrcurveto
+          -51 -147 rmoveto
+          159 857 rlineto
+          -56 7 rlineto
+          -159 -858 rlineto
+          56 -6 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=2 -->
+        <BaselineTag index="0" value="ideo"/>
+        <BaselineTag index="1" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=4 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="1"/>
+              <!-- BaseCoordCount=2 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-170"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+  </BASE>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="size"/>
+        <Feature>
+          <FeatureParamsSize>
+            <DesignSize value="10.0"/>
+            <SubfamilyID value="0"/>
+            <SubfamilyNameID value="0"/>
+            <RangeStart value="0.0"/>
+            <RangeEnd value="0.0"/>
+          </FeatureParamsSize>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0" Format="1">
+          <Substitution in="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="62"/>
+    <mtx name="A" width="600" lsb="31"/>
+    <mtx name="T" width="600" lsb="41"/>
+    <mtx name="dollar" width="497" lsb="51"/>
+    <mtx name="dollar.a" width="600" lsb="85"/>
+  </hmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/models_test.py b/Tests/varLib/models_test.py
index 56f7104..1027f29 100644
--- a/Tests/varLib/models_test.py
+++ b/Tests/varLib/models_test.py
@@ -1,6 +1,6 @@
 from fontTools.misc.py23 import *
 from fontTools.varLib.models import (
-    normalizeLocation, supportScalar, VariationModel)
+    normalizeLocation, supportScalar, VariationModel, VariationModelError)
 import pytest
 
 
@@ -145,7 +145,7 @@
         assert model.deltaWeights == deltaWeights
 
     def test_init_duplicate_locations(self):
-        with pytest.raises(ValueError, match="locations must be unique"):
+        with pytest.raises(VariationModelError, match="Locations must be unique."):
             VariationModel(
                 [
                     {"foo": 0.0, "bar": 0.0},
diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py
index e5c2a00..58ac290 100644
--- a/Tests/varLib/varLib_test.py
+++ b/Tests/varLib/varLib_test.py
@@ -1,6 +1,7 @@
 from fontTools.misc.py23 import *
 from fontTools.ttLib import TTFont, newTable
-from fontTools.varLib import build
+from fontTools.varLib import build, load_designspace
+from fontTools.varLib.errors import VarLibValidationError
 from fontTools.varLib.mutator import instantiateVariableFont
 from fontTools.varLib import main as varLib_main, load_masters
 from fontTools.varLib import set_default_weight_width_slant
@@ -314,6 +315,28 @@
         tables = ["fvar", "CFF2"]
         self.expect_ttx(varfont, expected_ttx_path, tables)
 
+    def test_varlib_build_CFF2_from_CFF2(self):
+        ds_path = self.get_test_input('TestCFF2Input.designspace')
+        ttx_dir = self.get_test_input("master_cff2_input")
+        expected_ttx_path = self.get_test_output("BuildTestCFF2.ttx")
+
+        self.temp_dir()
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestCFF2_'):
+            self.compile_font(path, ".otf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        tables = ["fvar", "CFF2"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
     def test_varlib_build_sparse_CFF2(self):
         ds_path = self.get_test_input('TestSparseCFF2VF.designspace')
         ttx_dir = self.get_test_input("master_sparse_cff2")
@@ -475,6 +498,27 @@
         tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
         self.expect_ttx(varfont, expected_ttx_path, tables)
 
+    def test_varlib_build_lazy_masters(self):
+        # See https://github.com/fonttools/fonttools/issues/1808
+        ds_path = self.get_test_input("SparseMasters.designspace")
+        expected_ttx_path = self.get_test_output("SparseMasters.ttx")
+
+        def _open_font(master_path, master_finder=lambda s: s):
+            font = TTFont()
+            font.importXML(master_path)
+            buf = BytesIO()
+            font.save(buf, reorderTables=False)
+            buf.seek(0)
+            font = TTFont(buf, lazy=True)  # reopen in lazy mode, to reproduce #1808
+            return font
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        ds.loadSourceFonts(_open_font)
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
     def test_varlib_build_sparse_masters_MVAR(self):
         import fontTools.varLib.mvar
 
@@ -682,6 +726,13 @@
             ("B", "D"): 40,
         }
 
+    def test_designspace_fill_in_location(self):
+        ds_path = self.get_test_input("VarLibLocationTest.designspace")
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        ds_loaded = load_designspace(ds)
+
+        assert ds_loaded.instances[0].location == {"weight": 0, "width": 50}
+
 
 def test_load_masters_layerName_without_required_font():
     ds = DesignSpaceDocument()
@@ -691,7 +742,7 @@
     ds.addSource(s)
 
     with pytest.raises(
-        AttributeError,
+        VarLibValidationError,
         match="specified a layer name but lacks the required TTFont object",
     ):
         load_masters(ds)
diff --git a/setup.cfg b/setup.cfg
index 64283d5..63f7a21 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 4.2.3.dev0
+current_version = 4.4.1.dev0
 commit = True
 tag = False
 tag_name = {new_version}
diff --git a/setup.py b/setup.py
index a188d59..6f5f558 100755
--- a/setup.py
+++ b/setup.py
@@ -345,7 +345,7 @@
 
 setup(
 	name="fonttools",
-	version="4.2.3.dev0",
+	version="4.4.1.dev0",
 	description="Tools to manipulate font files",
 	author="Just van Rossum",
 	author_email="just@letterror.com",