mirror of
https://github.com/servo/servo.git
synced 2025-08-28 08:38:20 +01:00
fonts: Use fontations
to read the OS/2 table of DirectWrite fonts (#38851)
Instead of copying the font table data in memory and parsing it with the `truetype` crate, use a non-copying API from DirectWrite to implement a `fontations` `TableProvider`. This has two benefits: - We remove the dependency on the `truetype` crate finally. - We do not have to make an in-memory copy of the table data when parsing the table. The hope is that the `TableProvider` will be more generally useful in the future. Testing: There are no automated tests for Windows, but I manually verified that the data retrived via `fontations` matched that retrived by `truetype`. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
176e42d36d
commit
777373054f
5 changed files with 107 additions and 116 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -2708,11 +2708,11 @@ dependencies = [
|
||||||
"stylo",
|
"stylo",
|
||||||
"stylo_atoms",
|
"stylo_atoms",
|
||||||
"tracing",
|
"tracing",
|
||||||
"truetype",
|
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
"unicode-script",
|
"unicode-script",
|
||||||
"url",
|
"url",
|
||||||
"webrender_api",
|
"webrender_api",
|
||||||
|
"winapi",
|
||||||
"xml-rs",
|
"xml-rs",
|
||||||
"yeslogic-fontconfig-sys",
|
"yeslogic-fontconfig-sys",
|
||||||
]
|
]
|
||||||
|
@ -8988,15 +8988,6 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8"
|
checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "truetype"
|
|
||||||
version = "0.47.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05ce9543b570c6e8a392274b67e1001816bce953aa89724e52a4639db02a10e0"
|
|
||||||
dependencies = [
|
|
||||||
"typeface",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
@ -9037,12 +9028,6 @@ version = "2.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typeface"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "191ea6762cbcd705fbed8f58573fd6c654453ff3da60adb293d9f255bc92af8e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeid"
|
name = "typeid"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
|
@ -74,7 +74,7 @@ xml-rs = "0.8"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
dwrote = "0.11.4"
|
dwrote = "0.11.4"
|
||||||
truetype = { version = "0.47.3", features = ["ignore-invalid-language-ids"] }
|
winapi = { workspace = true }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(ohos_mock)'] }
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(ohos_mock)'] }
|
||||||
|
|
|
@ -18,6 +18,7 @@ use euclid::num::Zero;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use read_fonts::tables::os2::{Os2, SelectionFlags};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use style::computed_values::font_variant_caps;
|
use style::computed_values::font_variant_caps;
|
||||||
|
@ -119,6 +120,29 @@ pub trait PlatformFontMethods: Sized {
|
||||||
|
|
||||||
/// Return all the variation values that the font was instantiated with.
|
/// Return all the variation values that the font was instantiated with.
|
||||||
fn variations(&self) -> &[FontVariation];
|
fn variations(&self) -> &[FontVariation];
|
||||||
|
|
||||||
|
fn descriptor_from_os2_table(os2: &Os2) -> FontTemplateDescriptor {
|
||||||
|
let mut style = FontStyle::NORMAL;
|
||||||
|
if os2.fs_selection().contains(SelectionFlags::ITALIC) {
|
||||||
|
style = FontStyle::ITALIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
let weight = FontWeight::from_float(os2.us_weight_class() as f32);
|
||||||
|
let stretch = match os2.us_width_class() {
|
||||||
|
1 => FontStretch::ULTRA_CONDENSED,
|
||||||
|
2 => FontStretch::EXTRA_CONDENSED,
|
||||||
|
3 => FontStretch::CONDENSED,
|
||||||
|
4 => FontStretch::SEMI_CONDENSED,
|
||||||
|
5 => FontStretch::NORMAL,
|
||||||
|
6 => FontStretch::SEMI_EXPANDED,
|
||||||
|
7 => FontStretch::EXPANDED,
|
||||||
|
8 => FontStretch::EXTRA_EXPANDED,
|
||||||
|
9 => FontStretch::ULTRA_EXPANDED,
|
||||||
|
_ => FontStretch::NORMAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
FontTemplateDescriptor::new(weight, stretch, style)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to abstract over the shaper's choice of fixed int representation.
|
// Used to abstract over the shaper's choice of fixed int representation.
|
||||||
|
|
|
@ -15,14 +15,10 @@ use freetype_sys::{
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use parking_lot::ReentrantMutex;
|
use parking_lot::ReentrantMutex;
|
||||||
use read_fonts::tables::os2::SelectionFlags;
|
|
||||||
use read_fonts::types::Tag;
|
use read_fonts::types::Tag;
|
||||||
use read_fonts::{FontRef, ReadError, TableProvider};
|
use read_fonts::{FontRef, ReadError, TableProvider};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style::computed_values::font_stretch::T as FontStretch;
|
|
||||||
use style::computed_values::font_weight::T as FontWeight;
|
|
||||||
use style::values::computed::font::FontStyle;
|
|
||||||
use webrender_api::{FontInstanceFlags, FontVariation};
|
use webrender_api::{FontInstanceFlags, FontVariation};
|
||||||
|
|
||||||
use super::LocalFontIdentifier;
|
use super::LocalFontIdentifier;
|
||||||
|
@ -135,31 +131,10 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
let Ok(font_ref) = self.table_provider_data.font_ref() else {
|
let Ok(font_ref) = self.table_provider_data.font_ref() else {
|
||||||
return FontTemplateDescriptor::default();
|
return FontTemplateDescriptor::default();
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(os2) = font_ref.os2() else {
|
let Ok(os2) = font_ref.os2() else {
|
||||||
return FontTemplateDescriptor::default();
|
return FontTemplateDescriptor::default();
|
||||||
};
|
};
|
||||||
|
Self::descriptor_from_os2_table(&os2)
|
||||||
let mut style = FontStyle::NORMAL;
|
|
||||||
if os2.fs_selection().contains(SelectionFlags::ITALIC) {
|
|
||||||
style = FontStyle::ITALIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
let weight = FontWeight::from_float(os2.us_weight_class() as f32);
|
|
||||||
let stretch = match os2.us_width_class() {
|
|
||||||
1 => FontStretch::ULTRA_CONDENSED,
|
|
||||||
2 => FontStretch::EXTRA_CONDENSED,
|
|
||||||
3 => FontStretch::CONDENSED,
|
|
||||||
4 => FontStretch::SEMI_CONDENSED,
|
|
||||||
5 => FontStretch::NORMAL,
|
|
||||||
6 => FontStretch::SEMI_EXPANDED,
|
|
||||||
7 => FontStretch::EXPANDED,
|
|
||||||
8 => FontStretch::EXTRA_EXPANDED,
|
|
||||||
9 => FontStretch::ULTRA_EXPANDED,
|
|
||||||
_ => FontStretch::NORMAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
FontTemplateDescriptor::new(weight, stretch, style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
// information for an approach that we'll likely need to take when the
|
// information for an approach that we'll likely need to take when the
|
||||||
// renderer moves to a sandboxed process.
|
// renderer moves to a sandboxed process.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -16,19 +17,17 @@ use dwrote::{
|
||||||
DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile,
|
DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile,
|
||||||
};
|
};
|
||||||
use euclid::default::{Point2D, Rect, Size2D};
|
use euclid::default::{Point2D, Rect, Size2D};
|
||||||
use log::{debug, warn};
|
use log::debug;
|
||||||
|
use read_fonts::TableProvider;
|
||||||
|
use skrifa::Tag;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style::computed_values::font_stretch::T as StyleFontStretch;
|
|
||||||
use style::computed_values::font_weight::T as StyleFontWeight;
|
|
||||||
use style::values::computed::font::FontStyle as StyleFontStyle;
|
|
||||||
use truetype::tables::WindowsMetrics;
|
|
||||||
use truetype::value::Read;
|
|
||||||
use webrender_api::{FontInstanceFlags, FontVariation};
|
use webrender_api::{FontInstanceFlags, FontVariation};
|
||||||
|
use winapi::shared::minwindef::{BOOL, FALSE};
|
||||||
|
|
||||||
use super::font_list::LocalFontIdentifier;
|
use super::font_list::LocalFontIdentifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTableTag, FontTemplateDescriptor,
|
FontData, FontIdentifier, FontMetrics, FontTableMethods, FontTableTag, FontTemplateDescriptor,
|
||||||
FractionalPixel, GlyphId, PlatformFontMethods, ot_tag,
|
FractionalPixel, GlyphId, PlatformFontMethods,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
|
// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
|
||||||
|
@ -190,71 +189,11 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn descriptor(&self) -> FontTemplateDescriptor {
|
fn descriptor(&self) -> FontTemplateDescriptor {
|
||||||
// We need the font (DWriteFont) in order to be able to query things like
|
DirectWriteTableProvider::new(self)
|
||||||
// the family name, face name, weight, etc. On Windows 10, the
|
.os2()
|
||||||
// DWriteFontFace3 interface provides this on the FontFace, but that's only
|
.as_ref()
|
||||||
// available on Win10+.
|
.map(Self::descriptor_from_os2_table)
|
||||||
//
|
.unwrap_or_default()
|
||||||
// Instead, we do the parsing work using the truetype crate for raw fonts.
|
|
||||||
// We're just extracting basic info, so this is sufficient for now.
|
|
||||||
//
|
|
||||||
// The `dwrote` APIs take SFNT table tags in a reversed byte order, which
|
|
||||||
// is why `u32::swap_bytes()` is called here.
|
|
||||||
let windows_metrics_bytes = self
|
|
||||||
.face
|
|
||||||
.get_font_table(u32::swap_bytes(ot_tag!('O', 'S', '/', '2')));
|
|
||||||
if windows_metrics_bytes.is_none() {
|
|
||||||
warn!("Could not find OS/2 table in font.");
|
|
||||||
return FontTemplateDescriptor::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cursor = Cursor::new(windows_metrics_bytes.as_ref().unwrap());
|
|
||||||
let Ok(table) = WindowsMetrics::read(&mut cursor) else {
|
|
||||||
warn!("Could not read OS/2 table in font.");
|
|
||||||
return FontTemplateDescriptor::default();
|
|
||||||
};
|
|
||||||
|
|
||||||
let (weight_val, width_val, italic_bool) = match table {
|
|
||||||
WindowsMetrics::Version0(ref m) => {
|
|
||||||
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
|
|
||||||
},
|
|
||||||
WindowsMetrics::Version1(ref m) => {
|
|
||||||
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
|
|
||||||
},
|
|
||||||
WindowsMetrics::Version2(ref m) |
|
|
||||||
WindowsMetrics::Version3(ref m) |
|
|
||||||
WindowsMetrics::Version4(ref m) => {
|
|
||||||
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
|
|
||||||
},
|
|
||||||
WindowsMetrics::Version5(ref m) => {
|
|
||||||
(m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let weight = StyleFontWeight::from_float(weight_val as f32);
|
|
||||||
let stretch = match width_val.clamp(1, 9) {
|
|
||||||
1 => StyleFontStretch::ULTRA_CONDENSED,
|
|
||||||
2 => StyleFontStretch::EXTRA_CONDENSED,
|
|
||||||
3 => StyleFontStretch::CONDENSED,
|
|
||||||
4 => StyleFontStretch::SEMI_CONDENSED,
|
|
||||||
5 => StyleFontStretch::NORMAL,
|
|
||||||
6 => StyleFontStretch::SEMI_EXPANDED,
|
|
||||||
7 => StyleFontStretch::EXPANDED,
|
|
||||||
8 => StyleFontStretch::EXTRA_EXPANDED,
|
|
||||||
9 => StyleFontStretch::ULTRA_CONDENSED,
|
|
||||||
_ => {
|
|
||||||
warn!("Unknown stretch size.");
|
|
||||||
StyleFontStretch::NORMAL
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let style = if italic_bool {
|
|
||||||
StyleFontStyle::ITALIC
|
|
||||||
} else {
|
|
||||||
StyleFontStyle::NORMAL
|
|
||||||
};
|
|
||||||
|
|
||||||
FontTemplateDescriptor::new(weight, stretch, style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||||
|
@ -374,3 +313,71 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
&self.variations
|
&self.variations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper struct around [`PlatformFont`] which is responsible for
|
||||||
|
/// implementing [`TableProvider`] and cleaning up any font table contexts from
|
||||||
|
/// DirectWrite when the struct is dropped.
|
||||||
|
struct DirectWriteTableProvider<'platform_font> {
|
||||||
|
platform_font: &'platform_font PlatformFont,
|
||||||
|
contexts: RefCell<Vec<*mut c_void>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'platform_font> DirectWriteTableProvider<'platform_font> {
|
||||||
|
fn new(platform_font: &'platform_font PlatformFont) -> Self {
|
||||||
|
Self {
|
||||||
|
platform_font,
|
||||||
|
contexts: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DirectWriteTableProvider<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let direct_write_face = unsafe { self.platform_font.face.as_ptr() };
|
||||||
|
assert!(!direct_write_face.is_null());
|
||||||
|
|
||||||
|
let direct_write_face = unsafe { &*direct_write_face };
|
||||||
|
for context in self.contexts.borrow_mut().drain(..) {
|
||||||
|
unsafe { direct_write_face.ReleaseFontTable(context) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'platform_font> TableProvider<'platform_font> for DirectWriteTableProvider<'platform_font> {
|
||||||
|
fn data_for_tag(&self, tag: Tag) -> Option<read_fonts::FontData<'platform_font>> {
|
||||||
|
let direct_write_face = unsafe { self.platform_font.face.as_ptr() };
|
||||||
|
if direct_write_face.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let direct_write_face = unsafe { &*direct_write_face };
|
||||||
|
let direct_write_tag = u32::from_be_bytes(tag.to_be_bytes()).swap_bytes();
|
||||||
|
let mut table_data_ptr: *const u8 = std::ptr::null_mut();
|
||||||
|
let mut table_size: u32 = 0;
|
||||||
|
let mut table_context: *mut c_void = std::ptr::null_mut();
|
||||||
|
let mut exists: BOOL = FALSE;
|
||||||
|
|
||||||
|
let hr = unsafe {
|
||||||
|
direct_write_face.TryGetFontTable(
|
||||||
|
direct_write_tag,
|
||||||
|
&mut table_data_ptr as *mut *const _ as *mut *const c_void,
|
||||||
|
&mut table_size,
|
||||||
|
&mut table_context,
|
||||||
|
&mut exists,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if hr != 0 || exists == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.contexts.borrow_mut().push(table_context);
|
||||||
|
|
||||||
|
if table_data_ptr.is_null() || table_size == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = unsafe { std::slice::from_raw_parts(table_data_ptr, table_size as usize) };
|
||||||
|
Some(read_fonts::FontData::new(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue