1use crate::ttf_parser::GlyphId;
2use crate::ttf_parser::gdef::GlyphClass;
3use crate::ttf_parser::opentype_layout::LayoutTable;
4
5use crate::Variation;
6use crate::ot::{TableIndex, PositioningTable, SubstitutionTable};
7use crate::buffer::GlyphPropsFlags;
8
9
10const WINDOWS_SYMBOL_ENCODING: u16 = 0;
12const WINDOWS_UNICODE_BMP_ENCODING: u16 = 1;
13const WINDOWS_UNICODE_FULL_ENCODING: u16 = 10;
14
15const UNICODE_1_0_ENCODING: u16 = 0;
17const UNICODE_1_1_ENCODING: u16 = 1;
18const UNICODE_ISO_ENCODING: u16 = 2;
19const UNICODE_2_0_BMP_ENCODING: u16 = 3;
20const UNICODE_2_0_FULL_ENCODING: u16 = 4;
21const UNICODE_FULL_ENCODING: u16 = 6;
23
24
25#[derive(Clone)]
27pub struct Face<'a> {
28 pub(crate) ttfp_face: crate::ttf_parser::Face<'a>,
29 pub(crate) units_per_em: u16,
30 pixels_per_em: Option<(u16, u16)>,
31 pub(crate) points_per_em: Option<f32>,
32 prefered_cmap_encoding_subtable: Option<u16>,
33 pub(crate) gsub: Option<SubstitutionTable<'a>>,
34 pub(crate) gpos: Option<PositioningTable<'a>>,
35}
36
37impl<'a> AsRef<crate::ttf_parser::Face<'a>> for Face<'a> {
38 #[inline]
39 fn as_ref(&self) -> &crate::ttf_parser::Face<'a> {
40 &self.ttfp_face
41 }
42}
43
44impl<'a> AsMut<crate::ttf_parser::Face<'a>> for Face<'a> {
45 #[inline]
46 fn as_mut(&mut self) -> &mut crate::ttf_parser::Face<'a> {
47 &mut self.ttfp_face
48 }
49}
50
51impl<'a> core::ops::Deref for Face<'a> {
52 type Target = crate::ttf_parser::Face<'a>;
53
54 #[inline]
55 fn deref(&self) -> &Self::Target {
56 &self.ttfp_face
57 }
58}
59
60impl<'a> core::ops::DerefMut for Face<'a> {
61 #[inline]
62 fn deref_mut(&mut self) -> &mut Self::Target {
63 &mut self.ttfp_face
64 }
65}
66
67impl<'a> Face<'a> {
68 pub fn from_slice(data: &'a [u8], face_index: u32) -> Option<Self> {
72 let face = crate::ttf_parser::Face::parse(data, face_index).ok()?;
73 Some(Self::from_face(face))
74 }
75
76 pub fn from_face(face: crate::ttf_parser::Face<'a>) -> Self {
80 Face {
81 units_per_em: face.units_per_em(),
82 pixels_per_em: None,
83 points_per_em: None,
84 prefered_cmap_encoding_subtable: find_best_cmap_subtable(&face),
85 gsub: face.tables().gsub.map(SubstitutionTable::new),
86 gpos: face.tables().gpos.map(PositioningTable::new),
87 ttfp_face: face,
88 }
89 }
90
91 #[inline]
94 pub fn units_per_em(&self) -> i32 {
95 self.units_per_em as i32
96 }
97
98 #[inline]
99 pub(crate) fn pixels_per_em(&self) -> Option<(u16, u16)> {
100 self.pixels_per_em
101 }
102
103 #[inline]
109 pub fn set_pixels_per_em(&mut self, ppem: Option<(u16, u16)>) {
110 self.pixels_per_em = ppem;
111 }
112
113 #[inline]
119 pub fn set_points_per_em(&mut self, ptem: Option<f32>) {
120 self.points_per_em = ptem;
121 }
122
123 pub fn set_variations(&mut self, variations: &[Variation]) {
125 for variation in variations {
126 self.set_variation(variation.tag, variation.value);
127 }
128 }
129
130 pub(crate) fn has_glyph(&self, c: u32) -> bool {
131 self.glyph_index(c).is_some()
132 }
133
134 pub(crate) fn glyph_index(&self, c: u32) -> Option<GlyphId> {
135 let subtable_idx = self.prefered_cmap_encoding_subtable?;
136 let subtable = self.tables().cmap?.subtables.get(subtable_idx)?;
137 match subtable.glyph_index(c) {
138 Some(gid) => Some(gid),
139 None => {
140 if subtable.platform_id == crate::ttf_parser::PlatformId::Windows &&
143 subtable.encoding_id == WINDOWS_SYMBOL_ENCODING
144 {
145 if c <= 0x00FF {
146 return self.glyph_index(0xF000 + c);
152 }
153 }
154
155 None
156 }
157 }
158 }
159
160 pub(crate) fn glyph_h_advance(&self, glyph: GlyphId) -> i32 {
161 self.glyph_advance(glyph, false) as i32
162 }
163
164 pub(crate) fn glyph_v_advance(&self, glyph: GlyphId) -> i32 {
165 -(self.glyph_advance(glyph, true) as i32)
166 }
167
168 fn glyph_advance(&self, glyph: GlyphId, is_vertical: bool) -> u32 {
169 let face = &self.ttfp_face;
170 if face.is_variable() &&
171 face.has_non_default_variation_coordinates() &&
172 face.tables().hvar.is_none() &&
173 face.tables().vvar.is_none()
174 {
175 return match face.glyph_bounding_box(glyph) {
176 Some(bbox) => {
177 (if is_vertical {
178 bbox.y_max + bbox.y_min
179 } else {
180 bbox.x_max + bbox.x_min
181 }) as u32
182 }
183 None => 0,
184 };
185 }
186
187 if is_vertical && face.tables().vmtx.is_some() {
188 face.glyph_ver_advance(glyph).unwrap_or(0) as u32
189 } else if !is_vertical && face.tables().hmtx.is_some() {
190 face.glyph_hor_advance(glyph).unwrap_or(0) as u32
191 } else {
192 face.units_per_em() as u32
193 }
194 }
195
196 pub(crate) fn glyph_h_origin(&self, glyph: GlyphId) -> i32 {
197 self.glyph_h_advance(glyph) / 2
198 }
199
200 pub(crate) fn glyph_v_origin(&self, glyph: GlyphId) -> i32 {
201 match self.ttfp_face.glyph_y_origin(glyph) {
202 Some(y) => i32::from(y),
203 None => self.glyph_extents(glyph).map_or(0, |ext| ext.y_bearing)
204 + self.glyph_side_bearing(glyph, true)
205 }
206 }
207
208 pub(crate) fn glyph_side_bearing(&self, glyph: GlyphId, is_vertical: bool) -> i32 {
209 let face = &self.ttfp_face;
210 if face.is_variable() &&
211 face.tables().hvar.is_none() &&
212 face.tables().vvar.is_none()
213 {
214 return match face.glyph_bounding_box(glyph) {
215 Some(bbox) => (if is_vertical { bbox.x_min } else { bbox.y_min }) as i32,
216 None => 0,
217 }
218 }
219
220 if is_vertical {
221 face.glyph_ver_side_bearing(glyph).unwrap_or(0) as i32
222 } else {
223 face.glyph_hor_side_bearing(glyph).unwrap_or(0) as i32
224 }
225 }
226
227 pub(crate) fn glyph_extents(&self, glyph: GlyphId) -> Option<GlyphExtents> {
228 let pixels_per_em = match self.pixels_per_em {
229 Some(ppem) => ppem.0,
230 None => core::u16::MAX,
231 };
232
233 if let Some(img) = self.ttfp_face.glyph_raster_image(glyph, pixels_per_em) {
234 if img.format == crate::ttf_parser::RasterImageFormat::PNG {
236 let scale = self.units_per_em as f32 / img.pixels_per_em as f32;
237 return Some(GlyphExtents {
238 x_bearing: crate::round(f32::from(img.x) * scale) as i32,
239 y_bearing: crate::round((f32::from(img.y) + f32::from(img.height)) * scale) as i32,
240 width: crate::round(f32::from(img.width) * scale) as i32,
241 height: crate::round(-f32::from(img.height) * scale) as i32,
242 });
243 }
244 }
245
246 let bbox = self.ttfp_face.glyph_bounding_box(glyph)?;
247 Some(GlyphExtents {
248 x_bearing: i32::from(bbox.x_min),
249 y_bearing: i32::from(bbox.y_max),
250 width: i32::from(bbox.width()),
251 height: i32::from(bbox.y_min - bbox.y_max),
252 })
253 }
254
255 pub(crate) fn glyph_name(&self, glyph: GlyphId) -> Option<&str> {
256 self.ttfp_face.glyph_name(glyph)
257 }
258
259 pub(crate) fn glyph_props(&self, glyph: GlyphId) -> u16 {
260 let table = match self.tables().gdef {
261 Some(v) => v,
262 None => return 0,
263 };
264
265 match table.glyph_class(glyph) {
266 Some(GlyphClass::Base) => GlyphPropsFlags::BASE_GLYPH.bits(),
267 Some(GlyphClass::Ligature) => GlyphPropsFlags::LIGATURE.bits(),
268 Some(GlyphClass::Mark) => {
269 let class = table.glyph_mark_attachment_class(glyph);
270 (class << 8) | GlyphPropsFlags::MARK.bits()
271 }
272 _ => 0,
273 }
274 }
275
276 pub(crate) fn layout_table(&self, table_index: TableIndex) -> Option<&LayoutTable<'a>> {
277 match table_index {
278 TableIndex::GSUB => self.gsub.as_ref().map(|table| &table.inner),
279 TableIndex::GPOS => self.gpos.as_ref().map(|table| &table.inner),
280 }
281 }
282
283 pub(crate) fn layout_tables(&self) -> impl Iterator<Item = (TableIndex, &LayoutTable<'a>)> + '_ {
284 TableIndex::iter().filter_map(move |idx| self.layout_table(idx).map(|table| (idx, table)))
285 }
286}
287
288#[derive(Clone, Copy, Default)]
289pub struct GlyphExtents {
290 pub x_bearing: i32,
291 pub y_bearing: i32,
292 pub width: i32,
293 pub height: i32,
294}
295
296fn find_best_cmap_subtable(face: &crate::ttf_parser::Face) -> Option<u16> {
297 use crate::ttf_parser::PlatformId;
298
299 find_cmap_subtable(face, PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
303 .or_else(|| find_cmap_subtable(face, PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING))
305 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_FULL_ENCODING))
306 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_2_0_FULL_ENCODING))
307 .or_else(|| find_cmap_subtable(face, PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING))
309 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_2_0_BMP_ENCODING))
310 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_ISO_ENCODING))
311 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_1_1_ENCODING))
312 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_1_0_ENCODING))
313}
314
315fn find_cmap_subtable(
316 face: &crate::ttf_parser::Face,
317 platform_id: crate::ttf_parser::PlatformId,
318 encoding_id: u16,
319) -> Option<u16> {
320 for (i, subtable) in face.tables().cmap?.subtables.into_iter().enumerate() {
321 if subtable.platform_id == platform_id && subtable.encoding_id == encoding_id {
322 return Some(i as u16)
323 }
324 }
325
326 None
327}