mirror of
https://github.com/servo/servo.git
synced 2025-08-11 00:15:32 +01:00
fonts: Rework platform font initialization (#32127)
This change reworks the way that platform fonts are created and descriptor data is on `FontTemplate` is initialized. The main change here is that platform fonts for local font faces are always initialized using the font data loaded into memory from disk. This means that there is now only a single path for creating platform fonts. In addition, the font list is now responsible for getting the `FontTemplateDescriptor` for local `FontTemplate`s. Before the font had to be loaded into memory to get the weight, style, and width used for the descriptor. This is what fonts lists are for though, so for every platform we have that information before needing to load the font. In the future, hopefully this will allow discarding fonts before needing to load them into memory. Web fonts still get the descriptor from the platform handle, but hopefully that can be done with skrifa in the future. Thsese two fixes together allow properly loading indexed font variations on Linux machines. Before only the first variation could be instantiated. Fixes https://github.com/servo/servo/issues/13317. Fixes https://github.com/servo/servo/issues/24554. Co-authored-by: Martin Robinson <mrobinson@igalia.com> ---- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #13317 and #24554 - [x] There are tests for these changes --------- Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
25b182c372
commit
821893b2ee
21 changed files with 650 additions and 429 deletions
|
@ -11,7 +11,7 @@ use std::ops::Deref;
|
|||
use std::sync::Arc;
|
||||
|
||||
use app_units::Au;
|
||||
use dwrote::{Font, FontFace, FontFile, FontStretch, FontStyle};
|
||||
use dwrote::{FontFace, FontFile};
|
||||
use log::debug;
|
||||
use style::computed_values::font_stretch::T as StyleFontStretch;
|
||||
use style::computed_values::font_weight::T as StyleFontWeight;
|
||||
|
@ -22,7 +22,6 @@ use crate::font::{
|
|||
FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, PlatformFontMethods,
|
||||
};
|
||||
use crate::font_cache_thread::FontIdentifier;
|
||||
use crate::font_template::{FontTemplateRef, FontTemplateRefMethods};
|
||||
use crate::text::glyph::GlyphId;
|
||||
|
||||
// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
|
||||
|
@ -57,11 +56,6 @@ impl FontTableMethods for FontTable {
|
|||
}
|
||||
}
|
||||
|
||||
fn make_tag(tag_bytes: &[u8]) -> FontTableTag {
|
||||
assert_eq!(tag_bytes.len(), 4);
|
||||
unsafe { *(tag_bytes.as_ptr() as *const FontTableTag) }
|
||||
}
|
||||
|
||||
// We need the font (DWriteFont) in order to be able to query things like
|
||||
// the family name, face name, weight, etc. On Windows 10, the
|
||||
// DWriteFontFace3 interface provides this on the FontFace, but that's only
|
||||
|
@ -79,6 +73,16 @@ struct FontInfo {
|
|||
style: StyleFontStyle,
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Packs the components of a font tag name into 32 bytes, while respecting the
|
||||
/// necessary Rust 4-byte alignment for pointers. This is similar to
|
||||
/// [`crate::ot_tag`], but the bytes are reversed.
|
||||
macro_rules! font_tag {
|
||||
($t1:expr, $t2:expr, $t3:expr, $t4:expr) => {
|
||||
(($t4 as u32) << 24) | (($t3 as u32) << 16) | (($t2 as u32) << 8) | ($t1 as u32)
|
||||
};
|
||||
}
|
||||
|
||||
impl FontInfo {
|
||||
fn new_from_face(face: &FontFace) -> Result<FontInfo, &'static str> {
|
||||
use std::cmp::{max, min};
|
||||
|
@ -89,8 +93,10 @@ impl FontInfo {
|
|||
use truetype::tables::WindowsMetrics;
|
||||
use truetype::value::Read;
|
||||
|
||||
let names_bytes = face.get_font_table(make_tag(b"name"));
|
||||
let windows_metrics_bytes = face.get_font_table(make_tag(b"OS/2"));
|
||||
let name_tag = font_tag!('n', 'a', 'm', 'e');
|
||||
let os2_tag = font_tag!('O', 'S', '/', '2');
|
||||
let names_bytes = face.get_font_table(name_tag);
|
||||
let windows_metrics_bytes = face.get_font_table(os2_tag);
|
||||
if names_bytes.is_none() || windows_metrics_bytes.is_none() {
|
||||
return Err("No 'name' or 'OS/2' tables");
|
||||
}
|
||||
|
@ -166,36 +172,6 @@ impl FontInfo {
|
|||
style,
|
||||
})
|
||||
}
|
||||
|
||||
fn new_from_font(font: &Font) -> Result<FontInfo, &'static str> {
|
||||
let style = match font.style() {
|
||||
FontStyle::Normal => StyleFontStyle::NORMAL,
|
||||
FontStyle::Oblique => StyleFontStyle::OBLIQUE,
|
||||
FontStyle::Italic => StyleFontStyle::ITALIC,
|
||||
};
|
||||
let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32);
|
||||
let stretch = match font.stretch() {
|
||||
FontStretch::Undefined => FontStretchKeyword::Normal,
|
||||
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
|
||||
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
|
||||
FontStretch::Condensed => FontStretchKeyword::Condensed,
|
||||
FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
|
||||
FontStretch::Normal => FontStretchKeyword::Normal,
|
||||
FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
|
||||
FontStretch::Expanded => FontStretchKeyword::Expanded,
|
||||
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
|
||||
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
|
||||
}
|
||||
.compute();
|
||||
|
||||
Ok(FontInfo {
|
||||
family_name: font.family_name(),
|
||||
face_name: font.face_name(),
|
||||
style,
|
||||
weight,
|
||||
stretch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -203,7 +179,7 @@ pub struct PlatformFont {
|
|||
face: Nondebug<FontFace>,
|
||||
/// A reference to this data used to create this [`PlatformFont`], ensuring the
|
||||
/// data stays alive of the lifetime of this struct.
|
||||
data: Option<Arc<Vec<u8>>>,
|
||||
_data: Arc<Vec<u8>>,
|
||||
info: FontInfo,
|
||||
em_size: f32,
|
||||
du_to_px: f32,
|
||||
|
@ -226,32 +202,17 @@ impl<T> Deref for Nondebug<T> {
|
|||
}
|
||||
|
||||
impl PlatformFontMethods for PlatformFont {
|
||||
fn new_from_template(
|
||||
font_template: FontTemplateRef,
|
||||
fn new_from_data(
|
||||
_font_identifier: FontIdentifier,
|
||||
data: Arc<Vec<u8>>,
|
||||
face_index: u32,
|
||||
pt_size: Option<Au>,
|
||||
) -> Result<Self, &'static str> {
|
||||
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, data) = match direct_write_font {
|
||||
Some(font) => (
|
||||
font.create_font_face(),
|
||||
FontInfo::new_from_font(&font)?,
|
||||
None,
|
||||
),
|
||||
None => {
|
||||
let bytes = font_template.data();
|
||||
let font_file =
|
||||
FontFile::new_from_data(bytes).ok_or("Could not create FontFile")?;
|
||||
let face = font_file
|
||||
.create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE)
|
||||
.map_err(|_| "Could not create FontFace")?;
|
||||
let info = FontInfo::new_from_face(&face)?;
|
||||
(face, info, font_template.borrow().data_if_in_memory())
|
||||
},
|
||||
};
|
||||
let font_file = FontFile::new_from_data(data.clone()).ok_or("Could not create FontFile")?;
|
||||
let face = font_file
|
||||
.create_face(face_index, dwrote::DWRITE_FONT_SIMULATIONS_NONE)
|
||||
.map_err(|_| "Could not create FontFace")?;
|
||||
let info = FontInfo::new_from_face(&face)?;
|
||||
|
||||
let pt_size = pt_size.unwrap_or(au_from_pt(12.));
|
||||
let du_per_em = face.metrics().metrics0().designUnitsPerEm as f32;
|
||||
|
@ -264,7 +225,7 @@ impl PlatformFontMethods for PlatformFont {
|
|||
|
||||
Ok(PlatformFont {
|
||||
face: Nondebug(face),
|
||||
data,
|
||||
_data: data,
|
||||
info,
|
||||
em_size,
|
||||
du_to_px: design_units_to_pixels,
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dwrote::{Font, FontCollection, FontDescriptor};
|
||||
use dwrote::{Font, FontCollection, FontDescriptor, FontStretch, FontStyle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use style::values::computed::{FontStyle as StyleFontStyle, FontWeight as StyleFontWeight};
|
||||
use style::values::specified::font::FontStretchKeyword;
|
||||
use ucd::{Codepoint, UnicodeBlock};
|
||||
use webrender_api::NativeFontHandle;
|
||||
|
||||
use crate::font_template::{FontTemplate, FontTemplateDescriptor};
|
||||
use crate::text::util::unicode_plane;
|
||||
|
||||
pub static SANS_SERIF_FONT_FAMILY: &str = "Arial";
|
||||
|
@ -36,18 +38,10 @@ pub struct LocalFontIdentifier {
|
|||
}
|
||||
|
||||
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 fn index(&self) -> u32 {
|
||||
FontCollection::system()
|
||||
.get_font_from_descriptor(&self.font_descriptor)
|
||||
.map_or(0, |font| font.create_font_face().get_index())
|
||||
}
|
||||
|
||||
pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
|
||||
|
@ -72,27 +66,23 @@ impl Hash for LocalFontIdentifier {
|
|||
}
|
||||
}
|
||||
|
||||
// for_each_variation is supposed to return a string that can be
|
||||
// atomized and then uniquely used to return back to this font.
|
||||
// Some platforms use the full postscript name (MacOS X), or
|
||||
// a font filename.
|
||||
//
|
||||
// For windows we're going to use just a basic integer value that
|
||||
// we'll stringify, and then put them all in a HashMap with
|
||||
// the actual FontDescriptor there.
|
||||
|
||||
pub fn for_each_variation<F>(family_name: &str, mut callback: F)
|
||||
where
|
||||
F: FnMut(LocalFontIdentifier),
|
||||
F: FnMut(FontTemplate),
|
||||
{
|
||||
let system_fc = FontCollection::system();
|
||||
if let Some(family) = system_fc.get_font_family_by_name(family_name) {
|
||||
let count = family.get_font_count();
|
||||
for i in 0..count {
|
||||
let font = family.get_font(i);
|
||||
callback(LocalFontIdentifier {
|
||||
let template_descriptor = (&font).into();
|
||||
let local_font_identifier = LocalFontIdentifier {
|
||||
font_descriptor: Arc::new(font.to_descriptor()),
|
||||
});
|
||||
};
|
||||
callback(FontTemplate::new_local(
|
||||
local_font_identifier,
|
||||
template_descriptor,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,3 +347,28 @@ pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
|
|||
families.push("Arial Unicode MS");
|
||||
families
|
||||
}
|
||||
|
||||
impl From<&Font> for FontTemplateDescriptor {
|
||||
fn from(font: &Font) -> Self {
|
||||
let style = match font.style() {
|
||||
FontStyle::Normal => StyleFontStyle::NORMAL,
|
||||
FontStyle::Oblique => StyleFontStyle::OBLIQUE,
|
||||
FontStyle::Italic => StyleFontStyle::ITALIC,
|
||||
};
|
||||
let weight = StyleFontWeight::from_float(font.weight().to_u32() as f32);
|
||||
let stretch = match font.stretch() {
|
||||
FontStretch::Undefined => FontStretchKeyword::Normal,
|
||||
FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
|
||||
FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
|
||||
FontStretch::Condensed => FontStretchKeyword::Condensed,
|
||||
FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
|
||||
FontStretch::Normal => FontStretchKeyword::Normal,
|
||||
FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
|
||||
FontStretch::Expanded => FontStretchKeyword::Expanded,
|
||||
FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
|
||||
FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
|
||||
}
|
||||
.compute();
|
||||
FontTemplateDescriptor::new(weight, stretch, style)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue