Typst 升级为一等公民服务:文档排版渲染即服务
高校的运转离不开文档:学位论文、试卷、讲义、证书和行政公函。这些文档大多仍靠手工或脆弱的本地工具生成。在刚完成的集成冲刺中,Typst(一种基于标记语言的排版系统、LaTeX 的现代替代品)已成为 openEduSuite 的一等服务,可以驱动确定性的、基于模板的文档生产。
排版能力为何属于服务套件
服务套件已通过办公与编辑器服务覆盖了协作编辑。所缺少的是生产级排版:在服务器上依据模板和数据、以可复现的方式编译文档。这是另一种工作:从注册数据生成试卷、批量生成证书、从结构化数据渲染公函,它需要的是排版引擎,而不是交互式编辑器。
上游跟踪的 fork,而非分叉项目
套件使用 tobias-weiss-ai-xr/typst,这是 typst/typst 的一个 fork,并遵循严格的分离策略:
- 每周上游同步:计划工作流将上游
main合并进 fork。合并冲突会让工作流明确失败,由人来解决,而不是被悄悄掩埋。 - fork 专属代码各安其位:我们新增的一切都位于
render-service/和.github/workflows/之下,保证未来的上游合并不产生冲突。 - 版本号跟随上游:当前版本
v0.15.1沿用上游版本号,并在每次同步时重置。
目标是保持为一个精简、守规矩的 fork:我们提供的是部署价值,而非引擎分歧。
边界分明的渲染服务
新的渲染服务是围绕 typst CLI 的一个刻意精简的 HTTP 封装,仅使用 Python 标准库编写,无框架、无需审计的依赖:
POST /render {"source": "= Hello", "format": "pdf|png|svg",
"assets": {"data.csv": "..."}} -> 文档字节
GET /healthz -> {"status": "ok", "typst": "typst 0.15.1 ..."}
信任边界在进程内强制执行:
- 请求上限(请求体 5 MB、源码 2 MB)与 60 秒编译超时。
- 资源名称仅限扁平标识符,不存在路径穿越。
- 编译在隔离的临时目录中进行并限定根目录;编译器没有任何网络访问。
- 编译并发受限制,以保护集群节点免受 CPU 饱和影响。
- 容器以非特权用户运行服务。
- 镜像内置 Liberation 字体,覆盖大多数机构模板所依赖的度量兼容的 Arial/Times/Courier 字体族。
冲刺中的一条小经验:即便是零依赖的封装也需要真正的 API 测试。编译路径中的一个关键字参数不匹配问题被发现并修复,现在有一个 HTTP 端到端回归测试守护着它。
自主交付的镜像
每个版本都会向 GitHub Container Registry 发布两个多架构镜像(linux/amd64 与 linux/arm64):
ghcr.io/tobias-weiss-ai-xr/typst:原生 typst CLIghcr.io/tobias-weiss-ai-xr/typst-render:渲染服务
发布工作流自动构建、冒烟测试并发布这两个镜像;冒烟测试结果与镜像摘要(digest)绑定;被测试过的内容可证明就是被交付的内容。
双栈部署
| 栈 | 接入方式 |
|---|---|
| Kubernetes(高等教育) | Helmfile chart,锁定到发布标签 |
| Docker Compose(中小企业) | Compose 栈中的专用服务配置 |
两个部署都遵循与其他套件服务相同的纪律:仅走内部网络路径、基于 /healthz 的存活探针、按发布标签锁定镜像。该服务按设计面向机器,即模板流水线、管理工具与门户后端调用 API;它不直接暴露给浏览器。门户中供交互写作的入口指向 Typst 托管编辑器。
下一步
- 模板仓库:面向机构文档(试卷、证书、公函),供渲染 API 消费。
- 流水线集成:例如按计划从注册数据生成证书。
- 持续的上游跟踪:每周同步让 fork 在快速演进的 Typst 生态中保持最新。
行动号召
- 深入阅读:技术细节见姊妹篇《Typst 文档渲染架构》。
- 动手尝试:将模板流水线指向渲染 API,一个携带源码与资源的
POST /render调用即可。 - 参与共建:加入 openEduSuite 社区,一起建设模板仓库。
确定性排版,像其他服务一样部署;文档同样是基础设施。
商标声明: Typst 是 typst(typst.app)的产品。本文为独立内容,与 typst 无任何隶属、背书或赞助关系。其他所有产品名称均为其各自所有者的商标。