File size: 6,934 Bytes
fe41391 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
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."""
|