diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 85e25d8dfb3..8b62a183733 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -17,6 +17,7 @@ use font_kit::source::SystemSource; use gfx::font::FontHandleMethods; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context::FontContext; +use gfx::font_template::FontTemplateRefMethods; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::{debug, error, warn}; use num_traits::ToPrimitive; @@ -501,7 +502,7 @@ impl<'a> CanvasData<'a> { .first(font_context) .expect("couldn't find font"); let font = font.borrow_mut(); - Font::from_bytes(font.handle.template().bytes(), 0) + Font::from_bytes(font.handle.template().data(), 0) .ok() .or_else(|| load_system_font_from_style(Some(style))) }) diff --git a/components/gfx/font.rs b/components/gfx/font.rs index f01f916d4ba..ad1ef24e313 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -26,10 +26,9 @@ use webrender_api::FontInstanceKey; use crate::font_cache_thread::FontIdentifier; use crate::font_context::{FontContext, FontSource}; -use crate::font_template::FontTemplateDescriptor; +use crate::font_template::{FontTemplateDescriptor, FontTemplateRef}; use crate::platform::font::{FontHandle, FontTable}; pub use crate::platform::font_list::fallback_font_families; -use crate::platform::font_template::FontTemplateData; use crate::text::glyph::{ByteIndex, GlyphData, GlyphId, GlyphStore}; use crate::text::shaping::ShaperMethods; use crate::text::Shaper; @@ -55,11 +54,11 @@ static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = AtomicUsize::new(0); pub trait FontHandleMethods: Sized { fn new_from_template( - template: Arc, + template: FontTemplateRef, pt_size: Option, ) -> Result; - fn template(&self) -> Arc; + fn template(&self) -> FontTemplateRef; fn family_name(&self) -> Option; fn face_name(&self) -> Option; @@ -75,9 +74,6 @@ pub trait FontHandleMethods: Sized { fn can_do_fast_shaping(&self) -> bool; fn metrics(&self) -> FontMetrics; fn table_for_tag(&self, _: FontTableTag) -> Option; - - /// A unique identifier for the font, allowing comparison. - fn identifier(&self) -> &FontIdentifier; } // Used to abstract over the shaper's choice of fixed int representation. @@ -201,8 +197,8 @@ impl Font { } /// A unique identifier for the font, allowing comparison. - pub fn identifier(&self) -> &FontIdentifier { - self.handle.identifier() + pub fn identifier(&self) -> FontIdentifier { + self.handle.template().borrow().identifier.clone() } } diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index a5c6a2b0974..797a12317d6 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -3,8 +3,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; +use std::cell::RefCell; use std::collections::HashMap; use std::ops::Deref; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{f32, fmt, mem, thread}; @@ -25,22 +27,23 @@ use webrender_api::{FontInstanceKey, FontKey}; use crate::font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope}; use crate::font_context::FontSource; -use crate::font_template::{FontTemplate, FontTemplateDescriptor}; +use crate::font_template::{ + FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods, +}; use crate::platform::font_list::{ for_each_available_family, for_each_variation, system_default_family, LocalFontIdentifier, SANS_SERIF_FONT_FAMILY, }; -use crate::platform::font_template::FontTemplateData; /// A list of font templates that make up a given font family. #[derive(Default)] pub struct FontTemplates { - templates: Vec, + templates: Vec, } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug)] pub struct FontTemplateInfo { - pub font_template: Arc, + pub font_template: FontTemplateRef, pub font_key: FontKey, } @@ -59,13 +62,19 @@ pub enum FontIdentifier { #[derive(Debug, Deserialize, Serialize)] pub struct SerializedFontTemplate { identifier: FontIdentifier, + descriptor: Option, bytes_receiver: ipc_channel::ipc::IpcBytesReceiver, } impl SerializedFontTemplate { - pub fn to_font_template_data(&self) -> FontTemplateData { + pub fn to_font_template(&self) -> FontTemplate { let font_data = self.bytes_receiver.recv().ok(); - FontTemplateData::new(self.identifier.clone(), font_data).unwrap() + FontTemplate { + identifier: self.identifier.clone(), + descriptor: self.descriptor.clone(), + data: font_data.map(Arc::new), + is_valid: true, + } } } @@ -74,40 +83,37 @@ impl FontTemplates { pub fn find_font_for_style( &mut self, desc: &FontTemplateDescriptor, - ) -> Option> { + ) -> Option { // TODO(Issue #189): optimize lookup for // regular/bold/italic/bolditalic with fixed offsets and a // static decision table for fallback between these values. for template in &mut self.templates { - let maybe_template = template.data_for_descriptor(desc); - if maybe_template.is_some() { - return maybe_template; + if template.descriptor_matches(desc) { + return Some(template.clone()); } } // We didn't find an exact match. Do more expensive fuzzy matching. // TODO(#190): Do a better job. - let (mut best_template_data, mut best_distance) = (None, f32::MAX); - for template in &mut self.templates { - if let Some((template_data, distance)) = template.data_for_approximate_descriptor(desc) - { + let (mut best_template, mut best_distance) = (None, f32::MAX); + for template in self.templates.iter() { + if let Some(distance) = template.descriptor_distance(desc) { if distance < best_distance { - best_template_data = Some(template_data); + best_template = Some(template); best_distance = distance } } } - if best_template_data.is_some() { - return best_template_data; + if best_template.is_some() { + return best_template.cloned(); } // If a request is made for a font family that exists, // pick the first valid font in the family if we failed // to find an exact match for the descriptor. - for template in &mut self.templates { - let maybe_template = template.get(); - if maybe_template.is_some() { - return maybe_template; + for template in &mut self.templates.iter() { + if template.is_valid() { + return Some(template.clone()); } } @@ -116,13 +122,13 @@ impl FontTemplates { pub fn add_template(&mut self, identifier: FontIdentifier, maybe_data: Option>) { for template in &self.templates { - if *template.identifier() == identifier { + if *template.borrow().identifier() == identifier { return; } } if let Ok(template) = FontTemplate::new(identifier, maybe_data) { - self.templates.push(template); + self.templates.push(Rc::new(RefCell::new(template))); } } } @@ -196,29 +202,30 @@ impl FontCache { match msg { Command::GetFontTemplate(template_descriptor, family_descriptor, result) => { - let maybe_font_template = - self.find_font_template(&template_descriptor, &family_descriptor); - match maybe_font_template { - None => { - let _ = result.send(Reply::GetFontTemplateReply(None)); - }, - Some(font_template_info) => { - let (bytes_sender, bytes_receiver) = - ipc::bytes_channel().expect("failed to create IPC channel"); - let serialized_font_template = SerializedFontTemplate { - identifier: font_template_info.font_template.identifier.clone(), - bytes_receiver, - }; - - let _ = result.send(Reply::GetFontTemplateReply(Some( - SerializedFontTemplateInfo { - serialized_font_template, - font_key: font_template_info.font_key, - }, - ))); - let _ = bytes_sender.send(&font_template_info.font_template.bytes()); - }, + let Some(font_template_info) = + self.find_font_template(&template_descriptor, &family_descriptor) + else { + let _ = result.send(Reply::GetFontTemplateReply(None)); + continue; }; + + let (bytes_sender, bytes_receiver) = + ipc::bytes_channel().expect("failed to create IPC channel"); + let serialized_font_template = SerializedFontTemplate { + identifier: font_template_info.font_template.borrow().identifier.clone(), + descriptor: font_template_info.font_template.borrow().descriptor.clone(), + bytes_receiver, + }; + + let _ = result.send(Reply::GetFontTemplateReply(Some( + SerializedFontTemplateInfo { + serialized_font_template, + font_key: font_template_info.font_key, + }, + ))); + + // NB: This will load the font into memory if it hasn't been loaded already. + let _ = bytes_sender.send(&font_template_info.font_template.data()); }, Command::GetFontInstance(font_key, size, result) => { let webrender_api = &self.webrender_api; @@ -382,7 +389,7 @@ impl FontCache { &mut self, template_descriptor: &FontTemplateDescriptor, family_name: &FontFamilyName, - ) -> Option> { + ) -> Option { let family_name = self.transform_family(family_name); // TODO(Issue #188): look up localized font family names if canonical name not found @@ -414,7 +421,7 @@ impl FontCache { &mut self, template_descriptor: &FontTemplateDescriptor, family_name: &FontFamilyName, - ) -> Option> { + ) -> Option { let family_name = LowercaseString::from(family_name); if self.web_families.contains_key(&family_name) { @@ -425,25 +432,20 @@ impl FontCache { } } - fn get_font_template_info(&mut self, template: Arc) -> FontTemplateInfo { + fn get_font_key_for_template(&mut self, template: &FontTemplateRef) -> FontKey { let webrender_api = &self.webrender_api; let webrender_fonts = &mut self.webrender_fonts; - - let font_key = *webrender_fonts - .entry(template.identifier.clone()) + *webrender_fonts + .entry(template.borrow().identifier.clone()) .or_insert_with(|| { - let font = match (template.bytes_if_in_memory(), template.native_font()) { + let template = template.borrow(); + let font = match (template.data_if_in_memory(), template.native_font_handle()) { (Some(bytes), _) => FontData::Raw((*bytes).clone()), (None, Some(native_font)) => FontData::Native(native_font), - (None, None) => FontData::Raw((*template.bytes()).clone()), + (None, None) => unreachable!("Font should either be local or a web font."), }; webrender_api.add_font(font) - }); - - FontTemplateInfo { - font_template: template, - font_key, - } + }) } fn find_font_template( @@ -462,7 +464,10 @@ impl FontCache { self.find_font_in_local_family(template_descriptor, &family_descriptor.name) }, } - .map(|t| self.get_font_template_info(t)) + .map(|font_template| FontTemplateInfo { + font_key: self.get_font_key_for_template(&font_template), + font_template, + }) } } @@ -618,17 +623,17 @@ impl FontSource for FontCacheThread { match reply.unwrap() { Reply::GetFontTemplateReply(maybe_serialized_font_template_info) => { - match maybe_serialized_font_template_info { - None => None, - Some(serialized_font_template_info) => Some(FontTemplateInfo { - font_template: Arc::new( - serialized_font_template_info - .serialized_font_template - .to_font_template_data(), - ), + maybe_serialized_font_template_info.map(|serialized_font_template_info| { + let font_template = Rc::new(RefCell::new( + serialized_font_template_info + .serialized_font_template + .to_font_template(), + )); + FontTemplateInfo { + font_template, font_key: serialized_font_template_info.font_key, - }), - } + } + }) }, } } diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index d316ceb50c1..f37c1cd91fd 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -21,6 +21,8 @@ use crate::font::{ Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontHandleMethods, FontRef, }; use crate::font_cache_thread::FontTemplateInfo; +#[cfg(target_os = "macos")] +use crate::font_template::FontTemplate; use crate::font_template::FontTemplateDescriptor; use crate::platform::font::FontHandle; @@ -264,4 +266,7 @@ impl Hash for FontGroupCacheKey { #[inline] pub fn invalidate_font_caches() { FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst); + + #[cfg(target_os = "macos")] + FontTemplate::clear_core_text_font_cache(); } diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 66db059db13..226017efa79 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -2,20 +2,26 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::cell::RefCell; use std::fmt::{Debug, Error, Formatter}; use std::io::Error as IoError; -use std::sync::{Arc, Weak}; +use std::rc::Rc; +use std::sync::Arc; +use log::warn; use serde::{Deserialize, Serialize}; use style::computed_values::font_stretch::T as FontStretch; use style::computed_values::font_style::T as FontStyle; use style::properties::style_structs::Font as FontStyleStruct; use style::values::computed::font::FontWeight; +use webrender_api::NativeFontHandle; use crate::font::FontHandleMethods; use crate::font_cache_thread::FontIdentifier; use crate::platform::font::FontHandle; -use crate::platform::font_template::FontTemplateData; + +/// A reference to a [`FontTemplate`] with shared ownership and mutability. +pub(crate) type FontTemplateRef = Rc>; /// Describes how to select a font from a given family. This is very basic at the moment and needs /// to be expanded or refactored when we support more of the font styling parameters. @@ -84,12 +90,14 @@ impl<'a> From<&'a FontStyleStruct> for FontTemplateDescriptor { /// font instance handles. It contains a unique /// FontTemplateData structure that is platform specific. pub struct FontTemplate { - identifier: FontIdentifier, - descriptor: Option, - weak_ref: Option>, - // GWTODO: Add code path to unset the strong_ref for web fonts! - strong_ref: Option>, - is_valid: bool, + pub identifier: FontIdentifier, + pub descriptor: Option, + /// The data to use for this [`FontTemplate`]. For web fonts, this is always filled, but + /// for local fonts, this is loaded only lazily in layout. + /// + /// TODO: There is no mechanism for web fonts to unset their data! + pub data: Option>>, + pub is_valid: bool, } impl Debug for FontTemplate { @@ -102,24 +110,11 @@ impl Debug for FontTemplate { /// is common, regardless of the number of instances of /// this font handle per thread. impl FontTemplate { - pub fn new( - identifier: FontIdentifier, - maybe_bytes: Option>, - ) -> Result { - let maybe_data = match maybe_bytes { - Some(_) => Some(FontTemplateData::new(identifier.clone(), maybe_bytes)?), - None => None, - }; - - let maybe_strong_ref = maybe_data.map(Arc::new); - - let maybe_weak_ref = maybe_strong_ref.as_ref().map(Arc::downgrade); - + pub fn new(identifier: FontIdentifier, data: Option>) -> Result { Ok(FontTemplate { identifier, descriptor: None, - weak_ref: maybe_weak_ref, - strong_ref: maybe_strong_ref, + data: data.map(Arc::new), is_valid: true, }) } @@ -128,63 +123,79 @@ impl FontTemplate { &self.identifier } + /// Returns a reference to the bytes in this font if they are in memory. + /// This function never performs disk I/O. + pub fn data_if_in_memory(&self) -> Option>> { + self.data.clone() + } + + /// Returns a [`NativeFontHandle`] for this font template, if it is local. + pub fn native_font_handle(&self) -> Option { + match &self.identifier { + FontIdentifier::Local(local_identifier) => local_identifier.native_font_handle(), + FontIdentifier::Web(_) => None, + } + } +} + +pub trait FontTemplateRefMethods { + /// Returns a reference to the data in this font. This may be a hugely expensive + /// operation (depending on the platform) which performs synchronous disk I/O + /// and should never be done lightly. + fn data(&self) -> Arc>; + /// Return true if this is a valid [`FontTemplate`] ie it is possible to construct a [`FontHandle`] + /// from its data. + fn is_valid(&self) -> bool; + /// If not done already for this [`FontTemplate`] load the font into a platform font face and + /// populate the `descriptor` field. Note that calling [`FontTemplateRefMethods::descriptor()`] + /// does this implicitly. If this fails, [`FontTemplateRefMethods::is_valid()`] will return + /// false in the future. + fn instantiate(&self) -> Result<(), &'static str>; /// Get the descriptor. Returns `None` when instantiating the data fails. - pub fn descriptor(&mut self) -> Option { - // The font template data can be unloaded when nothing is referencing - // it (via the Weak reference to the Arc above). However, if we have - // already loaded a font, store the style information about it separately, - // so that we can do font matching against it again in the future - // without having to reload the font (unless it is an actual match). + fn descriptor(&self) -> Option; + /// Returns true if the given descriptor matches the one in this [`FontTemplate`]. + fn descriptor_matches(&self, requested_desc: &FontTemplateDescriptor) -> bool; + /// Calculate the distance from this [`FontTemplate`]s descriptor and return it + /// or None if this is not a valid [`FontTemplate`]. + fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> Option; +} - self.descriptor.or_else(|| { - if self.instantiate().is_err() { - return None; - }; +impl FontTemplateRefMethods for FontTemplateRef { + fn descriptor(&self) -> Option { + // Store the style information about the template separately from the data, + // so that we can do font matching against it again in the future without + // having to reload the font (unless it is an actual match). + if let Some(descriptor) = self.borrow().descriptor { + return Some(descriptor); + } - Some( - self.descriptor - .expect("Instantiation succeeded but no descriptor?"), - ) - }) + if let Err(error) = self.instantiate() { + warn!("Could not initiate FonteTemplate descriptor: {error:?}"); + } + + self.borrow().descriptor } - /// Get the data for creating a font if it matches a given descriptor. - pub fn data_for_descriptor( - &mut self, - requested_desc: &FontTemplateDescriptor, - ) -> Option> { - self.descriptor().and_then(|descriptor| { - if *requested_desc == descriptor { - self.data().ok() - } else { - None - } - }) + fn descriptor_matches(&self, requested_desc: &FontTemplateDescriptor) -> bool { + self.descriptor() + .map_or(false, |descriptor| descriptor == *requested_desc) } - /// Returns the font data along with the distance between this font's descriptor and the given - /// descriptor, if the font can be loaded. - pub fn data_for_approximate_descriptor( - &mut self, - requested_descriptor: &FontTemplateDescriptor, - ) -> Option<(Arc, f32)> { - self.descriptor().and_then(|descriptor| { - self.data() - .ok() - .map(|data| (data, descriptor.distance_from(requested_descriptor))) - }) + fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> Option { + self.descriptor() + .map(|descriptor| descriptor.distance_from(requested_descriptor)) } - fn instantiate(&mut self) -> Result<(), &'static str> { - if !self.is_valid { + fn instantiate(&self) -> Result<(), &'static str> { + if !self.borrow().is_valid { return Err("Invalid font template"); } - let data = self.data().map_err(|_| "Could not get FontTemplate data")?; - let handle = FontHandleMethods::new_from_template(data, None); - self.is_valid = handle.is_ok(); + let handle = FontHandleMethods::new_from_template(self.clone(), None); + let mut template = self.borrow_mut(); + template.is_valid = handle.is_ok(); let handle: FontHandle = handle?; - self.descriptor = Some(FontTemplateDescriptor::new( + template.descriptor = Some(FontTemplateDescriptor::new( handle.boldness(), handle.stretchiness(), handle.style(), @@ -192,31 +203,21 @@ impl FontTemplate { Ok(()) } - /// Get the data for creating a font. - pub fn get(&mut self) -> Option> { - if self.is_valid { - self.data().ok() - } else { - None - } + fn is_valid(&self) -> bool { + self.instantiate().is_ok() } - /// Get the font template data. If any strong references still - /// exist, it will return a clone, otherwise it will load the - /// font data and store a weak reference to it internally. - pub fn data(&mut self) -> Result, IoError> { - let maybe_data = match self.weak_ref { - Some(ref data) => data.upgrade(), - None => None, - }; - - if let Some(data) = maybe_data { - return Ok(data); - } - - assert!(self.strong_ref.is_none()); - let template_data = Arc::new(FontTemplateData::new(self.identifier.clone(), None)?); - self.weak_ref = Some(Arc::downgrade(&template_data)); - Ok(template_data) + fn data(&self) -> Arc> { + let mut template = self.borrow_mut(); + let identifier = template.identifier.clone(); + template + .data + .get_or_insert_with(|| match identifier { + FontIdentifier::Local(local_identifier) => { + Arc::new(local_identifier.read_data_from_file()) + }, + FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), + }) + .clone() } } diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs index 968cb460589..f2130dff45f 100644 --- a/components/gfx/platform/freetype/android/font_list.rs +++ b/components/gfx/platform/freetype/android/font_list.rs @@ -2,12 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::path::Path; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; use log::warn; use serde::{Deserialize, Serialize}; use style::Atom; use ucd::{Codepoint, UnicodeBlock}; +use webrender_api::NativeFontHandle; use super::xml::{Attribute, Node}; use crate::text::util::is_cjk; @@ -23,6 +26,24 @@ pub struct LocalFontIdentifier { pub path: Atom, } +impl LocalFontIdentifier { + pub(crate) fn native_font_handle(&self) -> Option { + Some(NativeFontHandle { + path: PathBuf::from(&*self.path), + index: 0, + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec { + let mut bytes = Vec::new(); + File::open(Path::new(&*self.path)) + .expect("Couldn't open font file!") + .read_to_end(&mut bytes) + .unwrap(); + bytes + } +} + // Android doesn't provide an API to query system fonts until Android O: // https://developer.android.com/reference/android/text/FontConfig.html // System font configuration files must be parsed until Android O version is set as the minimum target. diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 44e51bc218c..7d9f9c6a507 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -4,7 +4,6 @@ use std::ffi::CString; use std::os::raw::{c_char, c_long}; -use std::sync::Arc; use std::{mem, ptr}; use app_units::Au; @@ -28,7 +27,7 @@ use crate::font::{ KERN, }; use crate::font_cache_thread::FontIdentifier; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::FontTemplateRef; use crate::text::glyph::GlyphId; use crate::text::util::fixed_to_float; @@ -72,9 +71,9 @@ struct OS2Table { #[derive(Debug)] #[allow(unused)] pub struct FontHandle { - // The font binary. This must stay valid for the lifetime of the font, - // if the font is created using FT_Memory_Face. - font_data: Arc, + // The template contains the font data, which must stay valid for the + // lifetime of the platform [`FT_Face`], if it's created via [`FT_Memory_Face`]. + font_template: FontTemplateRef, face: FT_Face, can_do_fast_shaping: bool, } @@ -94,15 +93,18 @@ impl Drop for FontHandle { } } -fn create_face(template: &FontTemplateData, pt_size: Option) -> Result { +fn create_face(template: &FontTemplateRef, pt_size: Option) -> Result { unsafe { let mut face: FT_Face = ptr::null_mut(); let face_index = 0 as FT_Long; let library = FreeTypeLibraryHandle::get().lock(); - let result = match template.identifier { + let result = match template.borrow().identifier { FontIdentifier::Web(_) => { - let bytes = template.bytes(); + let bytes = template + .borrow() + .data_if_in_memory() + .expect("Web font should always have data."); FT_New_Memory_Face( library.freetype_library, bytes.as_ptr(), @@ -141,13 +143,13 @@ fn create_face(template: &FontTemplateData, pt_size: Option) -> Result, + template: FontTemplateRef, pt_size: Option, ) -> Result { let face = create_face(&template, pt_size)?; let mut handle = FontHandle { face, - font_data: template, + font_template: template.clone(), can_do_fast_shaping: false, }; // TODO (#11310): Implement basic support for GPOS and GSUB. @@ -156,8 +158,8 @@ impl FontHandleMethods for FontHandle { Ok(handle) } - fn template(&self) -> Arc { - self.font_data.clone() + fn template(&self) -> FontTemplateRef { + self.font_template.clone() } fn family_name(&self) -> Option { @@ -358,10 +360,6 @@ impl FontHandleMethods for FontHandle { Some(FontTable { buffer: buf }) } } - - fn identifier(&self) -> &FontIdentifier { - &self.font_data.identifier - } } impl<'a> FontHandle { diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index 4f17d0bd1b2..9cd7e7663d9 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -3,6 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::ffi::CString; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; use std::ptr; use fontconfig_sys::{ @@ -15,6 +18,7 @@ use libc::{c_char, c_int}; use log::debug; use serde::{Deserialize, Serialize}; use style::Atom; +use webrender_api::NativeFontHandle; use super::c_str_to_string; use crate::text::util::is_cjk; @@ -33,6 +37,25 @@ pub struct LocalFontIdentifier { pub variation_index: i32, } +impl LocalFontIdentifier { + pub(crate) fn native_font_handle(&self) -> Option { + Some(NativeFontHandle { + path: PathBuf::from(&*self.path), + // TODO: Shouldn't this be using the variation index from the LocalFontIdentifier? + index: 0, + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec { + let mut bytes = Vec::new(); + File::open(Path::new(&*self.path)) + .expect("Couldn't open font file!") + .read_to_end(&mut bytes) + .unwrap(); + bytes + } +} + pub fn for_each_available_family(mut callback: F) where F: FnMut(String), diff --git a/components/gfx/platform/freetype/font_template.rs b/components/gfx/platform/freetype/font_template.rs deleted file mode 100644 index b9896e15ea8..00000000000 --- a/components/gfx/platform/freetype/font_template.rs +++ /dev/null @@ -1,93 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::fmt; -use std::fs::File; -use std::io::{Error, Read}; -use std::path::{Path, PathBuf}; -use std::sync::{Arc, RwLock}; - -use serde::{Deserialize, Serialize}; -use webrender_api::NativeFontHandle; - -use crate::font_cache_thread::FontIdentifier; - -/// Platform specific font representation for Linux. -#[derive(Deserialize, Serialize)] -pub struct FontTemplateData { - /// Lazily-loaded (for local fonts) byte data that can be passed - /// to Freetype or Raqote directly. - pub font_data: RwLock>>>, - pub identifier: FontIdentifier, -} - -impl fmt::Debug for FontTemplateData { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("FontTemplateData") - .field("identifier", &self.identifier) - .field( - "font_data", - &self - .font_data - .read() - .unwrap() - .as_ref() - .map(|bytes| format!("[{} bytes]", bytes.len())), - ) - .finish() - } -} - -impl FontTemplateData { - pub fn new( - identifier: FontIdentifier, - font_data: Option>, - ) -> Result { - Ok(FontTemplateData { - identifier, - font_data: RwLock::new(font_data.map(Arc::new)), - }) - } - - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - pub fn bytes(&self) -> Arc> { - self.font_data - .write() - .unwrap() - .get_or_insert_with(|| { - let path = match &self.identifier { - FontIdentifier::Local(local) => local.path.clone(), - FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), - }; - let mut bytes = Vec::new(); - File::open(Path::new(&*path)) - .expect("Couldn't open font file!") - .read_to_end(&mut bytes) - .unwrap(); - Arc::new(bytes) - }) - .clone() - } - - /// Returns a reference to the bytes in this font if they are in memory. This function - /// never performs disk I/O. - pub fn bytes_if_in_memory(&self) -> Option>> { - self.font_data.read().unwrap().as_ref().cloned() - } - - /// Returns the native font that underlies this font template, if applicable. - pub fn native_font(&self) -> Option { - let local_identifier = match &self.identifier { - FontIdentifier::Local(local_identifier) => local_identifier, - FontIdentifier::Web(_) => return None, - }; - - Some(NativeFontHandle { - path: PathBuf::from(&*local_identifier.path), - index: 0, - }) - } -} diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index 3e355c7d1ce..05e8fdc0614 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -4,7 +4,6 @@ use std::cmp::Ordering; use std::ops::Range; -use std::sync::Arc; use std::{fmt, ptr}; /// Implementation of Quartz (CoreGraphics) fonts. @@ -21,12 +20,12 @@ use core_text::font_descriptor::{ use log::debug; use style::values::computed::font::{FontStretch, FontStyle, FontWeight}; +use super::font_template::CoreTextFontTemplateMethods; use crate::font::{ FontHandleMethods, FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, GPOS, GSUB, KERN, }; -use crate::font_cache_thread::FontIdentifier; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::FontTemplateRef; use crate::text::glyph::GlyphId; const KERN_PAIR_LEN: usize = 6; @@ -54,7 +53,7 @@ impl FontTableMethods for FontTable { #[derive(Debug)] pub struct FontHandle { - font_data: Arc, + font_template: FontTemplateRef, ctfont: CTFont, h_kern_subtable: Option, can_do_fast_shaping: bool, @@ -157,34 +156,33 @@ impl fmt::Debug for CachedKernTable { impl FontHandleMethods for FontHandle { fn new_from_template( - template: Arc, + font_template: FontTemplateRef, pt_size: Option, ) -> Result { let size = match pt_size { Some(s) => s.to_f64_px(), None => 0.0, }; - match template.ctfont(size) { - Some(ref ctfont) => { - let mut handle = FontHandle { - font_data: template.clone(), - ctfont: ctfont.clone_with_font_size(size), - h_kern_subtable: None, - can_do_fast_shaping: false, - }; - handle.h_kern_subtable = handle.find_h_kern_subtable(); - // TODO (#11310): Implement basic support for GPOS and GSUB. - handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() && - handle.table_for_tag(GPOS).is_none() && - handle.table_for_tag(GSUB).is_none(); - Ok(handle) - }, - None => Err("Could not generate CTFont for FontTemplateData"), - } + let Some(core_text_font) = font_template.core_text_font(size) else { + return Err("Could not generate CTFont for FontTemplateData"); + }; + + let mut handle = FontHandle { + font_template, + ctfont: core_text_font.clone_with_font_size(size), + h_kern_subtable: None, + can_do_fast_shaping: false, + }; + handle.h_kern_subtable = handle.find_h_kern_subtable(); + // TODO (#11310): Implement basic support for GPOS and GSUB. + handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() && + handle.table_for_tag(GPOS).is_none() && + handle.table_for_tag(GSUB).is_none(); + Ok(handle) } - fn template(&self) -> Arc { - self.font_data.clone() + fn template(&self) -> FontTemplateRef { + self.font_template.clone() } fn family_name(&self) -> Option { @@ -317,8 +315,4 @@ impl FontHandleMethods for FontHandle { let result: Option = self.ctfont.get_font_table(tag); result.map(FontTable::wrap) } - - fn identifier(&self) -> &FontIdentifier { - &self.font_data.identifier - } } diff --git a/components/gfx/platform/macos/font_list.rs b/components/gfx/platform/macos/font_list.rs index 14f9f1fa34a..8d3dc2a09e8 100644 --- a/components/gfx/platform/macos/font_list.rs +++ b/components/gfx/platform/macos/font_list.rs @@ -2,10 +2,15 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fs::File; +use std::io::Read; +use std::path::Path; + use log::debug; use serde::{Deserialize, Serialize}; use style::Atom; use ucd::{Codepoint, UnicodeBlock}; +use webrender_api::NativeFontHandle; use crate::text::util::unicode_plane; @@ -18,6 +23,24 @@ pub struct LocalFontIdentifier { pub path: Atom, } +impl LocalFontIdentifier { + pub(crate) fn native_font_handle(&self) -> Option { + Some(NativeFontHandle { + name: self.postscript_name.to_string(), + path: self.path.to_string(), + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec { + let mut bytes = Vec::new(); + File::open(Path::new(&*self.path)) + .expect("Couldn't open font file!") + .read_to_end(&mut bytes) + .unwrap(); + bytes + } +} + pub fn for_each_available_family(mut callback: F) where F: FnMut(String), diff --git a/components/gfx/platform/macos/font_template.rs b/components/gfx/platform/macos/font_template.rs index 8320a358622..f7a4cde5efa 100644 --- a/components/gfx/platform/macos/font_template.rs +++ b/components/gfx/platform/macos/font_template.rs @@ -2,182 +2,73 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; -use std::fmt; -use std::fs::File; -use std::io::{Error as IoError, Read}; -use std::ops::Deref; -use std::path::Path; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::OnceLock; use app_units::Au; use core_graphics::data_provider::CGDataProvider; use core_graphics::font::CGFont; use core_text::font::CTFont; -use serde::de::{Error, Visitor}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use webrender_api::NativeFontHandle; +use parking_lot::RwLock; use crate::font_cache_thread::FontIdentifier; +use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; -/// Platform specific font representation for MacOS. CTFont object is cached here for use -/// by the paint functions that create CGFont references. -#[derive(Deserialize, Serialize)] -pub struct FontTemplateData { - // If you add members here, review the Debug impl below - /// The `CTFont` object, if present. This is cached here so that we don't have to keep creating - /// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or - /// `font_data` fields. - /// - /// When sending a `FontTemplateData` instance across processes, this will be cleared out on - /// the other side, because `CTFont` instances cannot be sent across processes. This is - /// harmless, however, because it can always be recreated. - ctfont: CachedCTFont, - pub identifier: FontIdentifier, - pub font_data: RwLock>>>, +// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is +// always possible to create a `CTFont` using a `FontTemplate` even if it isn't in this +// cache. +static CTFONT_CACHE: OnceLock>> = OnceLock::new(); + +/// A [`HashMap`] of cached [`CTFont`] for a single [`FontIdentifier`]. There is one [`CTFont`] +/// for each cached font size. +type CachedCTFont = HashMap; + +pub(crate) trait CoreTextFontTemplateMethods { + /// Retrieves a [`CTFont`] instance, instantiating it if necessary if it is not + /// stored in the shared Core Text font cache. + fn core_text_font(&self, pt_size: f64) -> Option; } -impl fmt::Debug for FontTemplateData { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("FontTemplateData") - .field("ctfont", &self.ctfont) - .field("identifier", &self.identifier) - .field( - "font_data", - &self - .font_data - .read() - .unwrap() - .as_ref() - .map(|bytes| format!("[{} bytes]", bytes.len())), - ) - .finish() - } -} - -unsafe impl Send for FontTemplateData {} -unsafe impl Sync for FontTemplateData {} - -impl FontTemplateData { - pub fn new( - identifier: FontIdentifier, - font_data: Option>, - ) -> Result { - Ok(FontTemplateData { - ctfont: CachedCTFont(Mutex::new(HashMap::new())), - identifier, - font_data: RwLock::new(font_data.map(Arc::new)), - }) - } - - /// Retrieves the Core Text font instance, instantiating it if necessary. - pub fn ctfont(&self, pt_size: f64) -> Option { - let mut ctfonts = self.ctfont.lock().unwrap(); - - let entry = ctfonts.entry(Au::from_f64_px(pt_size)); - match entry { - Entry::Occupied(entry) => return Some(entry.get().clone()), - Entry::Vacant(_) => {}, - } - - // If you pass a zero font size to one of the Core Text APIs, it'll replace it with - // 12.0. We don't want that! (Issue #10492.) +impl CoreTextFontTemplateMethods for FontTemplateRef { + fn core_text_font(&self, pt_size: f64) -> Option { + //// If you pass a zero font size to one of the Core Text APIs, it'll replace it with + //// 12.0. We don't want that! (Issue #10492.) let clamped_pt_size = pt_size.max(0.01); + let au_size = Au::from_f64_px(clamped_pt_size); - let provider = CGDataProvider::from_buffer(self.bytes()); - let cgfont = CGFont::from_data_provider(provider).ok()?; - let ctfont = core_text::font::new_from_CGFont(&cgfont, clamped_pt_size); - - // Cache the newly created CTFont font. - entry.or_insert(ctfont.clone()); - - Some(ctfont) - } - - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - pub fn bytes(&self) -> Arc> { - self.font_data - .write() - .unwrap() - .get_or_insert_with(|| { - let path = match &self.identifier { - FontIdentifier::Local(local) => local.path.clone(), - FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), - }; - let mut bytes = Vec::new(); - File::open(Path::new(&*path)) - .expect("Couldn't open font file!") - .read_to_end(&mut bytes) - .unwrap(); - Arc::new(bytes) - }) - .clone() - } - - /// Returns a reference to the bytes in this font if they are in memory. - /// This function never performs disk I/O. - pub fn bytes_if_in_memory(&self) -> Option>> { - self.font_data.read().unwrap().as_ref().cloned() - } - - /// Returns the native font that underlies this font template, if applicable. - pub fn native_font(&self) -> Option { - let local_identifier = match &self.identifier { - FontIdentifier::Local(local_identifier) => local_identifier, - FontIdentifier::Web(_) => return None, - }; - Some(NativeFontHandle { - name: local_identifier.postscript_name.to_string(), - path: local_identifier.path.to_string(), - }) - } -} - -#[derive(Debug)] -pub struct CachedCTFont(Mutex>); - -impl Deref for CachedCTFont { - type Target = Mutex>; - fn deref(&self) -> &Mutex> { - &self.0 - } -} - -impl Serialize for CachedCTFont { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_none() - } -} - -impl<'de> Deserialize<'de> for CachedCTFont { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct NoneOptionVisitor; - - impl<'de> Visitor<'de> for NoneOptionVisitor { - type Value = CachedCTFont; - - fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "none") - } - - #[inline] - fn visit_none(self) -> Result - where - E: Error, + let cache = CTFONT_CACHE.get_or_init(Default::default); + let identifier = self.borrow().identifier.clone(); + { + let cache = cache.read(); + if let Some(core_text_font) = cache + .get(&identifier) + .and_then(|identifier_cache| identifier_cache.get(&au_size)) { - Ok(CachedCTFont(Mutex::new(HashMap::new()))) + return Some(core_text_font.clone()); } } - deserializer.deserialize_option(NoneOptionVisitor) + let mut cache = cache.write(); + let identifier_cache = cache.entry(identifier).or_insert_with(Default::default); + + // It could be that between the time of the cache miss above and now, after the write lock + // on the cache has been acquired, the cache was populated with the data that we need. Thus + // check again and return the CTFont if it is is already cached. + if let Some(core_text_font) = identifier_cache.get(&au_size) { + return Some(core_text_font.clone()); + } + + let provider = CGDataProvider::from_buffer(self.data()); + let cgfont = CGFont::from_data_provider(provider).ok()?; + let core_text_font = core_text::font::new_from_CGFont(&cgfont, clamped_pt_size); + identifier_cache.insert(au_size, core_text_font.clone()); + Some(core_text_font) + } +} + +impl FontTemplate { + pub(crate) fn clear_core_text_font_cache() { + let cache = CTFONT_CACHE.get_or_init(Default::default); + cache.write().clear(); } } diff --git a/components/gfx/platform/mod.rs b/components/gfx/platform/mod.rs index 6e3d191529e..72482e7df61 100644 --- a/components/gfx/platform/mod.rs +++ b/components/gfx/platform/mod.rs @@ -3,11 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #[cfg(any(target_os = "linux", target_os = "android"))] -pub use crate::platform::freetype::{font, font_list, font_template, library_handle}; +pub use crate::platform::freetype::{font, font_list, library_handle}; #[cfg(target_os = "macos")] pub use crate::platform::macos::{font, font_list, font_template}; #[cfg(target_os = "windows")] -pub use crate::platform::windows::{font, font_list, font_template}; +pub use crate::platform::windows::{font, font_list}; #[cfg(any(target_os = "linux", target_os = "android"))] mod freetype { @@ -36,7 +36,6 @@ mod freetype { #[cfg(target_os = "android")] pub use self::android::font_list; - pub mod font_template; pub mod library_handle; } @@ -51,5 +50,4 @@ mod macos { mod windows { pub mod font; pub mod font_list; - pub mod font_template; } diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs index 03b82022068..a2156f85e73 100644 --- a/components/gfx/platform/windows/font.rs +++ b/components/gfx/platform/windows/font.rs @@ -8,7 +8,6 @@ use std::fmt; use std::ops::Deref; -use std::sync::Arc; use app_units::Au; use dwrote::{Font, FontFace, FontFile, FontStretch, FontStyle}; @@ -22,7 +21,7 @@ use crate::font::{ FontHandleMethods, FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, }; use crate::font_cache_thread::FontIdentifier; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::{FontTemplateRef, FontTemplateRefMethods}; use crate::text::glyph::GlyphId; // 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch @@ -200,7 +199,7 @@ impl FontInfo { #[derive(Debug)] pub struct FontHandle { - font_data: Arc, + font_template: FontTemplateRef, face: Nondebug, info: FontInfo, em_size: f32, @@ -227,13 +226,18 @@ impl FontHandle {} impl FontHandleMethods for FontHandle { fn new_from_template( - template: Arc, + font_template: FontTemplateRef, pt_size: Option, ) -> Result { - let (face, info) = match template.get_font() { + let direct_write_font = match font_template.borrow().identifier { + FontIdentifier::Local(ref local_identifier) => local_identifier.direct_write_font(), + FontIdentifier::Web(_) => None, + }; + + let (face, info) = match direct_write_font { Some(font) => (font.create_font_face(), FontInfo::new_from_font(&font)?), None => { - let bytes = template.bytes(); + let bytes = font_template.data(); let font_file = FontFile::new_from_data(bytes).ok_or("Could not create FontFile")?; let face = font_file @@ -254,7 +258,7 @@ impl FontHandleMethods for FontHandle { let scaled_design_units_to_pixels = em_size / design_units_per_pixel; Ok(FontHandle { - font_data: template.clone(), + font_template, face: Nondebug(face), info, em_size, @@ -263,8 +267,8 @@ impl FontHandleMethods for FontHandle { }) } - fn template(&self) -> Arc { - self.font_data.clone() + fn template(&self) -> FontTemplateRef { + self.font_template.clone() } fn family_name(&self) -> Option { @@ -352,8 +356,4 @@ impl FontHandleMethods for FontHandle { .get_font_table(tag) .map(|bytes| FontTable { data: bytes }) } - - fn identifier(&self) -> &FontIdentifier { - &self.font_data.identifier - } } diff --git a/components/gfx/platform/windows/font_list.rs b/components/gfx/platform/windows/font_list.rs index bc5e0030972..d90c7d1aab8 100644 --- a/components/gfx/platform/windows/font_list.rs +++ b/components/gfx/platform/windows/font_list.rs @@ -5,9 +5,10 @@ use std::hash::Hash; use std::sync::Arc; -use dwrote::{FontCollection, FontDescriptor}; +use dwrote::{Font, FontCollection, FontDescriptor}; use serde::{Deserialize, Serialize}; use ucd::{Codepoint, UnicodeBlock}; +use webrender_api::NativeFontHandle; use crate::text::util::unicode_plane; @@ -34,6 +35,32 @@ pub struct LocalFontIdentifier { pub font_descriptor: Arc, } +impl LocalFontIdentifier { + /// Create a [`Font`] for this font. + pub fn direct_write_font(&self) -> Option { + FontCollection::system().get_font_from_descriptor(&self.font_descriptor) + } + + pub fn native_font_handle(&self) -> Option { + let face = self.direct_write_font()?.create_font_face(); + let path = face.get_files().first()?.get_font_file_path()?; + Some(NativeFontHandle { + path, + index: face.get_index(), + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec { + let font = FontCollection::system() + .get_font_from_descriptor(&self.font_descriptor) + .unwrap(); + let face = font.create_font_face(); + let files = face.get_files(); + assert!(!files.is_empty()); + files[0].get_font_file_bytes() + } +} + impl Eq for LocalFontIdentifier {} impl Hash for LocalFontIdentifier { diff --git a/components/gfx/platform/windows/font_template.rs b/components/gfx/platform/windows/font_template.rs deleted file mode 100644 index 9cdcb980e9a..00000000000 --- a/components/gfx/platform/windows/font_template.rs +++ /dev/null @@ -1,101 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::sync::{Arc, RwLock}; -use std::{fmt, io}; - -use dwrote::{Font, FontCollection}; -use serde::{Deserialize, Serialize}; -use webrender_api::NativeFontHandle; - -use crate::font_cache_thread::FontIdentifier; - -#[derive(Deserialize, Serialize)] -pub struct FontTemplateData { - /// The identifier for this font. - pub identifier: FontIdentifier, - /// The bytes of this font, lazily loaded if this is a local font. - pub font_data: RwLock>>>, -} - -impl fmt::Debug for FontTemplateData { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("FontTemplateData") - .field( - "font_data", - &self - .font_data - .read() - .unwrap() - .as_ref() - .map(|bytes| format!("[{} bytes]", bytes.len())), - ) - .field("identifier", &self.identifier) - .finish() - } -} - -impl FontTemplateData { - pub fn new( - identifier: FontIdentifier, - font_data: Option>, - ) -> Result { - Ok(FontTemplateData { - identifier, - font_data: RwLock::new(font_data.map(Arc::new)), - }) - } - - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - pub fn bytes(&self) -> Arc> { - self.font_data - .write() - .unwrap() - .get_or_insert_with(|| { - let font_descriptor = match &self.identifier { - FontIdentifier::Local(local_identifier) => { - local_identifier.font_descriptor.clone() - }, - FontIdentifier::Web(_) => unreachable!("Created a web font without data."), - }; - - let font = FontCollection::system() - .get_font_from_descriptor(&font_descriptor) - .unwrap(); - let face = font.create_font_face(); - let files = face.get_files(); - assert!(!files.is_empty()); - Arc::new(files[0].get_font_file_bytes()) - }) - .clone() - } - - /// Returns a reference to the bytes in this font if they are in memory. - /// This function never performs disk I/O. - pub fn bytes_if_in_memory(&self) -> Option>> { - self.font_data.read().unwrap().as_ref().cloned() - } - - /// Get a [`Font`] for this font if it is a local font or return `None` if it's a - /// web font. - pub fn get_font(&self) -> Option { - let font_descriptor = match &self.identifier { - FontIdentifier::Local(local_identifier) => local_identifier.font_descriptor.clone(), - FontIdentifier::Web(_) => return None, - }; - - FontCollection::system().get_font_from_descriptor(&font_descriptor) - } - - pub fn native_font(&self) -> Option { - let face = self.get_font()?.create_font_face(); - let path = face.get_files().first()?.get_font_file_path()?; - Some(NativeFontHandle { - path, - index: face.get_index(), - }) - } -} diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index 24965e272f1..9d26f7603dd 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -11,7 +11,8 @@ use std::rc::Rc; use app_units::Au; use gfx::font::{ - fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope, + fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, + FontHandleMethods, FontSearchScope, }; use gfx::font_cache_thread::{FontIdentifier, FontTemplateInfo, FontTemplates}; use gfx::font_context::{FontContext, FontSource}; @@ -175,7 +176,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -189,7 +190,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -203,7 +204,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'á') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-basic-regular") ); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); @@ -224,7 +225,7 @@ fn test_font_fallback() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-ascii"), "a family in the group should be used if there is a matching glyph" ); @@ -234,7 +235,7 @@ fn test_font_fallback() { .find_by_codepoint(&mut context, 'á') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-basic-regular"), "a fallback font should be used if there is no matching glyph in the group" ); diff --git a/components/gfx/tests/font_template.rs b/components/gfx/tests/font_template.rs index d35c3607497..5c8e9f8b37a 100644 --- a/components/gfx/tests/font_template.rs +++ b/components/gfx/tests/font_template.rs @@ -6,12 +6,14 @@ #[cfg(not(target_os = "macos"))] #[test] fn test_font_template_descriptor() { + use std::cell::RefCell; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; + use std::rc::Rc; use gfx::font_cache_thread::FontIdentifier; - use gfx::font_template::{FontTemplate, FontTemplateDescriptor}; + use gfx::font_template::{FontTemplate, FontTemplateDescriptor, FontTemplateRefMethods}; use servo_url::ServoUrl; use style::values::computed::font::{FontStretch, FontStyle, FontWeight}; @@ -28,11 +30,12 @@ fn test_font_template_descriptor() { path.push(format!("{}.ttf", filename)); let file = File::open(path.clone()).unwrap(); - let mut template = FontTemplate::new( + let template = FontTemplate::new( FontIdentifier::Web(ServoUrl::from_file_path(path).unwrap()), Some(file.bytes().map(|b| b.unwrap()).collect()), ) .unwrap(); + let template = Rc::new(RefCell::new(template)); template.descriptor().unwrap() } diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index a411c7125db..56f66d6cd1f 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -17,7 +17,7 @@ use webrender_api::FontInstanceKey; use xi_unicode::LineBreakLeafIter; use crate::font::{Font, FontHandleMethods, FontMetrics, RunMetrics, ShapingFlags, ShapingOptions}; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::FontTemplateRefMethods; use crate::text::glyph::{ByteIndex, GlyphStore}; thread_local! { @@ -30,7 +30,8 @@ thread_local! { pub struct TextRun { /// The UTF-8 string represented by this text run. pub text: Arc, - pub font_template: Arc, + /// This ensures the data stays alive as long as this TextRun is using this font. + font_data: Arc>, pub pt_size: Au, pub font_metrics: FontMetrics, pub font_key: FontInstanceKey, @@ -193,7 +194,7 @@ impl<'a> TextRun { TextRun { text: Arc::new(text), font_metrics: font.metrics.clone(), - font_template: font.handle.template(), + font_data: font.handle.template().data(), font_key: font.font_key, pt_size: font.descriptor.pt_size, glyphs: Arc::new(glyphs),