有没有办法从 LaTeX 数学公式生成 SVG 文件,不是从轮廓字形构建?即,我希望 SVG 文件包含<text>
标签内的数学字形,而不是<path>
标签。
背景
长期以来,MathJax 一直是在网页上显示数学的唯一体面方式。但是,使用 MathJax 加载满是数学的页面可能会非常慢。最近,出现了 KaTeX 项目,它提供了一种更快的渲染方法,但对 LaTeX 命令的覆盖范围比 MathJax 要小一些。但我想知道为什么似乎没有人想到在网络上进行数学排版的显而易见的 SVG 解决方案。嵌入在<text>
标签中的 SVG 文本具有固定位置、可扩展和可搜索,并且可以指定任何字体集。
因此,网络上数学排版最简单的解决方案似乎是直接进行 pdf/dvi/xdv --> svg 转换。这样,人们甚至可以通过 XeTeX/LuaTeX 利用 OpenType 数学字体。
现有工具
- Inkscape。但是,由 pdfLaTeX、XeLaTeX、LuaLaTeX 生成的简单 pdf 在导入 Inkscape 时会部分显示为垃圾,除非通过 poppler 按照概述的路径导入。
- 编辑。此工具有一个 pdf-to-svg 插件,但同样,它只能提供轮廓字形。
- MathJax。是的,MathJax 可以输出 SVG,但只能输出为轮廓字形。
- 韋斯維爾。迄今为止我发现的最佳解决方案。它以公式作为
<text>
标签,但有些字符显示错误。
强力解决方案
如果我知道 dvi 格式的具体细节,我可能会尝试自己实现一个解决方案。但是,我最终可能会重新实现 dvisvgm,结果却发现我的程序以与 dvisvgm 和 Inkscape 相同的方式失败 - 原因相同(尚不清楚)。
编辑:
在摆弄了 dvisvgm 工具之后,我接近找到解决方案。示例 XeTeX 文档:
\documentclass{standalone}
\usepackage{amsbsy}
\usepackage{fontspec}
\usepackage{unicode-math}
\usepackage{xunicode}
\setmainfont{XITS}
\setmathfont{XITS Math}
\begin{document}
aA$aA\mathrm{aA}\mathbf{aA}\mathbfit{aA}\alpha\boldsymbol{\alpha}\infty\ell^{a\alpha\infty}\int\sum\displaystyle\int\sum\mathcal{A}\mathbb{A}\mathfrak{A}$
\end{document}
通过 dvisvgm 运行 .xdv 文件并进行一些手动编辑后,我获得了 svg 文件
<?xml version='1.0'?>
<!-- This file was generated by dvisvgm 1.13 -->
<svg height='22.5026pt' version='1.1' viewBox='-72 -70.0443 176.006 22.5026' width='176.006pt' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>
<style type='text/css'>
text.f0 {font-family:"XITS";font-size:9.96264px}
text.f1 {font-family:"XITS Math";font-size:9.96264px}
text.f2 {font-family:"XITS Math";font-size:9.96264px}
text.f3 {font-family:"XITS";font-weight:bold;font-size:9.96264px}
text.f4 {font-family:"XITS Math";font-size:9.96264px}
text.f5 {font-family:"XITS Math";font-size:7.44319px}
</style>
<g id='page1'>
<text class='f0' x='-72' y='-58.2067'>aA</text>
<text class='f1' x='-60.3836' y='-58.2067'>
答案1
根据马丁的评论和我自己的研究,我决定回答这个问题。
是的,从 .dvi 到无轮廓 .svg 的转换是可行的。最适合这项工作的工具是dvisvgm
Martin Gieseking,但它最适合与 XeTeX 配合使用。通过 运行 XeTeX 生成的 .xdv 文件dvisvgm
,可以得到一个带有嵌入字体的 .svg 文件。通过删除指定嵌入字体的序言,并正确重命名 .svg 文件中的字体,可以获得所需的结果。
除了一件事:XeTeX 使用了一些不直接映射到 unicode 字符的字形。具体来说,这适用于大运算符,它们对\displaystyle
和有不同的字形\textstyle
。这些\displaystyle
字形在字体中“隐藏”。理论上,可以使用例如标签从 SVG 访问这些字形<glyphRef>
。但几乎没有主流浏览器支持此功能。
这个问题最简单、最安全的解决方案似乎是编辑字体文件,并为显示样式字形提供明确的 Unicode 映射。这样,提供的 .svg 文件dvisvgm
就可以与修改后的字体一起使用,在网络上显示数学方程式。
示例 Python 脚本使用 FontTools/TTX 库将未编码的字形映射到以 0xF0000 开头的 PUA 区域:
fontFile = "C:\\Windows\\Fonts\\xits-math.otf"
outFile = "C:\\Windows\\Fonts\\xits-mod-math.otf"
font = ttLib.TTFont(fontFile,
allowVID=False,
checkChecksums=False,
recalcBBoxes=False,
recalcTimestamp=True,
lazy=True)
font['cmap']; #Load the cmap table into font.tables
all_glyphs = font.getGlyphOrder()
for i, subtable in enumerate(font.tables['cmap'].tables):
if subtable.format == 12:
encoded_glyphs = subtable.cmap.values()
unencoded_glyphs = [g for g in all_glyphs if g not in encoded_glyphs]
charcodes = range(0xF0000, 0xF0000 + len(unencoded_glyphs))
new_cmap = dict(zip(charcodes, unencoded_glyphs))
font.tables['cmap'].tables[i].cmap.update(new_cmap)
font.save(outFile, False, False)
请注意,脚本会覆盖 PUA 区域中可能存在的映射。更复杂的脚本也可以处理这个问题。并且只添加了 cmap 格式 12 中的映射,如果字体中存在格式 10 和 8 的映射,则添加它们可能也很有意义。