You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用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");

关键说明

  1. Unicode码点转字符String.fromCodePoint(value.unicode)是核心——value.unicode是opentype.js返回的十进制Unicode码点,这个方法可以直接把码点转换成对应的实际字符,不管是字母、数字、标点还是特殊符号,只要有合法的unicode值就能正确显示。
  2. 处理无Unicode的字形:有些字体包含特殊字形(比如连字、装饰性字形)没有对应的Unicode码点,这时候我们可以显示它的name属性或者占位符,避免页面出现空白。
  3. 关于你看到的Unicode码点疑问:你提到字体里"A"的unicode是64,这应该是个小误解——U+0041(A)对应的十进制是65,64其实是@(U+0040)的码点,大概率是你在console里看错了对象属性。不过没关系,String.fromCodePoint()会严格按照字体返回的码点转换字符,确保显示的是字体里对应的字形。
  4. 字体加载的重要性:我添加了loadFont函数,通过@font-face把目标字体加载到页面,这样显示的字符才会用你解析的字体,而不是系统默认字体,保证显示效果符合预期。

额外优化建议

  • 可以给每个li元素添加样式,让字符和名称排版更美观
  • 对于没有unicode的特殊字形,可以考虑用opentype.js的draw方法直接在canvas上绘制,不过这是更进阶的需求了

备注:内容来源于stack exchange,提问作者lharby

火山引擎 最新活动