1 | //! A [Glyph Data Table]( |
2 | //! https://docs.microsoft.com/en-us/typography/opentype/spec/glyf) implementation. |
3 | |
4 | use core::num::NonZeroU16; |
5 | |
6 | use crate::parser::{LazyArray16, NumFrom, Stream, F2DOT14}; |
7 | use crate::{loca, BBox, GlyphId, OutlineBuilder, Rect}; |
8 | |
9 | pub(crate) struct Builder<'a> { |
10 | pub builder: &'a mut dyn OutlineBuilder, |
11 | pub transform: Transform, |
12 | is_default_ts: bool, // `bool` is faster than `Option` or `is_default`. |
13 | // We have to always calculate the bbox, because `gvar` doesn't store one |
14 | // and in case of a malformed bbox in `glyf`. |
15 | pub bbox: BBox, |
16 | first_on_curve: Option<Point>, |
17 | first_off_curve: Option<Point>, |
18 | last_off_curve: Option<Point>, |
19 | } |
20 | |
21 | impl<'a> Builder<'a> { |
22 | #[inline ] |
23 | pub fn new(transform: Transform, bbox: BBox, builder: &'a mut dyn OutlineBuilder) -> Self { |
24 | Builder { |
25 | builder, |
26 | transform, |
27 | is_default_ts: transform.is_default(), |
28 | bbox, |
29 | first_on_curve: None, |
30 | first_off_curve: None, |
31 | last_off_curve: None, |
32 | } |
33 | } |
34 | |
35 | #[inline ] |
36 | fn move_to(&mut self, mut x: f32, mut y: f32) { |
37 | if !self.is_default_ts { |
38 | self.transform.apply_to(&mut x, &mut y); |
39 | } |
40 | |
41 | self.bbox.extend_by(x, y); |
42 | |
43 | self.builder.move_to(x, y); |
44 | } |
45 | |
46 | #[inline ] |
47 | fn line_to(&mut self, mut x: f32, mut y: f32) { |
48 | if !self.is_default_ts { |
49 | self.transform.apply_to(&mut x, &mut y); |
50 | } |
51 | |
52 | self.bbox.extend_by(x, y); |
53 | |
54 | self.builder.line_to(x, y); |
55 | } |
56 | |
57 | #[inline ] |
58 | fn quad_to(&mut self, mut x1: f32, mut y1: f32, mut x: f32, mut y: f32) { |
59 | if !self.is_default_ts { |
60 | self.transform.apply_to(&mut x1, &mut y1); |
61 | self.transform.apply_to(&mut x, &mut y); |
62 | } |
63 | |
64 | self.bbox.extend_by(x1, y1); |
65 | self.bbox.extend_by(x, y); |
66 | |
67 | self.builder.quad_to(x1, y1, x, y); |
68 | } |
69 | |
70 | // Useful links: |
71 | // |
72 | // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html |
73 | // - https://stackoverflow.com/a/20772557 |
74 | #[inline ] |
75 | pub fn push_point(&mut self, x: f32, y: f32, on_curve_point: bool, last_point: bool) { |
76 | let p = Point { x, y }; |
77 | if self.first_on_curve.is_none() { |
78 | if on_curve_point { |
79 | self.first_on_curve = Some(p); |
80 | self.move_to(p.x, p.y); |
81 | } else { |
82 | if let Some(offcurve) = self.first_off_curve { |
83 | let mid = offcurve.lerp(p, 0.5); |
84 | self.first_on_curve = Some(mid); |
85 | self.last_off_curve = Some(p); |
86 | self.move_to(mid.x, mid.y); |
87 | } else { |
88 | self.first_off_curve = Some(p); |
89 | } |
90 | } |
91 | } else { |
92 | match (self.last_off_curve, on_curve_point) { |
93 | (Some(offcurve), true) => { |
94 | self.last_off_curve = None; |
95 | self.quad_to(offcurve.x, offcurve.y, p.x, p.y); |
96 | } |
97 | (Some(offcurve), false) => { |
98 | self.last_off_curve = Some(p); |
99 | let mid = offcurve.lerp(p, 0.5); |
100 | self.quad_to(offcurve.x, offcurve.y, mid.x, mid.y); |
101 | } |
102 | (None, true) => { |
103 | self.line_to(p.x, p.y); |
104 | } |
105 | (None, false) => { |
106 | self.last_off_curve = Some(p); |
107 | } |
108 | } |
109 | } |
110 | |
111 | if last_point { |
112 | self.finish_contour(); |
113 | } |
114 | } |
115 | |
116 | #[inline ] |
117 | fn finish_contour(&mut self) { |
118 | if let (Some(offcurve1), Some(offcurve2)) = (self.first_off_curve, self.last_off_curve) { |
119 | self.last_off_curve = None; |
120 | let mid = offcurve2.lerp(offcurve1, 0.5); |
121 | self.quad_to(offcurve2.x, offcurve2.y, mid.x, mid.y); |
122 | } |
123 | |
124 | if let (Some(p), Some(offcurve1)) = (self.first_on_curve, self.first_off_curve) { |
125 | self.quad_to(offcurve1.x, offcurve1.y, p.x, p.y); |
126 | } else if let (Some(p), Some(offcurve2)) = (self.first_on_curve, self.last_off_curve) { |
127 | self.quad_to(offcurve2.x, offcurve2.y, p.x, p.y); |
128 | } else if let Some(p) = self.first_on_curve { |
129 | self.line_to(p.x, p.y); |
130 | } |
131 | |
132 | self.first_on_curve = None; |
133 | self.first_off_curve = None; |
134 | self.last_off_curve = None; |
135 | |
136 | self.builder.close(); |
137 | } |
138 | } |
139 | |
140 | #[derive (Clone, Copy)] |
141 | pub(crate) struct Transform { |
142 | pub a: f32, |
143 | pub b: f32, |
144 | pub c: f32, |
145 | pub d: f32, |
146 | pub e: f32, |
147 | pub f: f32, |
148 | } |
149 | |
150 | impl Transform { |
151 | #[cfg (feature = "variable-fonts" )] |
152 | #[inline ] |
153 | pub fn new_translate(tx: f32, ty: f32) -> Self { |
154 | Transform { |
155 | a: 1.0, |
156 | b: 0.0, |
157 | c: 0.0, |
158 | d: 1.0, |
159 | e: tx, |
160 | f: ty, |
161 | } |
162 | } |
163 | |
164 | #[inline ] |
165 | pub fn combine(ts1: Self, ts2: Self) -> Self { |
166 | Transform { |
167 | a: ts1.a * ts2.a + ts1.c * ts2.b, |
168 | b: ts1.b * ts2.a + ts1.d * ts2.b, |
169 | c: ts1.a * ts2.c + ts1.c * ts2.d, |
170 | d: ts1.b * ts2.c + ts1.d * ts2.d, |
171 | e: ts1.a * ts2.e + ts1.c * ts2.f + ts1.e, |
172 | f: ts1.b * ts2.e + ts1.d * ts2.f + ts1.f, |
173 | } |
174 | } |
175 | |
176 | #[inline ] |
177 | fn apply_to(&self, x: &mut f32, y: &mut f32) { |
178 | let tx = *x; |
179 | let ty = *y; |
180 | *x = self.a * tx + self.c * ty + self.e; |
181 | *y = self.b * tx + self.d * ty + self.f; |
182 | } |
183 | |
184 | #[inline ] |
185 | fn is_default(&self) -> bool { |
186 | // A direct float comparison is fine in our case. |
187 | self.a == 1.0 |
188 | && self.b == 0.0 |
189 | && self.c == 0.0 |
190 | && self.d == 1.0 |
191 | && self.e == 0.0 |
192 | && self.f == 0.0 |
193 | } |
194 | } |
195 | |
196 | impl Default for Transform { |
197 | #[inline ] |
198 | fn default() -> Self { |
199 | Transform { |
200 | a: 1.0, |
201 | b: 0.0, |
202 | c: 0.0, |
203 | d: 1.0, |
204 | e: 0.0, |
205 | f: 0.0, |
206 | } |
207 | } |
208 | } |
209 | |
210 | impl core::fmt::Debug for Transform { |
211 | #[inline ] |
212 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
213 | write!( |
214 | f, |
215 | "Transform( {} {} {} {} {} {})" , |
216 | self.a, self.b, self.c, self.d, self.e, self.f |
217 | ) |
218 | } |
219 | } |
220 | |
221 | #[derive (Clone, Copy, Debug)] |
222 | pub(crate) struct CompositeGlyphInfo { |
223 | pub glyph_id: GlyphId, |
224 | pub transform: Transform, |
225 | #[allow (dead_code)] |
226 | pub flags: CompositeGlyphFlags, |
227 | } |
228 | |
229 | #[derive (Clone)] |
230 | pub(crate) struct CompositeGlyphIter<'a> { |
231 | stream: Stream<'a>, |
232 | } |
233 | |
234 | impl<'a> CompositeGlyphIter<'a> { |
235 | #[inline ] |
236 | pub fn new(data: &'a [u8]) -> Self { |
237 | CompositeGlyphIter { |
238 | stream: Stream::new(data), |
239 | } |
240 | } |
241 | } |
242 | |
243 | impl<'a> Iterator for CompositeGlyphIter<'a> { |
244 | type Item = CompositeGlyphInfo; |
245 | |
246 | #[inline ] |
247 | fn next(&mut self) -> Option<Self::Item> { |
248 | let flags = CompositeGlyphFlags(self.stream.read::<u16>()?); |
249 | let glyph_id = self.stream.read::<GlyphId>()?; |
250 | |
251 | let mut ts = Transform::default(); |
252 | |
253 | if flags.args_are_xy_values() { |
254 | if flags.arg_1_and_2_are_words() { |
255 | ts.e = f32::from(self.stream.read::<i16>()?); |
256 | ts.f = f32::from(self.stream.read::<i16>()?); |
257 | } else { |
258 | ts.e = f32::from(self.stream.read::<i8>()?); |
259 | ts.f = f32::from(self.stream.read::<i8>()?); |
260 | } |
261 | } |
262 | |
263 | if flags.we_have_a_two_by_two() { |
264 | ts.a = self.stream.read::<F2DOT14>()?.to_f32(); |
265 | ts.b = self.stream.read::<F2DOT14>()?.to_f32(); |
266 | ts.c = self.stream.read::<F2DOT14>()?.to_f32(); |
267 | ts.d = self.stream.read::<F2DOT14>()?.to_f32(); |
268 | } else if flags.we_have_an_x_and_y_scale() { |
269 | ts.a = self.stream.read::<F2DOT14>()?.to_f32(); |
270 | ts.d = self.stream.read::<F2DOT14>()?.to_f32(); |
271 | } else if flags.we_have_a_scale() { |
272 | ts.a = self.stream.read::<F2DOT14>()?.to_f32(); |
273 | ts.d = ts.a; |
274 | } |
275 | |
276 | if !flags.more_components() { |
277 | // Finish the iterator even if stream still has some data. |
278 | self.stream.jump_to_end(); |
279 | } |
280 | |
281 | Some(CompositeGlyphInfo { |
282 | glyph_id, |
283 | transform: ts, |
284 | flags, |
285 | }) |
286 | } |
287 | } |
288 | |
289 | // Due to some optimization magic, using f32 instead of i16 |
290 | // makes the code ~10% slower. At least on my machine. |
291 | // I guess it's due to the fact that with i16 the struct |
292 | // fits into the machine word. |
293 | #[derive (Clone, Copy, Debug)] |
294 | pub(crate) struct GlyphPoint { |
295 | pub x: i16, |
296 | pub y: i16, |
297 | /// Indicates that a point is a point on curve |
298 | /// and not a control point. |
299 | pub on_curve_point: bool, |
300 | pub last_point: bool, |
301 | } |
302 | |
303 | #[derive (Clone, Default)] |
304 | pub(crate) struct GlyphPointsIter<'a> { |
305 | endpoints: EndpointsIter<'a>, |
306 | flags: FlagsIter<'a>, |
307 | x_coords: CoordsIter<'a>, |
308 | y_coords: CoordsIter<'a>, |
309 | pub points_left: u16, // Number of points left in the glyph. |
310 | } |
311 | |
312 | #[cfg (feature = "variable-fonts" )] |
313 | impl GlyphPointsIter<'_> { |
314 | #[inline ] |
315 | pub fn current_contour(&self) -> u16 { |
316 | self.endpoints.index - 1 |
317 | } |
318 | } |
319 | |
320 | impl<'a> Iterator for GlyphPointsIter<'a> { |
321 | type Item = GlyphPoint; |
322 | |
323 | #[inline ] |
324 | fn next(&mut self) -> Option<Self::Item> { |
325 | self.points_left = self.points_left.checked_sub(1)?; |
326 | |
327 | // TODO: skip empty contours |
328 | |
329 | let last_point: bool = self.endpoints.next(); |
330 | let flags: SimpleGlyphFlags = self.flags.next()?; |
331 | Some(GlyphPoint { |
332 | x: self |
333 | .x_coords |
334 | .next(is_short:flags.x_short(), is_same_or_short:flags.x_is_same_or_positive_short()), |
335 | y: self |
336 | .y_coords |
337 | .next(is_short:flags.y_short(), is_same_or_short:flags.y_is_same_or_positive_short()), |
338 | on_curve_point: flags.on_curve_point(), |
339 | last_point, |
340 | }) |
341 | } |
342 | } |
343 | |
344 | /// A simple flattening iterator for glyph's endpoints. |
345 | /// |
346 | /// Translates endpoints like: 2 4 7 |
347 | /// into flags: 0 0 1 0 1 0 0 1 |
348 | #[derive (Clone, Copy, Default)] |
349 | struct EndpointsIter<'a> { |
350 | endpoints: LazyArray16<'a, u16>, // Each endpoint indicates a contour end. |
351 | index: u16, |
352 | left: u16, |
353 | } |
354 | |
355 | impl<'a> EndpointsIter<'a> { |
356 | #[inline ] |
357 | fn new(endpoints: LazyArray16<'a, u16>) -> Option<Self> { |
358 | Some(EndpointsIter { |
359 | endpoints, |
360 | index: 1, |
361 | left: endpoints.get(0)?, |
362 | }) |
363 | } |
364 | |
365 | #[inline ] |
366 | fn next(&mut self) -> bool { |
367 | if self.left == 0 { |
368 | if let Some(end) = self.endpoints.get(self.index) { |
369 | let prev = self.endpoints.get(self.index - 1).unwrap_or(0); |
370 | // Malformed font can have endpoints not in increasing order, |
371 | // so we have to use checked_sub. |
372 | self.left = end.saturating_sub(prev); |
373 | self.left = self.left.saturating_sub(1); |
374 | } |
375 | |
376 | // Always advance the index, so we can check the current contour number. |
377 | if let Some(n) = self.index.checked_add(1) { |
378 | self.index = n; |
379 | } |
380 | |
381 | true |
382 | } else { |
383 | self.left -= 1; |
384 | false |
385 | } |
386 | } |
387 | } |
388 | |
389 | #[derive (Clone, Default)] |
390 | struct FlagsIter<'a> { |
391 | stream: Stream<'a>, |
392 | // Number of times the `flags` should be used |
393 | // before reading the next one from `stream`. |
394 | repeats: u8, |
395 | flags: SimpleGlyphFlags, |
396 | } |
397 | |
398 | impl<'a> FlagsIter<'a> { |
399 | #[inline ] |
400 | fn new(data: &'a [u8]) -> Self { |
401 | FlagsIter { |
402 | stream: Stream::new(data), |
403 | repeats: 0, |
404 | flags: SimpleGlyphFlags(0), |
405 | } |
406 | } |
407 | } |
408 | |
409 | impl<'a> Iterator for FlagsIter<'a> { |
410 | type Item = SimpleGlyphFlags; |
411 | |
412 | #[inline ] |
413 | fn next(&mut self) -> Option<Self::Item> { |
414 | if self.repeats == 0 { |
415 | self.flags = SimpleGlyphFlags(self.stream.read::<u8>().unwrap_or(default:0)); |
416 | if self.flags.repeat_flag() { |
417 | self.repeats = self.stream.read::<u8>().unwrap_or(default:0); |
418 | } |
419 | } else { |
420 | self.repeats -= 1; |
421 | } |
422 | |
423 | Some(self.flags) |
424 | } |
425 | } |
426 | |
427 | #[derive (Clone, Default)] |
428 | struct CoordsIter<'a> { |
429 | stream: Stream<'a>, |
430 | prev: i16, // Points are stored as deltas, so we have to keep the previous one. |
431 | } |
432 | |
433 | impl<'a> CoordsIter<'a> { |
434 | #[inline ] |
435 | fn new(data: &'a [u8]) -> Self { |
436 | CoordsIter { |
437 | stream: Stream::new(data), |
438 | prev: 0, |
439 | } |
440 | } |
441 | |
442 | #[inline ] |
443 | fn next(&mut self, is_short: bool, is_same_or_short: bool) -> i16 { |
444 | // See https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description |
445 | // for details about Simple Glyph Flags processing. |
446 | |
447 | // We've already checked the coords data, so it's safe to fallback to 0. |
448 | |
449 | let mut n = 0; |
450 | if is_short { |
451 | n = i16::from(self.stream.read::<u8>().unwrap_or(0)); |
452 | if !is_same_or_short { |
453 | n = -n; |
454 | } |
455 | } else if !is_same_or_short { |
456 | n = self.stream.read::<i16>().unwrap_or(0); |
457 | } |
458 | |
459 | self.prev = self.prev.wrapping_add(n); |
460 | self.prev |
461 | } |
462 | } |
463 | |
464 | #[derive (Clone, Copy, Debug)] |
465 | struct Point { |
466 | x: f32, |
467 | y: f32, |
468 | } |
469 | |
470 | impl Point { |
471 | #[inline ] |
472 | fn lerp(self, other: Point, t: f32) -> Point { |
473 | Point { |
474 | x: self.x + t * (other.x - self.x), |
475 | y: self.y + t * (other.y - self.y), |
476 | } |
477 | } |
478 | } |
479 | |
480 | // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description |
481 | #[derive (Clone, Copy, Default)] |
482 | struct SimpleGlyphFlags(u8); |
483 | |
484 | #[rustfmt::skip] |
485 | impl SimpleGlyphFlags { |
486 | #[inline ] fn on_curve_point(self) -> bool { self.0 & 0x01 != 0 } |
487 | #[inline ] fn x_short(self) -> bool { self.0 & 0x02 != 0 } |
488 | #[inline ] fn y_short(self) -> bool { self.0 & 0x04 != 0 } |
489 | #[inline ] fn repeat_flag(self) -> bool { self.0 & 0x08 != 0 } |
490 | #[inline ] fn x_is_same_or_positive_short(self) -> bool { self.0 & 0x10 != 0 } |
491 | #[inline ] fn y_is_same_or_positive_short(self) -> bool { self.0 & 0x20 != 0 } |
492 | } |
493 | |
494 | // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description |
495 | #[derive (Clone, Copy, Debug)] |
496 | pub(crate) struct CompositeGlyphFlags(u16); |
497 | |
498 | #[rustfmt::skip] |
499 | impl CompositeGlyphFlags { |
500 | #[inline ] pub fn arg_1_and_2_are_words(self) -> bool { self.0 & 0x0001 != 0 } |
501 | #[inline ] pub fn args_are_xy_values(self) -> bool { self.0 & 0x0002 != 0 } |
502 | #[inline ] pub fn we_have_a_scale(self) -> bool { self.0 & 0x0008 != 0 } |
503 | #[inline ] pub fn more_components(self) -> bool { self.0 & 0x0020 != 0 } |
504 | #[inline ] pub fn we_have_an_x_and_y_scale(self) -> bool { self.0 & 0x0040 != 0 } |
505 | #[inline ] pub fn we_have_a_two_by_two(self) -> bool { self.0 & 0x0080 != 0 } |
506 | } |
507 | |
508 | // It's not defined in the spec, so we are using our own value. |
509 | pub(crate) const MAX_COMPONENTS: u8 = 32; |
510 | |
511 | #[allow (clippy::comparison_chain)] |
512 | #[inline ] |
513 | fn outline_impl( |
514 | loca_table: loca::Table, |
515 | glyf_table: &[u8], |
516 | data: &[u8], |
517 | depth: u8, |
518 | builder: &mut Builder, |
519 | ) -> Option<Option<Rect>> { |
520 | if depth >= MAX_COMPONENTS { |
521 | return None; |
522 | } |
523 | |
524 | let mut s = Stream::new(data); |
525 | let number_of_contours = s.read::<i16>()?; |
526 | s.advance(8); // Skip bbox. We use calculated one. |
527 | |
528 | if number_of_contours > 0 { |
529 | // Simple glyph. |
530 | |
531 | // u16 casting is safe, since we already checked that the value is positive. |
532 | let number_of_contours = NonZeroU16::new(number_of_contours as u16)?; |
533 | for point in parse_simple_outline(s.tail()?, number_of_contours)? { |
534 | builder.push_point( |
535 | f32::from(point.x), |
536 | f32::from(point.y), |
537 | point.on_curve_point, |
538 | point.last_point, |
539 | ); |
540 | } |
541 | } else if number_of_contours < 0 { |
542 | // Composite glyph. |
543 | for comp in CompositeGlyphIter::new(s.tail()?) { |
544 | if let Some(range) = loca_table.glyph_range(comp.glyph_id) { |
545 | if let Some(glyph_data) = glyf_table.get(range) { |
546 | let transform = Transform::combine(builder.transform, comp.transform); |
547 | let mut b = Builder::new(transform, builder.bbox, builder.builder); |
548 | outline_impl(loca_table, glyf_table, glyph_data, depth + 1, &mut b)?; |
549 | |
550 | // Take updated bbox. |
551 | builder.bbox = b.bbox; |
552 | } |
553 | } |
554 | } |
555 | } |
556 | |
557 | if builder.bbox.is_default() { |
558 | return Some(None); |
559 | } |
560 | |
561 | Some(builder.bbox.to_rect()) |
562 | } |
563 | |
564 | #[inline ] |
565 | pub(crate) fn parse_simple_outline( |
566 | glyph_data: &[u8], |
567 | number_of_contours: NonZeroU16, |
568 | ) -> Option<GlyphPointsIter> { |
569 | let mut s = Stream::new(glyph_data); |
570 | let endpoints = s.read_array16::<u16>(number_of_contours.get())?; |
571 | |
572 | let points_total = endpoints.last()?.checked_add(1)?; |
573 | |
574 | // Contours with a single point should be ignored. |
575 | // But this is not an error, so we should return an "empty" iterator. |
576 | if points_total == 1 { |
577 | return Some(GlyphPointsIter::default()); |
578 | } |
579 | |
580 | // Skip instructions byte code. |
581 | let instructions_len = s.read::<u16>()?; |
582 | s.advance(usize::from(instructions_len)); |
583 | |
584 | let flags_offset = s.offset(); |
585 | let (x_coords_len, y_coords_len) = resolve_coords_len(&mut s, points_total)?; |
586 | let x_coords_offset = s.offset(); |
587 | let y_coords_offset = x_coords_offset + usize::num_from(x_coords_len); |
588 | let y_coords_end = y_coords_offset + usize::num_from(y_coords_len); |
589 | |
590 | Some(GlyphPointsIter { |
591 | endpoints: EndpointsIter::new(endpoints)?, |
592 | flags: FlagsIter::new(glyph_data.get(flags_offset..x_coords_offset)?), |
593 | x_coords: CoordsIter::new(glyph_data.get(x_coords_offset..y_coords_offset)?), |
594 | y_coords: CoordsIter::new(glyph_data.get(y_coords_offset..y_coords_end)?), |
595 | points_left: points_total, |
596 | }) |
597 | } |
598 | |
599 | /// Resolves coordinate arrays length. |
600 | /// |
601 | /// The length depends on *Simple Glyph Flags*, so we have to process them all to find it. |
602 | fn resolve_coords_len(s: &mut Stream, points_total: u16) -> Option<(u32, u32)> { |
603 | let mut flags_left = u32::from(points_total); |
604 | let mut repeats; |
605 | let mut x_coords_len = 0; |
606 | let mut y_coords_len = 0; |
607 | while flags_left > 0 { |
608 | let flags = SimpleGlyphFlags(s.read::<u8>()?); |
609 | |
610 | // The number of times a glyph point repeats. |
611 | repeats = if flags.repeat_flag() { |
612 | let repeats = s.read::<u8>()?; |
613 | u32::from(repeats) + 1 |
614 | } else { |
615 | 1 |
616 | }; |
617 | |
618 | if repeats > flags_left { |
619 | return None; |
620 | } |
621 | |
622 | // No need to check for `*_coords_len` overflow since u32 is more than enough. |
623 | |
624 | // Non-obfuscated code below. |
625 | // Branchless version is surprisingly faster. |
626 | // |
627 | // if flags.x_short() { |
628 | // // Coordinate is 1 byte long. |
629 | // x_coords_len += repeats; |
630 | // } else if !flags.x_is_same_or_positive_short() { |
631 | // // Coordinate is 2 bytes long. |
632 | // x_coords_len += repeats * 2; |
633 | // } |
634 | // if flags.y_short() { |
635 | // // Coordinate is 1 byte long. |
636 | // y_coords_len += repeats; |
637 | // } else if !flags.y_is_same_or_positive_short() { |
638 | // // Coordinate is 2 bytes long. |
639 | // y_coords_len += repeats * 2; |
640 | // } |
641 | |
642 | x_coords_len += (flags.0 & 0x02 != 0) as u32 * repeats; |
643 | x_coords_len += (flags.0 & (0x02 | 0x10) == 0) as u32 * (repeats * 2); |
644 | |
645 | y_coords_len += (flags.0 & 0x04 != 0) as u32 * repeats; |
646 | y_coords_len += (flags.0 & (0x04 | 0x20) == 0) as u32 * (repeats * 2); |
647 | |
648 | flags_left -= repeats; |
649 | } |
650 | |
651 | Some((x_coords_len, y_coords_len)) |
652 | } |
653 | |
654 | /// A [Glyph Data Table]( |
655 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/glyf). |
656 | #[derive (Clone, Copy)] |
657 | pub struct Table<'a> { |
658 | pub(crate) data: &'a [u8], |
659 | loca_table: loca::Table<'a>, |
660 | } |
661 | |
662 | impl core::fmt::Debug for Table<'_> { |
663 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
664 | write!(f, "Table {{ ... }}" ) |
665 | } |
666 | } |
667 | |
668 | impl<'a> Table<'a> { |
669 | /// Parses a table from raw data. |
670 | #[inline ] |
671 | pub fn parse(loca_table: loca::Table<'a>, data: &'a [u8]) -> Option<Self> { |
672 | Some(Table { loca_table, data }) |
673 | } |
674 | |
675 | /// Outlines a glyph. |
676 | #[inline ] |
677 | pub fn outline(&self, glyph_id: GlyphId, builder: &mut dyn OutlineBuilder) -> Option<Rect> { |
678 | let mut b: Builder<'_> = Builder::new(Transform::default(), BBox::new(), builder); |
679 | let glyph_data: &[u8] = self.get(glyph_id)?; |
680 | outline_impl(self.loca_table, self.data, glyph_data, depth:0, &mut b)? |
681 | } |
682 | |
683 | #[inline ] |
684 | pub(crate) fn get(&self, glyph_id: GlyphId) -> Option<&'a [u8]> { |
685 | let range: Range = self.loca_table.glyph_range(glyph_id)?; |
686 | self.data.get(index:range) |
687 | } |
688 | } |
689 | |