read_fonts/tables/glyf/bytecode/
error.rs1use super::{DecodeError, Opcode, Program};
9use crate::types::GlyphId;
10
11#[derive(Clone, PartialEq, Debug)]
13pub enum HintErrorKind {
14 UnexpectedEndOfBytecode,
16 UnhandledOpcode(Opcode),
18 DefinitionInGlyphProgram,
21 NestedDefinition,
23 DefinitionTooLarge,
25 TooManyDefinitions,
27 InvalidDefinition(usize),
29 ValueStackOverflow,
31 ValueStackUnderflow,
33 CallStackOverflow,
35 CallStackUnderflow,
37 InvalidStackValue(i32),
39 InvalidPointIndex(usize),
41 InvalidPointRange(usize, usize),
43 InvalidContourIndex(usize),
45 InvalidCvtIndex(usize),
47 InvalidStorageIndex(usize),
49 DivideByZero,
51 InvalidZoneIndex(i32),
53 NegativeLoopCounter,
55 InvalidJump,
57 ExceededExecutionBudget,
59}
60
61impl core::fmt::Display for HintErrorKind {
62 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63 match self {
64 Self::UnexpectedEndOfBytecode => write!(f, "unexpected end of bytecode"),
65 Self::UnhandledOpcode(opcode) => write!(f, "unhandled instruction opcode {opcode}"),
66 Self::DefinitionInGlyphProgram => write!(
67 f,
68 "function or instruction definition present in glyph program"
69 ),
70 Self::NestedDefinition => write!(f, "nested function or instruction definition"),
71 Self::DefinitionTooLarge => write!(
72 f,
73 "function or instruction definition exceeded the maximum size of 64k"
74 ),
75 Self::TooManyDefinitions => write!(f, "too many function or instruction definitions"),
76 Self::InvalidDefinition(key) => {
77 write!(f, "function or instruction definition {key} not found")
78 }
79 Self::ValueStackOverflow => write!(f, "value stack overflow"),
80 Self::ValueStackUnderflow => write!(f, "value stack underflow"),
81 Self::CallStackOverflow => write!(f, "call stack overflow"),
82 Self::CallStackUnderflow => write!(f, "call stack underflow"),
83 Self::InvalidStackValue(value) => write!(
84 f,
85 "stack value {value} was invalid for the current operation"
86 ),
87 Self::InvalidPointIndex(index) => write!(f, "point index {index} was out of bounds"),
88 Self::InvalidPointRange(start, end) => {
89 write!(f, "point range {start}..{end} was out of bounds")
90 }
91 Self::InvalidContourIndex(index) => {
92 write!(f, "contour index {index} was out of bounds")
93 }
94 Self::InvalidCvtIndex(index) => write!(f, "cvt index {index} was out of bounds"),
95 Self::InvalidStorageIndex(index) => {
96 write!(f, "storage area index {index} was out of bounds")
97 }
98 Self::DivideByZero => write!(f, "attempt to divide by 0"),
99 Self::InvalidZoneIndex(index) => write!(
100 f,
101 "zone index {index} was invalid (only 0 or 1 are permitted)"
102 ),
103 Self::NegativeLoopCounter => {
104 write!(f, "attempt to set the loop counter to a negative value")
105 }
106 Self::InvalidJump => write!(f, "the target of a jump instruction was invalid"),
107 Self::ExceededExecutionBudget => write!(f, "too many instructions executed"),
108 }
109 }
110}
111
112impl From<DecodeError> for HintErrorKind {
113 fn from(_: DecodeError) -> Self {
114 Self::UnexpectedEndOfBytecode
115 }
116}
117
118#[derive(Clone, PartialEq, Debug)]
120pub struct HintError {
121 pub program: Program,
123 pub glyph_id: Option<GlyphId>,
125 pub pc: usize,
127 pub opcode: Option<Opcode>,
129 pub kind: HintErrorKind,
131}
132
133impl core::fmt::Display for HintError {
134 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135 f.write_str(match self.program {
136 Program::Font => "fpgm",
137 Program::ControlValue => "prep",
138 Program::Glyph => "glyf",
139 })?;
140 if let Some(glyph_id) = self.glyph_id {
141 write!(f, "[{}]", glyph_id.to_u32())?;
142 }
143 let (opcode, colon) = match self.opcode {
144 Some(opcode) => (opcode.name(), ":"),
145 _ => ("", ""),
146 };
147 write!(f, "@{}:{opcode}{colon} {}", self.pc, self.kind)
148 }
149}
150
151impl core::error::Error for HintError {}
152
153impl From<HintError> for crate::model::Error {
154 fn from(_: HintError) -> Self {
155 Self::InvalidOperation
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162 use alloc::string::ToString;
163
164 #[test]
166 fn error_message_carries_its_context() {
167 let error = HintError {
168 program: Program::Glyph,
169 glyph_id: Some(GlyphId::new(42)),
170 pc: 17,
171 opcode: Some(Opcode::SVTCA0),
172 kind: HintErrorKind::DivideByZero,
173 };
174 assert_eq!(
175 error.to_string(),
176 "glyf[42]@17:SVTCA[y]: attempt to divide by 0"
177 );
178 }
179
180 #[test]
182 fn error_message_omits_what_it_does_not_have() {
183 let error = HintError {
184 program: Program::ControlValue,
185 glyph_id: None,
186 pc: 0,
187 opcode: None,
188 kind: HintErrorKind::ValueStackUnderflow,
189 };
190 assert_eq!(error.to_string(), "prep@0: value stack underflow");
191 }
192
193 #[test]
195 fn decode_errors_become_unexpected_end() {
196 assert_eq!(
197 HintErrorKind::from(DecodeError),
198 HintErrorKind::UnexpectedEndOfBytecode
199 );
200 }
201}