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