[rust] Use harfrust table-backed font API (#6036)

* [rust] Use harfrust table-backed font API

* [rust] Roll HR forward
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 736265f..c91548d 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -4,7 +4,7 @@
 rust-version = "1.87.0"
 
 [dependencies]
-harfrust = { version = "0.8.3", optional = true }
+harfrust = { version = "0.10.0", features = ["experimental_font_api"], optional = true }
 # harfrust = { git = "https://github.com/harfbuzz/harfrust", optional = true }
 read-fonts = "0.39.*"
 skrifa = { version = "0.*", optional = true }
diff --git a/src/rust/shape.rs b/src/rust/shape.rs
index 26c47e3..3845923 100644
--- a/src/rust/shape.rs
+++ b/src/rust/shape.rs
@@ -5,16 +5,18 @@
 use super::hb::*;
 
 use std::ffi::c_void;
-use std::mem::{align_of, offset_of, size_of, transmute};
+use std::mem::{align_of, offset_of, size_of};
 use std::ptr::null_mut;
+use std::sync::Arc;
 
 use harfrust::{
-    funcs::{AdvanceWidthBatch, BuiltinFontFuncs, FontFuncs},
-    FontRef, GlyphExtents, GlyphFlags as HRGlyphFlags, GlyphInfo as HRGlyphInfo,
-    GlyphPosition as HRGlyphPosition, NormalizedCoord, ShapeOptions, Shaper, ShaperData,
-    ShaperInstance, Tag,
+    font::{
+        AdvanceWidthBatch, BuiltinFontFuncs, Font, FontBlob, FontFuncs, FontInstance,
+        FontTableFunction,
+    },
+    GlyphExtents, GlyphFlags as HRGlyphFlags, GlyphId, GlyphInfo as HRGlyphInfo,
+    GlyphPosition as HRGlyphPosition, NormalizedCoord, ShapeOptions, Tag,
 };
-use read_fonts::types::GlyphId;
 use smallvec::SmallVec;
 
 type HRFeatureVec = SmallVec<[harfrust::Feature; 4]>;
@@ -51,36 +53,84 @@
     assert!(offset_of!(hb_glyph_position_t, y_offset) == offset_of!(HRGlyphPosition, y_offset));
 };
 
-pub struct HBHarfRustFaceData<'a> {
-    face_blob: *mut hb_blob_t,
-    font_ref: FontRef<'a>,
-    shaper_data: ShaperData,
+pub struct HBHarfRustFaceData {
+    font: Font,
 }
 
+struct HbFace(*mut hb_face_t);
+
+impl HbFace {
+    unsafe fn new(face: *mut hb_face_t) -> Self {
+        Self(hb_face_reference(face))
+    }
+
+    fn reference_table(&self, tag: Tag) -> *mut hb_blob_t {
+        unsafe { hb_face_reference_table(self.0, u32::from_be_bytes(tag.to_be_bytes())) }
+    }
+}
+
+impl Drop for HbFace {
+    fn drop(&mut self) {
+        unsafe {
+            hb_face_destroy(self.0);
+        }
+    }
+}
+
+unsafe impl Send for HbFace {}
+unsafe impl Sync for HbFace {}
+
+struct HbBlob(*mut hb_blob_t);
+
+impl Drop for HbBlob {
+    fn drop(&mut self) {
+        unsafe {
+            hb_blob_destroy(self.0);
+        }
+    }
+}
+
+impl AsRef<[u8]> for HbBlob {
+    fn as_ref(&self) -> &[u8] {
+        let mut length = 0;
+        let data = unsafe { hb_blob_get_data(self.0, &mut length) };
+        if data.is_null() {
+            &[]
+        } else {
+            unsafe { std::slice::from_raw_parts(data.cast(), length as usize) }
+        }
+    }
+}
+
+unsafe impl Send for HbBlob {}
+unsafe impl Sync for HbBlob {}
+
 #[unsafe(no_mangle)]
 pub unsafe extern "C" fn _hb_harfrust_shaper_face_data_create_rs(
     face: *mut hb_face_t,
 ) -> *mut c_void {
     let face_index = hb_face_get_index(face);
-    let face_blob = hb_face_reference_blob(face);
-    let blob_length = hb_blob_get_length(face_blob);
-    let blob_data: *const u8 = hb_blob_get_data(face_blob, null_mut()).cast();
-    if blob_data.is_null() {
-        return null_mut();
-    }
-    let face_data = std::slice::from_raw_parts(blob_data, blob_length as usize);
+    let hb_face = HbFace::new(face);
+    let table_fn = FontTableFunction::new(Arc::new(move |tag| {
+        let blob = hb_face.reference_table(tag);
+        if blob.is_null() {
+            return None;
+        }
+        if unsafe { hb_blob_get_length(blob) } == 0 {
+            unsafe {
+                hb_blob_destroy(blob);
+            }
+            return None;
+        }
+        Some(FontBlob::Shared(Arc::new(HbBlob(blob))))
+    }));
 
-    let font_ref = match FontRef::from_index(face_data, face_index) {
-        Ok(f) => f,
+    let font = match Font::new(table_fn, face_index) {
+        Ok(font) => font,
         Err(_) => return null_mut(),
     };
-    let shaper_data = ShaperData::new(&font_ref);
 
-    let hr_face_data = Box::new(HBHarfRustFaceData {
-        face_blob,
-        font_ref,
-        shaper_data,
-    });
+    let hr_face_data = Box::new(HBHarfRustFaceData { font });
 
     Box::into_raw(hr_face_data) as *mut c_void
 }
@@ -88,15 +138,11 @@
 #[unsafe(no_mangle)]
 pub unsafe extern "C" fn _hb_harfrust_shaper_face_data_destroy_rs(data: *mut c_void) {
     let data = data as *mut HBHarfRustFaceData;
-    let hr_face_data = Box::from_raw(data);
-    let blob = hr_face_data.face_blob;
-    hb_blob_destroy(blob);
+    let _hr_face_data = Box::from_raw(data);
 }
 
 pub struct HBHarfRustFontData {
-    // Keep the instance alive because `shaper` borrows variation data from it.
-    _shaper_instance: Box<ShaperInstance>,
-    shaper: Shaper<'static>,
+    instance: FontInstance,
 }
 
 struct HBHarfBuzzFontFuncs {
@@ -173,7 +219,7 @@
     }
 }
 
-fn font_to_shaper_instance(font: *mut hb_font_t, font_ref: &FontRef<'_>) -> ShaperInstance {
+fn font_to_instance(font: *mut hb_font_t, font_ref: &Font) -> FontInstance {
     let mut num_coords: u32 = 0;
     let coords = unsafe { hb_font_get_var_coords_normalized(font, &mut num_coords) };
     let coords = if coords.is_null() {
@@ -182,7 +228,9 @@
         unsafe { std::slice::from_raw_parts(coords, num_coords as usize) }
     };
     let coords = coords.iter().map(|&v| NormalizedCoord::from_bits(v as i16));
-    ShaperInstance::from_coords(font_ref, coords)
+    FontInstance::builder(font_ref)
+        .normalized_coords(coords)
+        .build()
 }
 
 #[unsafe(no_mangle)]
@@ -192,19 +240,10 @@
 ) -> *mut c_void {
     let face_data = face_data as *const HBHarfRustFaceData;
 
-    let font_ref = &(*face_data).font_ref;
-    let shaper_instance = Box::new(font_to_shaper_instance(font, font_ref));
-    let shaper_instance_ref = &*(&*shaper_instance as *const _);
-    let shaper = (*face_data)
-        .shaper_data
-        .shaper(font_ref)
-        .instance(Some(shaper_instance_ref))
-        .build();
+    let instance = font_to_instance(font, &(*face_data).font);
+    let hr_font_data = HBHarfRustFontData { instance };
 
-    let hr_font_data = Box::new(HBHarfRustFontData {
-        _shaper_instance: shaper_instance,
-        shaper: transmute::<harfrust::Shaper<'_>, harfrust::Shaper<'_>>(shaper),
-    });
+    let hr_font_data = Box::new(hr_font_data);
     let hr_font_data_ptr = Box::into_raw(hr_font_data);
 
     hr_font_data_ptr as *mut c_void
@@ -282,10 +321,13 @@
     };
     let features = hb_feature_to_hr_feature(features, num_features);
 
-    let shaper = &(*font_data).shaper;
-
-    let hr_shape_plan =
-        harfrust::ShapePlan::new(shaper, direction, script, language.as_ref(), &features);
+    let hr_shape_plan = harfrust::ShapePlan::new(
+        &(*font_data).instance,
+        direction,
+        script,
+        language.as_ref(),
+        &features,
+    );
     let hr_shape_plan = Box::new(hr_shape_plan);
     Box::into_raw(hr_shape_plan) as *mut c_void
 }
@@ -385,15 +427,13 @@
     let mut y_scale = 0;
     hb_font_get_scale(font, &mut x_scale, &mut y_scale);
     let mut font_funcs = HBHarfBuzzFontFuncs { font };
-    let glyphs = (*font_data).shaper.shape(
-        hr_buffer,
-        ShapeOptions::new()
-            .plan(shape_plan)
-            .scale_separate(Some((x_scale, y_scale)))
-            .point_size(ptem)
-            .features(&features)
-            .font_funcs(Some(&mut font_funcs)),
-    );
+    let options = ShapeOptions::new()
+        .plan(shape_plan)
+        .scale_separate(Some((x_scale, y_scale)))
+        .point_size(ptem)
+        .features(&features)
+        .font_funcs(Some(&mut font_funcs));
+    let glyphs = harfrust::shape(&(*font_data).instance, hr_buffer, options);
 
     hb_buffer_set_content_type(
         buffer,