1#[cfg(feature = "outline")]
9mod outline;
10mod subset;
11mod variation;
12
13use std::convert::{TryFrom, TryInto};
14use std::{iter, mem};
15
16use bitflags::bitflags;
17use itertools::Itertools;
18use log::warn;
19use pathfinder_geometry::transform2d::Matrix2x2F;
20use pathfinder_geometry::vector::Vector2F;
21
22use crate::binary::read::{
23 ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope, ReadUnchecked,
24};
25use crate::binary::write::{WriteBinary, WriteBinaryDep, WriteContext};
26use crate::binary::{word_align, I16Be, U16Be, I8, U8};
27use crate::error::{ParseError, WriteError};
28use crate::tables::loca::{owned, LocaTable};
29use crate::tables::os2::Os2;
30use crate::tables::{F2Dot14, HheaTable, HmtxTable, IndexToLocFormat};
31
32pub use subset::SubsetGlyph;
33
34#[allow(unused)]
38const COMPOSITE_GLYPH_RECURSION_LIMIT: u8 = 6;
39
40bitflags! {
41 #[rustfmt::skip]
42 pub struct SimpleGlyphFlag: u8 {
43 const ON_CURVE_POINT = 0b00000001;
44 const X_SHORT_VECTOR = 0b00000010;
45 const Y_SHORT_VECTOR = 0b00000100;
46 const REPEAT_FLAG = 0b00001000;
47 const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 0b00010000;
48 const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 0b00100000;
49 }
50}
51
52bitflags! {
53 pub struct CompositeGlyphFlag: u16 {
54 const ARG_1_AND_2_ARE_WORDS = 0x0001;
57 const ARGS_ARE_XY_VALUES = 0x0002;
60 const ROUND_XY_TO_GRID = 0x0004;
62 const WE_HAVE_A_SCALE = 0x0008;
66 const MORE_COMPONENTS = 0x0020;
69 const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
71 const WE_HAVE_A_TWO_BY_TWO = 0x0080;
73 const WE_HAVE_INSTRUCTIONS = 0x0100;
75 const USE_MY_METRICS = 0x0200;
78 const OVERLAP_COMPOUND = 0x0400;
86 const SCALED_COMPONENT_OFFSET = 0x0800;
88 const UNSCALED_COMPONENT_OFFSET = 0x1000;
90 }
92}
93
94#[derive(Debug, PartialEq)]
98pub struct GlyfTable<'a> {
99 records: Vec<GlyfRecord<'a>>,
100}
101
102#[derive(Debug, PartialEq, Clone)]
103pub enum GlyfRecord<'a> {
104 Present {
105 number_of_contours: i16,
106 scope: ReadScope<'a>,
107 },
108 Parsed(Glyph<'a>),
109}
110
111pub type PhantomPoints = [Point; 4];
112
113#[derive(Debug, PartialEq, Clone)]
114pub enum Glyph<'a> {
115 Empty(EmptyGlyph),
116 Simple(SimpleGlyph<'a>),
117 Composite(CompositeGlyph<'a>),
118}
119
120#[derive(Debug, PartialEq, Clone)]
121pub struct EmptyGlyph {
122 pub phantom_points: Option<PhantomPoints>,
123}
124
125#[derive(Debug, PartialEq, Clone)]
126pub struct SimpleGlyph<'a> {
127 pub bounding_box: BoundingBox,
128 pub end_pts_of_contours: Vec<u16>,
129 pub instructions: &'a [u8],
130 pub coordinates: Vec<(SimpleGlyphFlag, Point)>,
131 pub phantom_points: Option<Box<PhantomPoints>>,
133}
134
135#[derive(Debug, PartialEq, Clone)]
136pub struct CompositeGlyph<'a> {
137 pub bounding_box: BoundingBox,
138 pub glyphs: Vec<CompositeGlyphComponent>,
139 pub instructions: &'a [u8],
140 pub phantom_points: Option<Box<PhantomPoints>>,
142}
143
144#[derive(Debug, PartialEq, Clone)]
145pub struct CompositeGlyphComponent {
146 pub flags: CompositeGlyphFlag,
147 pub glyph_index: u16,
148 pub argument1: CompositeGlyphArgument,
149 pub argument2: CompositeGlyphArgument,
150 pub scale: Option<CompositeGlyphScale>,
151}
152
153#[derive(Debug, PartialEq, Copy, Clone)]
154pub enum CompositeGlyphArgument {
155 U8(u8),
156 I8(i8),
157 U16(u16),
158 I16(i16),
159}
160
161#[derive(Debug, PartialEq, Copy, Clone)]
162pub enum CompositeGlyphScale {
163 Scale(F2Dot14),
164 XY { x_scale: F2Dot14, y_scale: F2Dot14 },
165 Matrix([[F2Dot14; 2]; 2]),
166}
167
168pub struct CompositeGlyphs {
169 pub glyphs: Vec<CompositeGlyphComponent>,
170 pub have_instructions: bool,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
174pub struct Point(pub i16, pub i16);
175
176#[derive(Debug, PartialEq, Copy, Clone)]
177pub struct BoundingBox {
178 pub x_min: i16,
179 pub x_max: i16,
180 pub y_min: i16,
181 pub y_max: i16,
182}
183
184impl<'b> ReadBinaryDep for GlyfTable<'b> {
185 type Args<'a> = &'a LocaTable<'a>;
186 type HostType<'a> = GlyfTable<'a>;
187
188 fn read_dep<'a>(
189 ctxt: &mut ReadCtxt<'a>,
190 loca: Self::Args<'a>,
191 ) -> Result<Self::HostType<'a>, ParseError> {
192 if loca.offsets.len() < 2 {
193 return Err(ParseError::BadIndex);
194 }
195
196 let glyph_records = loca
197 .offsets
198 .iter()
199 .tuple_windows()
200 .map(|(start, end)| match end.checked_sub(start) {
201 Some(0) => Ok(GlyfRecord::empty()),
202 Some(length) => {
203 let offset = usize::try_from(start)?;
204 let glyph_scope = ctxt.scope().offset_length(offset, usize::try_from(length)?);
205 match glyph_scope {
206 Ok(scope) => {
207 let number_of_contours = scope.read::<I16Be>()?;
208 Ok(GlyfRecord::Present {
209 number_of_contours,
210 scope,
211 })
212 }
213 Err(ParseError::BadEof) => {
214 warn!("glyph length out of bounds, trying to parse");
220 let scope = ctxt.scope().offset(offset);
221 scope.read::<Glyph<'_>>().map(GlyfRecord::Parsed)
222 }
223 Err(err) => Err(err),
224 }
225 }
226 None => Err(ParseError::BadOffset),
227 })
228 .collect::<Result<Vec<_>, _>>()?;
229
230 Ok(GlyfTable {
231 records: glyph_records,
232 })
233 }
234}
235
236impl<'a> WriteBinaryDep<Self> for GlyfTable<'a> {
237 type Output = owned::LocaTable;
238 type Args = IndexToLocFormat;
239
240 fn write_dep<C: WriteContext>(
277 ctxt: &mut C,
278 table: GlyfTable<'a>,
279 index_to_loc_format: IndexToLocFormat,
280 ) -> Result<Self::Output, WriteError> {
281 let mut offsets: Vec<u32> = Vec::with_capacity(table.records.len() + 1);
282
283 let start = ctxt.bytes_written();
284 for record in table.records {
285 let offset = ctxt.bytes_written();
286
287 offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
288
289 match record {
290 GlyfRecord::Present { scope, .. } => ReadScope::write(ctxt, scope)?,
291 GlyfRecord::Parsed(glyph) => Glyph::write(ctxt, glyph)?,
292 }
293
294 if index_to_loc_format == IndexToLocFormat::Short {
295 let length = ctxt.bytes_written() - offset;
296 let padded_length = word_align(length);
297 ctxt.write_zeros(padded_length - length)?;
298 }
299 }
300
301 offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
303
304 Ok(owned::LocaTable { offsets })
305 }
306}
307
308impl Glyph<'_> {
309 pub fn number_of_contours(&self) -> i16 {
310 match self {
311 Glyph::Empty(_) => 0,
312 Glyph::Simple(simple) => simple.number_of_contours(),
313 Glyph::Composite(_) => -1,
314 }
315 }
316
317 pub fn bounding_box(&self) -> Option<BoundingBox> {
321 match self {
322 Glyph::Empty(_) => None,
323 Glyph::Simple(simple) => Some(simple.bounding_box),
324 Glyph::Composite(composite) => Some(composite.bounding_box),
325 }
326 }
327
328 pub fn phantom_points(&self) -> Option<PhantomPoints> {
332 match self {
333 Glyph::Empty(empty) => empty.phantom_points,
334 Glyph::Simple(SimpleGlyph { phantom_points, .. })
335 | Glyph::Composite(CompositeGlyph { phantom_points, .. }) => {
336 phantom_points.as_deref().copied()
337 }
338 }
339 }
340
341 fn number_of_points(&self) -> Result<u16, ParseError> {
343 match self {
344 Glyph::Empty(_) => Ok(0),
345 Glyph::Simple(glyph) => Ok(glyph.coordinates.len().try_into()?),
346 Glyph::Composite(composite) => Ok(composite.glyphs.len().try_into()?),
347 }
348 }
349}
350
351pub(crate) fn calculate_phantom_points(
355 glyph_id: u16,
356 bounding_box: Option<BoundingBox>,
357 hmtx: &HmtxTable<'_>,
358 vmtx: Option<&HmtxTable<'_>>,
359 os2: Option<&Os2>,
360 hhea: &HheaTable,
361) -> Result<[Point; 4], ParseError> {
362 let horizonal_metrics = hmtx.metric(glyph_id)?;
380 let x_min = bounding_box.map(|bbox| bbox.x_min).unwrap_or(0);
381 let y_max = bounding_box.map(|bbox| bbox.y_max).unwrap_or(0);
382 let pp1 = Point(x_min - horizonal_metrics.lsb, 0);
383 let pp2 = Point(pp1.0 + i16::try_from(horizonal_metrics.advance_width)?, 0);
384
385 let (advance_height, tsb) = match vmtx {
386 Some(vmtx) => vmtx.metric(glyph_id).and_then(|metric| {
387 i16::try_from(metric.advance_width)
388 .map(|aw| (aw, metric.lsb))
389 .map_err(|_| ParseError::LimitExceeded)
390 })?,
391 None => {
393 let (default_ascender, default_descender) =
394 match os2.and_then(|os2| os2.version0.as_ref()) {
395 Some(os2) => (os2.s_typo_ascender, os2.s_typo_descender),
396 None => (hhea.ascender, hhea.descender),
397 };
398 let advance_height = default_ascender - default_descender;
399 let tsb = default_ascender - y_max;
400 (advance_height, tsb)
401 }
402 };
403
404 let x = 0;
405 let pp3 = Point(x, y_max + tsb);
406 let pp4 = Point(x, pp3.1 - advance_height);
407
408 Ok([pp1, pp2, pp3, pp4])
409}
410
411impl<'b> ReadBinary for Glyph<'b> {
412 type HostType<'a> = Glyph<'a>;
413
414 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
415 let number_of_contours = ctxt.read_i16be()?;
416
417 if number_of_contours >= 0 {
418 let glyph = ctxt.read_dep::<SimpleGlyph<'_>>(number_of_contours as u16)?;
421 Ok(Glyph::Simple(glyph))
422 } else {
423 let glyph = ctxt.read::<CompositeGlyph<'_>>()?;
425 Ok(Glyph::Composite(glyph))
426 }
427 }
428}
429
430impl<'a> WriteBinary for Glyph<'a> {
431 type Output = ();
432
433 fn write<C: WriteContext>(ctxt: &mut C, glyph: Glyph<'a>) -> Result<(), WriteError> {
434 match glyph {
435 Glyph::Empty(_) => Ok(()),
436 Glyph::Simple(simple_glyph) => SimpleGlyph::write(ctxt, simple_glyph),
437 Glyph::Composite(composite) => CompositeGlyph::write(ctxt, composite),
438 }
439 }
440}
441
442impl<'a> SimpleGlyph<'a> {
443 pub fn number_of_contours(&self) -> i16 {
444 self.end_pts_of_contours.len() as i16
447 }
448
449 pub fn contours(&self) -> impl Iterator<Item = &[(SimpleGlyphFlag, Point)]> {
450 self.end_pts_of_contours.iter().scan(0, move |i, &end| {
451 let start = *i;
452 let end = usize::from(end);
453 *i = end + 1;
454 self.coordinates.get(start..=end)
455 })
456 }
457
458 pub fn bounding_box(&self) -> BoundingBox {
459 BoundingBox::from_points(self.coordinates.iter().copied().map(|(_flag, point)| point))
460 }
461}
462
463impl<'b> ReadBinaryDep for SimpleGlyph<'b> {
464 type Args<'a> = u16;
465 type HostType<'a> = SimpleGlyph<'a>;
466
467 fn read_dep<'a>(
468 ctxt: &mut ReadCtxt<'a>,
469 number_of_contours: Self::Args<'_>,
470 ) -> Result<Self::HostType<'a>, ParseError> {
471 let number_of_contours = usize::from(number_of_contours);
472 let bounding_box = ctxt.read::<BoundingBox>()?;
473 let end_pts_of_contours = ctxt.read_array::<U16Be>(number_of_contours)?.to_vec();
474 let instruction_length = ctxt.read::<U16Be>()?;
475 let instructions = ctxt.read_slice(usize::from(instruction_length))?;
476 let number_of_coordinates = end_pts_of_contours
479 .last()
480 .map_or(0, |&last| usize::from(last) + 1);
481
482 let mut coordinates = Vec::with_capacity(number_of_coordinates);
484 while coordinates.len() < number_of_coordinates {
485 let flag = ctxt.read::<SimpleGlyphFlag>()?;
486 if flag.is_repeated() {
487 let count = usize::from(ctxt.read::<U8>()?) + 1; let repeat = iter::repeat((flag, Point::zero())).take(count);
489 coordinates.extend(repeat)
490 } else {
491 coordinates.push((flag, Point::zero()));
492 }
493 }
494
495 for (flag, Point(x, _y)) in coordinates.iter_mut() {
497 *x = if flag.x_is_short() {
498 ctxt.read::<U8>()
499 .map(|val| i16::from(val) * flag.x_short_sign())?
500 } else if flag.x_is_same_or_positive() {
501 0
502 } else {
503 ctxt.read::<I16Be>()?
504 }
505 }
506
507 let mut prev_point = Point::zero();
509 for (flag, point) in coordinates.iter_mut() {
510 let y = if flag.y_is_short() {
511 ctxt.read::<U8>()
512 .map(|val| i16::from(val) * flag.y_short_sign())?
513 } else if flag.y_is_same_or_positive() {
514 0
515 } else {
516 ctxt.read::<I16Be>()?
517 };
518
519 prev_point = Point(prev_point.0 + point.0, prev_point.1 + y);
523 *point = prev_point
524 }
525
526 Ok(SimpleGlyph {
527 bounding_box,
528 end_pts_of_contours,
529 instructions,
530 coordinates,
531 phantom_points: None,
532 })
533 }
534}
535
536impl<'a> WriteBinary for SimpleGlyph<'a> {
537 type Output = ();
538
539 fn write<C: WriteContext>(ctxt: &mut C, glyph: SimpleGlyph<'_>) -> Result<(), WriteError> {
540 I16Be::write(ctxt, glyph.number_of_contours())?;
541 BoundingBox::write(ctxt, glyph.bounding_box)?;
542 ctxt.write_vec::<U16Be, _>(glyph.end_pts_of_contours)?;
543 U16Be::write(ctxt, u16::try_from(glyph.instructions.len())?)?;
544 ctxt.write_bytes(glyph.instructions)?;
545
546 let mask = SimpleGlyphFlag::ON_CURVE_POINT; for flag in glyph.coordinates.iter().map(|(flag, _)| *flag) {
553 U8::write(ctxt, (flag & mask).bits())?;
554 }
555
556 let mut prev_x = 0;
558 for (_, Point(x, _)) in &glyph.coordinates {
559 let delta_x = x - prev_x;
560 I16Be::write(ctxt, delta_x)?;
561 prev_x = *x;
562 }
563
564 let mut prev_y = 0;
566 for (_, Point(_, y)) in &glyph.coordinates {
567 let delta_y = y - prev_y;
568 I16Be::write(ctxt, delta_y)?;
569 prev_y = *y;
570 }
571
572 Ok(())
573 }
574}
575
576impl ReadFrom for SimpleGlyphFlag {
577 type ReadType = U8;
578
579 fn read_from(flag: u8) -> Self {
580 SimpleGlyphFlag::from_bits_truncate(flag)
581 }
582}
583
584impl ReadBinary for CompositeGlyphs {
585 type HostType<'a> = Self;
586
587 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
588 let mut have_instructions = false;
589 let mut glyphs = Vec::new();
590 loop {
591 let flags = ctxt.read::<CompositeGlyphFlag>()?;
592 let data = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
593
594 if flags.we_have_instructions() {
595 have_instructions = true;
596 }
597
598 glyphs.push(data);
599
600 if !flags.more_components() {
601 break;
602 }
603 }
604
605 Ok(CompositeGlyphs {
606 glyphs,
607 have_instructions,
608 })
609 }
610}
611
612impl ReadBinary for CompositeGlyph<'_> {
613 type HostType<'a> = CompositeGlyph<'a>;
614
615 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
616 let bounding_box = ctxt.read::<BoundingBox>()?;
617 let glyphs = ctxt.read::<CompositeGlyphs>()?;
618
619 let instruction_length = if glyphs.have_instructions {
620 usize::from(ctxt.read::<U16Be>()?)
621 } else {
622 0
623 };
624 let instructions = ctxt.read_slice(instruction_length)?;
625
626 Ok(CompositeGlyph {
627 bounding_box,
628 glyphs: glyphs.glyphs,
629 instructions,
630 phantom_points: None,
631 })
632 }
633}
634
635impl WriteBinary for CompositeGlyph<'_> {
636 type Output = ();
637
638 fn write<C: WriteContext>(ctxt: &mut C, composite: Self) -> Result<Self::Output, WriteError> {
639 I16Be::write(ctxt, -1_i16)?; BoundingBox::write(ctxt, composite.bounding_box)?;
641 let mut has_instructions = false;
642 for glyph in composite.glyphs {
643 has_instructions |= glyph.flags.we_have_instructions();
644 CompositeGlyphComponent::write(ctxt, glyph)?;
645 }
646 if has_instructions {
647 U16Be::write(ctxt, u16::try_from(composite.instructions.len())?)?;
648 ctxt.write_bytes(composite.instructions)?;
649 }
650 Ok(())
651 }
652}
653
654impl SimpleGlyphFlag {
655 pub fn is_on_curve(self) -> bool {
656 self & Self::ON_CURVE_POINT == Self::ON_CURVE_POINT
657 }
658
659 pub fn x_is_short(self) -> bool {
660 self & Self::X_SHORT_VECTOR == Self::X_SHORT_VECTOR
661 }
662
663 pub fn y_is_short(self) -> bool {
664 self & Self::Y_SHORT_VECTOR == Self::Y_SHORT_VECTOR
665 }
666
667 pub fn is_repeated(self) -> bool {
668 self & Self::REPEAT_FLAG == Self::REPEAT_FLAG
669 }
670
671 pub fn x_short_sign(self) -> i16 {
672 if self.x_is_same_or_positive() {
673 1
674 } else {
675 -1
676 }
677 }
678
679 pub fn y_short_sign(self) -> i16 {
680 if self.y_is_same_or_positive() {
681 1
682 } else {
683 -1
684 }
685 }
686
687 pub fn x_is_same_or_positive(self) -> bool {
688 self & Self::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
689 == Self::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR
690 }
691
692 pub fn y_is_same_or_positive(self) -> bool {
693 self & Self::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR
694 == Self::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR
695 }
696}
697
698impl ReadFrom for CompositeGlyphFlag {
699 type ReadType = U16Be;
700
701 fn read_from(flag: u16) -> Self {
702 CompositeGlyphFlag::from_bits_truncate(flag)
703 }
704}
705
706impl ReadBinaryDep for CompositeGlyphArgument {
707 type Args<'a> = CompositeGlyphFlag;
708 type HostType<'a> = Self;
709
710 fn read_dep<'a>(ctxt: &mut ReadCtxt<'a>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
711 let arg = match (flags.arg_1_and_2_are_words(), flags.args_are_xy_values()) {
712 (true, true) => CompositeGlyphArgument::I16(ctxt.read_i16be()?),
713 (true, false) => CompositeGlyphArgument::U16(ctxt.read_u16be()?),
714 (false, true) => CompositeGlyphArgument::I8(ctxt.read_i8()?),
715 (false, false) => CompositeGlyphArgument::U8(ctxt.read_u8()?),
716 };
717
718 Ok(arg)
719 }
720}
721
722impl WriteBinary for CompositeGlyphArgument {
723 type Output = ();
724
725 fn write<C: WriteContext>(ctxt: &mut C, arg: CompositeGlyphArgument) -> Result<(), WriteError> {
726 match arg {
727 CompositeGlyphArgument::U8(val) => U8::write(ctxt, val),
728 CompositeGlyphArgument::I8(val) => I8::write(ctxt, val),
729 CompositeGlyphArgument::U16(val) => U16Be::write(ctxt, val),
730 CompositeGlyphArgument::I16(val) => I16Be::write(ctxt, val),
731 }
732 }
733}
734
735impl ReadBinaryDep for CompositeGlyphComponent {
736 type Args<'a> = CompositeGlyphFlag;
737 type HostType<'a> = Self;
738
739 fn read_dep<'a>(ctxt: &mut ReadCtxt<'a>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
740 let glyph_index = ctxt.read_u16be()?;
741 let argument1 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
742 let argument2 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
743
744 let scale = if flags.we_have_a_scale() {
745 Some(CompositeGlyphScale::Scale(ctxt.read::<F2Dot14>()?))
746 } else if flags.we_have_an_x_and_y_scale() {
747 Some(CompositeGlyphScale::XY {
748 x_scale: ctxt.read::<F2Dot14>()?,
749 y_scale: ctxt.read::<F2Dot14>()?,
750 })
751 } else if flags.we_have_a_two_by_two() {
752 Some(CompositeGlyphScale::Matrix([
753 [ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
754 [ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
755 ]))
756 } else {
757 None
758 };
759
760 Ok(CompositeGlyphComponent {
761 flags,
762 glyph_index,
763 argument1,
764 argument2,
765 scale,
766 })
767 }
768}
769
770impl WriteBinary for CompositeGlyphComponent {
771 type Output = ();
772
773 fn write<C: WriteContext>(
774 ctxt: &mut C,
775 glyph: CompositeGlyphComponent,
776 ) -> Result<(), WriteError> {
777 U16Be::write(ctxt, glyph.flags.bits())?;
778 U16Be::write(ctxt, glyph.glyph_index)?;
779 CompositeGlyphArgument::write(ctxt, glyph.argument1)?;
780 CompositeGlyphArgument::write(ctxt, glyph.argument2)?;
781 if let Some(scale) = glyph.scale {
782 CompositeGlyphScale::write(ctxt, scale)?;
783 }
784 Ok(())
785 }
786}
787
788impl WriteBinary for CompositeGlyphScale {
789 type Output = ();
790
791 fn write<C: WriteContext>(ctxt: &mut C, scale: CompositeGlyphScale) -> Result<(), WriteError> {
792 match scale {
793 CompositeGlyphScale::Scale(scale) => F2Dot14::write(ctxt, scale)?,
794 CompositeGlyphScale::XY { x_scale, y_scale } => {
795 F2Dot14::write(ctxt, x_scale)?;
796 F2Dot14::write(ctxt, y_scale)?;
797 }
798 CompositeGlyphScale::Matrix(matrix) => {
799 F2Dot14::write(ctxt, matrix[0][0])?;
800 F2Dot14::write(ctxt, matrix[0][1])?;
801 F2Dot14::write(ctxt, matrix[1][0])?;
802 F2Dot14::write(ctxt, matrix[1][1])?;
803 }
804 }
805
806 Ok(())
807 }
808}
809
810impl ReadFrom for BoundingBox {
811 type ReadType = ((I16Be, I16Be), (I16Be, I16Be));
812
813 fn read_from(((x_min, y_min), (x_max, y_max)): ((i16, i16), (i16, i16))) -> Self {
814 BoundingBox {
815 x_min,
816 y_min,
817 x_max,
818 y_max,
819 }
820 }
821}
822
823impl WriteBinary for BoundingBox {
824 type Output = ();
825
826 fn write<C: WriteContext>(ctxt: &mut C, bbox: BoundingBox) -> Result<(), WriteError> {
827 I16Be::write(ctxt, bbox.x_min)?;
828 I16Be::write(ctxt, bbox.y_min)?;
829 I16Be::write(ctxt, bbox.x_max)?;
830 I16Be::write(ctxt, bbox.y_max)?;
831 Ok(())
832 }
833}
834
835impl<'a> GlyfTable<'a> {
836 pub fn new(records: Vec<GlyfRecord<'a>>) -> Result<Self, ParseError> {
837 if records.len() > usize::from(u16::MAX) {
838 return Err(ParseError::LimitExceeded);
839 }
840 Ok(GlyfTable { records })
841 }
842
843 pub fn num_glyphs(&self) -> u16 {
847 self.records.len() as u16
849 }
850
851 pub fn records(&self) -> &[GlyfRecord<'a>] {
852 &self.records
853 }
854
855 pub fn records_mut(&mut self) -> &mut [GlyfRecord<'a>] {
856 &mut self.records
857 }
858
859 pub fn push(&mut self, record: GlyfRecord<'a>) -> Result<(), ParseError> {
860 if self.num_glyphs() < u16::MAX {
861 self.records.push(record);
862 Ok(())
863 } else {
864 Err(ParseError::LimitExceeded)
865 }
866 }
867
868 pub fn get_parsed_glyph(&mut self, glyph_index: u16) -> Result<&Glyph<'a>, ParseError> {
871 let record = self
872 .records
873 .get_mut(usize::from(glyph_index))
874 .ok_or(ParseError::BadIndex)?;
875 record.parse()?;
876 match record {
877 GlyfRecord::Parsed(glyph) => Ok(glyph),
878 GlyfRecord::Present { .. } => unreachable!("glyph should be parsed"),
879 }
880 }
881
882 pub(crate) fn take(&mut self, glyph_index: u16) -> Option<GlyfRecord<'a>> {
887 let target = self.records.get_mut(usize::from(glyph_index))?;
888 Some(mem::replace(target, GlyfRecord::empty()))
889 }
890
891 pub(crate) fn replace(
895 &mut self,
896 glyph_index: u16,
897 record: GlyfRecord<'a>,
898 ) -> Result<(), ParseError> {
899 let target = self
900 .records
901 .get_mut(usize::from(glyph_index))
902 .ok_or(ParseError::BadIndex)?;
903 *target = record;
904 Ok(())
905 }
906}
907
908impl<'a> GlyfRecord<'a> {
909 pub fn empty() -> Self {
911 GlyfRecord::Parsed(Glyph::Empty(EmptyGlyph::new()))
912 }
913
914 pub fn number_of_contours(&self) -> i16 {
915 match self {
916 GlyfRecord::Present {
917 number_of_contours, ..
918 } => *number_of_contours,
919 GlyfRecord::Parsed(glyph) => glyph.number_of_contours(),
920 }
921 }
922
923 pub fn number_of_points(&self) -> Result<u16, ParseError> {
925 match self {
932 GlyfRecord::Present {
933 scope,
934 number_of_contours,
935 } => {
936 let mut ctxt = scope.ctxt();
937 let _skip = ctxt.read_slice(U16Be::SIZE + BoundingBox::SIZE)?;
939 if *number_of_contours >= 0 {
940 let end_pts_of_contours =
942 ctxt.read_array::<U16Be>(*number_of_contours as usize)?;
943 match end_pts_of_contours.last() {
946 Some(last) => last.checked_add(1).ok_or(ParseError::LimitExceeded),
947 None => Ok(0),
948 }
949 } else {
950 let mut count = 0;
952 loop {
953 let flags = ctxt.read::<CompositeGlyphFlag>()?;
954 let _composite_glyph = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
955 count += 1;
956 if !flags.more_components() {
957 break;
958 }
959 }
960 Ok(count)
961 }
962 }
963 GlyfRecord::Parsed(glyph) => glyph.number_of_points(),
964 }
965 }
966
967 pub fn is_composite(&self) -> bool {
968 self.number_of_contours() < 0
969 }
970
971 pub fn parse(&mut self) -> Result<(), ParseError> {
973 if let GlyfRecord::Present { scope, .. } = self {
974 *self = scope.read::<Glyph<'_>>().map(GlyfRecord::Parsed)?;
975 }
976 Ok(())
977 }
978}
979
980impl<'a> From<SimpleGlyph<'a>> for GlyfRecord<'a> {
981 fn from(glyph: SimpleGlyph<'a>) -> GlyfRecord<'a> {
982 GlyfRecord::Parsed(Glyph::Simple(glyph))
983 }
984}
985
986impl<'a> From<CompositeGlyph<'a>> for GlyfRecord<'a> {
987 fn from(glyph: CompositeGlyph<'a>) -> GlyfRecord<'a> {
988 GlyfRecord::Parsed(Glyph::Composite(glyph))
989 }
990}
991
992impl EmptyGlyph {
993 pub fn new() -> Self {
994 EmptyGlyph {
995 phantom_points: None,
996 }
997 }
998}
999
1000impl CompositeGlyphFlag {
1001 pub fn arg_1_and_2_are_words(self) -> bool {
1002 self & Self::ARG_1_AND_2_ARE_WORDS == Self::ARG_1_AND_2_ARE_WORDS
1003 }
1004
1005 pub fn args_are_xy_values(self) -> bool {
1006 self & Self::ARGS_ARE_XY_VALUES == Self::ARGS_ARE_XY_VALUES
1007 }
1008
1009 pub fn we_have_a_scale(self) -> bool {
1010 self & Self::WE_HAVE_A_SCALE == Self::WE_HAVE_A_SCALE
1011 }
1012
1013 pub fn we_have_an_x_and_y_scale(self) -> bool {
1014 self & Self::WE_HAVE_AN_X_AND_Y_SCALE == Self::WE_HAVE_AN_X_AND_Y_SCALE
1015 }
1016
1017 pub fn we_have_a_two_by_two(self) -> bool {
1018 self & Self::WE_HAVE_A_TWO_BY_TWO == Self::WE_HAVE_A_TWO_BY_TWO
1019 }
1020
1021 pub fn more_components(self) -> bool {
1022 self & Self::MORE_COMPONENTS == Self::MORE_COMPONENTS
1023 }
1024
1025 pub fn we_have_instructions(self) -> bool {
1026 self & Self::WE_HAVE_INSTRUCTIONS == Self::WE_HAVE_INSTRUCTIONS
1027 }
1028
1029 pub fn component_offsets(self) -> ComponentOffsets {
1030 let scaled = self & Self::SCALED_COMPONENT_OFFSET == Self::SCALED_COMPONENT_OFFSET;
1046 let unscaled = self & Self::UNSCALED_COMPONENT_OFFSET == Self::UNSCALED_COMPONENT_OFFSET;
1047 match (scaled, unscaled) {
1048 (true, false) => ComponentOffsets::Scaled,
1049 (false, true) => ComponentOffsets::Unscaled,
1050 (true, true) | (false, false) => ComponentOffsets::Unscaled,
1052 }
1053 }
1054}
1055
1056#[derive(Copy, Clone, Eq, PartialEq)]
1057pub enum ComponentOffsets {
1058 Scaled,
1059 Unscaled,
1060}
1061
1062impl Point {
1063 pub fn zero() -> Self {
1064 Point(0, 0)
1065 }
1066}
1067
1068impl BoundingBox {
1069 pub fn empty() -> Self {
1070 BoundingBox {
1071 x_min: 0,
1072 x_max: 0,
1073 y_min: 0,
1074 y_max: 0,
1075 }
1076 }
1077
1078 pub fn from_points(points: impl ExactSizeIterator<Item = Point>) -> Self {
1082 assert!(points.len() > 0);
1083 let mut points = points.peekable();
1084
1085 let &Point(initial_x, initial_y) = points.peek().unwrap();
1087 let initial = BoundingBox {
1088 x_min: initial_x,
1089 x_max: initial_x,
1090 y_min: initial_y,
1091 y_max: initial_y,
1092 };
1093
1094 points.fold(initial, |mut bounding_box, point| {
1095 bounding_box.add(point);
1096 bounding_box
1097 })
1098 }
1099
1100 pub fn add(&mut self, Point(x, y): Point) {
1102 self.x_min = i16::min(x, self.x_min);
1103 self.x_max = i16::max(x, self.x_max);
1104 self.y_min = i16::min(y, self.y_min);
1105 self.y_max = i16::max(y, self.y_max);
1106 }
1107}
1108
1109impl std::ops::Add for Point {
1110 type Output = Self;
1111
1112 fn add(self, Point(x1, y1): Point) -> Self::Output {
1113 let Point(x, y) = self;
1114 Point(x + x1, y + y1)
1115 }
1116}
1117
1118impl From<CompositeGlyphArgument> for i32 {
1119 fn from(arg: CompositeGlyphArgument) -> Self {
1120 match arg {
1121 CompositeGlyphArgument::U8(value) => i32::from(value),
1122 CompositeGlyphArgument::I8(value) => i32::from(value),
1123 CompositeGlyphArgument::U16(value) => i32::from(value),
1124 CompositeGlyphArgument::I16(value) => i32::from(value),
1125 }
1126 }
1127}
1128
1129impl TryFrom<CompositeGlyphArgument> for u16 {
1130 type Error = std::num::TryFromIntError;
1131
1132 fn try_from(arg: CompositeGlyphArgument) -> Result<Self, Self::Error> {
1133 match arg {
1134 CompositeGlyphArgument::U8(value) => Ok(u16::from(value)),
1135 CompositeGlyphArgument::I8(value) => u16::try_from(value),
1136 CompositeGlyphArgument::U16(value) => Ok(value),
1137 CompositeGlyphArgument::I16(value) => u16::try_from(value),
1138 }
1139 }
1140}
1141
1142impl From<CompositeGlyphScale> for Matrix2x2F {
1143 fn from(scale: CompositeGlyphScale) -> Self {
1144 match scale {
1145 CompositeGlyphScale::Scale(scale) => {
1146 let scale = f32::from(scale);
1147 Matrix2x2F::from_scale(scale)
1148 }
1149 CompositeGlyphScale::XY { x_scale, y_scale } => {
1150 let scale = Vector2F::new(f32::from(x_scale), f32::from(y_scale));
1151 Matrix2x2F::from_scale(scale)
1152 }
1153 CompositeGlyphScale::Matrix(matrix) => Matrix2x2F::row_major(
1154 f32::from(matrix[0][0]),
1155 f32::from(matrix[0][1]),
1156 f32::from(matrix[1][0]),
1157 f32::from(matrix[1][1]),
1158 ),
1159 }
1160 }
1161}
1162
1163#[cfg(test)]
1164mod tests {
1165 use super::*;
1166 use crate::binary::write::WriteBuffer;
1167 use crate::error::ReadWriteError;
1168
1169 pub(super) fn simple_glyph_fixture() -> SimpleGlyph<'static> {
1170 SimpleGlyph {
1171 bounding_box: BoundingBox {
1172 x_min: 60,
1173 x_max: 915,
1174 y_min: -105,
1175 y_max: 702,
1176 },
1177 end_pts_of_contours: vec![8],
1178 instructions: &[],
1179 coordinates: vec![
1180 (
1181 SimpleGlyphFlag::ON_CURVE_POINT
1182 | SimpleGlyphFlag::Y_SHORT_VECTOR
1183 | SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
1184 Point(433, 77),
1185 ),
1186 (
1187 SimpleGlyphFlag::X_SHORT_VECTOR
1188 | SimpleGlyphFlag::Y_SHORT_VECTOR
1189 | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1190 Point(499, 30),
1191 ),
1192 (
1193 SimpleGlyphFlag::ON_CURVE_POINT
1194 | SimpleGlyphFlag::X_SHORT_VECTOR
1195 | SimpleGlyphFlag::Y_SHORT_VECTOR
1196 | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1197 Point(625, 2),
1198 ),
1199 (
1200 SimpleGlyphFlag::X_SHORT_VECTOR
1201 | SimpleGlyphFlag::Y_SHORT_VECTOR
1202 | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1203 Point(756, -27),
1204 ),
1205 (
1206 SimpleGlyphFlag::ON_CURVE_POINT
1207 | SimpleGlyphFlag::X_SHORT_VECTOR
1208 | SimpleGlyphFlag::Y_SHORT_VECTOR
1209 | SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
1210 Point(915, -31),
1211 ),
1212 (
1213 SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
1214 Point(891, -47),
1215 ),
1216 (
1217 SimpleGlyphFlag::ON_CURVE_POINT
1218 | SimpleGlyphFlag::X_SHORT_VECTOR
1219 | SimpleGlyphFlag::Y_SHORT_VECTOR,
1220 Point(862, -60),
1221 ),
1222 (
1223 SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
1224 Point(832, -73),
1225 ),
1226 (
1227 SimpleGlyphFlag::ON_CURVE_POINT
1228 | SimpleGlyphFlag::X_SHORT_VECTOR
1229 | SimpleGlyphFlag::Y_SHORT_VECTOR,
1230 Point(819, -103),
1231 ),
1232 ],
1233 phantom_points: None,
1234 }
1235 }
1236
1237 pub(super) fn composite_glyph_fixture(instructions: &'static [u8]) -> CompositeGlyph<'static> {
1238 CompositeGlyph {
1239 bounding_box: BoundingBox {
1240 x_min: 205,
1241 x_max: 4514,
1242 y_min: 0,
1243 y_max: 1434,
1244 },
1245 glyphs: vec![
1246 CompositeGlyphComponent {
1247 flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1248 | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1249 | CompositeGlyphFlag::ROUND_XY_TO_GRID
1250 | CompositeGlyphFlag::MORE_COMPONENTS
1251 | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
1252 glyph_index: 5,
1253 argument1: CompositeGlyphArgument::I16(3453),
1254 argument2: CompositeGlyphArgument::I16(0),
1255 scale: None,
1256 },
1257 CompositeGlyphComponent {
1258 flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1259 | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1260 | CompositeGlyphFlag::ROUND_XY_TO_GRID
1261 | CompositeGlyphFlag::MORE_COMPONENTS
1262 | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
1263 glyph_index: 4,
1264 argument1: CompositeGlyphArgument::I16(2773),
1265 argument2: CompositeGlyphArgument::I16(0),
1266 scale: None,
1267 },
1268 CompositeGlyphComponent {
1269 flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1270 | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1271 | CompositeGlyphFlag::ROUND_XY_TO_GRID
1272 | CompositeGlyphFlag::MORE_COMPONENTS
1273 | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
1274 glyph_index: 3,
1275 argument1: CompositeGlyphArgument::I16(1182),
1276 argument2: CompositeGlyphArgument::I16(0),
1277 scale: None,
1278 },
1279 CompositeGlyphComponent {
1280 flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
1281 | CompositeGlyphFlag::ARGS_ARE_XY_VALUES
1282 | CompositeGlyphFlag::ROUND_XY_TO_GRID
1283 | CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET
1284 | CompositeGlyphFlag::WE_HAVE_INSTRUCTIONS,
1285 glyph_index: 2,
1286 argument1: CompositeGlyphArgument::I16(205),
1287 argument2: CompositeGlyphArgument::I16(0),
1288 scale: None,
1289 },
1290 ],
1291 instructions,
1292 phantom_points: None,
1293 }
1294 }
1295
1296 #[test]
1297 fn test_point_bounding_box() {
1298 let points = [Point(1761, 565), Point(2007, 565), Point(1884, 1032)];
1299
1300 let expected = BoundingBox {
1301 x_min: 1761,
1302 y_min: 565,
1303 x_max: 2007,
1304 y_max: 1032,
1305 };
1306
1307 assert_eq!(BoundingBox::from_points(points.iter().copied()), expected);
1308 }
1309
1310 #[test]
1311 fn write_glyf_table_loca_sanity_check() {
1312 let glyf = GlyfTable {
1313 records: vec![GlyfRecord::empty(), GlyfRecord::empty()],
1314 };
1315 let num_glyphs = glyf.records.len();
1316 let mut buffer = WriteBuffer::new();
1317 let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
1318 assert_eq!(loca.offsets.len(), num_glyphs + 1);
1319 }
1320
1321 #[test]
1322 fn write_composite_glyf_instructions() {
1323 let glyph = Glyph::Composite(composite_glyph_fixture(&[1, 2, 3, 4]));
1324
1325 let mut buffer = WriteBuffer::new();
1326 Glyph::write(&mut buffer, glyph).unwrap();
1327
1328 match ReadScope::new(buffer.bytes()).read::<Glyph<'_>>() {
1330 Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
1331 assert_eq!(instructions, vec![1, 2, 3, 4].as_slice())
1332 }
1333 _ => panic!("did not read back expected instructions"),
1334 }
1335 }
1336
1337 #[test]
1338 fn read_glyph_offsets_correctly() {
1339 let glyph = simple_glyph_fixture();
1344
1345 let mut buffer = WriteBuffer::new();
1347 buffer.write_zeros(4).unwrap(); SimpleGlyph::write(&mut buffer, glyph).unwrap();
1349 let glyph_data = buffer.into_inner();
1350
1351 let mut buffer = WriteBuffer::new();
1352 let loca = owned::LocaTable {
1353 offsets: vec![4, 4, glyph_data.len() as u32 - 4],
1354 };
1355 owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
1356 .expect("unable to generate loca");
1357 let loca_data = buffer.into_inner();
1358
1359 let num_glyphs = 2;
1361 let loca = ReadScope::new(&loca_data)
1362 .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
1363 .expect("unable to read loca");
1364 let glyf = ReadScope::new(&glyph_data)
1365 .read_dep::<GlyfTable<'_>>(&loca)
1366 .expect("unable to read glyf");
1367 assert_eq!(glyf.records.len(), 2);
1368 assert_eq!(&glyf.records[0], &GlyfRecord::empty());
1369 let glyph = &glyf.records[1];
1370
1371 assert_eq!(glyph.number_of_contours(), 1);
1373 }
1374
1375 #[test]
1381 fn simple_glyph_with_zero_contours() {
1382 let glyph_data = &[
1383 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
1386 let expected = SimpleGlyph {
1387 bounding_box: BoundingBox::empty(),
1388 end_pts_of_contours: vec![],
1389 instructions: &[],
1390 coordinates: vec![],
1391 phantom_points: None,
1392 };
1393
1394 let glyph = ReadScope::new(glyph_data)
1395 .read_dep::<SimpleGlyph<'_>>(0)
1396 .unwrap();
1397 assert_eq!(glyph, expected);
1398 }
1399
1400 #[test]
1401 fn write_simple_glyph_with_zero_contours() {
1402 let glyph = SimpleGlyph {
1403 bounding_box: BoundingBox::empty(),
1404 end_pts_of_contours: vec![],
1405 instructions: &[],
1406 coordinates: vec![],
1407 phantom_points: None,
1408 };
1409
1410 let mut buffer = WriteBuffer::new();
1411 assert!(SimpleGlyph::write(&mut buffer, glyph).is_ok());
1412 }
1413
1414 #[test]
1415 fn read_glyph_with_incorrect_loca_length() {
1416 let glyph = simple_glyph_fixture();
1418 let mut buffer = WriteBuffer::new();
1419 Glyph::write(&mut buffer, Glyph::Simple(glyph)).unwrap();
1420 let glyph_data = buffer.into_inner();
1421
1422 let mut buffer = WriteBuffer::new();
1423 let loca = owned::LocaTable {
1424 offsets: vec![0, 0, glyph_data.len() as u32 + 1], };
1426 owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
1427 .expect("unable to generate loca");
1428 let loca_data = buffer.into_inner();
1429
1430 let num_glyphs = 2;
1432 let loca = ReadScope::new(&loca_data)
1433 .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
1434 .expect("unable to read loca");
1435 assert!(ReadScope::new(&glyph_data)
1436 .read_dep::<GlyfTable<'_>>(&loca)
1437 .is_ok())
1438 }
1439
1440 #[test]
1445 fn write_composite_glyph_with_empty_instructions() {
1446 let glyph = composite_glyph_fixture(&[]);
1447
1448 let mut buffer = WriteBuffer::new();
1449 Glyph::write(&mut buffer, Glyph::Composite(glyph)).unwrap();
1450
1451 match ReadScope::new(buffer.bytes()).read::<Glyph<'_>>() {
1453 Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
1454 assert_eq!(instructions, &[])
1455 }
1456 Ok(_) => panic!("did not read back expected glyph"),
1457 Err(_) => panic!("unable to read back glyph"),
1458 }
1459 }
1460
1461 #[test]
1462 fn test_number_of_points_empty() {
1463 let glyph = GlyfRecord::empty();
1464 assert_eq!(glyph.number_of_points().unwrap(), 0);
1465 }
1466
1467 #[test]
1468 fn test_number_of_points_simple_parsed() {
1469 let glyph = GlyfRecord::from(simple_glyph_fixture());
1470 assert_eq!(glyph.number_of_points().unwrap(), 9);
1471 }
1472
1473 #[test]
1474 fn test_number_of_points_simple_present() -> Result<(), ReadWriteError> {
1475 let glyph = GlyfRecord::from(simple_glyph_fixture());
1477 let glyf = GlyfTable {
1478 records: vec![GlyfRecord::empty(), glyph],
1479 };
1480 let num_glyphs = glyf.records.len();
1481 let mut buffer = WriteBuffer::new();
1482 let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
1483 let mut loca_buffer = WriteBuffer::new();
1484 owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
1485 let loca_data = loca_buffer.into_inner();
1486 let loca = ReadScope::new(&loca_data)
1487 .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
1488
1489 let glyf = ReadScope::new(&buffer.bytes())
1491 .read_dep::<GlyfTable<'_>>(&loca)
1492 .unwrap();
1493 let glyph = &glyf.records[1];
1494 assert!(matches!(glyph, GlyfRecord::Present { .. }));
1495 assert_eq!(glyph.number_of_points().unwrap(), 9);
1496 Ok(())
1497 }
1498
1499 #[test]
1500 fn test_number_of_points_composite_parsed() {
1501 let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
1503 assert_eq!(glyph.number_of_points().unwrap(), 4);
1504 }
1505
1506 #[test]
1507 fn test_number_of_points_composite_present() -> Result<(), ReadWriteError> {
1508 let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
1510 let glyf = GlyfTable {
1511 records: vec![GlyfRecord::empty(), glyph],
1512 };
1513 let num_glyphs = glyf.records.len();
1514 let mut buffer = WriteBuffer::new();
1515 let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
1516 let mut loca_buffer = WriteBuffer::new();
1517 owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
1518 let loca_data = loca_buffer.into_inner();
1519 let loca = ReadScope::new(&loca_data)
1520 .read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
1521
1522 let glyf = ReadScope::new(&buffer.bytes())
1524 .read_dep::<GlyfTable<'_>>(&loca)
1525 .unwrap();
1526 let glyph = &glyf.records[1];
1527 assert!(matches!(glyph, GlyfRecord::Present { .. }));
1528 assert_eq!(glyph.number_of_points().unwrap(), 4);
1529 Ok(())
1530 }
1531}