- 开发工具
- 文档
【免费下载链接】quarto-cli
Open-source scientific and technical publishing system built on Pandoc.
在 Quarto 中,crossref.custom允许你定义自己的浮动对象类别(float environment),为任意内容块分配编号、题注(caption)与引用前缀,并在 PDF/LaTeX 输出中自动生成对应的\newfloat浮动环境。本文以仓库中针对 GitHub issue #8711 编写的 smoke-all 回归测试文档 tests/docs/smoke-all/2024/02/12/8711.pdf.md 为主体,完整拆解其 YAML 配置、正文标记语法、LaTeX 代码注入机制与回归验证方式,帮助读者在自己的文档中安全地使用自定义交叉引用,并理解latex-env命名上的关键限制。
一、文档定位:一份 smoke-all 回归测试用例
该文档位于仓库的冒烟测试目录tests/docs/smoke-all/下,其路径命名遵循"日期 / issue 编号 / 目标格式"的约定:2024/02/12/8711.pdf.md表示这是 2024 年 2 月 12 日针对 issue #8711 的、目标输出为 PDF 的测试用例。该测试由 tests/smoke/smoke-all.test.ts 驱动渲染(tests/timing-for-ci.txt中记录了./smoke/smoke-all.test.ts -- docs/smoke-all/2024/02/12/8711.pdf.md这一调用方式)。
关于 #8711:该 issue 反映的问题是,用户为自定义交叉引用类别命名
latex-env: output时,会与 LaTeXlongtable宏包发生命名冲突,导致渲染失败。本测试文档正是围绕这一 Bug 的回归验证:它在同一篇文档中同时放置了knitr::kable(mtcars)渲染出的长表格,以及一个使用自定义 float 类别的输出块,并特意将自定义环境命名为notoutput以避开冲突。
文档开头声明了format: pdf与keep-md: true,即渲染 PDF 的同时保留中间 Markdown,因此这份.pdf.md文件本身就是渲染产物,完整记录了测试输入(YAML 前置元数据 + 正文)的结构。
二、YAML 前置元数据解析:crossref.custom 配置
该测试文档的核心配置如下:
--- title: "Untitled" format: pdf: keep-md: true crossref: custom: - kind: float key: out latex-env: notoutput reference-prefix: Output ---crossref.custom是一个数组,每一项声明一种自定义交叉引用类别。其字段定义可在 src/resources/schema/document-crossref.yml 中找到,Schema 明确规定kind、reference-prefix、key三个字段为必填项(required: ["kind", "reference-prefix", "key"]),其余字段可选:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
kind | 枚举float | —(必填) | 交叉引用类别,目前仅支持float |
key | 字符串 | —(必填) | 引用标签的前缀,如fig、tbl、lst,本文为out |
reference-prefix | 字符串 | —(必填) | 渲染引用时的前缀文本,本文为Output |
caption-prefix | 字符串 | 取reference-prefix | 题注中使用的前缀;省略时复用reference-prefix |
latex-env | 字符串 | 无 | LaTeX 输出中自定义 float 环境的名称,本文为notoutput |
caption-location | top/bottom/margin | bottom | 题注相对浮动内容的位置 |
latex-list-of-file-extension | 字符串 | lo+ref-type | LaTeX 收集"列表"条目用的辅助文件扩展名 |
latex-list-of-description | 字符串 | 取reference-prefix | 自定义"列表"标题中对对象的描述文本 |
space-before-numbering | 布尔 | true | 为false时前缀与编号之间不留空格 |
测试中只使用了四个字段:kind: float声明浮动类别;key: out使标签以out-开头(正文中对应#out-2);reference-prefix: Output决定引用文本为 "Output";latex-env: notoutput指定 LaTeX 环境名。关于这些字段的完整语义与默认值,Schema 文件 src/resources/schema/document-crossref.yml 是权威依据。
三、正文结构与关键写法
配置之外,正文展示了三种内容形态,分别对应表格、代码输出和自定义浮动块:
1. R 代码单元与长表格
knitr::kable(mtcars)该单元由 knitr 引擎执行,输出为一个 32 行 × 11 列的管道表格(pipe table),包含mpg、cyl、disp、hp、drat、wt、qsec、vs、am、gear、carb等变量(mtcars是 R 内置数据集,测试仅使用其前几行作为样例)。在 LaTeX 输出中,这份宽表格会由 Pandoc 渲染为longtable环境,这正是与latex-env: output冲突的对象。
2. 自定义浮动块与交叉引用
::: {#out-2} ::: {.cell} ::: {.cell-output .cell-output-stdout}Call: aov(formula = yield ~ block + N * P + K, data = npk)
Terms: block N P K N:P Residuals Sum of Squares 343.2950 189.2817 8.4017 95.2017 21.2817 218.9033 Deg. of Freedom 5 1 1 1 1 14
Residual standard error: 3.954232 Estimated effects may be unbalanced
::: ::: Sample ANOVA output ::: See @out-2要点如下:
- 块引用目标:
::: {#out-2}是一个带 ID 的 fenced div,out-前缀对应 YAML 中key: out,2是自动分配或手动指定的编号,最终引用形式为@out-2; - 代码输出单元格:内部嵌套
.cell与.cell-output .cell-output-stdout,承载一次aov()方差分析的 stdout 输出; - 题注文本:div 内 "Sample ANOVA output" 一行作为该浮动块的题注;
- 引用语法:正文末行
See @out-2使用@key语法产生交叉引用,渲染后在 PDF 中呈现为 "Output 2"(前缀取自reference-prefix,编号取自out-2)。
四、源码级原理:crossref.custom 如何被解析
理解上述配置如何生效,需要追踪渲染管线中负责自定义交叉引用的 Lua 过滤器。核心实现在 src/resources/filters/crossref/custom.lua 的initialize_custom_crossref_categories(meta)函数中:
- 读取文档元数据
meta["crossref"]["custom"],若存在则设置全局标志flags.has_custom_crossrefs = true; - 遍历数组中的每一项,通过映射表把 YAML 字段(kebab-case)转换为内部对象字段(snake_case),例如
reference-prefix→name、caption-prefix→prefix、key→ref_type、latex-env→latex_env; caption-location缺省时补为bottom;prefix缺省时复用name;- 调用
add_crossref_category(obj_entry)注册该类别。该函数定义在 src/resources/filters/mainstateinit.lua 中,它将类别插入crossref.categories.all,并重建按ref_type与按name索引的两个查找表(by_ref_type/by_name)。
引用解析阶段由 src/resources/filters/crossref/refs.lua 的resolveRefs()处理:遇到@out-2这类引用时,先从标签前缀反查类别(refType(label)),再按输出格式生成引用文本——LaTeX 输出注入\ref{label}(若类别定义了custom_ref_command则注入对应自定义命令),AsciiDoc 输出生成<<label>>,Typst 输出生成#ref(<label>, ...),其余格式则在 HTML/渲染层手工拼接前缀与编号。前缀文本的格式化逻辑位于 src/resources/filters/crossref/format.lua,其titlePrefix()依据类别的space_before_numbering决定前缀与编号之间是否插入不间断空格。
五、LaTeX 注入细节:newfloat、floatstyle 与题注位置
当输出格式为 PDF/LaTeX 时(src/resources/filters/crossref/custom.lua 中quarto.doc.isFormat("pdf")分支),Quarto 会向生成文档的导言区注入一段 LaTeX 代码,为每个自定义类别声明一个真正的浮动环境:
\usepackage{float} \floatstyle{plain} \@ifundefined{c@chapter}{\newfloat{notoutput}{h}{loout}}{\newfloat{notoutput}{h}{loout}[chapter]} \floatname{notoutput}{Output}其中:
\newfloat{<env>}{h}{<aux>}借助float宏包声明新浮动环境,辅助文件扩展名缺省为lo+ref_type(即loout);若文档有\chapter结构(如 book 项目),则追加[chapter]使编号按章节重置;\floatname{<env>}{Output}设置浮动环境的显示名称,其文本取自reference-prefix(或caption-prefix);- 每个类别还会生成一个
\listof<env>s命令,用于输出该对象的列表("List of Outputs")。
caption-location选项会改变浮动样式:默认bottom使用\floatstyle{plain};当配置为top时,过滤器额外注入\floatstyle{plaintop}与\restylefloat{<env>},把题注移动到浮动块顶部(src/resources/filters/crossref/custom.lua 中cap_location == "top"分支)。
此外,当space-before-numbering: false且前缀文本含空格时(典型场景如reference-prefix: Table S),过滤器会定义\quarto<reftype>ref这样的自定义引用命令、引入caption宏包并声明\DeclareCaptionLabelFormat,确保题注与引用中前缀和编号之间都不留空格。
六、关键限制:latex-env 不得命名为 "output"
这是 #8711 测试文档最核心的验证点。src/resources/filters/crossref/custom.lua 中有一段专门注释引用 issue 讨论并强制校验:
-- https://github.com/quarto-dev/quarto-cli/issues/8711#issuecomment-1946763141 -- using the name 'output' for a new float environment -- very specifically causes problems with the longtable package, so we disallow it here. if env_name == "output" then fail("The value 'output' is not allowed for the latex-env entry in a custom float environment, as it conflicts with the longtable package. Please choose a different value.") return end即:longtable宏包内部使用了名为output的环境/计数器,若用户自定义的 float 环境也叫output,生成的 LaTeX 将无法编译。由于 Schema 目前不支持否定式断言,这个限制无法在 src/resources/schema/document-crossref.yml 中静态表达,因此以运行时校验的形式实现在过滤器代码里——遇到该值会直接报错并终止。
这也解释了测试文档为何刻意选择latex-env: notoutput:既验证了自定义 float 类别与longtable表格在同一文档中共存的能力,又规避了被禁用的保留名称。
七、回归验证机制:keep-md 与 ensureFileRegexMatches
同一 issue 在仓库中留有三个验证变体,可对照学习:
| 文件 | 输出格式 | 验证方式 |
|---|---|---|
| tests/docs/smoke-all/2024/02/12/8711.pdf.md | pdf+keep-md: true | 渲染 PDF 并保留中间 Markdown |
| tests/docs/smoke-all/2024/02/13/8711.pdf.md | latex | ensureFileRegexMatches断言存在\begin{longtable} |
| tests/docs/smoke-all/2024/02/16/8711.pdf.md | latex+keep-md: true | 断言同时存在\begin{longtable}与\begin{tabular} |
后两个变体在 YAML 中通过_quarto.tests.latex.ensureFileRegexMatches声明了对渲染产物的正则断言:
_quarto: tests: latex: ensureFileRegexMatches: - ["\\\\begin\\{longtable"] - []第一组正则必须命中,第二组必须为空(不允许匹配)。其含义是:即使文档中存在自定义 float 环境notoutput,knitr::kable(mtcars)生成的宽表仍必须以longtable环境正常输出,不能被自定义浮动环境或float宏包破坏。这正是一份"回归测试"应有的姿态——验证修复(禁用output)之后,原有长表格能力不受影响。同时,02/16 变体还验证了longtable与普通tabular表格在同一文档中并存。
八、实战:在自己的文档中定义自定义 float 类别
参考仓库内更完整的示例 tests/docs/crossrefs/v1.4/custom-categories/diagrams.qmd,一篇文档可以同时注册多个自定义类别:
crossref: custom: - kind: float key: dia reference-prefix: Diagram latex-env: diagram latex-list-of-file-extension: lod - kind: float key: vid reference-prefix: Video latex-env: video latex-list-of-file-extension: lov - kind: float key: supptbl reference-prefix: Table S space-before-numbering: false latex-env: supptbl latex-list-of-file-extension: lost随后用::: {#dia-1}、::: {#vid-1}、::: {#supptbl-1}包裹任意内容(Mermaid 图、视频 shortcode、表格等),并在正文中用@dia-1、@vid-1、@supptbl-1引用。该示例还演示了space-before-numbering: false与自定义latex-list-of-file-extension的配合用法。
实战中请特别注意以下约束:
kind目前只能是float,Schema 未开放其他类型;latex-env禁止命名为output(与longtable宏包冲突),命名时避开longtable内部使用的保留字;- 引用标签前缀要全局唯一,
key决定了 div ID 与引用标签的命名空间(如dia、vid、supptbl),避免与内置的fig、tbl、lst、eq、sec等冲突; caption-location、space-before-numbering等选项只在 LaTeX 输出中产生对应的浮动样式调整,HTML 等其他格式的行为以过滤器默认逻辑为准。
总结
crossref.custom是 Quarto 交叉引用体系中面向高级用户的扩展点,它把"自定义对象类别"从 YAML 配置一路打通到 LaTeX 浮动环境的生成与引用解析。本文所依托的 #8711 测试文档不仅演示了完整的配置与正文写法,更以回归测试的形式固化了latex-env命名的边界条件——理解这一限制,能帮助你在实际项目中避免踩坑,并借助ensureFileRegexMatches等测试机制为自己的文档建立可验证的渲染保障。
- 开发工具
- 文档
【免费下载链接】quarto-cli
Open-source scientific and technical publishing system built on Pandoc.
相关推荐
Quarto CLI高级功能:交叉引用、浮动图表、悬停引用的终极指南
Quarto CLI高级功能:交叉引用、浮动图表、悬停引用的终极指南 Quarto CLI是一个基于Pandoc的开源科学和技术出版系统,提供了强大的文档编写和
开发工具文档pandoc 如何解析 LaTeX 自定义环境(\newenvironment):从 8573 号回归测试看宏展开原理
pandoc 如何解析 LaTeX 自定义环境(\newenvironment):从 8573 号回归测试看宏展开原理 导读 在把 LaTeX 文档转换为 Ma
文档开发工具CLISphinx 代码块标题(caption)、命名锚点与交叉引用:基于 caption.rst 测试用例的源码级解析
Sphinx 代码块标题(caption)、命名锚点与交叉引用:基于 caption.rst 测试用例的源码级解析 导读 本文以 Sphinx 仓库中的集成测试
文档开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考