在浏览器中用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"); });
问题原因及解决方案
核心原因
- 浏览器环境没有
std::fs模块对应的文件系统访问能力,std::fs::read()在WASM浏览器环境下直接panic,unwrap()将panic转为WASM的unreachable指令,触发RuntimeError。 - 浏览器禁止直接访问本地文件路径(如
/Users/ollimeier/...),路径参数完全无效。
解决方案步骤
- 替换文件读取逻辑:用浏览器File API获取字体二进制数据,传递给Rust,抛弃
std::fs。 - 重构Rust函数接口:接收二进制数据而非文件路径,用
Result处理错误避免panic。 - 调整输出逻辑:浏览器无法直接写入本地文件,需将处理后的数据返回给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
相关产品推荐
相关产品推荐

