xref: /aosp_15_r20/external/fonttools/Lib/fontTools/varLib/errors.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesimport textwrap
2*e1fe3e4aSElliott Hughes
3*e1fe3e4aSElliott Hughes
4*e1fe3e4aSElliott Hughesclass VarLibError(Exception):
5*e1fe3e4aSElliott Hughes    """Base exception for the varLib module."""
6*e1fe3e4aSElliott Hughes
7*e1fe3e4aSElliott Hughes
8*e1fe3e4aSElliott Hughesclass VarLibValidationError(VarLibError):
9*e1fe3e4aSElliott Hughes    """Raised when input data is invalid from varLib's point of view."""
10*e1fe3e4aSElliott Hughes
11*e1fe3e4aSElliott Hughes
12*e1fe3e4aSElliott Hughesclass VarLibMergeError(VarLibError):
13*e1fe3e4aSElliott Hughes    """Raised when input data cannot be merged into a variable font."""
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hughes    def __init__(self, merger=None, **kwargs):
16*e1fe3e4aSElliott Hughes        self.merger = merger
17*e1fe3e4aSElliott Hughes        if not kwargs:
18*e1fe3e4aSElliott Hughes            kwargs = {}
19*e1fe3e4aSElliott Hughes        if "stack" in kwargs:
20*e1fe3e4aSElliott Hughes            self.stack = kwargs["stack"]
21*e1fe3e4aSElliott Hughes            del kwargs["stack"]
22*e1fe3e4aSElliott Hughes        else:
23*e1fe3e4aSElliott Hughes            self.stack = []
24*e1fe3e4aSElliott Hughes        self.cause = kwargs
25*e1fe3e4aSElliott Hughes
26*e1fe3e4aSElliott Hughes    @property
27*e1fe3e4aSElliott Hughes    def reason(self):
28*e1fe3e4aSElliott Hughes        return self.__doc__
29*e1fe3e4aSElliott Hughes
30*e1fe3e4aSElliott Hughes    def _master_name(self, ix):
31*e1fe3e4aSElliott Hughes        if self.merger is not None:
32*e1fe3e4aSElliott Hughes            ttf = self.merger.ttfs[ix]
33*e1fe3e4aSElliott Hughes            if "name" in ttf and ttf["name"].getBestFullName():
34*e1fe3e4aSElliott Hughes                return ttf["name"].getBestFullName()
35*e1fe3e4aSElliott Hughes            elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"):
36*e1fe3e4aSElliott Hughes                return ttf.reader.file.name
37*e1fe3e4aSElliott Hughes        return f"master number {ix}"
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughes    @property
40*e1fe3e4aSElliott Hughes    def offender(self):
41*e1fe3e4aSElliott Hughes        if "expected" in self.cause and "got" in self.cause:
42*e1fe3e4aSElliott Hughes            index = [x == self.cause["expected"] for x in self.cause["got"]].index(
43*e1fe3e4aSElliott Hughes                False
44*e1fe3e4aSElliott Hughes            )
45*e1fe3e4aSElliott Hughes            master_name = self._master_name(index)
46*e1fe3e4aSElliott Hughes            if "location" in self.cause:
47*e1fe3e4aSElliott Hughes                master_name = f"{master_name} ({self.cause['location']})"
48*e1fe3e4aSElliott Hughes            return index, master_name
49*e1fe3e4aSElliott Hughes        return None, None
50*e1fe3e4aSElliott Hughes
51*e1fe3e4aSElliott Hughes    @property
52*e1fe3e4aSElliott Hughes    def details(self):
53*e1fe3e4aSElliott Hughes        if "expected" in self.cause and "got" in self.cause:
54*e1fe3e4aSElliott Hughes            offender_index, offender = self.offender
55*e1fe3e4aSElliott Hughes            got = self.cause["got"][offender_index]
56*e1fe3e4aSElliott Hughes            return f"Expected to see {self.stack[0]}=={self.cause['expected']!r}, instead saw {got!r}\n"
57*e1fe3e4aSElliott Hughes        return ""
58*e1fe3e4aSElliott Hughes
59*e1fe3e4aSElliott Hughes    def __str__(self):
60*e1fe3e4aSElliott Hughes        offender_index, offender = self.offender
61*e1fe3e4aSElliott Hughes        location = ""
62*e1fe3e4aSElliott Hughes        if offender:
63*e1fe3e4aSElliott Hughes            location = f"\n\nThe problem is likely to be in {offender}:\n"
64*e1fe3e4aSElliott Hughes        context = "".join(reversed(self.stack))
65*e1fe3e4aSElliott Hughes        basic = textwrap.fill(
66*e1fe3e4aSElliott Hughes            f"Couldn't merge the fonts, because {self.reason}. "
67*e1fe3e4aSElliott Hughes            f"This happened while performing the following operation: {context}",
68*e1fe3e4aSElliott Hughes            width=78,
69*e1fe3e4aSElliott Hughes        )
70*e1fe3e4aSElliott Hughes        return "\n\n" + basic + location + self.details
71*e1fe3e4aSElliott Hughes
72*e1fe3e4aSElliott Hughes
73*e1fe3e4aSElliott Hughesclass ShouldBeConstant(VarLibMergeError):
74*e1fe3e4aSElliott Hughes    """some values were different, but should have been the same"""
75*e1fe3e4aSElliott Hughes
76*e1fe3e4aSElliott Hughes    @property
77*e1fe3e4aSElliott Hughes    def details(self):
78*e1fe3e4aSElliott Hughes        basic_message = super().details
79*e1fe3e4aSElliott Hughes
80*e1fe3e4aSElliott Hughes        if self.stack[0] != ".FeatureCount" or self.merger is None:
81*e1fe3e4aSElliott Hughes            return basic_message
82*e1fe3e4aSElliott Hughes
83*e1fe3e4aSElliott Hughes        assert self.stack[0] == ".FeatureCount"
84*e1fe3e4aSElliott Hughes        offender_index, _ = self.offender
85*e1fe3e4aSElliott Hughes        bad_ttf = self.merger.ttfs[offender_index]
86*e1fe3e4aSElliott Hughes        good_ttf = next(
87*e1fe3e4aSElliott Hughes            ttf
88*e1fe3e4aSElliott Hughes            for ttf in self.merger.ttfs
89*e1fe3e4aSElliott Hughes            if self.stack[-1] in ttf
90*e1fe3e4aSElliott Hughes            and ttf[self.stack[-1]].table.FeatureList.FeatureCount
91*e1fe3e4aSElliott Hughes            == self.cause["expected"]
92*e1fe3e4aSElliott Hughes        )
93*e1fe3e4aSElliott Hughes
94*e1fe3e4aSElliott Hughes        good_features = [
95*e1fe3e4aSElliott Hughes            x.FeatureTag
96*e1fe3e4aSElliott Hughes            for x in good_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
97*e1fe3e4aSElliott Hughes        ]
98*e1fe3e4aSElliott Hughes        bad_features = [
99*e1fe3e4aSElliott Hughes            x.FeatureTag
100*e1fe3e4aSElliott Hughes            for x in bad_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
101*e1fe3e4aSElliott Hughes        ]
102*e1fe3e4aSElliott Hughes        return basic_message + (
103*e1fe3e4aSElliott Hughes            "\nIncompatible features between masters.\n"
104*e1fe3e4aSElliott Hughes            f"Expected: {', '.join(good_features)}.\n"
105*e1fe3e4aSElliott Hughes            f"Got: {', '.join(bad_features)}.\n"
106*e1fe3e4aSElliott Hughes        )
107*e1fe3e4aSElliott Hughes
108*e1fe3e4aSElliott Hughes
109*e1fe3e4aSElliott Hughesclass FoundANone(VarLibMergeError):
110*e1fe3e4aSElliott Hughes    """one of the values in a list was empty when it shouldn't have been"""
111*e1fe3e4aSElliott Hughes
112*e1fe3e4aSElliott Hughes    @property
113*e1fe3e4aSElliott Hughes    def offender(self):
114*e1fe3e4aSElliott Hughes        index = [x is None for x in self.cause["got"]].index(True)
115*e1fe3e4aSElliott Hughes        return index, self._master_name(index)
116*e1fe3e4aSElliott Hughes
117*e1fe3e4aSElliott Hughes    @property
118*e1fe3e4aSElliott Hughes    def details(self):
119*e1fe3e4aSElliott Hughes        cause, stack = self.cause, self.stack
120*e1fe3e4aSElliott Hughes        return f"{stack[0]}=={cause['got']}\n"
121*e1fe3e4aSElliott Hughes
122*e1fe3e4aSElliott Hughes
123*e1fe3e4aSElliott Hughesclass NotANone(VarLibMergeError):
124*e1fe3e4aSElliott Hughes    """one of the values in a list was not empty when it should have been"""
125*e1fe3e4aSElliott Hughes
126*e1fe3e4aSElliott Hughes    @property
127*e1fe3e4aSElliott Hughes    def offender(self):
128*e1fe3e4aSElliott Hughes        index = [x is not None for x in self.cause["got"]].index(True)
129*e1fe3e4aSElliott Hughes        return index, self._master_name(index)
130*e1fe3e4aSElliott Hughes
131*e1fe3e4aSElliott Hughes    @property
132*e1fe3e4aSElliott Hughes    def details(self):
133*e1fe3e4aSElliott Hughes        cause, stack = self.cause, self.stack
134*e1fe3e4aSElliott Hughes        return f"{stack[0]}=={cause['got']}\n"
135*e1fe3e4aSElliott Hughes
136*e1fe3e4aSElliott Hughes
137*e1fe3e4aSElliott Hughesclass MismatchedTypes(VarLibMergeError):
138*e1fe3e4aSElliott Hughes    """data had inconsistent types"""
139*e1fe3e4aSElliott Hughes
140*e1fe3e4aSElliott Hughes
141*e1fe3e4aSElliott Hughesclass LengthsDiffer(VarLibMergeError):
142*e1fe3e4aSElliott Hughes    """a list of objects had inconsistent lengths"""
143*e1fe3e4aSElliott Hughes
144*e1fe3e4aSElliott Hughes
145*e1fe3e4aSElliott Hughesclass KeysDiffer(VarLibMergeError):
146*e1fe3e4aSElliott Hughes    """a list of objects had different keys"""
147*e1fe3e4aSElliott Hughes
148*e1fe3e4aSElliott Hughes
149*e1fe3e4aSElliott Hughesclass InconsistentGlyphOrder(VarLibMergeError):
150*e1fe3e4aSElliott Hughes    """the glyph order was inconsistent between masters"""
151*e1fe3e4aSElliott Hughes
152*e1fe3e4aSElliott Hughes
153*e1fe3e4aSElliott Hughesclass InconsistentExtensions(VarLibMergeError):
154*e1fe3e4aSElliott Hughes    """the masters use extension lookups in inconsistent ways"""
155*e1fe3e4aSElliott Hughes
156*e1fe3e4aSElliott Hughes
157*e1fe3e4aSElliott Hughesclass UnsupportedFormat(VarLibMergeError):
158*e1fe3e4aSElliott Hughes    """an OpenType subtable (%s) had a format I didn't expect"""
159*e1fe3e4aSElliott Hughes
160*e1fe3e4aSElliott Hughes    def __init__(self, merger=None, **kwargs):
161*e1fe3e4aSElliott Hughes        super().__init__(merger, **kwargs)
162*e1fe3e4aSElliott Hughes        if not self.stack:
163*e1fe3e4aSElliott Hughes            self.stack = [".Format"]
164*e1fe3e4aSElliott Hughes
165*e1fe3e4aSElliott Hughes    @property
166*e1fe3e4aSElliott Hughes    def reason(self):
167*e1fe3e4aSElliott Hughes        s = self.__doc__ % self.cause["subtable"]
168*e1fe3e4aSElliott Hughes        if "value" in self.cause:
169*e1fe3e4aSElliott Hughes            s += f" ({self.cause['value']!r})"
170*e1fe3e4aSElliott Hughes        return s
171*e1fe3e4aSElliott Hughes
172*e1fe3e4aSElliott Hughes
173*e1fe3e4aSElliott Hughesclass InconsistentFormats(UnsupportedFormat):
174*e1fe3e4aSElliott Hughes    """an OpenType subtable (%s) had inconsistent formats between masters"""
175*e1fe3e4aSElliott Hughes
176*e1fe3e4aSElliott Hughes
177*e1fe3e4aSElliott Hughesclass VarLibCFFMergeError(VarLibError):
178*e1fe3e4aSElliott Hughes    pass
179*e1fe3e4aSElliott Hughes
180*e1fe3e4aSElliott Hughes
181*e1fe3e4aSElliott Hughesclass VarLibCFFDictMergeError(VarLibCFFMergeError):
182*e1fe3e4aSElliott Hughes    """Raised when a CFF PrivateDict cannot be merged."""
183*e1fe3e4aSElliott Hughes
184*e1fe3e4aSElliott Hughes    def __init__(self, key, value, values):
185*e1fe3e4aSElliott Hughes        error_msg = (
186*e1fe3e4aSElliott Hughes            f"For the Private Dict key '{key}', the default font value list:"
187*e1fe3e4aSElliott Hughes            f"\n\t{value}\nhad a different number of values than a region font:"
188*e1fe3e4aSElliott Hughes        )
189*e1fe3e4aSElliott Hughes        for region_value in values:
190*e1fe3e4aSElliott Hughes            error_msg += f"\n\t{region_value}"
191*e1fe3e4aSElliott Hughes        self.args = (error_msg,)
192*e1fe3e4aSElliott Hughes
193*e1fe3e4aSElliott Hughes
194*e1fe3e4aSElliott Hughesclass VarLibCFFPointTypeMergeError(VarLibCFFMergeError):
195*e1fe3e4aSElliott Hughes    """Raised when a CFF glyph cannot be merged because of point type differences."""
196*e1fe3e4aSElliott Hughes
197*e1fe3e4aSElliott Hughes    def __init__(self, point_type, pt_index, m_index, default_type, glyph_name):
198*e1fe3e4aSElliott Hughes        error_msg = (
199*e1fe3e4aSElliott Hughes            f"Glyph '{glyph_name}': '{point_type}' at point index {pt_index} in "
200*e1fe3e4aSElliott Hughes            f"master index {m_index} differs from the default font point type "
201*e1fe3e4aSElliott Hughes            f"'{default_type}'"
202*e1fe3e4aSElliott Hughes        )
203*e1fe3e4aSElliott Hughes        self.args = (error_msg,)
204*e1fe3e4aSElliott Hughes
205*e1fe3e4aSElliott Hughes
206*e1fe3e4aSElliott Hughesclass VarLibCFFHintTypeMergeError(VarLibCFFMergeError):
207*e1fe3e4aSElliott Hughes    """Raised when a CFF glyph cannot be merged because of hint type differences."""
208*e1fe3e4aSElliott Hughes
209*e1fe3e4aSElliott Hughes    def __init__(self, hint_type, cmd_index, m_index, default_type, glyph_name):
210*e1fe3e4aSElliott Hughes        error_msg = (
211*e1fe3e4aSElliott Hughes            f"Glyph '{glyph_name}': '{hint_type}' at index {cmd_index} in "
212*e1fe3e4aSElliott Hughes            f"master index {m_index} differs from the default font hint type "
213*e1fe3e4aSElliott Hughes            f"'{default_type}'"
214*e1fe3e4aSElliott Hughes        )
215*e1fe3e4aSElliott Hughes        self.args = (error_msg,)
216*e1fe3e4aSElliott Hughes
217*e1fe3e4aSElliott Hughes
218*e1fe3e4aSElliott Hughesclass VariationModelError(VarLibError):
219*e1fe3e4aSElliott Hughes    """Raised when a variation model is faulty."""
220