在 KaTeX 中使用 Fira Math

Posted on May 25, 2025 by Yu Cong with help from many others
Tags:

现在有一个半成品可用 https://github.com/congyu711/KaTeX

Fira Math 这字体很不错, 想要在 KaTeX 中使用它.

有用的链接:
  1. https://yingtongli.me/blog/2022/09/24/katex-custom-fonts.html – alphanumeric text only
  2. https://github.com/KaTeX/KaTeX/discussions/3716
  3. https://github.com/firamath/firamath.github.io/blob/master/bibliography.md
  4. https://learn.microsoft.com/en-us/typography/opentype/spec/math

1 KaTeX 字体部分如何工作?

关于 fonts 和 metrics 有关的东西首先要看dockers/fonts/. 看起来 fonts/里面的字体是从 docker 中安装的 texlive 的 computer modern fonts 提取的. 然后dockers/fonts/buildMetrics.sh 调用 src/metrics里面的代码,直接从有关的 tfm 文件里读一些 metric 信息.

(我猜)生成的 ttf,woff2 等字体是保存 unicode -> (字形 + 一些信息) 这个映射关系的文件.

src/metrics/mapping.pl 就是从 tex math 到 unicode 的映射. 做完这个输出这样的东西
{
    ...
    "AMS-Regular": {
        "65": {
            "char": 65,
            "yshift": 0,
            "xshift": 0,
            "font": "msbm10"
        },
        ...

之后输入到src/metrics/extract_tfms.py 得到
{
    "AMS-Regular": {
        "65": {
            "depth": 0.0,
            "height": 0.68889,
            "italic": 0.0,
            "skew": 0.0,
            "width": 0.72222
        },
        ...

最后会再做点工作然后把 json 塞入src/fontMetricsData.js. 我猜如果成功的把 Fira Math 的 metric 弄到这个 js 文件里, 接下来改改 css 里的字体然后 rebuild 就行了.

采集 metric 和生成 fonts 的过程是独立的, 所以看起来大部分东西都是可以复用的.

2 Plan

  1. 由于 KaTeX 本身字体分成很多小文件, 首先我也把 Fira Math 分成小文件
  2. 然后想办法搞到正确的 src/metrics/mapping.pl for Fira Math
  3. 最后改改css或者先rebuild再改css之类的.

在与 Fira Math 的作者交流之后意识到:
  • 在 1. 中把Fira Math 像computer modern一样分成小文件可能可以避免让我需要修改大量KaTeX代码. KaTeX这样做是因为cm在TeX中是Type 1 font, 单个文件只能容纳256字符.
  • Fira Math是Open Type font. glyph id 到对应字符的 unicode 码位的映射已经包含在了字体的cmap表中. KaTeX生成的html里是unicode字符, 所以我不需要为Fira Math修改src/metrics/mapping.pl, 但是仍然需要对应的src/fontMetricsData.js.

3 Extract fonts

KaTeX 把\mathbb{A} \mathcal{B} 等等符号全部映射到普通拉丁字母 A,B 上, 这就导致需要在映射的时候做点工作. 而且另一个问题是 KaTeX_AMS-Regular 等字体在使用 unicode private use area 表示一些 unicode 中缺少的符号, 比如src/fonts/makeFF中有这样的代码
    0x0A => 0xE010,         # \nleqslant
    0x0B => 0xE00F,         # \ngeqslant

因为这些不是我会用的符号, 我决定先不管它.

KaTeX_Main-Regular, KaTeX_Math-Italic.ttf 这些字体表现比较正常, 基本都是 Fira Math 的子集, 用这里的代码转换. 对于 KaTeX_AMS-Regular 和 KaTeX_Size{k} 这些字体就需要单独处理, 我决定下载 fontforge 手动处理. Caligraphic 和 Fraktur 字体 Fira Math 中也没有覆盖 , Fira Math 本来就是 sans serif 字体, 所以我觉得其他字体都可直接用 KaTeX 版本.

3.1 Italic

看起来 Fira Math 字体并无斜体, 查了查貌似 TeX 是通过 OpenType MATH table 来实现斜体, 我需要想想如何搞到 Fira Math 的斜体版本… 原来 Fira Math 是有斜体的, 用 fontforge 修改比手写 unicode 映射要简单点(目前只有常用符号做了修改, 很多东西不能正常工作) KaTeX 的很多设计让人疑惑, 为什么不做像 unicode-math 一样的设计, 把所有东西映射到正确的 unicode 上呢? 又比如\(\neq\), KaTeX 的做法是把一个\(=\)和一个类似\(\setminus\)的东西组合起来, 而且在字体中这个像\(\setminus\)的字符被映射到了 private use area.

Fira Math specimen 有点老旧, 实际上其中很多 missing glyph 现在已经补上了. Texlive 里的 firamath-regular.otf 貌似也是旧版本, 比如缺少(0x2216 ∖)

事实上从 KaTeX 的 commit history 能看到字体主要是 ylemkimon 的工作. 我发邮件问了问他这些问题, 没有得到回复. 不过我看到这个知乎回答之后觉得映射问题可能是为了兼容 screen reader? 不过我不觉得这样做就能让 screen reader 正确读出公式. 另外要谢谢曾祥东老师, 他是 Fira Math 的主要作者, 读博客介绍 FiraMath 的知乎回答都让我学到很多东西.

如果只是更换字体的话, 很多东西看起来有点奇怪
修改字体、没有调整metric

但是我觉得最常用的 LP, SDP 之类的规划问题显示起来效果还不错
XeLaTeX with firamath

以下是 mathjax 4.0

\[\begin{equation} \begin{aligned} \min& & \sum_x \delta_x& & &\\ s.t.& & (1-\delta_x - \delta_y) d^2(x,y)\leq \|v_x-v_y\|^2 &\leq (c^2+(\delta_x+\delta_y)f(k)) d^2(x,y) & &\forall x,y\in X\\ & & \delta_x\in [0,1], v_x&\in \R^p & &\forall x\in X \end{aligned} \end{equation}\]

4 接下来…

现在 Typst 看起来是个输出有点排版需求的 html 内容的好选择. 这里是个例子, 可以看到公式被 Typst 变成了 svg. 效果不错, 但是不知道如果有很多公式的话绘制一堆 svg 会不会很慢, 页面会不会变得很大.

如果要修改 KaTeX 的话, 我觉得会有很大的工作量. 一方面 KaTeX 本身有点古老, 很多功能的实现方式太局限了. 另一方面我不懂 js 也不懂排版, 估计需要了解 open type 字体、unicode-math 的工作方式等.

4.1 Typst svg

用这个来让 Typst 在 html 导出中把所有公式都变成 svg.
#show math.equation: html.frame
#show math.equation.where(block: false): box
typst compile input.typ -f html htmloutput.html --features html

测试用的文档是 https://typst.app/project/rgLBUHLLRwTyTh16Ej3qlM
+-----------------+-------+
|    outputs      | size  |
+-----------------+-------+
| PDF-XeTeX       | 25KB  |
| PDF-Typst       | 39KB  |
| html-Typst      | 194KB |
| FiraMath font   | 122KB |
+-----------------+-------+

5 MathJax 4.0 is out

https://docs.mathjax.org/en/latest/upgrading/whats-new-4.0.html

MathJax 支持 FiraMath. 所以不用再修改 KaTeX.