flowchart LR
A["linebreak.lua<br/>默认滤镜"] --> B["crossref-tight-num.lua<br/>post-render"]
B --> C["docx-table-caption.lua<br/>post-render · 仅 docx"]
C --> D["docx-table-layout.lua<br/>post-render · 仅 docx"]Quarto 中文书稿 docx:图题、表题与交叉引用滤镜
可复用前提、必备 YAML,以及 scripts/quarto-docx 的挂载顺序
摘要
中文书稿把 Quarto 渲成 Word 时,默认会得到出版社不接受的版式:网站项目的图号写成「图2- 13」,book 项目写成「图 3.1」;表题/图题被包进 1×1 外层表且左对齐;短列(年份/序号)被 Pandoc 按字符数压到折行。本包把 report-tech2026 已经跑通的 Word 参考文档 + Lua 滤镜 收到 harness scripts/quarto-docx/。同一目录里有两个安装剖面:网站用 图N- 收紧,book 用 图N-M 改写,并可选一页横向表。
核心结论:
- 这套滤镜不是通用英文论文模板。网站剖面假定前缀是「图{章号}-」「表{章号}-」。book 剖面假定前缀只是「图」「表」,由滤镜把
3.1收成图3-1。不要把网站的图2-写进 book 的 YAML。 - 软件底线是 Quarto 1.8+(已在 1.8.26 验证;1.10.18 于 2026-09-29 复测网站剖面,并于 2026-10-15 用
run-check-all.ps1复测网站 + book 剖面通过)。不要给Para/Table写.attr。 - 两个剖面的编号滤镜不要同时挂。题注拆壳与列宽都在
post-render。横向页滤镜只在 book 剖面,并且必须at: pre-quarto。只写项目根_quarto.yml时,单独渲某一章可能吃不到滤镜。
可拷贝源与安装命令见 目录索引。
1. 这套包解决什么、不解决什么
1.1 解决
| 现象 | 滤镜 / 模板 |
|---|---|
| 「图2- 13」「(见 图2-13 )」(网站,前缀里已有章号) | crossref-tight-num.lua |
| 「图 3.1」「见表 3.1a」(book,前缀只有「图」「表」) | crossref-book-num.lua |
| 宽表需要单独一页横向 A4,且不能多出空白纵向页 | docx-landscape.lua(pre-quarto,只认一层 landscape) |
| 中英双行题注挤在一段 | linebreak.lua(把 <br> 变成换行) |
表题在表内、图题在图框内,样式还是 Image Caption、左对齐 |
docx-table-caption.lua + 模板样式「表题」「图题」 |
| 「年份」「2024」列过窄、表头看起来像小四 | docx-table-layout.lua + 模板 Table firstRow |
1.2 不解决
- Windows 默认
png()+ 宋体出空白图(那是 knitrdev = ragg_png,不是本包) - 纵轴题竖排、xlsx 源图、DEA / 年鉴数据
- Word「插入题注」那种
SEQ域;Quarto 的「表2-1」是静态字 - 英文-only
fig-title: "Figure"的 IEEE/APA 论文版式
2. 可复用前提(不满足则不要整包拷贝)
下面是硬前提。缺任何一条,滤镜会静默少做事或拆错对象。
2.1 软件
| 项 | 要求 | 说明 |
|---|---|---|
| Quarto | 1.8+(已验证 1.8.26、1.10.18) | 1.8 的 walker 给 Para/Table 赋 .attr 会报 Trying to set unavailable property attr。本包已经避开;1.10.18 上网站与 book 结构验收均通过(最近一次含 book:2026-10-15)。未在 1.7 验证。 |
| Pandoc | 随 Quarto 捆绑即可 | 不必另装 |
| 输出格式 | 至少要渲 docx | crossref-tight-num.lua 对 html 同样有效;caption / layout 仅 docx |
| Word | 能打开 reference-doc |
验收时看样式廊是否有「表题」「图题」 |
2.2 网站剖面:交叉引用前缀是「图N-」「表N-」
网站滤镜用首字符「图」或「表」+ 一位及以上数字 + 连字符识别前缀,例如 图2-、表5-、图10-。这不是 book 的写法。book 见下一节。
YAML 必须类似:
crossref:
title-delim: "" # 只管序号与题注正文;去不掉前缀与序号之间的 nbsp
fig-title: "图2-"
fig-prefix: "图2-"
tbl-title: "表2-"
tbl-prefix: "表2-"| 写法 | 本包是否处理 |
|---|---|
图2- / 表5- |
是(设计目标) |
图 2-(前缀内空格) |
否 |
仅 Figure / Table(英文 Quarto 前缀) |
否;手写在 fig_cap 里的 Figure 2-13 不受 nbsp 影响,也不走 tight-num |
自定义 float 覆盖 key: fig / tbl 并开 space-before-numbering: false |
不要;会破坏 knitr #\| label: fig- |
title-delim: "" 仍建议保留,但它替代不了 crossref-tight-num.lua。
2.2a book 剖面:前缀只写「图」「表」
Quarto book 在前缀后插入不间断空格和 章.序号(可带字母后缀,如 3.1a),页面上是「图 3.1」。crossref-book-num.lua 把它收成「图3-1」「表3-1a」,并去掉中文语境里交叉引用两侧多余的空格。源文件仍要写成 见 @fig-id,@ 不能贴住前面的字。
crossref:
title-delim: ""
fig-title: "图"
fig-prefix: "图"
tbl-title: "表"
tbl-prefix: "表"不要在 book 里写 fig-title: "图2-"。那是网站剖面的手写章号,和 book 的自动 章.序号 叠在一起会变成「图2-3.1」一类无法改写的串。
题注拆壳同时接受两类编号:网站的 图2- / 图2- 13,以及 book 的 图3-1 或尚未改写的 图3.1 / 图3.1a。因此两个剖面共用 docx-table-caption.lua。
横向页不是编号的一部分。需要时只标记一次 class landscape:包住浮层和紧随的「注」「资料来源」,或只写在 chunk 的 classes 上。滤镜在 pre-quarto 去掉这个 class,避免 Quarto 自带的 landscape 处理再插入空的分节符。页脚关系默认 rId14(本包 reference-doc.docx 里页码页脚的 id)。业务仓参考文档若不同,设置元数据 docx-landscape-footer。纵向页的纸型和页边距仍由该项目自己的 reference-doc 决定;滤镜只在它切开的分节上写入 A4 尺寸。
2.3 题注文本:中文行 + <br> + 英文行(推荐,非识别所必需)
业务仓约定:
fig_cap <- glue("旱区……分布情况<br>Figure 2-{num_fig} Amounts distribution of …")
tbl_cap <- glue("……名单<br>Table 5-{num_tbl} Illustrative …")- 中文行由 Quarto 再在前面加上
图2-13/表5-1。 - 英文行是字符串里手写的
Figure 2-N/Table 5-N,不是fig-title。 <br>必须配linebreak.lua,否则 Word 里中英挤在一行。
拆壳滤镜识别题注的方式有两条,满足其一即可:
- 题注纯文本以网站编号
图{数字}-/表{数字}-开头,或以 book 编号图3-1/图3.1/图3.1a开头;或 - 段落 OpenXML 带
ImageCaption/TableCaption/BiaoTi/TuTi。
因此:可以只用中文题、不写英文行,拆壳仍会工作。双语是出版社版式,不是 Lua 的硬编码。
2.4 chunk 标签与正文引用
| 项 | 必备写法 | 反例 |
|---|---|---|
| 图 chunk | #\| label: fig-… 且 #\| fig-cap: |
去掉 label 以「躲开」浮层(交叉引用会断) |
| 表 chunk | #\| label: tbl-… 且 #\| tbl-cap: |
同上 |
| 正文 | 见 @fig-id(@ 前、label 后都有空格) |
见@fig-id(Pandoc 当 citation 粘住,根本不编号) |
拆壳靠 fig- / tbl- identifier 判断图还是表。不要把图标成 tbl-。
2.5 Word 参考文档里的样式(本包已备好)
reference-doc.docx 必须含:
| styleId | Word 样式名 | 用途 |
|---|---|---|
BiaoTi |
表题 | 表外题,居中 |
TuTi |
图题 | 图下题,居中 |
Compact |
Compact | 表单元格,五号 sz=21 |
Table 的 firstRow |
(表样式条件) | 表头加粗 + sz=21 |
滤镜 custom-style 对的是样式名「表题」「图题」,不是 styleId。若业务仓另做 reference-doc,必须克隆这两套样式,否则 Word 会静默回落正文。
文档默认字号若是小四(sz=24),表头一旦丢掉 Compact 就会变大。docx-table-layout.lua 因此把表头保持为 Plain(Pandoc 才写成 Compact)。
2.6 滤镜顺序与挂载位置
网站:
book:
flowchart LR
L["docx-landscape.lua<br/>pre-quarto · 仅 docx"] --> A["linebreak.lua<br/>默认滤镜"]
A --> B["crossref-book-num.lua<br/>post-render"]
B --> C["docx-table-caption.lua<br/>post-render · 仅 docx"]
C --> D["docx-table-layout.lua<br/>post-render · 仅 docx"]| 滤镜 | at: |
原因 |
|---|---|---|
docx-landscape.lua |
pre-quarto(仅 book,可省略) | 先去掉 class landscape,再写一对完整分节符;晚于 Quarto 自带处理就会叠出空白页 |
linebreak.lua |
默认(不要 post-render) | 尽早把 <br> 变成 LineBreak |
crossref-tight-num.lua 或 crossref-book-num.lua |
post-render | 图号在 render 阶段才写入;两个编号滤镜只挂其中一个 |
docx-table-caption.lua |
post-render | 须在图号已经写成「表5-1」或「表3-1」之后拆壳 |
docx-table-layout.lua |
post-render | 须在拆壳之后改内层数据表 |
三处都要写(路径按该文件相对 lua/ 调整):
- 项目
_quarto.yml - 子目录
_metadata.yaml(若单章 render 会读它) - 实际被
quarto render path/to/chapter.qmd的那份 YAML
只写根配置、章文件又不在 project.render 列表里时,单独渲该章会漏滤镜。
从仓库根渲染。章内有行内 r 时不要 --no-execute。
3. 源文件最小协议(拷贝滤镜后仍要遵守)
#| label: fig-bar-example
#| fig-cap: !expr fig_cap#| label: tbl-example
#| tbl-cap: !expr tbl_cap正文:……(见 @fig-bar-example )。
不要:
- 用 custom float 重定义
fig/tbl - 渲完用 Python 改
document.xml当主方案 - 靠每张表手写
tbl-colwidths代替 layout 滤镜 - 改成 officedown / flextable 另写一套交叉引用(本包不覆盖)
4. 如何装进其他项目
推荐(Windows):
cd D:\github\harness
.\scripts\powershell\Install-Harness.ps1 `
-InstallProfile quarto-docx `
-ProjectRoot "D:\path\to\your-repo"book 项目改用 -InstallProfile quarto-docx-book。它复制 crossref-book-num.lua、docx-landscape.lua 和 _quarto.harness-docx-book-filters.yml,不复制网站编号滤镜。两个剖面都包含换行、拆壳、列宽和参考文档。
然后把对应的滤镜片段合并进该书 YAML,并设置:
format:
docx:
reference-doc: template/reference-doc.docx仍可手工复制 scripts/quarto-docx/ 下的 lua/ 与 reference-doc.docx。
5. 验收
Harness 标准样例(仓库内,Windows):
cd scripts\quarto-docx\test
.\scripts\run-check-all.ps1自动 render mod-acceptance.qmd,再用 verify_docx.py 断言网站剖面的 1、2、4、6(交叉引用收紧、题注样式在表外、题注内换行、callout 仍为表)。同一脚本接着渲染 test/book/,用 verify_book.py 断言 图N-M、一对 A4 分节,以及「注」「资料来源」落在这一页横向节里。3、5(无残留 ImageCaption、列宽/五号表头)仍建议 Word 目视。详见 资产效果展示与标准验收。
5.1 版本复测记录
滤镜源未改。复测前用 sync-mirror.ps1 把 canonical lua/ 与 reference-doc.docx 同步进 test/,再跑 run-check-all.ps1。
| 日期 | Quarto | 结果 |
|---|---|---|
| 2026-09-08 | 1.8.26 | 文档记载的验证版本(当时尚未单列本次 L0/L1 流水) |
| 2026-09-29 | 1.10.18 | 本机由 1.8.26 升级后复测。L0:<br> → LineBreak。L1:docx 与 html 均渲染成功;verify_docx.py 七项通过(正文交叉引用无多余空格、题注前缀收紧、图题 TuTi 与表题 BiaoTi 均在 w:tbl 外、题注内有 w:br、表内含「年份」「2024」、专栏标题仍在表内)。列宽与五号表头未在 Word 中目视。 |
本地流水写在 scripts/quarto-docx/test/TEST-LOG.md(已 gitignore,不提交)。规程见 quarto-docx-test-guide.md。
业务仓亦可手动:渲一章 --to html 与 --to docx 各一次。
- 正文「(见图2-13)」,不要「见 图2-13 」;html 无
图2- 。 - 表题在表外,
pStyle=BiaoTi,居中;图题在图下,pStyle=TuTi,居中。 - 无题注仍落在
ImageCaption。 - 中英双行中间有
w:br(若用了<br>)。 - 「年份」列可单行放下「2024」;表头加粗且为五号(Compact /
sz=21)。 - 专栏/callout 不要被拆(它们是 1 列表、没有「表N-」+ 内层数据表)。
6. 与业务仓同步
源副本记录在 SOURCE.txt。业务仓若继续改 Lua,请把 diff 回灌本目录,避免两套滤镜分叉。