You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在浏览器中用Rust WASM调用read-fonts遇RuntimeError问题求助

在浏览器环境用Rust+WASM调用read-fonts处理字体时触发RuntimeError

问题概述

目标是在浏览器环境中,通过Rust编译的WebAssembly调用read-fonts库处理字体文件,但运行时触发RuntimeError: unreachable错误。仅保留alert文本的Rust函数可正常调用,相同代码在非WASM环境运行完全正常。

错误信息

hello_wasm_bg.wasm:0x5117 Uncaught (in promise) RuntimeError: unreachable
    at hello_wasm_bg.wasm:0x5117
    at hello_wasm_bg.wasm:0x5ebe
    at hello_wasm_bg.wasm:0x5872
    at hello_wasm_bg.wasm:0x2e10
    at mod_os2_us_weight_class (hello_wasm.js:96:10)
    at index.html:12:9

复现代码

Rust代码

use wasm_bindgen::prelude::*;
use read_fonts::{FontRead, FontRef, TableProvider, TopLevelTable};

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn mod_os2_us_weight_class(input_path: &str, output_path: &str, value_str: &str,) {
    let value: u16 = value_str.parse().unwrap();

    let font_data = std::fs::read(input_path).unwrap();
    let font = FontRef::new(&font_data).unwrap();

    let os2 = font.os2().expect("missing OS/2 table");
    alert(&format!("os2.us_weight_class(): {}!", os2.us_weight_class()));
}

JavaScript调用代码

import init, { greet, mod_os2_us_weight_class } from "./pkg/hello_wasm.js";
init().then(() => {
    mod_os2_us_weight_class("/Users/ollimeier/Documents/Vary-Black.otf", "/Users/ollimeier/Documents/Vary-Black_mod20230607.otf", "666");
});

问题原因及解决方案

核心原因

  1. 浏览器环境没有std::fs模块对应的文件系统访问能力,std::fs::read()在WASM浏览器环境下直接panic,unwrap()将panic转为WASM的unreachable指令,触发RuntimeError。
  2. 浏览器禁止直接访问本地文件路径(如/Users/ollimeier/...),路径参数完全无效。

解决方案步骤

  1. 替换文件读取逻辑:用浏览器File API获取字体二进制数据,传递给Rust,抛弃std::fs。
  2. 重构Rust函数接口:接收二进制数据而非文件路径,用Result处理错误避免panic。
  3. 调整输出逻辑:浏览器无法直接写入本地文件,需将处理后的数据返回给JS,由JS触发下载。

修改后的Rust代码

use wasm_bindgen::prelude::*;
use read_fonts::{FontRead, FontRef, TableProvider, TopLevelTable};

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn mod_os2_us_weight_class(font_data: &[u8], value_str: &str) -> Result<(), JsValue> {
    // 解析参数时捕获错误,转为JsValue以便JS捕获
    let value: u16 = value_str.parse()
        .map_err(|e| JsValue::from_str(&format!("参数解析失败: {}", e)))?;
    
    // 解析字体文件时捕获错误
    let font = FontRef::new(font_data)
        .map_err(|e| JsValue::from_str(&format!("字体解析失败: {}", e)))?;
    
    // 获取OS/2表时避免panic
    let os2 = font.os2()
        .ok_or_else(|| JsValue::from_str("字体文件缺少OS/2表"))?;
    
    alert(&format!("当前字重值: {}!", os2.us_weight_class()));
    Ok(())
}

修改后的JavaScript代码

import init, { mod_os2_us_weight_class } from "./pkg/hello_wasm.js";

// 假设页面有文件选择器:<input type="file" id="fontFile" accept=".otf,.ttf">
document.getElementById('fontFile').addEventListener('change', async (e) => {
    await init();
    const file = e.target.files[0];
    if (!file) return;
    
    // 读取文件为二进制数据
    const arrayBuffer = await file.arrayBuffer();
    const uint8Array = new Uint8Array(arrayBuffer);
    
    try {
        await mod_os2_us_weight_class(uint8Array, "666");
    } catch (err) {
        console.error(err);
        alert(`处理失败: ${err}`);
    }
});

关键注意事项

  • 永远不要在WASM浏览器环境中使用unwrap()/expect()这类会触发panic的方法,用Result封装错误并转为JsValue,确保错误能在JS层被捕获处理。
  • 浏览器中所有文件操作必须通过用户交互触发(如文件选择器),无法直接读取/写入本地文件路径。

内容的提问来源于stack exchange,提问作者Olli Meier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 21:22:48