I'm using rustybuzz and wasm-bindgen to typeset text on an HTML5 canvas. I would like to create a global font cache, indexed by String
s, so that I can load and parse font files once and then reuse them multiple times (cue comments about how global variables are bad... if anyone has a better way to do this, please let me know). Specifically, I want some variation of a HashMap<String, rustybuzz::Face>
that I can access anywhere. I then want to expose a register_font
function to the JavaScript side so I can load in ArrayBuffers
, something like:
#[wasm_bindgen]
pub fn register_font(name: &str, font_data: &[u8]) {
MY_FONT_CACHE_SOMEHOW.insert(
name.to_string(),
rustybuzz::Face::from_slice(&font_data, 0).unwrap()
);
}
And, for completion, I'd like an internal get_font
function to retrieve:
fn get_font(name: &String) -> rustybuzz::Face {
MY_FONT_CACHE_SOMEHOW.get(name).unwrap()
}
I know I've got issues with variable lifetimes, I just don't know how to solve them. The rustybuzz::Face
struct just references its internal data, it doesn't own it. wasm-bindgen
doesn't support marking the incoming ArrayBuffer
/[u8]
on the register_font
function as 'static
, which seems to be one of the main problems. But I'm new to this whole rust thing. Anyone know how to do this (or a better way to do this)?
If you only add to the hash table and never delete you could copy and leak font_data to make it static:
#[wasm_bindgen]
pub fn register_font(name: &str, font_data: &[u8]) {
let font_data: &'static [u8] = font_data.to_owned().leak();
//...
}
If you want to be able to delete fonts from the cache you have to keep the font_data
and the Face
together. That is the infamous issue of a self-referential type
.
You can go the unsafe way and keep a pointer to a dynamically allocated *const [u8]
type, or you can use one of the many crates that try to solve this issue.
Currently my favourite is ouroboros
: it would be something like this (untested):
#[self_referencing]
struct MyFace {
font_data: Vec<u8>,
#[borrows(font_data)]
face: Face<'this>,
}
#[wasm_bindgen]
pub fn register_font(name: &str, font_data: &[u8]) {
let face = MyFaceBuilder {
font_data: font_data.to_owned(),
face_builder: |font_data: &[u8]| Face::from_slice(font_data).unwrap(),
}.build();
MY_FONT_CACHE_SOMEHOW.insert(
name.to_string(),
face,
);
}