mirror of
https://github.com/servo/servo.git
synced 2025-08-25 07:08:21 +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_atoms",
|
||||
"tracing",
|
||||
"truetype",
|
||||
"unicode-properties",
|
||||
"unicode-script",
|
||||
"url",
|
||||
"webrender_api",
|
||||
"winapi",
|
||||
"xml-rs",
|
||||
"yeslogic-fontconfig-sys",
|
||||
]
|
||||
|
@ -8988,15 +8988,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8"
|
||||
|
||||
[[package]]
|
||||
name = "truetype"
|
||||
version = "0.47.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ce9543b570c6e8a392274b67e1001816bce953aa89724e52a4639db02a10e0"
|
||||
dependencies = [
|
||||
"typeface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
|
@ -9037,12 +9028,6 @@ version = "2.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "typeface"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "191ea6762cbcd705fbed8f58573fd6c654453ff3da60adb293d9f255bc92af8e"
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.3"
|
||||
|
|
|
@ -74,7 +74,7 @@ xml-rs = "0.8"
|
|||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
dwrote = "0.11.4"
|
||||
truetype = { version = "0.47.3", features = ["ignore-invalid-language-ids"] }
|
||||
winapi = { workspace = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(ohos_mock)'] }
|
||||
|
|
|
@ -18,6 +18,7 @@ use euclid::num::Zero;
|
|||
use log::debug;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use parking_lot::RwLock;
|
||||
use read_fonts::tables::os2::{Os2, SelectionFlags};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::SmallVec;
|
||||
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.
|
||||
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.
|
||||
|
|
|
@ -15,14 +15,10 @@ use freetype_sys::{
|
|||
use log::debug;
|
||||
use memmap2::Mmap;
|
||||
use parking_lot::ReentrantMutex;
|
||||
use read_fonts::tables::os2::SelectionFlags;
|
||||
use read_fonts::types::Tag;
|
||||
use read_fonts::{FontRef, ReadError, TableProvider};
|
||||
use servo_arc::Arc;
|
||||
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 super::LocalFontIdentifier;
|
||||
|
@ -135,31 +131,10 @@ impl PlatformFontMethods for PlatformFont {
|
|||
let Ok(font_ref) = self.table_provider_data.font_ref() else {
|
||||
return FontTemplateDescriptor::default();
|
||||
};
|
||||
|
||||
let Ok(os2) = font_ref.os2() else {
|
||||
return FontTemplateDescriptor::default();
|
||||
};
|
||||
|
||||
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)
|
||||
Self::descriptor_from_os2_table(&os2)
|
||||
}
|
||||
|
||||
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
|
||||
// renderer moves to a sandboxed process.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::c_void;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -16,19 +17,17 @@ use dwrote::{
|
|||
DWRITE_FONT_AXIS_VALUE, DWRITE_FONT_SIMULATIONS_NONE, FontCollection, FontFace, FontFile,
|
||||
};
|
||||
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::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 winapi::shared::minwindef::{BOOL, FALSE};
|
||||
|
||||
use super::font_list::LocalFontIdentifier;
|
||||
use crate::{
|
||||
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
|
||||
|
@ -190,71 +189,11 @@ impl PlatformFontMethods for PlatformFont {
|
|||
}
|
||||
|
||||
fn descriptor(&self) -> FontTemplateDescriptor {
|
||||
// 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
|
||||
// available on Win10+.
|
||||
//
|
||||
// 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)
|
||||
DirectWriteTableProvider::new(self)
|
||||
.os2()
|
||||
.as_ref()
|
||||
.map(Self::descriptor_from_os2_table)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||
|
@ -374,3 +313,71 @@ impl PlatformFontMethods for PlatformFont {
|
|||
&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