diff options
Diffstat (limited to 'contrib/python/fonttools/fontTools/varLib/errors.py')
| -rw-r--r-- | contrib/python/fonttools/fontTools/varLib/errors.py | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/contrib/python/fonttools/fontTools/varLib/errors.py b/contrib/python/fonttools/fontTools/varLib/errors.py new file mode 100644 index 00000000000..4f30f901bab --- /dev/null +++ b/contrib/python/fonttools/fontTools/varLib/errors.py @@ -0,0 +1,219 @@ +import textwrap + + +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.""" + + def __init__(self, merger=None, **kwargs): + self.merger = merger + 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): + if self.merger is not None: + ttf = self.merger.ttfs[ix] + if "name" in ttf and ttf["name"].getBestFullName(): + return ttf["name"].getBestFullName() + elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"): + return ttf.reader.file.name + return f"master number {ix}" + + @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 + ) + master_name = self._master_name(index) + if "location" in self.cause: + master_name = f"{master_name} ({self.cause['location']})" + return index, master_name + return None, None + + @property + 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']!r}, instead saw {got!r}\n" + return "" + + def __str__(self): + 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 {self.reason}. " + f"This happened while performing the following operation: {context}", + width=78, + ) + return "\n\n" + basic + location + self.details + + +class ShouldBeConstant(VarLibMergeError): + """some values were different, but should have been the same""" + + @property + def details(self): + basic_message = super().details + + if self.stack[0] != ".FeatureCount" or self.merger is None: + return basic_message + + assert self.stack[0] == ".FeatureCount" + offender_index, _ = self.offender + bad_ttf = self.merger.ttfs[offender_index] + good_ttf = next( + ttf + for ttf in self.merger.ttfs + if self.stack[-1] in ttf + and ttf[self.stack[-1]].table.FeatureList.FeatureCount + == self.cause["expected"] + ) + + good_features = [ + x.FeatureTag + 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 basic_message + ( + "\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): + index = [x is None for x in self.cause["got"]].index(True) + return index, self._master_name(index) + + @property + def details(self): + cause, stack = self.cause, self.stack + return f"{stack[0]}=={cause['got']}\n" + + +class NotANone(VarLibMergeError): + """one of the values in a list was not empty when it should have been""" + + @property + def offender(self): + index = [x is not None for x in self.cause["got"]].index(True) + return index, self._master_name(index) + + @property + def details(self): + cause, stack = self.cause, self.stack + return f"{stack[0]}=={cause['got']}\n" + + +class MismatchedTypes(VarLibMergeError): + """data had inconsistent types""" + + +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""" + + def __init__(self, merger=None, **kwargs): + super().__init__(merger, **kwargs) + if not self.stack: + self.stack = [".Format"] + + @property + def reason(self): + s = self.__doc__ % self.cause["subtable"] + if "value" in self.cause: + s += f" ({self.cause['value']!r})" + return s + + +class InconsistentFormats(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): + 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(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): + 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 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): + error_msg = ( + f"Glyph '{glyph_name}': '{hint_type}' at index {cmd_index} in " + f"master index {m_index} differs from the default font hint type " + f"'{default_type}'" + ) + self.args = (error_msg,) + + +class VariationModelError(VarLibError): + """Raised when a variation model is faulty.""" |
