| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| mod sources; |
| mod vmo_stream; |
| |
| use { |
| anyhow::{format_err, Error}, |
| char_set::CharSet, |
| freetype_ffi::{ |
| FT_Add_Default_Modules, FT_Done_Face, FT_Done_Library, FT_Get_First_Char, FT_Get_Next_Char, |
| FT_Library, FT_New_Library, FT_Open_Face, FT_MEMORY, |
| }, |
| std::{convert::TryInto, ptr}, |
| }; |
| |
| pub use crate::sources::FTOpenArgs; |
| pub use crate::sources::FontAssetSource; |
| |
| /// Contains information parsed from a font file. |
| #[derive(Clone, Debug, Eq, PartialEq)] |
| pub struct FontInfo { |
| pub char_set: CharSet, |
| } |
| |
| /// An object that can load a `FontInfo` from a `FontAssetSource`. |
| /// |
| /// Separate from the [implementation][`FontInfoLoaderImpl`] to allow easier mocking for tests. |
| pub trait FontInfoLoader { |
| /// Load information about a font from the given file source, located at the given index within |
| /// the file. |
| /// |
| /// `S` is any type for which a conversion is defined into `FT_Open_Args`. In practice, |
| /// this must be either a something that contains an `FT_Stream`, or a file path. |
| /// |
| /// For simplicity, use [crates::sources::FontAssetSource]. |
| fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error> |
| where |
| S: TryInto<FontAssetSource, Error = E>, |
| E: Sync + Send + Into<Error>; |
| } |
| |
| /// Reads information from font files using FreeType library. |
| pub struct FontInfoLoaderImpl { |
| ft_library: FT_Library, |
| } |
| |
| impl std::ops::Drop for FontInfoLoaderImpl { |
| fn drop(&mut self) { |
| // Unsafe to call freetype FFI. |
| // This is required in order to clean up the FreeType library instance. |
| unsafe { |
| FT_Done_Library(self.ft_library); |
| } |
| } |
| } |
| |
| impl FontInfoLoaderImpl { |
| pub fn new() -> Result<FontInfoLoaderImpl, Error> { |
| // Unsafe to call freetype FFI. On success stores the FT_Library pointer |
| // in FontInfoLoader, which calls FT_Done_Library in drop(). Must ensure |
| // that all allocated memory is freed on failure. |
| unsafe { |
| let mut ft_library = ptr::null_mut(); |
| if FT_New_Library(&FT_MEMORY, &mut ft_library) != 0 { |
| return Err(format_err!("Failed to initialize FreeType library.")); |
| } |
| FT_Add_Default_Modules(ft_library); |
| Ok(FontInfoLoaderImpl { ft_library }) |
| } |
| } |
| |
| pub fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error> |
| where |
| S: TryInto<FontAssetSource, Error = E>, |
| E: Sync + Send + Into<Error>, |
| { |
| let mut codepoints: Vec<u32> = Vec::new(); |
| |
| let source: FontAssetSource = source.try_into().map_err(|e| e.into())?; |
| let open_args: FTOpenArgs<'_> = (&source).try_into()?; |
| |
| // Unsafe to call freetype FFI. Call FT_Open_Face() to load a typeface. |
| // If it succeeds then enumerate character map with FT_Get_First_Char() |
| // and FT_Get_Next_Char(). FT_Done_Face() must be called if the typeface |
| // was initialized successfully. |
| unsafe { |
| let mut ft_face = ptr::null_mut(); |
| let err = FT_Open_Face( |
| self.ft_library, |
| open_args.as_ref(), |
| index as libc::c_long, |
| &mut ft_face, |
| ); |
| |
| if err != 0 { |
| return Err(format_err!( |
| "Failed to parse font {} from {:?}: FreeType error {}", |
| index, |
| source, |
| err |
| )); |
| } |
| |
| let mut glyph_index = 0; |
| let mut codepoint = FT_Get_First_Char(ft_face, &mut glyph_index); |
| while glyph_index > 0 { |
| codepoints.push(codepoint as u32); |
| codepoint = FT_Get_Next_Char(ft_face, codepoint, &mut glyph_index); |
| } |
| |
| FT_Done_Face(ft_face); |
| } |
| |
| Ok(FontInfo { char_set: CharSet::new(codepoints) }) |
| } |
| } |
| |
| impl FontInfoLoader for FontInfoLoaderImpl { |
| fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error> |
| where |
| S: TryInto<FontAssetSource, Error = E>, |
| E: Sync + Send + Into<Error>, |
| { |
| FontInfoLoaderImpl::load_font_info(&self, source, index) |
| } |
| } |