diff --git a/src/handle.rs b/src/handle.rs index d6f8bbc..c102263 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -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. /// @@ -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::as_native`] to retrieve the font object. + inner: Arc, + }, } impl Handle { @@ -66,6 +73,25 @@ impl Handle { Handle::Memory { bytes, font_index } } + /// Creates a new handle from a system handle. + pub fn from_native(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 as_native(&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 { diff --git a/src/loader.rs b/src/loader.rs index fc94617..3fba207 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -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). /// @@ -63,22 +63,26 @@ 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 { - 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), + } => Self::from_path(path, *font_index), #[cfg(target_arch = "wasm32")] Handle::Path { .. } => Err(FontLoadingError::NoFilesystem), + Handle::Native { .. } => { + if let Some(native) = handle.as_native::() { + unsafe { Ok(Self::from_native_font(native)) } + } else { + Err(FontLoadingError::UnknownFormat) + } + } } } diff --git a/src/loaders/core_text.rs b/src/loaders/core_text.rs index 8685321..1235558 100644 --- a/src/loaders/core_text.rs +++ b/src/loaders/core_text.rs @@ -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, } } @@ -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, + )) } } @@ -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) } diff --git a/src/loaders/directwrite.rs b/src/loaders/directwrite.rs index b39b7d9..6135842 100644 --- a/src/loaders/directwrite.rs +++ b/src/loaders/directwrite.rs @@ -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, @@ -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, @@ -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) } diff --git a/src/loaders/freetype.rs b/src/loaders/freetype.rs index 538ec28..603465e 100644 --- a/src/loaders/freetype.rs +++ b/src/loaders/freetype.rs @@ -197,9 +197,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 { @@ -1036,7 +1037,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) } diff --git a/src/sources/core_text.rs b/src/sources/core_text.rs index c323eb6..5499b7f 100644 --- a/src/sources/core_text.rs +++ b/src/sources/core_text.rs @@ -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; @@ -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>, - file_type: FileType, -} - fn create_handles_from_core_text_collection( collection: CTFontCollection, ) -> Result, SelectionError> { let mut fonts = vec![]; if let Some(descriptors) = collection.get_descriptors() { - let mut font_data_info_cache: HashMap = 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() { diff --git a/tests/select_font.rs b/tests/select_font.rs index 9b892ba..6e031b0 100644 --- a/tests/select_font.rs +++ b/tests/select_font.rs @@ -44,6 +44,7 @@ macro_rules! match_handle { font_index, $index ); } + Handle::Native { .. } => {} } }; } diff --git a/tests/tests.rs b/tests/tests.rs index 6f4d83b..0b9bc1a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -622,18 +622,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() {