1use crate::{
2 tokens::{Token, WritingDirection},
3 BoundingBox, Error, Font, Glyph, Size, Value
4};
5use bit_vec::BitVec;
6use log::debug;
7use std::{
8 collections::{BTreeSet, HashMap},
9 io::BufRead,
10 mem
11};
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum State {
15 Initial,
17
18 Font,
20
21 Properties { len: usize },
23
24 Chars { len: usize },
26
27 Glyph { chars: usize },
30
31 Bitmap { chars: usize, len: usize },
34
35 Final
37}
38
39impl State {
40 fn assert_initial(self, token: &Token) -> Result<(), Error> {
41 match self {
42 Self::Initial => Ok(()),
43 _ => Err(Error::InvalidContext(token.clone(), self, "Initial"))
44 }
45 }
46
47 fn assert_font(self, token: &Token) -> Result<(), Error> {
48 match self {
49 Self::Font => Ok(()),
50 _ => Err(Error::InvalidContext(token.clone(), self, "Font"))
51 }
52 }
53
54 fn assert_properties(self, token: &Token) -> Result<usize, Error> {
55 match self {
56 Self::Properties { len } => Ok(len),
57 _ => Err(Error::InvalidContext(token.clone(), self, "Properties"))
58 }
59 }
60
61 fn assert_chars(self, token: &Token) -> Result<usize, Error> {
62 match self {
63 Self::Chars { len } => Ok(len),
64 _ => Err(Error::InvalidContext(token.clone(), self, "Chars"))
65 }
66 }
67
68 fn assert_glyph(self, token: &Token) -> Result<usize, Error> {
69 match self {
70 Self::Glyph { chars } => Ok(chars),
71 _ => Err(Error::InvalidContext(token.clone(), self, "Glyph"))
72 }
73 }
74
75 fn assert_bitmap(self, token: &Token) -> Result<(usize, usize), Error> {
76 match self {
77 Self::Bitmap { chars, len } => Ok((chars, len)),
78 _ => Err(Error::InvalidContext(token.clone(), self, "Bitmap"))
79 }
80 }
81}
82
83impl Font {
84 pub fn read<R: BufRead>(reader: R) -> Result<Self, Error> {
85 let mut font_version = None;
86 let mut font_name = None;
87 let mut font_size = None;
88 let mut font_bbox = None;
89 let mut font_swidth = None;
90 let mut font_dwidth = None;
91 let mut font_properties = HashMap::new();
92 let mut font_glyphs = BTreeSet::new();
93
94 let mut glyph_name = None;
95 let mut glyph_encoding = None;
96 let mut glyph_swidth = None;
97 let mut glyph_dwidth = None;
98 let mut glyph_bbox = None;
99 let mut glyph_bitmap = Vec::new();
100
101 let mut state = State::Initial;
102 for (ll, line) in reader.lines().enumerate().map(|(ll, line)| (ll + 1, line)) {
103 let line = line?;
104 if line.trim().is_empty() {
105 debug!("Skipping blank line {ll}");
106 continue;
107 }
108 debug!("Parsing line {ll} {line:?}, state={state:?}");
109
110 match &mut state {
111 State::Properties { len } if *len > 0 => {
112 let idx: usize = line
113 .chars()
114 .take_while(|ch| !ch.is_ascii_whitespace())
115 .map(|ch| ch.len_utf8())
116 .sum();
117 let key = &line[0 .. idx];
118 let value_str = &line[idx + 1 ..];
119 let v = if value_str.starts_with('"') && value_str.ends_with('"') {
120 Value::String(value_str.trim_matches('"').replace("''", "\""))
121 } else {
122 Value::Integer(
123 value_str.parse().map_err(Error::InvalidPropertyValue)?
124 )
125 };
126 font_properties.insert(key.to_owned(), v);
127
128 *len -= 1;
129 continue;
130 },
131
132 State::Bitmap { len, .. } if *len > 0 => {
133 let mut iter = line.chars().filter(|ch| ch.is_ascii_hexdigit());
134 let mut raw = Vec::new();
135 while let Some(first) = iter.next() {
136 let second = iter.next().ok_or_else(|| {
137 Error::InvalidBitmapValue(format!("{first}"))
138 })?;
139 let hex = format!("{first}{second}");
140 let byte = u8::from_str_radix(&hex, 16)
141 .map_err(|_| Error::InvalidBitmapValue(hex))?;
142 raw.push(byte);
143 }
144 glyph_bitmap.push(BitVec::from_bytes(&raw));
145
146 *len -= 1;
147 continue;
148 },
149
150 _ => {}
151 }
152
153 let token = Token::parse_line(&line)?;
154 match token {
155 Token::StartFont { .. } => {
156 state.assert_initial(&token)?;
157 state = State::Font;
158 },
159
160 Token::ContentVersion { ver } => {
161 state.assert_font(&token)?;
162 font_version = Some(ver);
163 },
164
165 Token::Font { ref name } => {
166 state.assert_font(&token)?;
167 font_name = Some(name.into());
168 },
169
170 Token::Size { pt, xres, yres } => {
171 state.assert_font(&token)?;
172 font_size = Some(Size { pt, xres, yres });
173 },
174
175 Token::FontBoundingBox {
176 fbbx,
177 fbby,
178 xoff,
179 yoff
180 } => {
181 state.assert_font(&token)?;
182 font_bbox = Some(BoundingBox {
183 width: fbbx,
184 height: fbby,
185 offset_x: xoff,
186 offset_y: yoff
187 });
188 },
189
190 Token::MetricsSet {
191 dir: WritingDirection::Horizontal
192 } => {},
193 Token::MetricsSet { dir } => {
194 unimplemented!("METRICSSET {dir:?} is currently not supported");
195 },
196
197 Token::SWidth { swx0, swy0 } if matches!(state, State::Font) => {
198 font_swidth = Some((swx0, swy0));
199 },
200 Token::SWidth { swx0, swy0 } => {
201 state.assert_glyph(&token)?;
202 glyph_swidth = Some((swx0, swy0));
203 },
204
205 Token::DWidth { dwx0, dwy0 } if matches!(state, State::Font) => {
206 font_dwidth = Some((dwx0, dwy0));
207 },
208 Token::DWidth { dwx0, dwy0 } => {
209 state.assert_glyph(&token)?;
210 glyph_dwidth = Some((dwx0, dwy0));
211 },
212
213 Token::SWidthVertical { swx1, swy1 } => {
214 unimplemented!("SWIDTH1 {swx1} {swy1} is currently not supported");
215 },
216 Token::DWidthVertical { dwx1, dwy1 } => {
217 unimplemented!("DWIDTH1 {dwx1} {dwy1} is currently not supported");
218 },
219 Token::VVector { xoff, yoff } => {
220 unimplemented!("VVECTOR {xoff} {yoff} is currently not supported");
221 },
222
223 Token::StartProperties { n } => {
224 state.assert_font(&token)?;
225 state = State::Properties { len: n };
226 },
227
228 Token::EndProperties => {
229 let len = state.assert_properties(&token)?;
230 if len != 0 {
231 return Err(Error::UnexpectedEnd("Properties"));
232 }
233 state = State::Font;
234 },
235
236 Token::Chars { nglyphs } => {
237 state.assert_font(&token)?;
238 state = State::Chars { len: nglyphs };
239 },
240
241 Token::StartChar { ref name } => {
242 let chars = state.assert_chars(&token)?;
243 state = State::Glyph { chars };
244
245 glyph_name = Some(name.to_owned());
246 glyph_encoding = None;
247 glyph_swidth = font_swidth;
248 glyph_dwidth = font_dwidth;
249 glyph_bbox = font_bbox;
250 glyph_bitmap.clear();
251 },
252
253 Token::Encoding { enc } => {
254 state.assert_glyph(&token)?;
255 glyph_encoding = Some(enc);
256 },
257
258 Token::BoundingBox {
259 bbw,
260 bbh,
261 bbxoff,
262 bbyoff
263 } => {
264 state.assert_glyph(&token)?;
265 glyph_bbox = Some(BoundingBox {
266 width: bbw,
267 height: bbh,
268 offset_x: bbxoff,
269 offset_y: bbyoff
270 });
271 },
272
273 Token::Bitmap => {
274 let chars = state.assert_glyph(&token)?;
275 state = State::Bitmap {
276 chars,
277 len: glyph_bbox.ok_or(Error::MissingGlyphBoundingBox)?.height
278 as usize
279 }
280 },
281
282 Token::EndChar => {
283 let (chars, len) = state.assert_bitmap(&token)?;
284 if len != 0 {
285 return Err(Error::UnexpectedEnd("Char"));
286 }
287 state = State::Chars { len: chars - 1 };
288
289 font_glyphs.insert(
290 Glyph {
291 name: glyph_name.take().unwrap(),
292 encoding: glyph_encoding
293 .ok_or(Error::MissingGlyphEncoding)?,
294 swidth: glyph_swidth,
295 dwidth: glyph_dwidth,
296 bbox: glyph_bbox.ok_or(Error::MissingGlyphBoundingBox)?,
297 bitmap: mem::take(&mut glyph_bitmap)
298 }
299 .into()
300 );
301 },
302
303 Token::EndFont => {
304 let chars = state.assert_chars(&token)?;
305 if chars != 0 {
306 return Err(Error::UnexpectedEnd("Font"));
307 }
308 state = State::Final;
309 },
310
311 Token::Comment { .. } => {}
313 };
314 }
315
316 Ok(Self {
318 version: font_version,
319 name: font_name.ok_or(Error::MissingFontName)?,
320 bbox: font_bbox.ok_or(Error::MissingFontBoundingBox)?,
321 size: font_size.ok_or(Error::MissingFontSize)?,
322 properties: font_properties,
323 glyphs: font_glyphs
324 })
325 }
326}