kas_text/fonts/face.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4// https://www.apache.org/licenses/LICENSE-2.0
5
6//! Font face types
7
8use crate::conv::{LineMetrics, DPU};
9use crate::GlyphId;
10use ttf_parser::Face;
11
12/// Handle to a loaded font face
13#[derive(Copy, Clone, Debug)]
14pub struct FaceRef<'a>(pub(crate) &'a Face<'a>);
15
16impl<'a> FaceRef<'a> {
17 /// Get glyph identifier for a char
18 ///
19 /// If the char is not found, `GlyphId(0)` is returned (the 'missing glyph'
20 /// representation).
21 #[inline]
22 pub fn glyph_index(&self, c: char) -> GlyphId {
23 // GlyphId 0 is required to be a special glyph representing a missing
24 // character (see cmap table / TrueType specification).
25 GlyphId(self.0.glyph_index(c).map(|id| id.0).unwrap_or(0))
26 }
27
28 /// Convert `dpem` to `dpu`
29 ///
30 /// Output: a font-specific scale.
31 ///
32 /// Input: `dpem` is pixels/em
33 ///
34 /// ```none
35 /// dpem
36 /// = pt_size × dpp
37 /// = pt_size × dpi / 72
38 /// = pt_size × scale_factor × (96 / 72)
39 /// ```
40 #[inline]
41 pub fn dpu(self, dpem: f32) -> DPU {
42 DPU(dpem / f32::from(self.0.units_per_em()))
43 }
44
45 /// Get a scaled reference
46 ///
47 /// Units: `dpem` is dots (pixels) per Em (module documentation).
48 #[inline]
49 pub fn scale_by_dpem(self, dpem: f32) -> ScaledFaceRef<'a> {
50 ScaledFaceRef(self.0, self.dpu(dpem))
51 }
52
53 /// Get a scaled reference
54 ///
55 /// Units: `dpu` is dots (pixels) per font-unit (see module documentation).
56 #[inline]
57 pub fn scale_by_dpu(self, dpu: DPU) -> ScaledFaceRef<'a> {
58 ScaledFaceRef(self.0, dpu)
59 }
60}
61
62/// Handle to a loaded font face
63///
64/// TODO: verify whether these values need adjustment for variations.
65#[derive(Copy, Clone, Debug)]
66pub struct ScaledFaceRef<'a>(&'a Face<'a>, DPU);
67impl<'a> ScaledFaceRef<'a> {
68 /// Unscaled face
69 #[inline]
70 pub fn face(&self) -> FaceRef<'_> {
71 FaceRef(self.0)
72 }
73
74 /// Scale
75 #[inline]
76 pub fn dpu(&self) -> DPU {
77 self.1
78 }
79
80 /// Horizontal advancement after this glyph, without shaping or kerning
81 #[inline]
82 pub fn h_advance(&self, id: GlyphId) -> f32 {
83 let x = self.0.glyph_hor_advance(id.into()).unwrap();
84 self.1.u16_to_px(x)
85 }
86
87 /// Horizontal side bearing
88 ///
89 /// If unspecified by the font this resolves to 0.
90 #[inline]
91 pub fn h_side_bearing(&self, id: GlyphId) -> f32 {
92 let x = self.0.glyph_hor_side_bearing(id.into()).unwrap_or(0);
93 self.1.i16_to_px(x)
94 }
95
96 /// Ascender
97 #[inline]
98 pub fn ascent(&self) -> f32 {
99 self.1.i16_to_px(self.0.ascender())
100 }
101
102 /// Descender
103 #[inline]
104 pub fn descent(&self) -> f32 {
105 self.1.i16_to_px(self.0.descender())
106 }
107
108 /// Line gap
109 #[inline]
110 pub fn line_gap(&self) -> f32 {
111 self.1.i16_to_px(self.0.line_gap())
112 }
113
114 /// Line height
115 #[inline]
116 pub fn height(&self) -> f32 {
117 self.1.i16_to_px(self.0.height())
118 }
119
120 /// Metrics for underline
121 #[inline]
122 pub fn underline_metrics(&self) -> Option<LineMetrics> {
123 self.0
124 .underline_metrics()
125 .map(|m| self.1.to_line_metrics(m))
126 }
127
128 /// Metrics for strike-through
129 #[inline]
130 pub fn strikethrough_metrics(&self) -> Option<LineMetrics> {
131 self.0
132 .strikeout_metrics()
133 .map(|m| self.1.to_line_metrics(m))
134 }
135}