1 //! Helpers for selecting a font size and location in variation space. 2 3 use read_fonts::types::Fixed; 4 5 use crate::small_array::SmallArray; 6 7 /// Type for a normalized variation coordinate. 8 pub type NormalizedCoord = read_fonts::types::F2Dot14; 9 10 /// Font size in pixels per em units. 11 /// 12 /// Sizes in this crate are represented as a ratio of pixels to the size of 13 /// the em square defined by the font. This is equivalent to the `px` unit 14 /// in CSS (assuming a DPI scale factor of 1.0). 15 /// 16 /// To retrieve metrics and outlines in font units, use the [unscaled](Self::unscaled) 17 /// construtor on this type. 18 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] 19 pub struct Size(Option<f32>); 20 21 impl Size { 22 /// Creates a new font size from the given value in pixels per em units. new(ppem: f32) -> Self23 pub fn new(ppem: f32) -> Self { 24 Self(Some(ppem)) 25 } 26 27 /// Creates a new font size for generating unscaled metrics or outlines in 28 /// font units. unscaled() -> Self29 pub fn unscaled() -> Self { 30 Self(None) 31 } 32 33 /// Returns the raw size in pixels per em units. 34 /// 35 /// Results in `None` if the size is unscaled. ppem(self) -> Option<f32>36 pub fn ppem(self) -> Option<f32> { 37 self.0 38 } 39 40 /// Computes a linear scale factor for this font size and the given units 41 /// per em value which can be retrieved from the [Metrics](crate::metrics::Metrics) 42 /// type or from the [head](read_fonts::tables::head::Head) table. 43 /// 44 /// Returns 1.0 for an unscaled size or when `units_per_em` is 0. linear_scale(self, units_per_em: u16) -> f3245 pub fn linear_scale(self, units_per_em: u16) -> f32 { 46 match self.0 { 47 Some(ppem) if units_per_em != 0 => ppem / units_per_em as f32, 48 _ => 1.0, 49 } 50 } 51 52 /// Computes a fixed point linear scale factor that matches FreeType. fixed_linear_scale(self, units_per_em: u16) -> Fixed53 pub(crate) fn fixed_linear_scale(self, units_per_em: u16) -> Fixed { 54 // FreeType computes a 16.16 scale factor that converts to 26.6. 55 // This is done in two steps, assuming use of FT_Set_Pixel_Size: 56 // 1) height is multiplied by 64: 57 // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/49781ab72b2dfd0f78172023921d08d08f323ade/src/base/ftobjs.c#L3596> 58 // 2) this value is divided by UPEM: 59 // (here, scaled_h=height and h=upem) 60 // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/49781ab72b2dfd0f78172023921d08d08f323ade/src/base/ftobjs.c#L3312> 61 match self.0 { 62 Some(ppem) if units_per_em > 0 => { 63 Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(units_per_em as i32) 64 } 65 _ => { 66 // This is an identity scale for the pattern 67 // `mul_div(value, scale, 64)` 68 Fixed::from_bits(0x10000 * 64) 69 } 70 } 71 } 72 } 73 74 /// Reference to an ordered sequence of normalized variation coordinates. 75 /// 76 /// This type represents a position in the variation space where each 77 /// coordinate corresponds to an axis (in the same order as the `fvar` table) 78 /// and is a normalized value in the range `[-1..1]`. 79 /// 80 /// See [Coordinate Scales and Normalization](https://learn.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization) 81 /// for further details. 82 /// 83 /// If the array is larger in length than the number of axes, extraneous 84 /// values are ignored. If it is smaller, unrepresented axes are assumed to be 85 /// at their default positions (i.e. 0). 86 /// 87 /// A value of this type constructed with `default()` represents the default 88 /// position for each axis. 89 /// 90 /// Normalized coordinates are ignored for non-variable fonts. 91 #[derive(Copy, Clone, Default, Debug)] 92 pub struct LocationRef<'a>(&'a [NormalizedCoord]); 93 94 impl<'a> LocationRef<'a> { 95 /// Creates a new sequence of normalized coordinates from the given array. new(coords: &'a [NormalizedCoord]) -> Self96 pub fn new(coords: &'a [NormalizedCoord]) -> Self { 97 Self(coords) 98 } 99 100 /// Returns the underlying array of normalized coordinates. coords(&self) -> &'a [NormalizedCoord]101 pub fn coords(&self) -> &'a [NormalizedCoord] { 102 self.0 103 } 104 } 105 106 impl<'a> From<&'a [NormalizedCoord]> for LocationRef<'a> { from(value: &'a [NormalizedCoord]) -> Self107 fn from(value: &'a [NormalizedCoord]) -> Self { 108 Self(value) 109 } 110 } 111 112 impl<'a> IntoIterator for LocationRef<'a> { 113 type IntoIter = core::slice::Iter<'a, NormalizedCoord>; 114 type Item = &'a NormalizedCoord; 115 into_iter(self) -> Self::IntoIter116 fn into_iter(self) -> Self::IntoIter { 117 self.0.iter() 118 } 119 } 120 121 impl<'a> IntoIterator for &'_ LocationRef<'a> { 122 type IntoIter = core::slice::Iter<'a, NormalizedCoord>; 123 type Item = &'a NormalizedCoord; 124 into_iter(self) -> Self::IntoIter125 fn into_iter(self) -> Self::IntoIter { 126 self.0.iter() 127 } 128 } 129 130 /// Maximum number of coords to store inline in a `Location` object. 131 /// 132 /// This value was chosen to maximize use of space in the underlying 133 /// `SmallArray` storage. 134 const MAX_INLINE_COORDS: usize = 8; 135 136 /// Ordered sequence of normalized variation coordinates. 137 /// 138 /// This is an owned version of [`LocationRef`]. See the documentation on that 139 /// type for more detail. 140 #[derive(Clone, Debug)] 141 pub struct Location { 142 coords: SmallArray<NormalizedCoord, MAX_INLINE_COORDS>, 143 } 144 145 impl Location { 146 /// Creates a new location with the given number of normalized coordinates. 147 /// 148 /// Each element will be initialized to the default value (0.0). new(len: usize) -> Self149 pub fn new(len: usize) -> Self { 150 Self { 151 coords: SmallArray::new(NormalizedCoord::default(), len), 152 } 153 } 154 155 /// Returns the underlying slice of normalized coordinates. coords(&self) -> &[NormalizedCoord]156 pub fn coords(&self) -> &[NormalizedCoord] { 157 self.coords.as_slice() 158 } 159 160 /// Returns a mutable reference to the underlying slice of normalized 161 /// coordinates. coords_mut(&mut self) -> &mut [NormalizedCoord]162 pub fn coords_mut(&mut self) -> &mut [NormalizedCoord] { 163 self.coords.as_mut_slice() 164 } 165 } 166 167 impl Default for Location { default() -> Self168 fn default() -> Self { 169 Self { 170 coords: SmallArray::new(NormalizedCoord::default(), 0), 171 } 172 } 173 } 174 175 impl<'a> From<&'a Location> for LocationRef<'a> { from(value: &'a Location) -> Self176 fn from(value: &'a Location) -> Self { 177 LocationRef(value.coords()) 178 } 179 } 180 181 impl<'a> IntoIterator for &'a Location { 182 type IntoIter = core::slice::Iter<'a, NormalizedCoord>; 183 type Item = &'a NormalizedCoord; 184 into_iter(self) -> Self::IntoIter185 fn into_iter(self) -> Self::IntoIter { 186 self.coords().iter() 187 } 188 } 189 190 impl<'a> IntoIterator for &'a mut Location { 191 type IntoIter = core::slice::IterMut<'a, NormalizedCoord>; 192 type Item = &'a mut NormalizedCoord; 193 into_iter(self) -> Self::IntoIter194 fn into_iter(self) -> Self::IntoIter { 195 self.coords_mut().iter_mut() 196 } 197 } 198