gt表格中的KaTeX公式未能正确转换为DOCX公式
我有一个gt表格,我使用 gt::md() 来添加LaTeX公式。这个在html导出时工作良好,但这并不能在docx中渲染为有效的公式。它将公式原样保留,没有任何解决方案。
---
title: "Testing"
format:
docx: default
html: default
---
library(gt)
exibble |>
dplyr::select(currency, char) |>
gt() |>
tab_header(title = md("$$\\frac{1}{2}$$")) |>
cols_label(
currency = md("$$Currency_{1}$$")
)
以下是docx结果的截图:

以下是在HTML中的正确期望格式:

我在寻找一个能同时适用于这两种格式的解决方案。
解决方案
用LaTeX编写的数学公式现在可以通过md() 和fmt_markdown() 渲染为HTML。LaTeX公式可以在 $ 或 $$ 分隔符之间设置,用于行内或块级渲染。虽然这需要katex包来工作(这是一个可选依赖),但输出中的公式渲染是无依赖的 (#375, #616, #1163)。 (#1578) [1]
这似乎表明 {gt} 无法将MathJax转换为Word的 Office Open XML。
现在我们可以将gt表格导出为Word文档。这要归功于 @thebioengineer(Ellis Hughes!),他不仅通过gtsave() 实现了这种输出(写出.docx文件),还通过as_word()(给你一个OOXML字符串)实现了同样的效果 (#121, #929)。 (#962, #986, #1016) [2]
听起来似乎能保留格式,但gt仓库中关于docx的开放问题却给出不同的情况。
1. {flextable}
我建议使用 flextable,它让你可以使用 as_equation 来创建在HTML和 Docx输出中都能正确显示的“有效”公式。
---
title: "Testing"
output:
word_document: default
html_document: default
---
```{r, echo=FALSE, message=FALSE}
library(gt) # for exibble
library(flextable) # for flextable
library(ftExtra) # for colformat_md formatting markdown
library(equatags) # To use "as_equation", package 'equatags' is required; also equatags::mathjax_install() must be executed only once to install necessary dependencies.
set_flextable_defaults(na_str = "NA")
flextable(exibble[, c("currency", "char")]) |>
set_header_labels(currency = "$$Currency_{1}$$") |>
# format markdown within header and body!
colformat_md(part = "all") |>
autofit() |>
set_caption(
caption = as_paragraph(
as_equation("\\dfrac{1}{2}")
)
)
得到
[](/uploadfile/blog2026/9640658002ad4412a91e2550e48e1860.jpg)
---
### 2. 使用图片(折衷做法)
一种折衷的解决方案是使用 `gtsave()` 将你的 `gt` 表格截成图片,再由它包装 `webshot2::webshot`。不过要让它工作,我们需要用 `gt::html(katex::katex_mathml("\\frac{1}{2}"))` 将你的公式转换为 HTML,这样转换后的 MathJax 才能被正确捕获。
```none
---
title: "Testing"
format:
docx: default
html: default
---
```{r echo=FALSE, message=FALSE, warning=FALSE, out.width = "200px"}
library(gt)
library(katex)
X <- exibble[, c("currency", "char")] |>
gt() |>
tab_header(title = html(katex_mathml("\\frac{1}{2}"))) |>
cols_label(
currency = html(katex_mathml("Currency_{1}"))
)
gtsave(X, "table.png", zoom = 7)
knitr::include_graphics("table.png")
得到
[](/uploadfile/blog2026/c47cc1da496c46d0a2c66120f48ca6a5.jpg)
---
### 3. 将 $$content$$ 替换为在 [word run](https://learn.microsoft.com/en-sg/answers/questions/5644120/microsoft-word-fails-to-correctly-render-nested-ma) 内的有效 MathML
[`equatags::transform_mathjax()`](https://www.rdocumentation.org/packages/equatags/versions/0.2.2/topics/transform_mathjax) 提供了一种将 MathJax 转换为“MathML”XML 的方法,如下所示:
```r
transform_mathjax(x = "Currency_{1}", to = "mml") |> cat()
<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mml="http://www.w3.org/1998/Math/MathML">
<m:r>
<m:t>Currenc</m:t>
</m:r>
<m:sSub>
<m:e>
<m:r>
<m:t>y</m:t>
</m:r>
</m:e>
<m:sub>
<m:r>
<m:t>1</m:t>
</m:r>
</m:sub>
</m:sSub>
</m:oMath>
现在如果你把gt表导出为word-xml,你可以清楚地看到MathJax Currency_{1} 并没有被转换成Word能正确读取为公式的有效OOXML:
exibble[, c("currency", "char")] |>
gt() |>
tab_header(title = md("$$\\frac{1}{2}$$")) |>
cols_label(currency = md("$$Currency_{1}$$")) |>
as_word() |> cat()
## ...
<w:t xml:space="preserve">$$Currency_{1}$$</w:t>
## ...
因此思路是用上方由 transform_mathjax 提供的MathML替换 $$ 标签内的内容,然后把这些片段插入到Word运行标签中。注意:只有输出为Word时才会执行此步骤。如果输出为HTML,则会输出你正常的 gt 小部件。
代码
---
title: "Testing"
format:
docx: default
html: default
---
```{r echo=FALSE, message=FALSE, warning=FALSE, results='asis'}
library(gt)
library(equatags)
library(gsubfn)
X <- exibble[, c("currency", "char")] |>
gt() |>
tab_header(title = md("$$\\frac{1}{2}$$")) |>
cols_label(currency = md("$$Currency_{1}$$"))
if (!knitr::is_html_output() & !knitr::is_latex_output()){
replace_math <- \(x) gsubfn("\\$\\$(.*?)\\$\\$", \(m) sprintf("<w:r>%s</w:r>", transform_mathjax(m, to="mml")), x)
cat("```{=openxml}", replace_math(as_word(X)), "```", sep = "\n")
}else X
```
注意事项:
- "1/2" 会被写成表格捕获文本,而不是居中显示在表格之上
- 如果你打开这个Word文件,会出现一个与安全相关的弹出消息
