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