mirror of
https://github.com/servo/servo.git
synced 2025-08-23 22:35:33 +01:00
gfx: Remove FontTemplateData
(#32034)
Now that `FontTemplateData` is more or less the same on all platforms, it can be removed. This is a preparatory change for a full refactor of the font system on Servo. The major changes here are: - Remove `FontTemplateData` and move its members into `FontTemplate` - Make `FontTemplate` have full interior mutability instead of only the `FontTemplateData` member. This is preparation for having these data types `Send` and `Sync` with locking. - Remove the strong/weak reference concept for font data. In practice, all font data references were strong, so this was never fully complete. Instead of using this approach, the new font system will use a central font data cache with references associated to layouts. - The `CTFont` cache is now a global cache, so `CTFont`s can be shared between threads. The cache is cleared when clearing font caches. A benefit of this change (apart from `CTFont` sharing) is that font data loading is platform-independent now.
This commit is contained in:
parent
689c144714
commit
6b2fa91357
19 changed files with 394 additions and 600 deletions
|
@ -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<NativeFontHandle> {
|
||||
Some(NativeFontHandle {
|
||||
path: PathBuf::from(&*self.path),
|
||||
index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
|
||||
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.
|
||||
|
|
|
@ -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<FontTemplateData>,
|
||||
// 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<Au>) -> Result<FT_Face, &'static str> {
|
||||
fn create_face(template: &FontTemplateRef, pt_size: Option<Au>) -> Result<FT_Face, &'static str> {
|
||||
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<Au>) -> Result<FT_Fa
|
|||
|
||||
impl FontHandleMethods for FontHandle {
|
||||
fn new_from_template(
|
||||
template: Arc<FontTemplateData>,
|
||||
template: FontTemplateRef,
|
||||
pt_size: Option<Au>,
|
||||
) -> Result<FontHandle, &'static str> {
|
||||
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<FontTemplateData> {
|
||||
self.font_data.clone()
|
||||
fn template(&self) -> FontTemplateRef {
|
||||
self.font_template.clone()
|
||||
}
|
||||
|
||||
fn family_name(&self) -> Option<String> {
|
||||
|
@ -358,10 +360,6 @@ impl FontHandleMethods for FontHandle {
|
|||
Some(FontTable { buffer: buf })
|
||||
}
|
||||
}
|
||||
|
||||
fn identifier(&self) -> &FontIdentifier {
|
||||
&self.font_data.identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FontHandle {
|
||||
|
|
|
@ -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<NativeFontHandle> {
|
||||
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<u8> {
|
||||
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<F>(mut callback: F)
|
||||
where
|
||||
F: FnMut(String),
|
||||
|
|
|
@ -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<Option<Arc<Vec<u8>>>>,
|
||||
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<Vec<u8>>,
|
||||
) -> Result<FontTemplateData, Error> {
|
||||
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<Vec<u8>> {
|
||||
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<Arc<Vec<u8>>> {
|
||||
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<NativeFontHandle> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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<FontTemplateData>,
|
||||
font_template: FontTemplateRef,
|
||||
ctfont: CTFont,
|
||||
h_kern_subtable: Option<CachedKernTable>,
|
||||
can_do_fast_shaping: bool,
|
||||
|
@ -157,34 +156,33 @@ impl fmt::Debug for CachedKernTable {
|
|||
|
||||
impl FontHandleMethods for FontHandle {
|
||||
fn new_from_template(
|
||||
template: Arc<FontTemplateData>,
|
||||
font_template: FontTemplateRef,
|
||||
pt_size: Option<Au>,
|
||||
) -> Result<FontHandle, &'static str> {
|
||||
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<FontTemplateData> {
|
||||
self.font_data.clone()
|
||||
fn template(&self) -> FontTemplateRef {
|
||||
self.font_template.clone()
|
||||
}
|
||||
|
||||
fn family_name(&self) -> Option<String> {
|
||||
|
@ -317,8 +315,4 @@ impl FontHandleMethods for FontHandle {
|
|||
let result: Option<CFData> = self.ctfont.get_font_table(tag);
|
||||
result.map(FontTable::wrap)
|
||||
}
|
||||
|
||||
fn identifier(&self) -> &FontIdentifier {
|
||||
&self.font_data.identifier
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NativeFontHandle> {
|
||||
Some(NativeFontHandle {
|
||||
name: self.postscript_name.to_string(),
|
||||
path: self.path.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
|
||||
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<F>(mut callback: F)
|
||||
where
|
||||
F: FnMut(String),
|
||||
|
|
|
@ -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<Option<Arc<Vec<u8>>>>,
|
||||
// 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<RwLock<HashMap<FontIdentifier, CachedCTFont>>> = OnceLock::new();
|
||||
|
||||
/// A [`HashMap`] of cached [`CTFont`] for a single [`FontIdentifier`]. There is one [`CTFont`]
|
||||
/// for each cached font size.
|
||||
type CachedCTFont = HashMap<Au, CTFont>;
|
||||
|
||||
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<CTFont>;
|
||||
}
|
||||
|
||||
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<Vec<u8>>,
|
||||
) -> Result<FontTemplateData, IoError> {
|
||||
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<CTFont> {
|
||||
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<CTFont> {
|
||||
//// 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<Vec<u8>> {
|
||||
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<Arc<Vec<u8>>> {
|
||||
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<NativeFontHandle> {
|
||||
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<HashMap<Au, CTFont>>);
|
||||
|
||||
impl Deref for CachedCTFont {
|
||||
type Target = Mutex<HashMap<Au, CTFont>>;
|
||||
fn deref(&self) -> &Mutex<HashMap<Au, CTFont>> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for CachedCTFont {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CachedCTFont {
|
||||
fn deserialize<D>(deserializer: D) -> Result<CachedCTFont, D::Error>
|
||||
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<E>(self) -> Result<CachedCTFont, E>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<FontTemplateData>,
|
||||
font_template: FontTemplateRef,
|
||||
face: Nondebug<FontFace>,
|
||||
info: FontInfo,
|
||||
em_size: f32,
|
||||
|
@ -227,13 +226,18 @@ impl FontHandle {}
|
|||
|
||||
impl FontHandleMethods for FontHandle {
|
||||
fn new_from_template(
|
||||
template: Arc<FontTemplateData>,
|
||||
font_template: FontTemplateRef,
|
||||
pt_size: Option<Au>,
|
||||
) -> Result<Self, &'static str> {
|
||||
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<FontTemplateData> {
|
||||
self.font_data.clone()
|
||||
fn template(&self) -> FontTemplateRef {
|
||||
self.font_template.clone()
|
||||
}
|
||||
|
||||
fn family_name(&self) -> Option<String> {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<FontDescriptor>,
|
||||
}
|
||||
|
||||
impl LocalFontIdentifier {
|
||||
/// Create a [`Font`] for this font.
|
||||
pub fn direct_write_font(&self) -> Option<Font> {
|
||||
FontCollection::system().get_font_from_descriptor(&self.font_descriptor)
|
||||
}
|
||||
|
||||
pub fn native_font_handle(&self) -> Option<NativeFontHandle> {
|
||||
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<u8> {
|
||||
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 {
|
||||
|
|
|
@ -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<Option<Arc<Vec<u8>>>>,
|
||||
}
|
||||
|
||||
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<Vec<u8>>,
|
||||
) -> Result<FontTemplateData, io::Error> {
|
||||
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<Vec<u8>> {
|
||||
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<Arc<Vec<u8>>> {
|
||||
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<Font> {
|
||||
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<NativeFontHandle> {
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue