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