在打印视图和电子邮件中包含已发布的 Discourse-math

问题
Discourse-math 在浏览器中渲染效果极佳,但在无法运行 JavaScript 的环境中,数学公式会回退为原始 LaTeX 源代码。两个影响重大的场景:

  • 打印视图(/print 或浏览器打印为 PDF): 数学公式显示为 $...$ 而不是渲染后的符号。
  • 电子邮件(摘要、通知、订阅): 数学公式以原始形式发送,收件人除非复制粘贴到 LaTeX 编辑器中,否则无法阅读。

这破坏了依赖 Discourse 处理技术内容的社区的重要工作流程。


重要性

  • STEM(科学、技术、工程、数学)领域的教育者和研究人员经常需要打印主题转发电子邮件。如果数学公式无法辨认,这些导出将无法使用。
  • 打印/电子邮件中的其他缺失功能(隐藏内容、oneboxes、图片)已得到修复——数学公式是一个明显的遗漏。
  • 在非 JS 环境中支持已渲染的数学公式将使 Discourse 成为技术社区的一流平台。

解决方案建议

  • 轻量级步骤(打印):
    • 确保 /print 视图包含已渲染的数学公式 HTML。
    • 在打印前触发 MathJax 排版。
  • 重量级步骤(电子邮件):
    • 探索服务器端 MathJax 渲染,如 Sam 建议的那样
    • 将数学公式渲染为 SVG 或预渲染的 HTML,以便收件人可以直接在电子邮件客户端中看到清晰的方程式。
  • 可选站点设置:
    • “在打印视图/电子邮件中使用已渲染的数学公式”→ 允许管理员在原始源代码和渲染后的数学公式之间进行选择。

相关问题/先前工作

  • :white_check_mark: [details] 面板内的数学公式已修复(PR #111,2025 年 6 月)。
  • :memo: 打印视图已对隐藏内容/oneboxes 进行修复 → 包含已渲染元素的先例。
  • :e_mail: 电子邮件:Sam 的评论(#214)将服务器端渲染确定为长期解决方案。

摘要/TL;DR
目前,打印视图和电子邮件中的数学公式会回退为原始 LaTeX。添加已渲染的数学公式将使 PDF 和摘要可读、专业且与用户在浏览器中看到的内容一致

在 Discourse 上使用 iOS Safari 复现

即使在 Safari 中启用了请求桌面站点,当打开 /print 时,Discourse 仍然会附加
?mobile_view=1,这强制使用了简化的移动打印视图。

解决方法:手动更改为 ?mobile_view=0 以获得完整的桌面打印布局。

示例
/t/fw-the-email-subject/12345/print?mobile_view=0

3 个赞

是的,我认为 Sam 的总结很准确:如果我们希望电子邮件/打印视图显示渲染后的数学公式,我们需要服务器端数学渲染(或至少是服务器端“预渲染”),因为电子邮件客户端不会运行 MathJax。

一个现实的方法是:

  • 在编译(cooking)期间(或编译后在后台作业中),查找数学跨度(行内 + 显示)。
  • 使用 MathJax 在 Node 环境中将每个表达式渲染为 SVG(或 MathML 作为后备)。
  • 将编译后的 HTML 中用于电子邮件/打印的数学公式替换为以下之一:
    • 行内 \<svg ...\>(最佳保真度,无需外部获取),或
    • \<img src=\"data:image/svg+xml;base64,...\"\>(对某些客户端兼容性更好,但可能会很大)。
  • 使用稳定的密钥(例如 sha256(latex + display_mode + macros + font_config))进行缓存,这样我们对每个唯一的公式只渲染一次。

棘手的部分(如果范围界定得当,是可以管理的):

  • DOM 模拟:MathJax 的“浏览器”输出需要一个 DOM;因此我们可能需要 mathjax-full + jsdom(或使用纯适配器路线)。
  • 性能/超时:在作业队列中异步执行此操作,并优雅降级(如果渲染失败,则保留 LaTeX 不变)。
  • 电子邮件客户端的怪癖:一些客户端会剥离 SVG;因此制定后备计划很重要(例如,纯文本/LaTeX,或在支持 MathML 的地方使用 MathML)。

如果有人想快速进行实验,我首先会做的实验是:

  1. 一个使用 mathjax-full 的微型 Node 脚本,将 $begin:math:text$E\\=mc\\^2$end:math:text$ 渲染为 SVG,
  2. 查看它做出了哪些假设(DOM 与适配器),
  3. 然后决定 MiniRacer 是否是死胡同,我们应该将其视为“需要 Node 可用”(或一个小型服务)。

如果该实验成功,我们就可以讨论它插入的位置(电子邮件管道与编译与打印视图),以及我们缓存/存储的内容。