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()
    }
}