ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

pandoc 将 Span 内联元素转换为 AsciiDoc 带样式标记的源码级解析

2026/9/20 6:32:50 拓冰建站 浏览量
pandoc 将 Span 内联元素转换为 AsciiDoc 带样式标记的源码级解析 文档开发工具CLI【免费下载链接】pandocUniversal markup converter项目地址https://gitcode.com/gh_mirrors/pa/pandoc点击查看免费下载pandoc 作为通用文档格式转换器其-t asciidoc写入器Writer在将 Pandoc 内部的 Span 内联元素输出为 AsciiDoc/Asciidoctor 时会采用[.class]#内容#的样式化标记styled phrase语法使类名、id 与内联样式在目标文档中得到保留。本文以 test/command/5080.md 中两组命令测试为切入点逐行还原从 Markdown 源文与 HTML 源文到 AsciiDoc 输出的转换行为并结合写入器与 HTML 读取器的源码说明 Span 属性如何被解析、加工与最终渲染。测试用例的定位与执行机制test/command/5080.md属于 pandoc 的命令测试体系。仓库中 test/Tests/Command.hs 对此有明确约定命令测试是一个代码块第一行以%开头是要执行的 shell 命令其后的内容作为命令的标准输入输入以单独一行^D结束后续内容即为期望输出。也就是说文件中的每个代码块都是一次独立的输入 → 命令 → 期望输出验证任何不匹配都会被测试框架判定为失败。该测试文件恰好覆盖了同一条转换链路的两类输入源测试块输入格式命令期望输出第一块Markdown含 Span 语法pandoc -t asciidoc[.small .red]#foo _bar_#第二块HTML含small标签pandoc -f html -t asciidoc[.small]#SMALL#用例一Markdown 中的 Span 属性如何映射为样式标记第一块测试的输入是[foo *bar*]{.small .red keyval}这是 pandoc Markdown 读取器支持的属性语法方括号内是内容花括号内是属性。其中foo是普通文本*bar*是强调Emph.small与.red是类名classkeyval是键值属性。读取器会将该结构解析为一个Span (ident, classes, keyvals) inlines即一个携带 id、类名和键值属性列表的内联元素这正是 AsciiDoc 写入器中Span分支src/Text/Pandoc/Writers/AsciiDoc.hs的处理对象inlineToAsciiDoc opts (Span (ident,classes,_) ils) do contents - inlineListToAsciiDoc opts ils isIntraword - gets intraword let marker if isIntraword then ## else # case classes of [] | T.null ident - return contents [mark] | T.null ident - return $ marker contents marker _ - do let modifier brackets $ literal $ T.unwords $ [ # ident | not (T.null ident)] map (. ) classes return $ modifier marker contents marker从源码可以得出几个关键结论核心语法是属性前缀 #分隔符写入器将#作为标记边界#内容#形成 AsciiDoc 的行内格式化短语属性修饰符放在第一个#之前。类名映射为.类名每个类名前加.多个类名用空格连接。{.small .red}因此输出为[.small .red]这正是测试期望输出的前缀部分。id 映射为#id若 Span 带有 id如{#myid}会以#myid的形式出现在修饰符列表中与其他类名一起用空格连接例如[#myid .small]。键值属性keyval被丢弃源码中模式匹配的是(ident,classes,_)第三位键值属性列表被显式忽略因此keyval不会出现在输出中——AsciiDoc 的[.class]#...#语法本身不支持行内键值属性。词内intraword场景使用##若 Span 出现在单词中间intraword 状态为真分隔符变为##避免与 AsciiDoc 中作为强调标记的单个#冲突。mark类有快捷路径类名恰为mark且无 id 时直接输出#内容#等价于 AsciiDoc 的#mark高亮标记这是 AsciiDoc 语义与 HTMLmark标签对齐的专门处理。另外测试输入中的*bar*在强调分支src/Text/Pandoc/Writers/AsciiDoc.hs中转换为_bar_因此嵌套结果正是[.small .red]#foo _bar_#与测试期望完全一致。这里值得注意AsciiDoc 中强调的默认标记是_而非 Markdown 的*写入器依据intraword状态选择_或__词内场景与 Span 的#/##选择逻辑同源。用例二HTML 的small标签在往返转换中的行为第二块测试的输入是smallSMALL/smallHTML 读取器src/Text/Pandoc/Readers/HTML.hs将small标签映射到pSmall解析器该解析器同文件 L806-L815 附近通过pInlinesInTags small将其包装为B.spanWith (,[small],[])即一个类名为small的 Span。经过上一节描述的 Span 分支写入器输出[.small]#SMALL#。这个用例揭示了一个重要的工程细节pandoc 的 AsciiDoc 写入器并不为small提供专门的#small#语法而是复用通用 Span 路径。同时spanWith生成的类名small恰好满足写入器中[mark]之外的通用分支最终以[.small]#形式输出。该输出完全符合 Asciidoctor 的样式化短语语法——[.small]#SMALL#在渲染时等价于 HTML 的span classsmallSMALL/span从而实现小字号样式的保真传递。需要注意这两个用例展示的是现代 AsciiDoc/Asciidoctor 方言的输出。写入器在 src/Text/Pandoc/Writers/AsciiDoc.hs 中区分了三套入口默认的writeAsciiDoc现代 AsciiDoc、writeAsciiDoctor已废弃的 Asciidoctor 别名以及writeAsciiDocLegacy遗留 AsciiDoc 方言。[.class]#...#语法属于前两者而writeAsciiDocLegacy会在legacy状态标志为真时走不同的强调、引用与代码转义路径输出格式与本文示例有差异。对比小结三种输入形态到 AsciiDoc 的输出为便于理解 Span 语义的传递规律将相关输入形态及其输出汇总如下均以现代 AsciiDoc 方言、非词内场景为准输入Markdown 属性语法 / HTML内部表示Span 的 ident, classes, keyvalsAsciiDoc 输出[text]{.small .red}(, [small,red], [])[.small .red]#text#[text]{#myid .small}(myid, [small], [])[#myid .small]#text#smallSMALL/small(, [small], [])[.small]#SMALL#[text]{.mark}(, [mark], [])#text#[text]{}或spantext/span无属性(, [], [])text原样输出[text]{.small keyval}(, [small], [(key,val)])[.small]#text#键值属性被忽略其中无属性 Span 原样输出由源码中[] | T.null ident - return contents分支保证键值属性被忽略则由(ident,classes,_)的丢弃行为保证。类名顺序与输入顺序一致多个类名以空格分隔。在 AsciiDoc 目标文档中的实际语义[.small .red]#foo _bar_#在 Asciidoctor 中会被渲染为span classsmall redemfoo bar/em/span之类的等价结构其中.small与.red成为 CSS 类_bar_渲染为斜体。这意味着只要目标 AsciiDoc 工具链加载了对应的 CSS/样式表pandoc 转换结果就能保留源文档的视觉样式意图。反过来若在 AsciiDoc 源中手写[.small .red]#内容#pandoc 的 AsciiDoc 读取器也能识别该语法从而完成双向语义保留。这也解释了为何写入器选择类名优先、忽略键值属性的策略AsciiDoc 行内样式标记的设计目标就是承载 CSS 类与 id而非任意键值元数据跨格式转换时键值属性往往只能在少数目标格式如 HTML 的data-*中落地因此在 AsciiDoc 路径上被有意舍弃。如何在本仓库中复现与验证无需预先安装二进制可直接利用仓库内的测试基础设施验证本文结论运行命令测试在仓库根目录执行cabal test会触发 test/Tests/Command.hs 读取test/command/目录下所有*.md文件并逐一比对期望输出5080.md的通过即证明上述两条转换规则成立。单独验证单个文件若已构建出 pandoc 可执行文件可按测试约定手动复现——先执行pandoc -t asciidoc并输入[foo *bar*]{.small .red keyval}以^D结束应得到[.small .red]#foo _bar_#再执行pandoc -f html -t asciidoc并输入smallSMALL/small应得到[.small]#SMALL#。查看写入器实现src/Text/Pandoc/Writers/AsciiDoc.hs 的 Span 分支是上述行为的唯一实现点可在此基础上继续探索Emph、Strong、Underline[.underline]#...#、Strikeout[line-through]#...#等同族的行内格式化转换它们共用delimited辅助函数与intraword状态机。结语test/command/5080.md虽只有短短两个测试块却完整刻画了 pandoc 在Span/HTML 小标签 → AsciiDoc 样式标记这条链路上的设计取舍类名与 id 通过[.class #id]#内容#语法无损传递键值属性被有意忽略词内场景自动切换##分隔符mark类享受专属快捷语法。理解这组规则无论是排查 AsciiDoc 转换结果中的样式丢失问题还是为自定义过滤器编写 Span 处理逻辑都能事半功倍。赞分享文档开发工具CLI【免费下载链接】pandocUniversal markup converter项目地址https://gitcode.com/gh_mirrors/pa/pandoc点击查看免费下载相关推荐Pandoc 转换 AsciiDoc从 11374 测试用例解读 SmallCaps 内联元素的输出机制Pandoc 转换 AsciiDoc从 11374 测试用例解读 SmallCaps 内联元素的输出机制 导读 本篇文章围绕 Pandoc 命令测试用例 te文档开发工具CLIDocling 将带样式的 WebVTT 字幕转换为 Markdownwebvtt_example_04 groundtruth 源码级全解读Docling 将带样式的 WebVTT 字幕转换为 Markdownwebvtt_example_04 groundtruth 源码级全解读 WebVTTAI 应用计算机视觉OCRPandoc 命令测试11362Markdown 到 AsciiDoc 转换的脚注、内联样式与特殊字符转义实战解析Pandoc 命令测试11362Markdown 到 AsciiDoc 转换的脚注、内联样式与特殊字符转义实战解析 导读 本文以 Pandoc 官方命令测文档开发工具CLI上一篇告别繁琐部署5分钟上手Astro静态站点自动化工作流下一篇Jaeger扩展功能自定义采样算法实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考