首页 > 解决方案 > 创建全局字体缓存

问题描述

我正在使用rustybuzzwasm-bindgen在 HTML5 画布上排版文本。我想创建一个由Strings 索引的全局字体缓存,这样我就可以加载和解析字体文件一次,然后多次重用它们(提示关于全局变量如何不好的评论......如果有人有更好的方法来做这个,请告诉我)。具体来说,我想要一些HashMap<String, rustybuzz::Face>我可以在任何地方访问的变体。然后,我想向register_fontJavaScript 端公开一个函数,以便可以加载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不支持将函数上的传入ArrayBuffer/标记为,这似乎是主要问题之一。但我对这整个生锈的事情很陌生。任何人都知道如何做到这一点(或更好的方法来做到这一点)?[u8]register_font'static

标签: rusthtml5-canvaswasm-bindgen

解决方案


如果您只添加到哈希表并且从不删除,您可以复制和泄漏 font_data 以使其成为静态:

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

如果您希望能够从缓存中删除字体,则必须将font_data和保持Face在一起。这就是臭名昭著的问题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,
    );
}

推荐阅读