如何使用opentype.js解析字体文件并在页面上正确显示所有字形
如何使用opentype.js解析字体文件并在页面上正确显示所有字形
看起来你已经用opentype.js成功解析了字体文件,但卡在了如何正确显示每个字形的实际字符上,我来帮你解决这个问题!
你的当前问题分析
你现在的代码遍历了字体的所有字形,但只显示了字形的name属性——这就是为什么字母能正常显示(因为A的name就是"A"),但数字显示成"one"、标点显示成对应的名称。你尝试用HTML实体的思路方向是对的,但用name来拼接实体显然行不通,因为只有部分字形的name和HTML实体名一致。
正确的思路是利用字形的unicode属性:每个字形对应的Unicode码点,我们可以直接把这个码点转换成对应的字符。
修正后的代码
我把你的代码做了优化,改用async/await让逻辑更清晰,同时实现了显示实际字符的功能:
import opentype from "opentype.js"; const htmlElem = document.querySelector("html"); const fontPath = "/src/resources"; // 先通过@font-face加载字体,确保显示的字符用目标字体(需要配合CSS) const loadFont = (fontName, fontFamilyName) => { const style = document.createElement('style'); style.textContent = ` @font-face { font-family: '${fontFamilyName}'; src: url('${fontPath}/${fontName}') format('opentype'); } .glyph-char { font-family: '${fontFamilyName}', sans-serif; font-size: 28px; margin-right: 8px; } .glyph-name { font-size: 14px; color: #666; } `; document.head.appendChild(style); }; const fontMap = async (fontName, fontFamilyName = 'Custom Font') => { const url = `${fontPath}/${fontName}`; try { // 加载字体文件并解析 const res = await fetch(url); const data = await res.arrayBuffer(); const font = opentype.parse(data); const wrapper = htmlElem.querySelector(".characters ul"); const glyphs = font.glyphs.glyphs; // 先加载字体到页面 loadFont(fontName, fontFamilyName); // 遍历所有字形 for (const [key, value] of Object.entries(glyphs)) { if (!value.name) continue; // 跳过无名称的字形(可选) // 生成要显示的字符:优先用unicode码点转字符,无码点则显示占位符 let displayChar = '(无对应字符)'; if (value.unicode !== undefined) { displayChar = String.fromCodePoint(value.unicode); } // 插入到页面,同时显示字符和字形名称 const template = ` <li> <span class="glyph-char">${displayChar}</span> <span class="glyph-name">${value.name}</span> </li> `; wrapper.insertAdjacentHTML("beforeend", template); } } catch (error) { console.error('加载或解析字体时出错:', error); } }; fontMap("Calluna-Regular.otf", "Calluna Regular");
关键说明
- Unicode码点转字符:
String.fromCodePoint(value.unicode)是核心——value.unicode是opentype.js返回的十进制Unicode码点,这个方法可以直接把码点转换成对应的实际字符,不管是字母、数字、标点还是特殊符号,只要有合法的unicode值就能正确显示。 - 处理无Unicode的字形:有些字体包含特殊字形(比如连字、装饰性字形)没有对应的Unicode码点,这时候我们可以显示它的
name属性或者占位符,避免页面出现空白。 - 关于你看到的Unicode码点疑问:你提到字体里"A"的unicode是64,这应该是个小误解——U+0041(A)对应的十进制是65,64其实是@(U+0040)的码点,大概率是你在console里看错了对象属性。不过没关系,
String.fromCodePoint()会严格按照字体返回的码点转换字符,确保显示的是字体里对应的字形。 - 字体加载的重要性:我添加了
loadFont函数,通过@font-face把目标字体加载到页面,这样显示的字符才会用你解析的字体,而不是系统默认字体,保证显示效果符合预期。
额外优化建议
- 可以给每个li元素添加样式,让字符和名称排版更美观
- 对于没有unicode的特殊字形,可以考虑用opentype.js的
draw方法直接在canvas上绘制,不过这是更进阶的需求了
备注:内容来源于stack exchange,提问作者lharby




