blob: c184e42bacb3984b302c1a34098508abe24d4f0f [file] [log] [blame]
// 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)
}
}