| 1 | //! ttf-parser crate specific code. ttf-parser types should not be leaked publicly. |
| 2 | mod outliner; |
| 3 | #[cfg (feature = "variable-fonts" )] |
| 4 | mod variable; |
| 5 | |
| 6 | use crate::{point, v2, Font, GlyphId, GlyphImageFormat, GlyphSvg, InvalidFont, Outline, Rect}; |
| 7 | use alloc::boxed::Box; |
| 8 | #[cfg (not(feature = "std" ))] |
| 9 | use alloc::vec::Vec; |
| 10 | use core::fmt; |
| 11 | use owned_ttf_parser::{self as ttfp, AsFaceRef}; |
| 12 | |
| 13 | impl From<GlyphId> for ttfp::GlyphId { |
| 14 | #[inline ] |
| 15 | fn from(id: GlyphId) -> Self { |
| 16 | Self(id.0) |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | /// Font data handle stored as a `&[u8]` + parsed data. |
| 21 | /// See [`Font`] for more methods. |
| 22 | /// |
| 23 | /// Also see the owned version [`FontVec`]. |
| 24 | /// |
| 25 | /// # Example |
| 26 | /// ``` |
| 27 | /// use ab_glyph::{Font, FontRef}; |
| 28 | /// |
| 29 | /// # fn main() -> Result<(), ab_glyph::InvalidFont> { |
| 30 | /// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf" ))?; |
| 31 | /// |
| 32 | /// assert_eq!(font.glyph_id('s' ), ab_glyph::GlyphId(56)); |
| 33 | /// # Ok(()) } |
| 34 | /// ``` |
| 35 | #[derive (Clone)] |
| 36 | pub struct FontRef<'font>(ttfp::PreParsedSubtables<'font, ttfp::Face<'font>>); |
| 37 | |
| 38 | impl fmt::Debug for FontRef<'_> { |
| 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 40 | write!(f, "FontRef" ) |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | impl<'font> FontRef<'font> { |
| 45 | /// Creates an `FontRef` from a byte-slice. |
| 46 | /// |
| 47 | /// For font collections see |
| 48 | /// [`FontRef::try_from_slice_and_index`]. |
| 49 | /// |
| 50 | /// # Example |
| 51 | /// ``` |
| 52 | /// # use ab_glyph::*; |
| 53 | /// # fn main() -> Result<(), InvalidFont> { |
| 54 | /// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf" ))?; |
| 55 | /// # Ok(()) } |
| 56 | /// ``` |
| 57 | #[inline ] |
| 58 | pub fn try_from_slice(data: &'font [u8]) -> Result<Self, InvalidFont> { |
| 59 | Self::try_from_slice_and_index(data, 0) |
| 60 | } |
| 61 | |
| 62 | /// Creates an `FontRef` from byte-slice. |
| 63 | /// |
| 64 | /// You can set index for font collections. For simple fonts use `0` or |
| 65 | /// [`FontRef::try_from_slice`]. |
| 66 | /// |
| 67 | /// # Example |
| 68 | /// ``` |
| 69 | /// # use ab_glyph::*; |
| 70 | /// # fn main() -> Result<(), InvalidFont> { |
| 71 | /// let font = |
| 72 | /// FontRef::try_from_slice_and_index(include_bytes!("../../dev/fonts/Exo2-Light.otf" ), 0)?; |
| 73 | /// # Ok(()) } |
| 74 | /// ``` |
| 75 | #[inline ] |
| 76 | pub fn try_from_slice_and_index(data: &'font [u8], index: u32) -> Result<Self, InvalidFont> { |
| 77 | Ok(Self(ttfp::PreParsedSubtables::from( |
| 78 | ttfp::Face::parse(data, index).map_err(|_| InvalidFont)?, |
| 79 | ))) |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// Font data handle stored in a `Vec<u8>` + parsed data. |
| 84 | /// See [`Font`] for more methods. |
| 85 | /// |
| 86 | /// Also see [`FontRef`]. |
| 87 | /// |
| 88 | /// # Example |
| 89 | /// ``` |
| 90 | /// use ab_glyph::{Font, FontVec}; |
| 91 | /// |
| 92 | /// # fn main() -> Result<(), ab_glyph::InvalidFont> { |
| 93 | /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf" ).to_vec(); |
| 94 | /// let font = FontVec::try_from_vec_and_index(owned_font_data, 0)?; |
| 95 | /// |
| 96 | /// assert_eq!(font.glyph_id('s' ), ab_glyph::GlyphId(56)); |
| 97 | /// # Ok(()) } |
| 98 | /// ``` |
| 99 | pub struct FontVec(ttfp::PreParsedSubtables<'static, ttfp::OwnedFace>); |
| 100 | |
| 101 | impl fmt::Debug for FontVec { |
| 102 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 103 | write!(f, "FontVec" ) |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | impl FontVec { |
| 108 | /// Creates an `FontVec` from owned data. |
| 109 | /// |
| 110 | /// For font collections see |
| 111 | /// [`FontVec::try_from_vec_and_index`]. |
| 112 | /// |
| 113 | /// # Example |
| 114 | /// ``` |
| 115 | /// # use ab_glyph::*; |
| 116 | /// # fn main() -> Result<(), InvalidFont> { |
| 117 | /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf" ).to_vec(); |
| 118 | /// let font = FontVec::try_from_vec(owned_font_data)?; |
| 119 | /// # Ok(()) } |
| 120 | /// ``` |
| 121 | #[inline ] |
| 122 | pub fn try_from_vec(data: Vec<u8>) -> Result<Self, InvalidFont> { |
| 123 | Self::try_from_vec_and_index(data, 0) |
| 124 | } |
| 125 | |
| 126 | /// Creates an `FontVec` from owned data. |
| 127 | /// |
| 128 | /// You can set index for font collections. For simple fonts use `0` or |
| 129 | /// [`FontVec::try_from_vec`]. |
| 130 | /// |
| 131 | /// # Example |
| 132 | /// ``` |
| 133 | /// # use ab_glyph::*; |
| 134 | /// # fn main() -> Result<(), InvalidFont> { |
| 135 | /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf" ).to_vec(); |
| 136 | /// let font = FontVec::try_from_vec_and_index(owned_font_data, 0)?; |
| 137 | /// # Ok(()) } |
| 138 | /// ``` |
| 139 | #[inline ] |
| 140 | pub fn try_from_vec_and_index(data: Vec<u8>, index: u32) -> Result<Self, InvalidFont> { |
| 141 | Ok(Self(ttfp::PreParsedSubtables::from( |
| 142 | ttfp::OwnedFace::from_vec(data, index).map_err(|_| InvalidFont)?, |
| 143 | ))) |
| 144 | } |
| 145 | |
| 146 | /// Extracts a slice containing the data passed into e.g. [`FontVec::try_from_vec`]. |
| 147 | /// |
| 148 | /// # Example |
| 149 | /// ``` |
| 150 | /// # use ab_glyph::*; |
| 151 | /// # fn main() -> Result<(), InvalidFont> { |
| 152 | /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf" ).to_vec(); |
| 153 | /// let font_data_clone = owned_font_data.clone(); |
| 154 | /// let font = FontVec::try_from_vec(owned_font_data)?; |
| 155 | /// assert_eq!(font.as_slice(), font_data_clone); |
| 156 | /// # Ok(()) } |
| 157 | /// ``` |
| 158 | #[inline ] |
| 159 | pub fn as_slice(&self) -> &[u8] { |
| 160 | self.0.face.as_slice() |
| 161 | } |
| 162 | |
| 163 | /// Unwraps the data passed into e.g. [`FontVec::try_from_vec`]. |
| 164 | /// |
| 165 | /// # Example |
| 166 | /// ``` |
| 167 | /// # use ab_glyph::*; |
| 168 | /// # fn main() -> Result<(), InvalidFont> { |
| 169 | /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf" ).to_vec(); |
| 170 | /// let font_data_clone = owned_font_data.clone(); |
| 171 | /// let font = FontVec::try_from_vec(owned_font_data)?; |
| 172 | /// assert_eq!(font.into_vec(), font_data_clone); |
| 173 | /// # Ok(()) } |
| 174 | /// ``` |
| 175 | #[inline ] |
| 176 | pub fn into_vec(self) -> Vec<u8> { |
| 177 | self.0.face.into_vec() |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | /// Implement `Font` for `Self(AsFontRef)` types. |
| 182 | macro_rules! impl_font { |
| 183 | ($font:ty) => { |
| 184 | impl Font for $font { |
| 185 | #[inline] |
| 186 | fn units_per_em(&self) -> Option<f32> { |
| 187 | // TODO unwrap signature when making next breaking change |
| 188 | Some(self.0.as_face_ref().units_per_em().into()) |
| 189 | } |
| 190 | |
| 191 | #[inline] |
| 192 | fn ascent_unscaled(&self) -> f32 { |
| 193 | self.0.as_face_ref().ascender().into() |
| 194 | } |
| 195 | |
| 196 | #[inline] |
| 197 | fn descent_unscaled(&self) -> f32 { |
| 198 | self.0.as_face_ref().descender().into() |
| 199 | } |
| 200 | |
| 201 | #[inline] |
| 202 | fn line_gap_unscaled(&self) -> f32 { |
| 203 | self.0.as_face_ref().line_gap().into() |
| 204 | } |
| 205 | |
| 206 | #[inline] |
| 207 | fn glyph_id(&self, c: char) -> GlyphId { |
| 208 | // Note: Using `PreParsedSubtables` method for better performance. |
| 209 | let index = self.0.glyph_index(c).map(|id| id.0).unwrap_or(0); |
| 210 | GlyphId(index) |
| 211 | } |
| 212 | |
| 213 | #[inline] |
| 214 | fn h_advance_unscaled(&self, id: GlyphId) -> f32 { |
| 215 | self.0 |
| 216 | .as_face_ref() |
| 217 | .glyph_hor_advance(id.into()) |
| 218 | .unwrap_or_default() |
| 219 | .into() |
| 220 | } |
| 221 | |
| 222 | #[inline] |
| 223 | fn h_side_bearing_unscaled(&self, id: GlyphId) -> f32 { |
| 224 | self.0 |
| 225 | .as_face_ref() |
| 226 | .glyph_hor_side_bearing(id.into()) |
| 227 | .unwrap_or_default() |
| 228 | .into() |
| 229 | } |
| 230 | |
| 231 | #[inline] |
| 232 | fn v_advance_unscaled(&self, id: GlyphId) -> f32 { |
| 233 | self.0 |
| 234 | .as_face_ref() |
| 235 | .glyph_ver_advance(id.into()) |
| 236 | .unwrap_or_default() |
| 237 | .into() |
| 238 | } |
| 239 | |
| 240 | #[inline] |
| 241 | fn v_side_bearing_unscaled(&self, id: GlyphId) -> f32 { |
| 242 | self.0 |
| 243 | .as_face_ref() |
| 244 | .glyph_ver_side_bearing(id.into()) |
| 245 | .unwrap_or_default() |
| 246 | .into() |
| 247 | } |
| 248 | |
| 249 | #[inline] |
| 250 | fn kern_unscaled(&self, first: GlyphId, second: GlyphId) -> f32 { |
| 251 | // Note: Using `PreParsedSubtables` method for better performance. |
| 252 | self.0 |
| 253 | .glyphs_hor_kerning(first.into(), second.into()) |
| 254 | .map(f32::from) |
| 255 | .unwrap_or_default() |
| 256 | } |
| 257 | |
| 258 | fn outline(&self, id: GlyphId) -> Option<Outline> { |
| 259 | let mut outliner = outliner::OutlineCurveBuilder::default(); |
| 260 | |
| 261 | let ttfp::Rect { |
| 262 | x_min, |
| 263 | x_max, |
| 264 | y_min, |
| 265 | y_max, |
| 266 | } = self |
| 267 | .0 |
| 268 | .as_face_ref() |
| 269 | .outline_glyph(id.into(), &mut outliner) |
| 270 | // invalid bounds are treated as having no outline |
| 271 | .filter(|b| b.x_min < b.x_max && b.y_min < b.y_max)?; |
| 272 | |
| 273 | let curves = outliner.take_outline(); |
| 274 | |
| 275 | let bounds = Rect { |
| 276 | min: point(x_min.into(), y_max.into()), |
| 277 | max: point(x_max.into(), y_min.into()), |
| 278 | }; |
| 279 | |
| 280 | Some(Outline { bounds, curves }) |
| 281 | } |
| 282 | |
| 283 | #[inline] |
| 284 | fn glyph_count(&self) -> usize { |
| 285 | self.0.as_face_ref().number_of_glyphs() as _ |
| 286 | } |
| 287 | |
| 288 | fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> { |
| 289 | let face_ref = self.0.as_face_ref(); |
| 290 | |
| 291 | #[cfg(feature = "std" )] |
| 292 | let mut used_indices = |
| 293 | std::collections::HashSet::with_capacity(face_ref.number_of_glyphs() as _); |
| 294 | #[cfg(not(feature = "std" ))] |
| 295 | let mut used_indices = alloc::collections::BTreeSet::new(); |
| 296 | |
| 297 | let inner = Box::new( |
| 298 | face_ref |
| 299 | .tables() |
| 300 | .cmap |
| 301 | .iter() |
| 302 | .flat_map(|c| c.subtables) |
| 303 | .filter(|s| s.is_unicode()) |
| 304 | .flat_map(move |subtable| { |
| 305 | let mut pairs = Vec::new(); |
| 306 | subtable.codepoints(|c| { |
| 307 | if let Ok(ch) = char::try_from(c) { |
| 308 | if let Some(idx) = subtable.glyph_index(c).filter(|i| i.0 > 0) { |
| 309 | if used_indices.insert(idx.0) { |
| 310 | pairs.push((GlyphId(idx.0), ch)); |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | }); |
| 315 | pairs |
| 316 | }), |
| 317 | ); |
| 318 | |
| 319 | crate::CodepointIdIter { inner } |
| 320 | } |
| 321 | |
| 322 | fn glyph_raster_image2(&self, id: GlyphId, size: u16) -> Option<v2::GlyphImage> { |
| 323 | use GlyphImageFormat::*; |
| 324 | |
| 325 | let img = self.0.as_face_ref().glyph_raster_image(id.into(), size)?; |
| 326 | Some(v2::GlyphImage { |
| 327 | origin: point(img.x.into(), img.y.into()), |
| 328 | width: img.width, |
| 329 | height: img.height, |
| 330 | pixels_per_em: img.pixels_per_em, |
| 331 | data: img.data, |
| 332 | format: match img.format { |
| 333 | ttfp::RasterImageFormat::PNG => Png, |
| 334 | ttfp::RasterImageFormat::BitmapMono => BitmapMono, |
| 335 | ttfp::RasterImageFormat::BitmapMonoPacked => BitmapMonoPacked, |
| 336 | ttfp::RasterImageFormat::BitmapGray2 => BitmapGray2, |
| 337 | ttfp::RasterImageFormat::BitmapGray2Packed => BitmapGray2Packed, |
| 338 | ttfp::RasterImageFormat::BitmapGray4 => BitmapGray4, |
| 339 | ttfp::RasterImageFormat::BitmapGray4Packed => BitmapGray4Packed, |
| 340 | ttfp::RasterImageFormat::BitmapGray8 => BitmapGray8, |
| 341 | ttfp::RasterImageFormat::BitmapPremulBgra32 => BitmapPremulBgra32, |
| 342 | }, |
| 343 | }) |
| 344 | } |
| 345 | |
| 346 | fn glyph_svg_image(&self, id: GlyphId) -> Option<GlyphSvg> { |
| 347 | let img = self.0.as_face_ref().glyph_svg_image(id.into())?; |
| 348 | |
| 349 | Some(GlyphSvg { |
| 350 | data: img.data, |
| 351 | start_glyph_id: GlyphId(img.start_glyph_id.0), |
| 352 | end_glyph_id: GlyphId(img.end_glyph_id.0), |
| 353 | }) |
| 354 | } |
| 355 | |
| 356 | #[inline] |
| 357 | fn font_data(&self) -> &[u8] { |
| 358 | self.0.as_face_ref().raw_face().data |
| 359 | } |
| 360 | } |
| 361 | }; |
| 362 | } |
| 363 | |
| 364 | impl_font!(FontRef<'_>); |
| 365 | impl_font!(FontVec); |
| 366 | |