From 4ae2610c24bfbd7acabf7ff327cf97d6df55c06f Mon Sep 17 00:00:00 2001 From: Cristian Brinza Date: Sat, 31 Aug 2024 02:16:26 +0300 Subject: [PATCH] fonts: Enable fast text shaping on Windows (#33123) * Use patched dwrote Signed-off-by: crbrz * Enable fast text shaping Signed-off-by: crbrz * Add fast text shape test Signed-off-by: crbrz * Update dwrote to 0.11.1 Signed-off-by: crbrz --------- Signed-off-by: crbrz --- Cargo.lock | 4 +- components/fonts/Cargo.toml | 4 +- components/fonts/font.rs | 96 ++++++++++++++++++++++- components/fonts/platform/windows/font.rs | 14 ++-- 4 files changed, 103 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39dc7b48f3f..9d1674359e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1516,9 +1516,9 @@ dependencies = [ [[package]] name = "dwrote" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +checksum = "2da3498378ed373237bdef1eddcc64e7be2d3ba4841f4c22a998e81cadeea83c" dependencies = [ "lazy_static", "libc", diff --git a/components/fonts/Cargo.toml b/components/fonts/Cargo.toml index 6aef355c1af..c7706973c6b 100644 --- a/components/fonts/Cargo.toml +++ b/components/fonts/Cargo.toml @@ -9,7 +9,7 @@ publish.workspace = true [lib] name = "fonts" path = "lib.rs" -test = false +test = true doctest = false [dependencies] @@ -75,5 +75,5 @@ harfbuzz-sys = { version = "0.6.1", features = ["bundled"] } [target.'cfg(target_os = "windows")'.dependencies] harfbuzz-sys = { version = "0.6", features = ["bundled"] } -dwrote = "0.11" +dwrote = "0.11.1" truetype = { version = "0.47.3", features = ["ignore-invalid-language-ids"] } diff --git a/components/fonts/font.rs b/components/fonts/font.rs index 14c55b928de..809d852ef26 100644 --- a/components/fonts/font.rs +++ b/components/fonts/font.rs @@ -325,7 +325,6 @@ struct ShapeCacheEntry { impl Font { pub fn shape_text(&self, text: &str, options: &ShapingOptions) -> Arc { - let this = self as *const Font; let lookup_key = ShapeCacheEntry { text: text.to_owned(), options: *options, @@ -353,9 +352,7 @@ impl Font { self.shape_text_fast(text, options, &mut glyphs); } else { debug!("shape_text: Using Harfbuzz."); - self.shaper - .get_or_init(|| Shaper::new(this)) - .shape_text(text, options, &mut glyphs); + self.shape_text_harfbuzz(text, options, &mut glyphs); } let shaped_text = Arc::new(glyphs); @@ -371,6 +368,13 @@ impl Font { shaped_text } + fn shape_text_harfbuzz(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) { + let this = self as *const Font; + self.shaper + .get_or_init(|| Shaper::new(this)) + .shape_text(text, options, glyphs); + } + fn can_do_fast_shaping(&self, text: &str, options: &ShapingOptions) -> bool { options.script == Script::Latin && !options.flags.contains(ShapingFlags::RTL_FLAG) && @@ -869,3 +873,87 @@ pub(crate) fn map_platform_values_to_style_values(mapping: &[(f64, f64)], value: mapping[mapping.len() - 1].1 } + +#[cfg(test)] +mod test { + #[cfg(target_os = "windows")] + #[test] + fn test_shape_text_fast() { + use std::fs::File; + use std::io::Read; + use std::path::PathBuf; + use std::sync::Arc; + + use app_units::Au; + use euclid::num::Zero; + use servo_url::ServoUrl; + use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; + use style::values::computed::{FontStretch, FontStyle, FontWeight}; + use unicode_script::Script; + + use crate::platform::font::PlatformFont; + use crate::{ + Font, FontDescriptor, FontIdentifier, FontTemplate, GlyphStore, PlatformFontMethods, + ShapingFlags, ShapingOptions, + }; + + let path: PathBuf = [ + env!("CARGO_MANIFEST_DIR"), + "tests", + "support", + "dejavu-fonts-ttf-2.37", + "ttf", + "DejaVuSans.ttf", + ] + .iter() + .collect(); + + let identifier = FontIdentifier::Web(ServoUrl::from_file_path(path.clone()).unwrap()); + let file = File::open(path).unwrap(); + let data: Arc> = Arc::new(file.bytes().map(|b| b.unwrap()).collect()); + let platform_font = + PlatformFont::new_from_data(identifier.clone(), data.clone(), 0, None).unwrap(); + + let template = FontTemplate { + identifier, + descriptor: platform_font.descriptor(), + data: Some(data), + stylesheet: None, + }; + let descriptor = FontDescriptor { + weight: FontWeight::normal(), + stretch: FontStretch::hundred(), + style: FontStyle::normal(), + variant: FontVariantCaps::Normal, + pt_size: Au::from_px(24), + }; + let font = Font::new( + Arc::new(atomic_refcell::AtomicRefCell::new(template)), + descriptor, + None, + ) + .unwrap(); + + let shaping_options = ShapingOptions { + letter_spacing: None, + word_spacing: Au::zero(), + script: Script::Latin, + flags: ShapingFlags::empty(), + }; + let text = "WAVE"; + + assert!(font.can_do_fast_shaping(text, &shaping_options)); + + let mut expected_glyphs = GlyphStore::new(text.len(), false, false, false); + font.shape_text_harfbuzz(text, &shaping_options, &mut expected_glyphs); + + let mut glyphs = GlyphStore::new(text.len(), false, false, false); + font.shape_text_fast(text, &shaping_options, &mut glyphs); + + assert_eq!(glyphs.len(), expected_glyphs.len()); + assert_eq!( + glyphs.total_advance().to_nearest_px(), + expected_glyphs.total_advance().to_nearest_px() + ); + } +} diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs index e49fa22bbff..4974a225054 100644 --- a/components/fonts/platform/windows/font.rs +++ b/components/fonts/platform/windows/font.rs @@ -213,15 +213,15 @@ impl PlatformFontMethods for PlatformFont { /// Can this font do basic horizontal LTR shaping without Harfbuzz? fn can_do_fast_shaping(&self) -> bool { - // TODO copy CachedKernTable from the MacOS X implementation to - // somehwere global and use it here. We could also implement the - // IDirectWriteFontFace1 interface and use the glyph kerning pair - // methods there. - false + self.face.has_kerning_pairs() } - fn glyph_h_kerning(&self, _: GlyphId, _: GlyphId) -> FractionalPixel { - 0.0 + fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel { + let adjustment = self + .face + .get_glyph_pair_kerning_adjustment(first_glyph as u16, second_glyph as u16); + + (adjustment as f32 * self.scaled_du_to_px) as FractionalPixel } fn metrics(&self) -> FontMetrics {