1#[cfg(all(feature = "libm", not(feature = "std")))]
2use crate::nostd_float::FloatExt;
3use crate::{Font, Glyph, GlyphId, OutlinedGlyph, Rect};
4
5/// Pixel scale.
6///
7/// This is the pixel-height of text.
8///
9/// Usually one uses `x == y`, but one may use a different ratio to stretch a
10/// font horizontally or vertically.
11///
12/// To convert pt size into pixel-scale see [`Font::pt_to_px_scale`].
13///
14/// # Example
15/// ```
16/// use ab_glyph::PxScale;
17///
18/// let uniform_scale_24px = PxScale::from(24.0);
19/// ```
20#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
21pub struct PxScale {
22 /// Horizontal scale in pixels.
23 pub x: f32,
24 /// Vertical scale in pixels.
25 ///
26 /// By definition, this is the pixel-height of a font.
27 pub y: f32,
28}
29
30impl PxScale {
31 /// Returns a `PxScale` with both x & y scale values set to the nearest integer.
32 #[inline]
33 pub fn round(self) -> Self {
34 Self {
35 x: self.x.round(),
36 y: self.y.round(),
37 }
38 }
39}
40
41impl From<f32> for PxScale {
42 /// Uniform scaling where x & y are the same.
43 #[inline]
44 fn from(s: f32) -> Self {
45 PxScale { x: s, y: s }
46 }
47}
48
49/// 2D scale factors for use with unscaled metrics.
50#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
51pub struct PxScaleFactor {
52 pub horizontal: f32,
53 pub vertical: f32,
54}
55
56/// A [`Font`](trait.Font.html) with an associated pixel scale. This can be used to provide
57/// pixel scale values for glyph advances, heights etc.
58///
59/// # Example
60/// ```
61/// use ab_glyph::{Font, FontRef, PxScale, ScaleFont};
62///
63/// # fn main() -> Result<(), ab_glyph::InvalidFont> {
64/// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf"))?;
65///
66/// // Associate the font with a scale of 45px
67/// let scaled_font = font.as_scaled(PxScale::from(45.0));
68///
69/// assert_eq!(scaled_font.height(), 45.0);
70/// assert_eq!(scaled_font.h_advance(scaled_font.glyph_id('b')), 21.225);
71///
72/// // Replace associated scale with another
73/// let scaled_font = scaled_font.with_scale(180.0);
74///
75/// assert_eq!(scaled_font.height(), 180.0);
76/// assert_eq!(scaled_font.h_advance(scaled_font.glyph_id('b')), 84.9);
77/// # Ok(()) }
78/// ```
79pub trait ScaleFont<F: Font> {
80 /// Returns the pixel scale associated with this font.
81 fn scale(&self) -> PxScale;
82
83 /// Returns a font reference.
84 fn font(&self) -> &F;
85
86 /// Scale factor for unscaled font horizontal values.
87 #[inline]
88 fn h_scale_factor(&self) -> f32 {
89 self.scale().x / self.font().height_unscaled()
90 }
91
92 /// Scale factor for unscaled font vertical values.
93 #[inline]
94 fn v_scale_factor(&self) -> f32 {
95 self.scale().y / self.font().height_unscaled()
96 }
97
98 #[inline]
99 fn scale_factor(&self) -> PxScaleFactor {
100 PxScaleFactor {
101 horizontal: self.h_scale_factor(),
102 vertical: self.v_scale_factor(),
103 }
104 }
105
106 /// Pixel scaled glyph ascent.
107 #[inline]
108 fn ascent(&self) -> f32 {
109 self.v_scale_factor() * self.font().ascent_unscaled()
110 }
111
112 /// Pixel scaled glyph descent.
113 #[inline]
114 fn descent(&self) -> f32 {
115 self.v_scale_factor() * self.font().descent_unscaled()
116 }
117
118 /// Pixel scaled height `ascent - descent`.
119 ///
120 /// By definition of [`PxScale`], this is `self.scale().y`.
121 #[inline]
122 fn height(&self) -> f32 {
123 self.scale().y
124 }
125
126 /// Pixel scaled line gap.
127 #[inline]
128 fn line_gap(&self) -> f32 {
129 self.v_scale_factor() * self.font().line_gap_unscaled()
130 }
131
132 /// Lookup a `GlyphId` matching a given `char`.
133 #[inline]
134 fn glyph_id(&self, c: char) -> GlyphId {
135 self.font().glyph_id(c)
136 }
137
138 /// Construct a [`Glyph`](struct.Glyph.html) with the font's pixel scale at
139 /// position `point(0.0, 0.0)`.
140 ///
141 /// # Example
142 /// ```
143 /// # use ab_glyph::*;
144 /// # let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf")).unwrap();
145 /// let scaled_font = font.as_scaled(50.0);
146 ///
147 /// let a1 = scaled_font.scaled_glyph('a');
148 /// let a2 = font.glyph_id('a').with_scale(50.0); // equivalent
149 ///
150 /// # assert_eq!(a1.id, a2.id);
151 /// assert_eq!(a1.scale, PxScale::from(50.0));
152 /// assert_eq!(a1.position, point(0.0, 0.0));
153 /// ```
154 #[inline]
155 fn scaled_glyph(&self, c: char) -> Glyph {
156 self.font().glyph_id(c).with_scale(self.scale())
157 }
158
159 /// Pixel scaled horizontal advance for a given glyph.
160 #[inline]
161 fn h_advance(&self, id: GlyphId) -> f32 {
162 self.h_scale_factor() * self.font().h_advance_unscaled(id)
163 }
164
165 /// Pixel scaled horizontal side bearing for a given glyph.
166 #[inline]
167 fn h_side_bearing(&self, id: GlyphId) -> f32 {
168 self.h_scale_factor() * self.font().h_side_bearing_unscaled(id)
169 }
170
171 /// Pixel scaled vertical advance for a given glyph.
172 #[inline]
173 fn v_advance(&self, id: GlyphId) -> f32 {
174 self.v_scale_factor() * self.font().v_advance_unscaled(id)
175 }
176
177 /// Pixel scaled vertical side bearing for a given glyph.
178 #[inline]
179 fn v_side_bearing(&self, id: GlyphId) -> f32 {
180 self.v_scale_factor() * self.font().v_side_bearing_unscaled(id)
181 }
182
183 /// Returns additional pixel scaled kerning to apply for a particular pair of glyphs.
184 #[inline]
185 fn kern(&self, first: GlyphId, second: GlyphId) -> f32 {
186 self.h_scale_factor() * self.font().kern_unscaled(first, second)
187 }
188
189 /// Returns the layout bounds of this glyph. These are different to the outline `px_bounds()`.
190 ///
191 /// Horizontally: Glyph position +/- h_advance/h_side_bearing.
192 /// Vertically: Glyph position +/- ascent/descent.
193 ///
194 /// Note this method does not make use of the associated scale, as `Glyph`
195 /// already includes one of it's own.
196 #[inline]
197 fn glyph_bounds(&self, glyph: &Glyph) -> Rect {
198 self.font().glyph_bounds(glyph)
199 }
200
201 /// The number of glyphs present in this font. Glyph identifiers for this
202 /// font will always be in the range `0..self.glyph_count()`
203 #[inline]
204 fn glyph_count(&self) -> usize {
205 self.font().glyph_count()
206 }
207
208 /// Returns an iterator of all distinct `(GlyphId, char)` pairs. Not ordered.
209 ///
210 /// Same as [`Font::codepoint_ids`](trait.Font.html#tymethod.codepoint_ids).
211 fn codepoint_ids(&self) -> crate::CodepointIdIter<'_>;
212
213 /// Compute glyph outline ready for drawing.
214 ///
215 /// Note this method does not make use of the associated scale, as `Glyph`
216 /// already includes one of it's own.
217 #[inline]
218 fn outline_glyph(&self, glyph: Glyph) -> Option<OutlinedGlyph> {
219 self.font().outline_glyph(glyph)
220 }
221}
222
223impl<F: Font, SF: ScaleFont<F>> ScaleFont<F> for &SF {
224 #[inline]
225 fn scale(&self) -> PxScale {
226 (*self).scale()
227 }
228
229 #[inline]
230 fn font(&self) -> &F {
231 (*self).font()
232 }
233
234 #[inline]
235 fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
236 (*self).codepoint_ids()
237 }
238}
239
240/// A [`Font`](trait.Font.html) and an associated pixel scale.
241#[derive(Clone, Copy, Debug)]
242pub struct PxScaleFont<F> {
243 pub font: F,
244 pub scale: PxScale,
245}
246
247impl<F> PxScaleFont<F> {
248 #[inline]
249 pub fn with_scale<S: Into<PxScale>>(mut self, scale: S) -> Self {
250 self.scale = scale.into();
251 self
252 }
253}
254
255impl<F: Font> ScaleFont<F> for PxScaleFont<F> {
256 #[inline]
257 fn scale(&self) -> PxScale {
258 self.scale
259 }
260
261 #[inline]
262 fn font(&self) -> &F {
263 &self.font
264 }
265
266 #[inline]
267 fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
268 self.font.codepoint_ids()
269 }
270}
271