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()