From 21bfbb346c602110aa65126f7d2d9c3a59a2bad2 Mon Sep 17 00:00:00 2001 From: U2A5F Date: Wed, 10 Dec 2025 14:24:28 +0800 Subject: [PATCH 1/3] Replace FontRef with TableProvider --- benches/shaping.rs | 15 ++++---- examples/shape.rs | 6 ++-- src/hb/aat/mod.rs | 18 +++++----- src/hb/buffer.rs | 12 ++++--- src/hb/charmap.rs | 5 ++- src/hb/face.rs | 47 +++++++++++-------------- src/hb/glyph_metrics.rs | 23 ++++++------ src/hb/glyph_names.rs | 4 +-- src/hb/ot/mod.rs | 17 +++++---- src/hb/ot_shape.rs | 11 ++---- src/hb/ot_shape_plan.rs | 8 +++-- src/hb/tables.rs | 78 +++++++++++++++++------------------------ src/lib.rs | 2 +- tests/shaping/main.rs | 6 ++-- 14 files changed, 115 insertions(+), 137 deletions(-) diff --git a/benches/shaping.rs b/benches/shaping.rs index c0151531..892ac4da 100644 --- a/benches/shaping.rs +++ b/benches/shaping.rs @@ -1,4 +1,5 @@ use criterion::{criterion_group, criterion_main, Criterion}; +use harfrust::TableProvider; use std::path::Path; const BENCHES: &[(&str, &str)] = &[ @@ -38,9 +39,10 @@ struct ShapePlanCache { } impl ShapePlanCache { - fn get( + fn get<'a>( &mut self, - shaper: &harfrust::Shaper, + shaper: &'a harfrust::Shaper, + font: &impl TableProvider<'a>, buffer: &harfrust::UnicodeBuffer, ) -> &harfrust::ShapePlan { let key = harfrust::ShapePlanKey::new(Some(buffer.script()), buffer.direction()); @@ -49,6 +51,7 @@ impl ShapePlanCache { } else { self.plans.push(harfrust::ShapePlan::new( shaper, + font, buffer.direction(), Some(buffer.script()), None, @@ -86,7 +89,7 @@ fn bench(c: &mut Criterion) { let mut buffer = shared_buffer.take().unwrap(); buffer.push_str(line); buffer.guess_segment_properties(); - let plan = plan_cache.get(&shaper, &buffer); + let plan = plan_cache.get(&shaper, &font, &buffer); shared_buffer = Some(shaper.shape_with_plan(plan, buffer, &[]).clear()); } }); @@ -121,8 +124,7 @@ criterion_group! { criterion_main!(benches); struct HrTestState<'a> { - font: &'a harfrust::FontRef<'a>, - data: harfrust::ShaperData, + data: harfrust::ShaperData<'a>, _instance: Option, } @@ -130,7 +132,6 @@ impl<'a> HrTestState<'a> { fn new(font: &'a harfrust::FontRef<'a>) -> Self { let data = harfrust::ShaperData::new(font); Self { - font, data, _instance: None, } @@ -138,7 +139,7 @@ impl<'a> HrTestState<'a> { fn shaper(&self) -> harfrust::Shaper<'_> { self.data - .shaper(self.font) + .shaper() .instance(self._instance.as_ref()) .build() } diff --git a/examples/shape.rs b/examples/shape.rs index 000e571e..c684e8a3 100644 --- a/examples/shape.rs +++ b/examples/shape.rs @@ -155,7 +155,7 @@ fn main() { let data = ShaperData::new(&font); let instance = ShaperInstance::from_variations(&font, &args.variations); let shaper = data - .shaper(&font) + .shaper() .instance(Some(&instance)) .point_size(args.font_ptem) .build(); @@ -207,7 +207,7 @@ fn main() { buffer.guess_segment_properties(); - let glyph_buffer = shaper.shape(buffer, &args.features); + let glyph_buffer = shaper.shape(&font, buffer, &args.features); let mut format_flags = harfrust::SerializeFlags::default(); if args.no_glyph_names { @@ -234,7 +234,7 @@ fn main() { format_flags |= harfrust::SerializeFlags::GLYPH_FLAGS; } - println!("{}", glyph_buffer.serialize(&shaper, format_flags)); + println!("{}", glyph_buffer.serialize(&shaper, &font, format_flags)); } } diff --git a/src/hb/aat/mod.rs b/src/hb/aat/mod.rs index a453a5cd..13ef07d5 100644 --- a/src/hb/aat/mod.rs +++ b/src/hb/aat/mod.rs @@ -12,7 +12,7 @@ use crate::hb::tables::TableRanges; use alloc::vec::Vec; use read_fonts::{ tables::{ankr::Ankr, feat::Feat, kern::Kern, kerx::Kerx, morx::Morx, trak::Trak}, - FontRef, TableProvider, + TableProvider, }; #[derive(Default)] @@ -24,7 +24,7 @@ pub struct AatCache { impl AatCache { #[allow(unused)] - pub fn new(font: &FontRef) -> Self { + pub fn new<'a>(font: &impl TableProvider<'a>) -> Self { let mut cache = Self::default(); let num_glyphs = font .maxp() @@ -81,22 +81,22 @@ pub struct AatTables<'a> { } impl<'a> AatTables<'a> { - pub fn new(font: &FontRef<'a>, cache: &'a AatCache, table_ranges: &TableRanges) -> Self { + pub fn new(cache: &'a AatCache, table_ranges: &TableRanges<'a>) -> Self { let morx = table_ranges .morx - .resolve_table(font) + .resolve_table() .map(|table| (table, cache.morx.as_slice())); - let ankr = table_ranges.ankr.resolve_table(font); + let ankr = table_ranges.ankr.resolve_table(); let kern = table_ranges .kern - .resolve_table(font) + .resolve_table() .map(|table| (table, cache.kern.as_slice())); let kerx = table_ranges .kerx - .resolve_table(font) + .resolve_table() .map(|table| (table, cache.kerx.as_slice())); - let trak = table_ranges.trak.resolve_table(font); - let feat = table_ranges.feat.resolve_table(font); + let trak = table_ranges.trak.resolve_table(); + let feat = table_ranges.feat.resolve_table(); Self { morx, ankr, diff --git a/src/hb/buffer.rs b/src/hb/buffer.rs index ed0dbc22..2da7fb28 100644 --- a/src/hb/buffer.rs +++ b/src/hb/buffer.rs @@ -1,5 +1,6 @@ use crate::U32Set; use alloc::{string::String, vec::Vec}; +use read_fonts::TableProvider; use core::cmp::min; use core::convert::TryFrom; use read_fonts::types::{GlyphId, GlyphId16}; @@ -1912,13 +1913,14 @@ impl GlyphBuffer { } /// Converts the glyph buffer content into a string. - pub fn serialize(&self, face: &crate::Shaper, flags: SerializeFlags) -> String { - self.serialize_impl(face, flags).unwrap_or_default() + pub fn serialize<'t>(&self, face: &crate::Shaper<'t>, font: &impl TableProvider<'t>, flags: SerializeFlags) -> String { + self.serialize_impl(face, font, flags).unwrap_or_default() } - fn serialize_impl( + fn serialize_impl<'t>( &self, - face: &hb_font_t, + face: &hb_font_t<'t>, + font: &impl TableProvider<'t>, flags: SerializeFlags, ) -> Result { use core::fmt::Write; @@ -1929,7 +1931,7 @@ impl GlyphBuffer { let pos = self.glyph_positions(); let mut x = 0; let mut y = 0; - let names = face.glyph_names(); + let names = face.glyph_names(font); for (info, pos) in info.iter().zip(pos) { s.push(if s.is_empty() { '[' } else { '|' }); diff --git a/src/hb/charmap.rs b/src/hb/charmap.rs index b8a02707..8c339250 100644 --- a/src/hb/charmap.rs +++ b/src/hb/charmap.rs @@ -4,7 +4,6 @@ use super::cache::hb_cache_t; use read_fonts::{ tables::cmap::{Cmap, Cmap14, CmapSubtable, MapVariant}, types::GlyphId, - FontRef, }; pub type cache_t = hb_cache_t<21, 19, 256, 32>; @@ -17,8 +16,8 @@ pub struct Charmap<'a> { } impl<'a> Charmap<'a> { - pub fn new(font: &FontRef<'a>, table_ranges: &TableRanges, cache: &'a cache_t) -> Self { - if let Some(cmap) = table_ranges.cmap.resolve_table::(font) { + pub fn new(table_ranges: &TableRanges<'a>, cache: &'a cache_t) -> Self { + if let Some(cmap) = table_ranges.cmap.resolve_table::() { let data = cmap.offset_data(); let records = cmap.encoding_records(); let subtable = table_ranges diff --git a/src/hb/face.rs b/src/hb/face.rs index 9b4ca9fa..8dacb3f2 100644 --- a/src/hb/face.rs +++ b/src/hb/face.rs @@ -1,5 +1,5 @@ use read_fonts::types::{F2Dot14, Fixed, GlyphId}; -use read_fonts::{FontRef, TableProvider}; +use read_fonts::{TableProvider}; use smallvec::SmallVec; use super::aat::AatTables; @@ -15,16 +15,16 @@ use crate::hb::tables::TableRanges; use crate::{script, Feature, GlyphBuffer, NormalizedCoord, ShapePlan, UnicodeBuffer, Variation}; /// Data required for shaping with a single font. -pub struct ShaperData { - table_ranges: TableRanges, +pub struct ShaperData<'a> { + table_ranges: TableRanges<'a>, ot_cache: OtCache, aat_cache: AatCache, cmap_cache: cmap_cache_t, } -impl ShaperData { +impl<'a> ShaperData<'a> { /// Creates new cached shaper data for the given font. - pub fn new(font: &FontRef) -> Self { + pub fn new>(font: &Font) -> Self { let ot_cache = OtCache::new(font); let aat_cache = AatCache::new(font); let table_ranges = TableRanges::new(font); @@ -39,10 +39,9 @@ impl ShaperData { /// Returns a builder for constructing a new shaper with the given /// font. - pub fn shaper<'a>(&'a self, font: &FontRef<'a>) -> ShaperBuilder<'a> { + pub fn shaper(&'a self) -> ShaperBuilder<'a> { ShaperBuilder { data: self, - font: font.clone(), instance: None, point_size: None, } @@ -68,7 +67,7 @@ impl ShaperInstance { /// list of variation settings. /// /// The setting values are in user space and the order is insignificant. - pub fn from_variations(font: &FontRef, variations: V) -> Self + pub fn from_variations<'a, V>(font: &impl TableProvider<'a>, variations: V) -> Self where V: IntoIterator, V::Item: Into, @@ -82,7 +81,7 @@ impl ShaperInstance { /// set of normalized coordinates. /// /// The sequence of coordinates is expected to be in axis order. - pub fn from_coords(font: &FontRef, coords: impl IntoIterator) -> Self { + pub fn from_coords<'a>(font: &impl TableProvider<'a>, coords: impl IntoIterator) -> Self { let mut this = Self::default(); this.set_coords(font, coords); this @@ -90,7 +89,7 @@ impl ShaperInstance { /// Creates a new shaper instance for the given font using the variation /// position from the named instance at the specified index. - pub fn from_named_instance(font: &FontRef, index: usize) -> Self { + pub fn from_named_instance<'a>(font: &impl TableProvider<'a>, index: usize) -> Self { let mut this = Self::default(); this.set_named_instance(font, index); this @@ -102,7 +101,7 @@ impl ShaperInstance { } /// Resets the instance for the given font and variation settings. - pub fn set_variations(&mut self, font: &FontRef, variations: V) + pub fn set_variations<'a, V>(&mut self, font: &impl TableProvider<'a>, variations: V) where V: IntoIterator, V::Item: Into, @@ -125,7 +124,7 @@ impl ShaperInstance { } /// Resets the instance for the given font and normalized coordinates. - pub fn set_coords(&mut self, font: &FontRef, coords: impl IntoIterator) { + pub fn set_coords<'a>(&mut self, font: &impl TableProvider<'a>, coords: impl IntoIterator) { self.coords.clear(); if let Ok(fvar) = font.fvar() { let count = fvar.axis_count() as usize; @@ -138,7 +137,7 @@ impl ShaperInstance { /// Resets the instance for the given font using the variation /// position from the named instance at the specified index. - pub fn set_named_instance(&mut self, font: &FontRef, index: usize) { + pub fn set_named_instance<'a>(&mut self, font: &impl TableProvider<'a>, index: usize) { self.coords.clear(); if let Ok(fvar) = font.fvar() { if let Ok((axes, instance)) = fvar @@ -155,7 +154,7 @@ impl ShaperInstance { } } - fn set_feature_variations(&mut self, font: &FontRef) { + fn set_feature_variations<'a>(&mut self, font: &impl TableProvider<'a>) { self.feature_variations = [None; 2]; if self.coords.is_empty() { return; @@ -179,8 +178,7 @@ impl ShaperInstance { /// Builder type for constructing a [`Shaper`](crate::Shaper). pub struct ShaperBuilder<'a> { - data: &'a ShaperData, - font: FontRef<'a>, + data: &'a ShaperData<'a>, instance: Option<&'a ShaperInstance>, point_size: Option, } @@ -204,24 +202,21 @@ impl<'a> ShaperBuilder<'a> { /// Builds the shaper with the current configuration. pub fn build(self) -> crate::Shaper<'a> { - let font = self.font; let units_per_em = self.data.table_ranges.units_per_em; - let charmap = Charmap::new(&font, &self.data.table_ranges, &self.data.cmap_cache); - let glyph_metrics = GlyphMetrics::new(&font, &self.data.table_ranges); + let charmap = Charmap::new(&self.data.table_ranges, &self.data.cmap_cache); + let glyph_metrics = GlyphMetrics::new(&self.data.table_ranges); let (coords, feature_variations) = self .instance .map(|instance| (instance.coords(), instance.feature_variations)) .unwrap_or_default(); let ot_tables = OtTables::new( - &font, &self.data.ot_cache, &self.data.table_ranges, coords, feature_variations, ); - let aat_tables = AatTables::new(&font, &self.data.aat_cache, &self.data.table_ranges); + let aat_tables = AatTables::new(&self.data.aat_cache, &self.data.table_ranges); hb_font_t { - font, units_per_em, points_per_em: self.point_size, charmap, @@ -235,7 +230,6 @@ impl<'a> ShaperBuilder<'a> { /// A configured shaper. #[derive(Clone)] pub struct hb_font_t<'a> { - pub(crate) font: FontRef<'a>, pub(crate) units_per_em: u16, pub(crate) points_per_em: Option, charmap: Charmap<'a>, @@ -264,9 +258,10 @@ impl<'a> crate::Shaper<'a> { /// If you plan to shape multiple strings, prefer [`shape_with_plan`](Self::shape_with_plan). /// This is because [`ShapePlan`](crate::ShapePlan) initialization is pretty slow and should preferably /// be called once for each shaping configuration. - pub fn shape(&self, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer { + pub fn shape<'s>(&'s self, font: &impl TableProvider<'s>, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer { let plan = ShapePlan::new( self, + font, buffer.0.direction, buffer.0.script, buffer.0.language.as_ref(), @@ -381,8 +376,8 @@ impl<'a> crate::Shaper<'a> { } } - pub(crate) fn glyph_names(&self) -> GlyphNames<'a> { - GlyphNames::new(&self.font) + pub(crate) fn glyph_names(&self, font: &impl TableProvider<'a>) -> GlyphNames<'a> { + GlyphNames::new(font) } pub(crate) fn layout_table(&self, table_index: TableIndex) -> Option> { diff --git a/src/hb/glyph_metrics.rs b/src/hb/glyph_metrics.rs index ec6d6e8d..6556fb17 100644 --- a/src/hb/glyph_metrics.rs +++ b/src/hb/glyph_metrics.rs @@ -13,7 +13,6 @@ use read_fonts::{ vvar::Vvar, }, types::{BoundingBox, F2Dot14, Fixed, GlyphId, Point}, - FontRef, }; #[derive(Clone)] @@ -40,36 +39,36 @@ struct GlyfTables<'a> { } impl<'a> GlyphMetrics<'a> { - pub fn new(font: &FontRef<'a>, table_ranges: &TableRanges) -> Self { + pub fn new(table_ranges: &TableRanges<'a>) -> Self { let num_glyphs = table_ranges.num_glyphs; let upem = table_ranges.units_per_em; let hmtx = table_ranges .hmtx - .resolve_data(font) + .data() .and_then(|data| Hmtx::read(data, table_ranges.num_h_metrics).ok()); let h_metrics = hmtx .as_ref() .map(|hmtx| hmtx.h_metrics()) .unwrap_or_default(); - let hvar = table_ranges.hvar.resolve_table(font); + let hvar = table_ranges.hvar.resolve_table(); let vmtx = table_ranges .vmtx - .resolve_data(font) - .and_then(|data| Vmtx::read(data, table_ranges.num_v_metrics).ok()); - let vvar = table_ranges.vvar.resolve_table(font); - let vorg = table_ranges.vorg.resolve_table(font); + .data() + .and_then(|data: read_fonts::FontData<'_>| Vmtx::read(data, table_ranges.num_v_metrics).ok()); + let vvar = table_ranges.vvar.resolve_table(); + let vorg = table_ranges.vorg.resolve_table(); let loca = table_ranges .loca - .resolve_data(font) + .data() .and_then(|data| Loca::read(data, table_ranges.loca_long).ok()); - let glyf = table_ranges.glyf.resolve_table(font); + let glyf = table_ranges.glyf.resolve_table(); let glyf = if let Some((loca, glyf)) = loca.zip(glyf) { - let gvar = table_ranges.gvar.resolve_table(font); + let gvar = table_ranges.gvar.resolve_table(); Some(GlyfTables { loca, glyf, gvar }) } else { None }; - let mvar = table_ranges.mvar.resolve_table(font); + let mvar = table_ranges.mvar.resolve_table(); let ascent = table_ranges.ascent; let descent = table_ranges.descent; Self { diff --git a/src/hb/glyph_names.rs b/src/hb/glyph_names.rs index 7638372f..87dfb398 100644 --- a/src/hb/glyph_names.rs +++ b/src/hb/glyph_names.rs @@ -1,6 +1,6 @@ use read_fonts::{ tables::{cff::Cff, post::Post, postscript::Charset}, - FontRef, TableProvider, + TableProvider, }; #[derive(Clone)] @@ -11,7 +11,7 @@ pub(crate) enum GlyphNames<'a> { } impl<'a> GlyphNames<'a> { - pub fn new(font: &FontRef<'a>) -> Self { + pub fn new(font: &impl TableProvider<'a>) -> Self { if let Some((cff, charset)) = font .cff() .ok() diff --git a/src/hb/ot/mod.rs b/src/hb/ot/mod.rs index b5945264..13fefe3e 100644 --- a/src/hb/ot/mod.rs +++ b/src/hb/ot/mod.rs @@ -18,7 +18,7 @@ use read_fonts::{ variations::{DeltaSetIndex, ItemVariationStore}, }, types::{BigEndian, F2Dot14, GlyphId, Offset32}, - FontData, FontRef, ReadError, ResolveOffset, TableProvider, + FontData, ReadError, ResolveOffset, TableProvider, }; pub mod contextual; @@ -34,7 +34,7 @@ pub struct OtCache { } impl OtCache { - pub fn new(font: &FontRef) -> Self { + pub fn new<'a>(font: &impl TableProvider<'a>) -> Self { let gsub = font .gsub() .map(|t| LookupCache::new(&t)) @@ -101,8 +101,8 @@ pub struct GdefTable<'a> { } impl<'a> GdefTable<'a> { - fn new(font: &FontRef<'a>, table_ranges: &TableRanges) -> Self { - if let Some(gdef) = table_ranges.gdef.resolve_table::(font) { + fn new(table_ranges: &TableRanges<'a>) -> Self { + if let Some(gdef) = table_ranges.gdef.resolve_table::() { let classes = gdef.glyph_class_def().transpose().ok().flatten(); let mark_classes = gdef.mark_attach_class_def().transpose().ok().flatten(); let mark_sets = gdef @@ -137,22 +137,21 @@ pub struct OtTables<'a> { impl<'a> OtTables<'a> { pub fn new( - font: &FontRef<'a>, cache: &'a OtCache, - table_offsets: &TableRanges, + table_offsets: &TableRanges<'a>, coords: &'a [F2Dot14], feature_variations: [Option; 2], ) -> Self { let gsub = table_offsets .gsub - .resolve_table(font) + .resolve_table() .map(|table| GsubTable { table, lookups: &cache.gsub, }); let gpos = table_offsets .gpos - .resolve_table(font) + .resolve_table() .map(|table| GposTable { table, lookups: &cache.gpos, @@ -162,7 +161,7 @@ impl<'a> OtTables<'a> { } else { &[] }; - let gdef = GdefTable::new(font, table_offsets); + let gdef = GdefTable::new(table_offsets); let var_store = if !coords.is_empty() { gdef.table .as_ref() diff --git a/src/hb/ot_shape.rs b/src/hb/ot_shape.rs index 92476fce..ca6591e8 100644 --- a/src/hb/ot_shape.rs +++ b/src/hb/ot_shape.rs @@ -182,7 +182,7 @@ impl<'a> hb_ot_shape_planner_t<'a> { } } - pub fn compile(mut self, features: &[Feature]) -> hb_ot_shape_plan_t { + pub fn compile(mut self, features: &[Feature], font: &impl TableProvider<'a>) -> hb_ot_shape_plan_t { let ot_map = self.ot_map.compile(); let mut aat_map = AatMap::default(); if self.apply_morx { @@ -266,14 +266,7 @@ impl<'a> hb_ot_shape_planner_t<'a> { // According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. // https://github.com/googlefonts/fontations/issues/1492 - let apply_trak = self.face.font.trak().is_ok() - && self - .face - .font - .table_directory - .table_records() - .iter() - .any(|table| table.tag() == "STAT"); + let apply_trak = font.trak().is_ok() && font.stat().is_ok(); let mut plan = hb_ot_shape_plan_t { direction: self.direction, diff --git a/src/hb/ot_shape_plan.rs b/src/hb/ot_shape_plan.rs index 1ede59e8..fa69106a 100644 --- a/src/hb/ot_shape_plan.rs +++ b/src/hb/ot_shape_plan.rs @@ -1,4 +1,5 @@ use alloc::boxed::Box; +use read_fonts::TableProvider; use core::any::Any; use smallvec::SmallVec; @@ -50,8 +51,9 @@ pub struct hb_ot_shape_plan_t { impl hb_ot_shape_plan_t { /// Returns a plan that can be used for shaping any buffer with the /// provided properties. - pub fn new( - face: &hb_font_t, + pub fn new<'a>( + face: &'a hb_font_t, + font: &impl TableProvider<'a>, direction: Direction, script: Option