公式渲染方案

我在写博客主题时,经常会列出来可能会使用哪些信息表现形式,几乎所有内容发布平台都支持的有:文字、图片、超链接。但是,我作为编程人员,代码也应当支持。然后,还有很多其他形式要考虑:音频、视频、公式等等,不仅仅要支持,还要考虑支持到什么程度,用户体验会更好些。大体上,信息可能会有如下表现形式:

  • 文字
  • 图片
  • 表格
  • 音频
  • 视频
  • 公式
  • 代码
  • 等等

另外,还有可编辑代码、可交互式动画、VR 等表现形式,乃至游戏也算一种。不过有些几乎所有场景都需要的,有些特定场景才需要。

公式作为知识的重要组成部分,很多场景也都需要,尤其数学和物理相关领域,比如百科网站、问答网站、在线教育网站、内容发布平台、论文、书籍等。而现在信息展现的媒介也是多种多样,有书籍、电脑、平板、手机等。一些与公式展示相关的指标有:网络传输速率、屏幕尺寸、像素密度、字体的支持等。

公式通常使用特定的标记语言来书写,比如 TeX。TeX 是数学家和计算机科学家 Donald Knuth 设计的一个排版系统,广泛用于数学、计算机科学等领域的公式排版。

公式的渲染要考虑下面问题:

  • 屏幕尺寸:从手机到宽屏桌面显示器,也包括书籍。
  • 屏幕像素密度:有时公式可能以栅格图片展示,要考虑到高像素密度屏幕,随着技术发展也会越来越高。
  • 性能:我们可能会一次性渲染很多公式,也包括非常复杂的公式,我们期望能尽快高质量将渲染好的公式呈现给用户。

按照渲染的设备划分可有三种方式:客户端渲染、服务端渲染、客户端与服务端混合渲染。

客户端渲染

顾名思义,用户端渲染,有电脑、手机、平板等。应用类型主要是:原生应用与 Web 应用。当然,因为两类应用都支持图片,所以可以使用图片形式来展现公式。

无论是桌面端还是移动端原生应用,因为无相关经验,不太了解,客户端渲染公式方式。Github 上面可以搜到一些渲染公式的包,但是对于可靠性不太了解。

Web 应用主要使用 MathJaxKaTeX 两个包。

MathJax

  • 高质量:精确到像素的排版
  • 支持 LaTeX,MathML 等语言
  • 可扩展
  • 可靠性:无论是从开发者背景,还是使用者背景,都是经过多年检验的
  • 可访问性
  • 渲染速度慢:尤其遇到复杂的公式时

KaTeX

  • 渲染速度极快:大约 10 倍于 MathJax
  • 可靠性:可汗学院开发与使用
  • 渲染质量:满足大部分需求

可以查看 compare the output of KaTeX with MathJax 两者的渲染速度与渲染效果。

另外,在实际的使用当中,结合使用两者效果更好,优先使用 KaTeX 来渲染,如果渲染失败就是用 MathJax 来渲染。

服务端渲染

即使结合使用了 KaTeX 和 MathJax,有些时候仍然不能满足要求,用户不希望等三秒以上才能渲染好公式。通常,公式的数量也是有限度的,按照一亿用户,每人贡献 100 个公式,那就是大约 100 亿条公式,可能还有一些重复的。没有必要用户每次查看时都重复渲染一遍,也没有必要在用户的设备上面各自渲染。

缩减计算时间的第一条规则就是空间换时间,也称之为缓存

公式通常使用 TeX 标记语言来书写,输出结果通常有:HTML、MathML、图片,其中图片又分栅格图片(png、jpg、webp)和矢量图片(svg)。输出结果自然是计算的结果,然后可以使用分布式缓存(如CDN)来分发这些输出结果。

接口设计

创建公式对象

POST /equations
  • 请求参数:

    • input:可选,输入格式,比如 tex,mathml, asciimath,默认 tex。
    • output:可选,输出格式,可选值:svg, png, mathml, html 等,可以多个组合,默认 svg。
  • 请求内容:

    • 公式标记语言。
  • 返回值:公式对象 ID。

接受公式标记语言,比如 TeX、MathML、AsciiMath 等,分配一个唯一标识 ID,然后渲染公式为指定的一个活多个输出格式,公式输入与渲染结果都存储起来,可能还包括自动分发 CDN。

获取公式对象

GET /equations/:equationId
  • 请求参数:

    • output:输出格式。
  • 返回值:公式渲染结果

渲染服务

可以使用 Node 来运行 MathJax 或者 KaTeX,并结合 Puppeteer 来转成图片,当然这种方式性能不高,不过基本需求可以满足。如果对于渲染服务性能要求很高,可以考虑自己写 TeX 渲染组件或者考虑其他语言的 TeX 渲染组件,这方面不太了解,只知道维基百科就是自己写的 TeX 渲染组件。

性能

  • 使用 CDN,可以多域名来突破浏览器同时请求数最大上限。
  • 图片延迟加载:即公式图片将要进入可视区域再加载。

栅格图片与矢量图片

因为屏幕的像素密度有高有低,而栅格图片放大与缩小通常采用插值算法,这样会造成模糊,可以针对不同像素密度生成不同的图片来解决。如果使用矢量图片(比如 SVG)就可以无限缩放。

混合渲染

即时我们采用了服务端渲染,并且做了一些性能优化,如果一个页面有很多公式,可能几十上百个,有简单的,也有复杂的。如果每个简单的公式都发送一个单独的请求,对于网络环境较差的客户端可能会加载慢,降低用户体验。

可以将简单的公式与复杂的公式区分开:简单的公式使用客户端渲染,复杂的公式使用服务端渲染。简单与复杂划分的界限可能是由公式标记语言字节数传输时间加上公式客户端渲染时间公式单独一个请求传输时间比较来决定。

可搜索性

常见的搜索功能都是输入一些关键词,根据关键词匹配,也有搜索引擎支持图片搜索。简单的文本输入很难满足公式,而如果让用户使用 TeX 之类标记语言输入公式又才繁琐,可以通过手写公式,然后以图片形式传给搜索服务,可能会经过手写识别、公式识别等一系列解析,然后解析成某种格式,比如 TeX,然后使用 TeX 标记语言来匹配,至于匹配算法也会与常见的字符串匹配不同吧。

公式渲染不仅仅输出图片,也要输出其他标记语言,比如 MathML,然后便于搜索爬虫抓取,然后用于识别匹配。

目前一些国内拍题搜答案的移动应用比较类似,上传题目图片搜结果,这方面特别依赖人工智能相关技术,