1 //! Support for applying embedded hinting instructions.
2 
3 use super::{
4     cff, AdjustedMetrics, DrawError, Hinting, LocationRef, NormalizedCoord, OutlineCollectionKind,
5     OutlineGlyph, OutlineGlyphCollection, OutlineKind, OutlinePen, Size,
6 };
7 
8 /// Modes for embedded hinting.
9 ///
10 /// Only the `glyf` format supports all hinting modes.
11 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
12 pub enum EmbeddedHinting {
13     /// "Full" hinting mode. May generate rough outlines and poor horizontal
14     /// spacing.
15     Full,
16     /// Light hinting mode. This prevents most movement in the horizontal
17     /// direction with the exception of a per-font backward compatibility
18     /// opt in.
19     Light,
20     /// Same as light, but with additional support for RGB subpixel rendering.
21     LightSubpixel,
22     /// Same as light subpixel, but always prevents adjustment in the
23     /// horizontal direction. This is the default mode.
24     VerticalSubpixel,
25 }
26 
27 /// Hinting instance that uses information embedded in the font to perform
28 /// grid-fitting.
29 #[derive(Clone)]
30 pub struct EmbeddedHintingInstance {
31     size: Size,
32     coords: Vec<NormalizedCoord>,
33     mode: EmbeddedHinting,
34     kind: HinterKind,
35 }
36 
37 impl EmbeddedHintingInstance {
38     /// Creates a new embedded hinting instance for the given outline
39     /// collection, size, location in variation space and hinting mode.
new<'a>( outline_glyphs: &OutlineGlyphCollection, size: Size, location: impl Into<LocationRef<'a>>, mode: EmbeddedHinting, ) -> Result<Self, DrawError>40     pub fn new<'a>(
41         outline_glyphs: &OutlineGlyphCollection,
42         size: Size,
43         location: impl Into<LocationRef<'a>>,
44         mode: EmbeddedHinting,
45     ) -> Result<Self, DrawError> {
46         let mut hinter = Self {
47             size: Size::unscaled(),
48             coords: vec![],
49             mode,
50             kind: HinterKind::None,
51         };
52         hinter.reconfigure(outline_glyphs, size, location, mode)?;
53         Ok(hinter)
54     }
55 
56     /// Returns the currently configured size.
size(&self) -> Size57     pub fn size(&self) -> Size {
58         self.size
59     }
60 
61     /// Returns the currently configured normalized location in variation space.
location(&self) -> LocationRef62     pub fn location(&self) -> LocationRef {
63         LocationRef::new(&self.coords)
64     }
65 
66     /// Returns the currently configured hinting mode.
mode(&self) -> EmbeddedHinting67     pub fn mode(&self) -> EmbeddedHinting {
68         self.mode
69     }
70 
71     /// Resets the hinter state for a new font instance with the given
72     /// outline collection and settings.
reconfigure<'a>( &mut self, outlines: &OutlineGlyphCollection, size: Size, location: impl Into<LocationRef<'a>>, mode: EmbeddedHinting, ) -> Result<(), DrawError>73     pub fn reconfigure<'a>(
74         &mut self,
75         outlines: &OutlineGlyphCollection,
76         size: Size,
77         location: impl Into<LocationRef<'a>>,
78         mode: EmbeddedHinting,
79     ) -> Result<(), DrawError> {
80         self.size = size;
81         self.coords.clear();
82         self.coords.extend_from_slice(location.into().coords());
83         self.mode = mode;
84         // Reuse memory if the font contains the same outline format
85         let current_kind = core::mem::replace(&mut self.kind, HinterKind::None);
86         match &outlines.kind {
87             OutlineCollectionKind::Glyf(_) => {
88                 self.kind = HinterKind::Glyf;
89             }
90             OutlineCollectionKind::Cff(cff) => {
91                 let mut subfonts = match current_kind {
92                     HinterKind::Cff(subfonts) => subfonts,
93                     _ => vec![],
94                 };
95                 subfonts.clear();
96                 let ppem = size.ppem();
97                 for i in 0..cff.subfont_count() {
98                     subfonts.push(cff.subfont(i, ppem, &self.coords)?);
99                 }
100                 self.kind = HinterKind::Cff(subfonts);
101             }
102             OutlineCollectionKind::None => {}
103         }
104         Ok(())
105     }
106 
draw( &self, glyph: &OutlineGlyph, memory: Option<&mut [u8]>, pen: &mut impl OutlinePen, ) -> Result<AdjustedMetrics, DrawError>107     pub(super) fn draw(
108         &self,
109         glyph: &OutlineGlyph,
110         memory: Option<&mut [u8]>,
111         pen: &mut impl OutlinePen,
112     ) -> Result<AdjustedMetrics, DrawError> {
113         let ppem = self.size.ppem();
114         let coords = self.coords.as_slice();
115         match (&self.kind, &glyph.kind) {
116             (HinterKind::Glyf, OutlineKind::Glyf(glyf, outline)) => {
117                 super::with_glyf_memory(outline, Hinting::Embedded, memory, |buf| {
118                     let mem = outline
119                         .memory_from_buffer(buf, Hinting::Embedded)
120                         .ok_or(DrawError::InsufficientMemory)?;
121                     let scaled_outline = glyf.draw(mem, outline, ppem, coords)?;
122                     scaled_outline.to_path(pen)?;
123                     Ok(AdjustedMetrics {
124                         has_overlaps: outline.has_overlaps,
125                         lsb: Some(scaled_outline.adjusted_lsb().to_f32()),
126                         advance_width: Some(scaled_outline.adjusted_advance_width().to_f32()),
127                     })
128                 })
129             }
130             (HinterKind::Cff(subfonts), OutlineKind::Cff(cff, glyph_id, subfont_ix)) => {
131                 let Some(subfont) = subfonts.get(*subfont_ix as usize) else {
132                     return Err(DrawError::NoSources);
133                 };
134                 cff.draw(subfont, *glyph_id, &self.coords, true, pen)?;
135                 Ok(AdjustedMetrics::default())
136             }
137             _ => Err(DrawError::NoSources),
138         }
139     }
140 }
141 
142 #[derive(Clone)]
143 enum HinterKind {
144     /// Represents a hinting instance that is associated with an empty outline
145     /// collection.
146     None,
147     Glyf,
148     Cff(Vec<cff::Subfont>),
149 }
150