Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster OTC font loading #1

Merged
merged 7 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
//!
//! To open the font referenced by a handle, use a loader.

use std::any::Any;
use std::path::PathBuf;
use std::sync::Arc;

use crate::error::FontLoadingError;
use crate::font::Font;
use crate::loader::Loader;

/// Encapsulates the information needed to locate and open a font.
///
Expand All @@ -45,6 +47,11 @@ pub enum Handle {
/// If the memory consists of a single font, this value will be 0.
font_index: u32,
},
/// An already-loaded font.
Native {
/// Type-erased font storage. Use [`Self::from_native`] to retrieve the font object.
inner: Arc<dyn Any + Sync + Send>,
},
}

impl Handle {
Expand All @@ -66,6 +73,25 @@ impl Handle {
Handle::Memory { bytes, font_index }
}

/// Creates a new handle from a system handle.
pub fn from_native<T: Loader>(inner: &T) -> Self
where
T::NativeFont: Sync + Send,
{
Self::Native {
inner: Arc::new(inner.native_font()),
}
}
/// Retrieves a handle to the font object.
///
/// May return None if inner object is not of type `T` or if this handle does not contain a native font object.
pub fn native_as<T: 'static>(&self) -> Option<&T> {
if let Self::Native { inner } = self {
inner.downcast_ref()
} else {
None
}
}
/// A convenience method to load this handle with the default loader, producing a Font.
#[inline]
pub fn load(&self) -> Result<Font, FontLoadingError> {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,5 @@ pub mod source;
#[cfg(feature = "source")]
pub mod sources;

mod matching;
pub mod matching;
mod utils;
23 changes: 12 additions & 11 deletions src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use std::path::Path;
/// fonts.
pub trait Loader: Clone + Sized {
/// The handle that the API natively uses to represent a font.
type NativeFont;
type NativeFont: 'static;

/// Loads a font from raw font data (the contents of a `.ttf`/`.otf`/etc. file).
///
Expand Down Expand Up @@ -63,22 +63,23 @@ pub trait Loader: Clone + Sized {
}

/// Creates a font from a native API handle.
unsafe fn from_native_font(native_font: Self::NativeFont) -> Self;
unsafe fn from_native_font(native_font: &Self::NativeFont) -> Self;

/// Loads the font pointed to by a handle.
fn from_handle(handle: &Handle) -> Result<Self, FontLoadingError> {
match *handle {
Handle::Memory {
ref bytes,
font_index,
} => Self::from_bytes((*bytes).clone(), font_index),
match handle {
Handle::Memory { bytes, font_index } => Self::from_bytes((*bytes).clone(), *font_index),
#[cfg(not(target_arch = "wasm32"))]
Handle::Path {
ref path,
font_index,
} => Self::from_path(path, font_index),
Handle::Path { path, font_index } => Self::from_path(path, *font_index),
#[cfg(target_arch = "wasm32")]
Handle::Path { .. } => Err(FontLoadingError::NoFilesystem),
Handle::Native { .. } => {
if let Some(native) = handle.native_as::<Self::NativeFont>() {
unsafe { Ok(Self::from_native_font(native)) }
} else {
Err(FontLoadingError::UnknownFormat)
}
}
}
}

Expand Down
34 changes: 11 additions & 23 deletions src/loaders/core_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,29 +122,14 @@ impl Font {
}

/// Creates a font from a native API handle.
pub unsafe fn from_native_font(core_text_font: NativeFont) -> Font {
Font::from_core_text_font(core_text_font)
}

unsafe fn from_core_text_font(core_text_font: NativeFont) -> Font {
let mut font_data = FontData::Unavailable;
match core_text_font.url() {
None => warn!("No URL found for Core Text font!"),
Some(url) => match url.to_path() {
Some(path) => match File::open(path) {
Ok(ref mut file) => match utils::slurp_file(file) {
Ok(data) => font_data = FontData::Memory(Arc::new(data)),
Err(_) => warn!("Couldn't read file data for Core Text font!"),
},
Err(_) => warn!("Could not open file for Core Text font!"),
},
None => warn!("Could not convert URL from Core Text font to path!"),
},
}

pub unsafe fn from_native_font(core_text_font: &NativeFont) -> Font {
Font::from_core_text_font_no_path(core_text_font.clone())
}
/// Creates a font from a native API handle, without performing a lookup on the disk.
pub unsafe fn from_core_text_font_no_path(core_text_font: NativeFont) -> Font {
Font {
core_text_font,
font_data,
font_data: FontData::Unavailable,
}
}

Expand All @@ -153,7 +138,10 @@ impl Font {
/// This function is only available on the Core Text backend.
pub fn from_core_graphics_font(core_graphics_font: CGFont) -> Font {
unsafe {
Font::from_core_text_font(core_text::font::new_from_CGFont(&core_graphics_font, 16.0))
Font::from_core_text_font_no_path(core_text::font::new_from_CGFont(
&core_graphics_font,
16.0,
))
}
}

Expand Down Expand Up @@ -628,7 +616,7 @@ impl Loader for Font {
}

#[inline]
unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
unsafe fn from_native_font(native_font: &Self::NativeFont) -> Self {
Font::from_native_font(native_font)
}

Expand Down
6 changes: 4 additions & 2 deletions src/loaders/directwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const OPENTYPE_TABLE_TAG_HEAD: u32 = 0x68656164;

/// DirectWrite's representation of a font.
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct NativeFont {
/// The native DirectWrite font object.
pub dwrite_font: DWriteFont,
Expand Down Expand Up @@ -160,7 +161,8 @@ impl Font {

/// Creates a font from a native API handle.
#[inline]
pub unsafe fn from_native_font(native_font: NativeFont) -> Font {
pub unsafe fn from_native_font(native_font: &NativeFont) -> Font {
let native_font = native_font.clone();
Font {
dwrite_font: native_font.dwrite_font,
dwrite_font_face: native_font.dwrite_font_face,
Expand Down Expand Up @@ -747,7 +749,7 @@ impl Loader for Font {
}

#[inline]
unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
unsafe fn from_native_font(native_font: &Self::NativeFont) -> Self {
Font::from_native_font(native_font)
}

Expand Down
5 changes: 3 additions & 2 deletions src/loaders/freetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ impl Font {
}

/// Creates a font from a native API handle.
pub unsafe fn from_native_font(freetype_face: NativeFont) -> Font {
pub unsafe fn from_native_font(freetype_face: &NativeFont) -> Font {
// We make an in-memory copy of the underlying font data. This is because the native font
// does not necessarily hold a strong reference to the memory backing it.
let freetype_face = *freetype_face;
const CHUNK_SIZE: usize = 4096;
let mut font_data = vec![];
loop {
Expand Down Expand Up @@ -1013,7 +1014,7 @@ impl Loader for Font {
}

#[inline]
unsafe fn from_native_font(native_font: Self::NativeFont) -> Self {
unsafe fn from_native_font(native_font: &Self::NativeFont) -> Self {
Font::from_native_font(native_font)
}

Expand Down
62 changes: 6 additions & 56 deletions src/sources/core_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use core_foundation::array::CFArray;
use core_foundation::base::{CFType, TCFType};
use core_foundation::dictionary::CFDictionary;
use core_foundation::string::CFString;
use core_text::font::new_from_descriptor;
use core_text::font_collection::{self, CTFontCollection};
use core_text::font_descriptor::{self, CTFontDescriptor};
use core_text::font_manager;
Expand Down Expand Up @@ -153,68 +154,17 @@ fn css_stretchiness_to_core_text_width(css_stretchiness: Stretch) -> f32 {
0.25 * core_text_loader::piecewise_linear_find_index(css_stretchiness, &Stretch::MAPPING) - 1.0
}

#[derive(Clone)]
struct FontDataInfo {
data: Arc<Vec<u8>>,
file_type: FileType,
}

fn create_handles_from_core_text_collection(
collection: CTFontCollection,
) -> Result<Vec<Handle>, SelectionError> {
let mut fonts = vec![];
if let Some(descriptors) = collection.get_descriptors() {
let mut font_data_info_cache: HashMap<PathBuf, FontDataInfo> = HashMap::new();

'outer: for index in 0..descriptors.len() {
for index in 0..descriptors.len() {
let descriptor = descriptors.get(index).unwrap();
let font_path = descriptor.font_path().unwrap();

let data_info = if let Some(data_info) = font_data_info_cache.get(&font_path) {
data_info.clone()
} else {
let mut file = if let Ok(file) = File::open(&font_path) {
file
} else {
continue;
};
let data = if let Ok(data) = utils::slurp_file(&mut file) {
Arc::new(data)
} else {
continue;
};

let file_type = match Font::analyze_bytes(Arc::clone(&data)) {
Ok(file_type) => file_type,
Err(_) => continue,
};

let data_info = FontDataInfo { data, file_type };

font_data_info_cache.insert(font_path.clone(), data_info.clone());

data_info
};

match data_info.file_type {
FileType::Collection(font_count) => {
let postscript_name = descriptor.font_name();
for font_index in 0..font_count {
if let Ok(font) = Font::from_bytes(Arc::clone(&data_info.data), font_index)
{
if let Some(font_postscript_name) = font.postscript_name() {
if postscript_name == font_postscript_name {
fonts.push(Handle::from_memory(data_info.data, font_index));
continue 'outer;
}
}
}
}
}
FileType::Single => {
fonts.push(Handle::from_memory(data_info.data, 0));
}
}
let native = new_from_descriptor(&descriptor, 16.);
let font = unsafe { Font::from_core_text_font_no_path(native.clone()) };

fonts.push(Handle::from_native(&font));
}
}
if fonts.is_empty() {
Expand Down
1 change: 1 addition & 0 deletions tests/select_font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ macro_rules! match_handle {
font_index, $index
);
}
Handle::Native { .. } => {}
}
};
}
Expand Down
12 changes: 0 additions & 12 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,18 +625,6 @@ pub fn get_font_properties() {
assert_eq!(properties.stretch, Stretch(1.0));
}

#[cfg(feature = "source")]
#[test]
pub fn get_font_data() {
let font = SystemSource::new()
.select_best_match(&[FamilyName::SansSerif], &Properties::new())
.unwrap()
.load()
.unwrap();
let data = font.copy_font_data().unwrap();
debug_assert!(SFNT_VERSIONS.iter().any(|version| data[0..4] == *version));
}

#[cfg(feature = "source")]
#[test]
pub fn load_font_table() {
Expand Down
Loading