mirror of
https://github.com/servo/servo.git
synced 2025-09-29 16:19:14 +01:00
Fix loading raw data from .ttc
files on macos (#38753)
# Objective Ensure that functionality which uses the raw font data (such as rendering text to canvas) works correctly on macOS when the specified font is a system font that lives in an OpenType Collection (`.ttc`) file. ## Changes made - The `read_data_from_file` in each backend now returns a `index: u32` in addition to `data: Vec<u8>` - The `data` field on the `Font` type has been renamed to `raw` and the `data` method on the `Font` type has been renamed to `raw_font`. This allows the index to be cached as computing is moderately expensive on macOS (on the order of 100 microseconds). - Both of the above now store/return a `struct RawFont` instead of a `FontData` where `RawFont` is defined as `struct RawFont { data: FontData, index: u32 }`. - The users of the `data` method have been updated to use the cached index from `data` rather than calling `.index()` each time. --------- Signed-off-by: Nico Burns <nico@nicoburns.com>
This commit is contained in:
parent
3225d19907
commit
39629560c8
13 changed files with 164 additions and 76 deletions
|
@ -410,7 +410,7 @@ enum FreeTypeFaceTableProviderData {
|
|||
impl FreeTypeFaceTableProviderData {
|
||||
fn font_ref(&self) -> Result<FontRef<'_>, ReadError> {
|
||||
match self {
|
||||
Self::Web(ipc_shared_memory) => FontRef::new(&ipc_shared_memory.0),
|
||||
Self::Web(ipc_shared_memory) => FontRef::new(ipc_shared_memory.as_ref()),
|
||||
Self::Local(mmap, index) => FontRef::from_index(mmap, *index),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ use serde::{Deserialize, Serialize};
|
|||
use style::Atom;
|
||||
use webrender_api::NativeFontHandle;
|
||||
|
||||
use crate::{FontData, FontDataAndIndex};
|
||||
|
||||
pub mod font;
|
||||
mod freetype_face;
|
||||
|
||||
|
@ -57,9 +59,14 @@ impl LocalFontIdentifier {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
|
||||
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
|
||||
let file = File::open(Path::new(&*self.path)).ok()?;
|
||||
let mmap = unsafe { Mmap::map(&file).ok()? };
|
||||
Some(mmap[..].to_vec())
|
||||
let data = FontData::from_bytes(&mmap);
|
||||
|
||||
Some(FontDataAndIndex {
|
||||
data,
|
||||
index: self.variation_index as u32,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@ use std::fs::File;
|
|||
use std::path::Path;
|
||||
|
||||
use base::text::{UnicodeBlock, UnicodeBlockMethod, unicode_plane};
|
||||
use log::debug;
|
||||
use log::{debug, warn};
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use memmap2::Mmap;
|
||||
use read_fonts::types::NameId;
|
||||
use read_fonts::{FileRef, TableProvider as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use style::Atom;
|
||||
use style::values::computed::font::GenericFontFamily;
|
||||
|
@ -18,8 +20,8 @@ use webrender_api::NativeFontHandle;
|
|||
use crate::platform::add_noto_fallback_families;
|
||||
use crate::platform::font::CoreTextFontTraitsMapping;
|
||||
use crate::{
|
||||
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
|
||||
FontTemplateDescriptor, LowercaseFontFamilyName,
|
||||
EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex,
|
||||
FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
|
||||
};
|
||||
|
||||
/// An identifier for a local font on a MacOS system. These values comes from the CoreText
|
||||
|
@ -43,16 +45,61 @@ impl LocalFontIdentifier {
|
|||
0
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
|
||||
// TODO: This is incorrect, if the font file is a TTC (collection) with more than
|
||||
// one font. In that case we either need to reconstruct the pertinent tables into
|
||||
// a bundle of font data (expensive) or make sure that the value returned by
|
||||
// `index()` above is correct. The latter is potentially tricky as macOS might not
|
||||
// do an accurate mapping between the PostScript name that it gives us and what is
|
||||
// listed in the font.
|
||||
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
|
||||
let file = File::open(Path::new(&*self.path)).ok()?;
|
||||
let mmap = unsafe { Mmap::map(&file).ok()? };
|
||||
Some(mmap[..].to_vec())
|
||||
|
||||
// Determine index
|
||||
let file_ref = FileRef::new(mmap.as_ref()).ok()?;
|
||||
let index = ttc_index_from_postscript_name(file_ref, &self.postscript_name);
|
||||
|
||||
Some(FontDataAndIndex {
|
||||
data: FontData::from_bytes(&mmap),
|
||||
index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// CoreText font enumeration gives us a Postscript name rather than an index.
|
||||
/// This functions maps from a Postscript name to an index.
|
||||
///
|
||||
/// This mapping works for single-font files and for simple TTC files, but may not work in all cases.
|
||||
/// We are not 100% sure which cases (if any) will not work. But we suspect that variable fonts may cause
|
||||
/// issues due to the Postscript names corresponding to instances not being straightforward, and the possibility
|
||||
/// that CoreText may return a non-standard in that scenerio.
|
||||
fn ttc_index_from_postscript_name(font_file: FileRef<'_>, postscript_name: &str) -> u32 {
|
||||
match font_file {
|
||||
// File only contains one font: simply return 0
|
||||
FileRef::Font(_) => 0,
|
||||
// File is a collection: iterate through each font in the collection and check
|
||||
// whether the name matches
|
||||
FileRef::Collection(collection) => {
|
||||
for i in 0..collection.len() {
|
||||
let font = collection.get(i).unwrap();
|
||||
let name_table = font.name().unwrap();
|
||||
if name_table
|
||||
.name_record()
|
||||
.iter()
|
||||
.filter(|record| record.name_id() == NameId::POSTSCRIPT_NAME)
|
||||
.any(|record| {
|
||||
record
|
||||
.string(name_table.string_data())
|
||||
.unwrap()
|
||||
.chars()
|
||||
.eq(postscript_name.chars())
|
||||
})
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// If we fail to find a font, just use the first font in the file.
|
||||
warn!(
|
||||
"Font with postscript_name {} not found in collection",
|
||||
postscript_name
|
||||
);
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ use style::values::specified::font::FontStretchKeyword;
|
|||
use webrender_api::NativeFontHandle;
|
||||
|
||||
use crate::{
|
||||
EmojiPresentationPreference, FallbackFontSelectionOptions, FontIdentifier, FontTemplate,
|
||||
FontTemplateDescriptor, LowercaseFontFamilyName,
|
||||
EmojiPresentationPreference, FallbackFontSelectionOptions, FontData, FontDataAndIndex,
|
||||
FontIdentifier, FontTemplate, FontTemplateDescriptor, LowercaseFontFamilyName,
|
||||
};
|
||||
|
||||
pub fn for_each_available_family<F>(mut callback: F)
|
||||
|
@ -67,14 +67,19 @@ impl LocalFontIdentifier {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_from_file(&self) -> Option<Vec<u8>> {
|
||||
pub(crate) fn font_data_and_index(&self) -> Option<FontDataAndIndex> {
|
||||
let font = FontCollection::system()
|
||||
.font_from_descriptor(&self.font_descriptor)
|
||||
.ok()??;
|
||||
let face = font.create_font_face();
|
||||
let index = face.get_index();
|
||||
let files = face.get_files();
|
||||
assert!(!files.is_empty());
|
||||
Some(files[0].get_font_file_bytes())
|
||||
|
||||
let data = files[0].get_font_file_bytes();
|
||||
let data = FontData::from_bytes(&data);
|
||||
|
||||
Some(FontDataAndIndex { data, index })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue