创建全局字体缓存

Create a global font cache

我正在使用 rustybuzz and wasm-bindgen 在 HTML5 canvas 上排版文本。我想创建一个全局字体缓存,由 Strings 索引,这样我就可以加载和解析一次字体文件,然后多次重用它们(提示关于全局变量有多糟糕的评论......如果有人有更好的方法,请告诉我)。具体来说,我想要可以在任何地方访问的 HashMap<String, rustybuzz::Face> 的一些变体。然后我想在 JavaScript 端公开一个 register_font 函数,这样我就可以加载 ArrayBuffers,比如:

#[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()
  );
}

并且,为了完成,我想要一个内部 get_font 函数来检索:

fn get_font(name: &String) -> rustybuzz::Face {
  MY_FONT_CACHE_SOMEHOW.get(name).unwrap()
}

我知道我遇到了可变生命周期的问题,只是不知道如何解决。 rustybuzz::Face 结构只是引用它的内部数据,它并不拥有它。 wasm-bindgen 不支持将 register_font 函数上的传入 ArrayBuffer/[u8] 标记为 'static,这似乎是主要问题之一。但是我对这整个生锈的事情还是陌生的。任何人都知道如何做到这一点(或更好的方法)?

如果你只添加到散列 table 而从不删除你可以复制和泄漏 font_data 使其静态:

#[wasm_bindgen]
pub fn register_font(name: &str, font_data: &[u8]) {
    let font_data: &'static [u8] = font_data.to_owned().leak();
    //...
}

如果您希望能够从缓存中删除字体,您必须将 font_dataFace 放在一起。那就是臭名昭著的 self-referential type.

问题

您可以采取不安全的方式并保留指向动态分配的 *const [u8] 类型的指针,或者您可以使用试图解决此问题的众多 crate 之一。

目前我最喜欢的是ouroboros:应该是这样的(未测试):

#[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,
    );
}