diff --git a/Lib/fontTools/varLib/errors.py b/Lib/fontTools/varLib/errors.py
index 128629bfc..5840070f7 100644
--- a/Lib/fontTools/varLib/errors.py
+++ b/Lib/fontTools/varLib/errors.py
@@ -1,22 +1,6 @@
-from enum import Enum, auto
import textwrap
-class VarLibMergeFailure(Enum):
- ShouldBeConstant = "some values were different, but should have been the same"
- MismatchedTypes = "data had inconsistent types"
- LengthsDiffer = "a list of objects had inconsistent lengths"
- KeysDiffer = "a list of objects had different keys"
- InconsistentGlyphOrder = "the glyph order was inconsistent between masters"
- FoundANone = "one of the values in a list was empty when it shouldn't have been"
- NotANone = "one of the values in a list was not empty when it should have been"
- UnsupportedFormat = "an OpenType subtable (%s) had a format I didn't expect"
- InconsistentFormat = (
- "an OpenType subtable (%s) had inconsistent formats between masters"
- )
- InconsistentExtensions = "the masters use extension lookups in inconsistent ways"
-
-
class VarLibError(Exception):
"""Base exception for the varLib module."""
@@ -28,47 +12,144 @@ class VarLibValidationError(VarLibError):
class VarLibMergeError(VarLibError):
"""Raised when input data cannot be merged into a variable font."""
- def __init__(self, merger, args):
+ def __init__(self, merger, **kwargs):
self.merger = merger
- self.args = args
+ if not kwargs:
+ kwargs = {}
+ if "stack" in kwargs:
+ self.stack = kwargs["stack"]
+ del kwargs["stack"]
+ else:
+ self.stack = []
+ self.cause = kwargs
+
+ @property
+ def reason(self):
+ return self.__doc__
+
+ def _master_name(self, ix):
+ ttf = self.merger.ttfs[ix]
+ if (
+ "name" in ttf
+ and ttf["name"].getDebugName(1)
+ and ttf["name"].getDebugName(2)
+ ):
+ return ttf["name"].getDebugName(1) + " " + ttf["name"].getDebugName(2)
+ elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"):
+ return ttf.reader.file.name
+ else:
+ return "master number %i" % ix
+
+ @property
+ def offender(self):
+ if "expected" in self.cause and "got" in self.cause:
+ index = [x == self.cause["expected"] for x in self.cause["got"]].index(
+ False
+ )
+ return index, self._master_name(index)
+ return None, None
+
+ @property
+ def details(self):
+ if "expected" in self.cause and "got" in self.cause:
+ offender_index, offender = self.offender
+ got = self.cause["got"][offender_index]
+ return f"Expected to see {self.stack[0]}=={self.cause['expected']}, instead saw {got}\n"
+ return ""
def __str__(self):
- cause, stack = self.args[0], self.args[1:]
- fontnames = [
- ttf["name"].getDebugName(1) + " " + ttf["name"].getDebugName(2)
- for ttf in self.merger.ttfs
- ]
- context = "".join(reversed(stack))
- details = ""
- reason = cause["reason"].value
- if reason == VarLibMergeFailure.FoundANone:
- offender = [x is None for x in cause["got"]].index(True)
- details = (
- f"\n\nThe problem is likely to be in {fontnames[offender]}:\n"
- f"{stack[0]}=={cause['got']}\n"
- )
- elif "expected" in cause and "got" in cause:
- offender = [x == cause["expected"] for x in cause["got"]].index(False)
- got = cause["got"][offender]
- details = (
- f"\n\nThe problem is likely to be in {fontnames[offender]}:\n"
- f"Expected to see {stack[0]}=={cause['expected']}, instead saw {got}\n"
- )
-
- if (
- reason == VarLibMergeFailure.UnsupportedFormat
- or reason == VarLibMergeFailure.InconsistentFormat
- ):
- reason = reason % cause["subtable"]
+ offender_index, offender = self.offender
+ location = ""
+ if offender:
+ location = f"\n\nThe problem is likely to be in {offender}:\n"
+ context = "".join(reversed(self.stack))
basic = textwrap.fill(
- f"Couldn't merge the fonts, because {reason}. "
+ f"Couldn't merge the fonts, because {self.reason}. "
f"This happened while performing the following operation: {context}",
width=78,
)
- return "\n\n" + basic + details
+ return "\n\n" + basic + location + self.details
-class VarLibCFFDictMergeError(VarLibMergeError):
+class ShouldBeConstant(VarLibMergeError):
+ """some values were different, but should have been the same"""
+
+ @property
+ def details(self):
+ if self.stack[0] != ".FeatureCount":
+ return super().details
+ offender_index, offender = self.offender
+ bad_ttf = self.merger.ttfs[offender_index]
+ good_ttf = self.merger.ttfs[offender_index - 1]
+
+ good_features = [
+ x.FeatureTag
+ for x in good_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
+ ]
+ bad_features = [
+ x.FeatureTag
+ for x in bad_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
+ ]
+ return (
+ "\nIncompatible features between masters.\n"
+ f"Expected: {', '.join(good_features)}.\n"
+ f"Got: {', '.join(bad_features)}.\n"
+ )
+
+
+class FoundANone(VarLibMergeError):
+ """one of the values in a list was empty when it shouldn't have been"""
+
+ @property
+ def offender(self):
+ cause = self.argv[0]
+ index = [x is None for x in cause["got"]].index(True)
+ return index, self._master_name(index)
+
+ @property
+ def details(self):
+ cause, stack = self.args[0], self.args[1:]
+ return f"{stack[0]}=={cause['got']}\n"
+
+
+class MismatchedTypes(VarLibMergeError):
+ """data had inconsistent types"""
+
+
+class LengthsDiffer(VarLibMergeError):
+ """a list of objects had inconsistent lengths"""
+
+
+class KeysDiffer(VarLibMergeError):
+ """a list of objects had different keys"""
+
+
+class InconsistentGlyphOrder(VarLibMergeError):
+ """the glyph order was inconsistent between masters"""
+
+
+class InconsistentExtensions(VarLibMergeError):
+ """the masters use extension lookups in inconsistent ways"""
+
+
+class UnsupportedFormat(VarLibMergeError):
+ """an OpenType subtable (%s) had a format I didn't expect"""
+
+ @property
+ def reason(self):
+ cause, stack = self.args[0], self.args[1:]
+ return self.__doc__ % cause["subtable"]
+
+
+class UnsupportedFormat(UnsupportedFormat):
+ """an OpenType subtable (%s) had inconsistent formats between masters"""
+
+
+class VarLibCFFMergeError(VarLibError):
+ pass
+
+
+class VarLibCFFDictMergeError(VarLibCFFMergeError):
"""Raised when a CFF PrivateDict cannot be merged."""
def __init__(self, key, value, values):
@@ -81,7 +162,7 @@ class VarLibCFFDictMergeError(VarLibMergeError):
self.args = (error_msg,)
-class VarLibCFFPointTypeMergeError(VarLibMergeError):
+class VarLibCFFPointTypeMergeError(VarLibCFFMergeError):
"""Raised when a CFF glyph cannot be merged because of point type differences."""
def __init__(self, point_type, pt_index, m_index, default_type, glyph_name):
@@ -93,7 +174,7 @@ class VarLibCFFPointTypeMergeError(VarLibMergeError):
self.args = (error_msg,)
-class VarLibCFFHintTypeMergeError(VarLibMergeError):
+class VarLibCFFHintTypeMergeError(VarLibCFFMergeError):
"""Raised when a CFF glyph cannot be merged because of hint type differences."""
def __init__(self, hint_type, cmd_index, m_index, default_type, glyph_name):
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py
index 969a7a05b..b8b7b27d6 100644
--- a/Lib/fontTools/varLib/merger.py
+++ b/Lib/fontTools/varLib/merger.py
@@ -14,8 +14,18 @@ from fontTools.varLib.varStore import VarStoreInstancer
from functools import reduce
from fontTools.otlLib.builder import buildSinglePos
-from .errors import VarLibMergeError, VarLibMergeFailure
-
+from .errors import (
+ ShouldBeConstant,
+ FoundANone,
+ MismatchedTypes,
+ LengthsDiffer,
+ KeysDiffer,
+ InconsistentGlyphOrder,
+ InconsistentExtensions,
+ UnsupportedFormat,
+ UnsupportedFormat,
+ VarLibMergeError,
+)
class Merger(object):
@@ -69,10 +79,9 @@ class Merger(object):
item.ensureDecompiled()
keys = sorted(vars(out).keys())
if not all(keys == sorted(vars(v).keys()) for v in lst):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.KeysDiffer,
- "expected": keys,
- "got": [sorted(vars(v).keys()) for v in lst]},))
+ raise KeysDiffer(self, expected=keys,
+ got=[sorted(vars(v).keys()) for v in lst]
+ )
mergers = self.mergersFor(out)
defaultMerger = mergers.get('*', self.__class__.mergeThings)
try:
@@ -82,29 +91,26 @@ class Merger(object):
values = [getattr(table, key) for table in lst]
mergerFunc = mergers.get(key, defaultMerger)
mergerFunc(self, value, values)
- except Exception as e:
- e.args = e.args + ('.'+key,)
+ except VarLibMergeError as e:
+ e.stack.append('.'+key)
raise
def mergeLists(self, out, lst):
if not allEqualTo(out, lst, len):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.LengthsDiffer,
- "expected": len(out),
- "got": [len(x) for x in lst]},))
+ raise LengthsDiffer(self, expected=len(out), got=[len(x) for x in lst])
for i,(value,values) in enumerate(zip(out, zip(*lst))):
try:
self.mergeThings(value, values)
- except Exception as e:
- e.args = e.args + ('[%d]' % i,)
+ except VarLibMergeError as e:
+ e.stack.append('[%d]' % i)
raise
def mergeThings(self, out, lst):
if not allEqualTo(out, lst, type):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.MismatchedTypes,
- "expected": type(out),
- "got": [type(x) for x in lst]},))
+ raise MismatchedTypes(self,
+ expected=type(out).__name__,
+ got=[type(x).__name__ for x in lst]
+ )
mergerFunc = self.mergersFor(out).get(None, None)
if mergerFunc is not None:
mergerFunc(self, out, lst)
@@ -114,17 +120,18 @@ class Merger(object):
self.mergeLists(out, lst)
else:
if not allEqualTo(out, lst):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.ShouldBeConstant,
- "expected": out,
- "got": lst},))
+ raise ShouldBeConstant(self, expected=out, got=lst)
def mergeTables(self, font, master_ttfs, tableTags):
- self.ttfs = master_ttfs # For error reporting
for tag in tableTags:
if tag not in font: continue
- self.mergeThings(font[tag], [m[tag] if tag in m else None
- for m in master_ttfs])
+ try:
+ self.ttfs = [m for m in master_ttfs if tag in m]
+ self.mergeThings(font[tag], [m[tag] if tag in m else None
+ for m in master_ttfs])
+ except VarLibMergeError as e:
+ e.stack.append(tag)
+ raise
#
# Aligning merger
@@ -136,11 +143,7 @@ class AligningMerger(Merger):
def merge(merger, self, lst):
if self is None:
if not allNone(lst):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.NotANone,
- "expected": None,
- "got": lst
- },))
+ raise NotANone(self, expected=None, got=lst)
return
lst = [l.classDefs for l in lst]
@@ -153,11 +156,7 @@ def merge(merger, self, lst):
for k in allKeys:
allValues = nonNone(l.get(k) for l in lst)
if not allEqual(allValues):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.ShouldBeConstant,
- "expected": allValues[0],
- "got": lst,
- }, "."+k))
+ raise ShouldBeConstant(self, expected=allValues[0], got=lst, stack="."+k)
if not allValues:
self[k] = None
else:
@@ -194,9 +193,7 @@ def _merge_GlyphOrders(font, lst, values_lst=None, default=None):
order = sorted(combined, key=sortKey)
# Make sure all input glyphsets were in proper order
if not all(sorted(vs, key=sortKey) == vs for vs in lst):
- raise VarLibMergeError(self, ({
- "reason" : VarLibMergeFailure.InconsistentGlyphOrder
- },))
+ raise InconsistentGlyphOrder(self)
del combined
paddedValues = None
@@ -223,10 +220,7 @@ def _Lookup_SinglePos_get_effective_value(subtables, glyph):
elif self.Format == 2:
return self.Value[self.Coverage.glyphs.index(glyph)]
else:
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "single positioning lookup"
- },))
+ raise UnsupportedFormat(self, subtable="single positioning lookup")
return None
def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph):
@@ -248,21 +242,14 @@ def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph)
klass2 = self.ClassDef2.classDefs.get(secondGlyph, 0)
return self.Class1Record[klass1].Class2Record[klass2]
else:
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "pair positioning lookup"
- },))
+ raise UnsupportedFormat(self, subtable="pair positioning lookup")
return None
@AligningMerger.merger(ot.SinglePos)
def merge(merger, self, lst):
self.ValueFormat = valueFormat = reduce(int.__or__, [l.ValueFormat for l in lst], 0)
if not (len(lst) == 1 or (valueFormat & ~0xF == 0)):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "single positioning lookup"
- },))
-
+ raise UnsupportedFormat(self, subtable="single positioning lookup")
# If all have same coverage table and all are format 1,
coverageGlyphs = self.Coverage.glyphs
@@ -548,10 +535,7 @@ def merge(merger, self, lst):
elif self.Format == 2:
_PairPosFormat2_merge(self, lst, merger)
else:
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "pair positioning lookup"
- },))
+ raise UnsupportedFormat(self, subtable="pair positioning lookup")
del merger.valueFormat1, merger.valueFormat2
@@ -617,8 +601,7 @@ def _MarkBasePosFormat1_merge(self, lst, merger, Mark='Mark', Base='Base'):
# input masters.
if not allEqual(allClasses):
- raise VarLibMergeError(self, allClasses)
- if not allClasses:
+ raise allClasses(self, allClasses)
rec = None
else:
rec = ot.MarkRecord()
@@ -667,35 +650,28 @@ def _MarkBasePosFormat1_merge(self, lst, merger, Mark='Mark', Base='Base'):
@AligningMerger.merger(ot.MarkBasePos)
def merge(merger, self, lst):
if not allEqualTo(self.Format, (l.Format for l in lst)):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.InconsistentFormats,
- "subtable": "mark-to-base positioning lookup",
- "expected": self.Format,
- "got": [l.Format for l in lst]},))
+ raise InconsistentFormats(self,
+ subtable="mark-to-base positioning lookup",
+ expected=self.Format,
+ got=[l.Format for l in lst]
+ )
if self.Format == 1:
_MarkBasePosFormat1_merge(self, lst, merger)
else:
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "mark-to-base positioning lookup",
- }))
+ raise UnsupportedFormat(self, subtable="mark-to-base positioning lookup")
@AligningMerger.merger(ot.MarkMarkPos)
def merge(merger, self, lst):
if not allEqualTo(self.Format, (l.Format for l in lst)):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.InconsistentFormats,
- "subtable": "mark-to-mark positioning lookup",
- "expected": self.Format,
- "got": [l.Format for l in lst]},))
+ raise InconsistentFormats(self,
+ subtable="mark-to-mark positioning lookup",
+ expected=self.Format,
+ got=[l.Format for l in lst]
+ )
if self.Format == 1:
_MarkBasePosFormat1_merge(self, lst, merger, 'Mark1', 'Mark2')
else:
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "mark-to-mark positioning lookup",
- }))
-
+ raise UnsupportedFormat(self, subtable="mark-to-mark positioning lookup")
def _PairSet_flatten(lst, font):
self = ot.PairSet()
@@ -824,15 +800,12 @@ def merge(merger, self, lst):
continue
if sts[0].__class__.__name__.startswith('Extension'):
if not allEqual([st.__class__ for st in sts]):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.InconsistentExtensions,
- "expected": "Extension",
- "got": [st.__class__.__name__ for st in sts]
- },))
+ raise InconsistentExtensions(self,
+ expected="Extension",
+ got=[st.__class__.__name__ for st in sts]
+ )
if not allEqual([st.ExtensionLookupType for st in sts]):
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.InconsistentExtensions,
- },))
+ raise InconsistentExtensions(self)
l.LookupType = sts[0].ExtensionLookupType
new_sts = [st.ExtSubTable for st in sts]
del sts[:]
@@ -1061,10 +1034,7 @@ class VariationMerger(AligningMerger):
if None in lst:
if allNone(lst):
if out is not None:
- raise VarLibMergeError(self, ({
- "reason": VarLibMergeFailure.FoundANone,
- "got": lst
- },))
+ raise FoundANone(self, got=lst)
return
masterModel = self.model
model, lst = masterModel.getSubModel(lst)
@@ -1085,10 +1055,7 @@ def buildVarDevTable(store_builder, master_values):
@VariationMerger.merger(ot.BaseCoord)
def merge(merger, self, lst):
if self.Format != 1:
- raise VarLibMergeError(self, ({
- "cause": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "a baseline coordinate"
- },))
+ raise UnsupportedFormat(self, subtable="a baseline coordinate")
self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
if DeviceTable:
self.Format = 3
@@ -1097,10 +1064,7 @@ def merge(merger, self, lst):
@VariationMerger.merger(ot.CaretValue)
def merge(merger, self, lst):
if self.Format != 1:
- raise VarLibMergeError(self, ({
- "cause": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "a caret"
- },))
+ raise UnsupportedFormat(self, subtable="a caret")
self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
if DeviceTable:
self.Format = 3
@@ -1109,10 +1073,7 @@ def merge(merger, self, lst):
@VariationMerger.merger(ot.Anchor)
def merge(merger, self, lst):
if self.Format != 1:
- raise VarLibMergeError(self, ({
- "cause": VarLibMergeFailure.UnsupportedFormat,
- "subtable": "an anchor"
- },))
+ raise UnsupportedFormat(self, subtable="an anchor")
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/Tests/varLib/data/IncompatibleArrays.designspace b/Tests/varLib/data/IncompatibleArrays.designspace
new file mode 100644
index 000000000..399810ea0
--- /dev/null
+++ b/Tests/varLib/data/IncompatibleArrays.designspace
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/IncompatibleFeatures.designspace b/Tests/varLib/data/IncompatibleFeatures.designspace
new file mode 100644
index 000000000..ab275164a
--- /dev/null
+++ b/Tests/varLib/data/IncompatibleFeatures.designspace
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/IncompatibleLookupTypes.designspace b/Tests/varLib/data/IncompatibleLookupTypes.designspace
new file mode 100644
index 000000000..c7d357549
--- /dev/null
+++ b/Tests/varLib/data/IncompatibleLookupTypes.designspace
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Bold.ttx b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Bold.ttx
new file mode 100644
index 000000000..1869dd426
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Bold.ttx
@@ -0,0 +1,612 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Two Axis
+
+
+ Bold
+
+
+ 1.000;NONE;SimpleTwoAxis-Bold
+
+
+ Simple Two Axis Bold
+
+
+ Version 1.000
+
+
+ SimpleTwoAxis-Bold
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Regular.ttx b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Regular.ttx
new file mode 100644
index 000000000..7186a3e97
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Regular.ttx
@@ -0,0 +1,626 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Two Axis
+
+
+ Regular
+
+
+ 1.000;NONE;SimpleTwoAxis-Regular
+
+
+ Simple Two Axis Regular
+
+
+ Version 1.000
+
+
+ SimpleTwoAxis-Regular
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Bold.ttx b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Bold.ttx
new file mode 100644
index 000000000..a1803e9e9
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Bold.ttx
@@ -0,0 +1,578 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Two Axis
+
+
+ Bold
+
+
+ 1.000;NONE;SimpleTwoAxis-Bold
+
+
+ Simple Two Axis Bold
+
+
+ Version 1.000
+
+
+ SimpleTwoAxis-Bold
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Regular.ttx b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Regular.ttx
new file mode 100644
index 000000000..7186a3e97
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Regular.ttx
@@ -0,0 +1,626 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Two Axis
+
+
+ Regular
+
+
+ 1.000;NONE;SimpleTwoAxis-Regular
+
+
+ Simple Two Axis Regular
+
+
+ Version 1.000
+
+
+ SimpleTwoAxis-Regular
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx
new file mode 100644
index 000000000..cf25178da
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx
@@ -0,0 +1,622 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Two Axis
+
+
+ Bold
+
+
+ 1.000;NONE;SimpleTwoAxis-Bold
+
+
+ Simple Two Axis Bold
+
+
+ Version 1.000
+
+
+ SimpleTwoAxis-Bold
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx
new file mode 100644
index 000000000..b1aac860b
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx
@@ -0,0 +1,626 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple Two Axis
+
+
+ Regular
+
+
+ 1.000;NONE;SimpleTwoAxis-Regular
+
+
+ Simple Two Axis Regular
+
+
+ Version 1.000
+
+
+ SimpleTwoAxis-Regular
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py
index da1f94f4a..0be1c99de 100644
--- a/Tests/varLib/varLib_test.py
+++ b/Tests/varLib/varLib_test.py
@@ -2,6 +2,7 @@ from fontTools.misc.py23 import *
from fontTools.ttLib import TTFont, newTable
from fontTools.varLib import build, load_designspace
from fontTools.varLib.errors import VarLibValidationError
+import fontTools.varLib.errors as varLibErrors
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
@@ -813,6 +814,62 @@ class BuildTest(unittest.TestCase):
assert ds_loaded.instances[0].location == {"weight": 0, "width": 50}
+ def test_varlib_build_incompatible_features(self):
+ with pytest.raises(
+ varLibErrors.ShouldBeConstant,
+ match = """
+
+Couldn't merge the fonts, because some values were different, but should have
+been the same. This happened while performing the following operation:
+GPOS.table.FeatureList.FeatureCount
+
+The problem is likely to be in Simple Two Axis Bold:
+
+Incompatible features between masters.
+Expected: kern, mark.
+Got: kern.
+"""):
+
+ self._run_varlib_build_test(
+ designspace_name="IncompatibleFeatures",
+ font_name="IncompatibleFeatures",
+ tables=["GPOS"],
+ expected_ttx_name="IncompatibleFeatures",
+ save_before_dump=True,
+ )
+
+ def test_varlib_build_incompatible_lookup_types(self):
+ with pytest.raises(
+ varLibErrors.MismatchedTypes,
+ match = r"MarkBasePos, instead saw PairPos"
+ ):
+ self._run_varlib_build_test(
+ designspace_name="IncompatibleLookupTypes",
+ font_name="IncompatibleLookupTypes",
+ tables=["GPOS"],
+ expected_ttx_name="IncompatibleLookupTypes",
+ save_before_dump=True,
+ )
+
+ def test_varlib_build_incompatible_arrays(self):
+ with pytest.raises(
+ varLibErrors.ShouldBeConstant,
+ match = """
+
+Couldn't merge the fonts, because some values were different, but should have
+been the same. This happened while performing the following operation:
+GPOS.table.ScriptList.ScriptCount
+
+The problem is likely to be in Simple Two Axis Bold:
+Expected to see .ScriptCount==1, instead saw 0"""
+ ):
+ self._run_varlib_build_test(
+ designspace_name="IncompatibleArrays",
+ font_name="IncompatibleArrays",
+ tables=["GPOS"],
+ expected_ttx_name="IncompatibleArrays",
+ save_before_dump=True,
+ )
def test_load_masters_layerName_without_required_font():
ds = DesignSpaceDocument()