ARTICLE DETAIL

建站实战干货

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

Pandoc HTML 读取器解析 `<figure>`/`<figcaption>` 的完整指南:从命令行测试到源码实现

2026/9/20 1:40:24 拓冰建站 浏览量
Pandoc HTML 读取器解析 `<figure>`/`<figcaption>` 的完整指南:从命令行测试到源码实现 Pandoc HTML 读取器解析figure/figcaption的完整指南从命令行测试到源码实现【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandocPandoc 是通用的标记格式转换器Universal markup converter其 HTML 读取器reader负责把 HTML5 文档转换为 Pandoc 内部的抽象语法树native AST。本指南以仓库中的命令测试用例 test/command/4183.md 为骨架逐条剖析 Pandoc 如何把figure与figcaption元素解析为Figure块并结合 HTML 读取器源码 说明其底层实现原理。读完本文你将掌握Figure块在 native 输出中的精确结构、figcaption与图片alt文本各自的去向、空标题与嵌套块级内容的处理规则以及如何自行运行与扩展此类命令测试。一、背景HTML5figure与 Pandoc 的Figure块HTML5 引入figure元素用于承载插图、图表、代码片段等独立于正文的内容通常配合figcaption提供标题说明。Pandoc 的 HTML 读取器专门为这一对元素提供了块级block映射figure对应 Pandoc 的Figure块figcaption中的内容被收集为图的标题captionfigure内的其余块构成图的主体body。命令测试 test/command/4183.md 是 Pandoc 官方测试套件golden test的一部分它通过三组pandoc -f html -t native命令用输入/期望输出对的方式锁定了该解析行为。命令测试本身由 test/command 目录下的大量.md文件驱动运行入口为 test/Tests/Command.hs。在 native 语法树中Figure块的通用形态为Figure attr caption body其中attr是(id, classes, key-values)三元组即元素的标识符、类名列表与键值对属性caption是Caption其结构为Caption shortCaption blocksshortCaption一般为Nothingblocks为标题的块列表body是Figure内除去标题后的块列表通常是一个Plain包裹的Image。Image的 native 形态为Image attr inlines target其中inlines是图片的可选文字通常来自alt属性target是(src, title)二元组。理解这些结构后下面三个用例就能逐字段对上号。二、用例一无figcaption的裸figure测试文件的第一组命令如下% pandoc -f html -t native figure img srcfoo altbar /figure ^D [ Figure ( , [] , [] ) (Caption Nothing []) [ Plain [ Image ( , [] , [] ) [ Str bar ] ( foo , ) ] ] ]这里figure只包含一个img没有任何figcaption。解析结果为Figure (, [], [])figure元素没有任何id/class/自定义属性因此属性三元组全部为空Caption Nothing []没有figcaption标题块列表为空[ Plain [ Image (, [], []) [Str bar] (foo, ) ] ]主体中只有一个Plain块内含一个Image。图片的inlines是[Str bar]它直接来自img的altbartarget为(foo, )即srcfoo、无title。值得注意的是没有figcaption时图片的alt文本会被放进Image的inlines但不会成为Figure的标题。标题caption与图片的替代文本alt text在 Pandoc 的 AST 中是两个不同的概念前者来自figcaption后者来自img的alt属性。三、用例二带块级figcaption的figure测试文件的第二组命令验证了figcaption内容为块级元素时的处理% pandoc -f html -t native figure img srcfoo altbar figcaption div baz /div /figcaption /figure ^D [ Figure ( , [] , [] ) (Caption Nothing [ Div ( , [] , [] ) [ Plain [ Str baz ] ] ]) [ Plain [ Image ( , [] , [] ) [ Str bar ] ( foo , ) ] ] ]关键变化在Caption部分figcaption里的divbaz/div被解析为一个块级Div (, [], [])其内容为[ Plain [ Str baz ] ]该Div整体进入Caption Nothing [ Div ... ]成为标题的块列表主体body仍然只有那个altbar的Image与用例一完全一致。这揭示了源码中的一个设计figcaption的内容是按照“块”block来解析的而不是“行内”inline。因此figcaption内部的div、p等块级容器会原样保留在标题的块列表中。这一点与第三个用例形成对照。四、用例三figcaption内含段落与行内格式测试文件的第三组命令展示了更接近真实排版的情况——figcaption内含p与行内强调% pandoc -f html -t native figure img srcfoo figcaptionpembaz/em/p/figcaption /figure ^D [ Figure ( , [] , [] ) (Caption Nothing [ Para [ Emph [ Str baz ] ] ]) [ Plain [ Image ( , [] , [] ) [] ( foo , ) ] ] ] ]两个细节值得展开标题按块解析figcaption内的pembaz/em/p被解析为Para [ Emph [ Str baz ] ]。这里em标签被正确转换为行内元素Emph而p则整体成为标题中的一个Para块——再次印证“块级解析”规则标题可以容纳多个块并保留段落与行内格式的层级关系。alt缺失时的Imageimg srcfoo没有alt属性因此Image的inlines为空列表[]即Image (, [], []) [] (foo, )。说明 Pandoc 不会为缺失的alt注入任何占位文本。对比用例一altbar产生[Str bar]可以看到Image的inlines完全取决于 HTML 中alt属性的有无与内容。五、源码实现剖析pFigure解析器上述三个用例的行为并非散落在各处而是由 src/Text/Pandoc/Readers/HTML.hs 中单一函数统一定义。首先块级分发表中figure - pFigure见 HTML.hs 第 258 行把figure开标签路由到专用解析器。pFigure的实现位于 HTML.hs 第 666-675 行pFigure :: PandocMonad m TagParser m Blocks pFigure do TagOpen tag attrList - pSatisfy $ matchTagOpen figure [] let parser Left $ pInTags figcaption block | (Right $ block) (captions, rest) - partitionEithers $ manyTill parser (pCloses tag | eof) -- Concatenate all captions together return $ B.figureWith (toAttr attrList) (B.simpleCaption (mconcat captions)) (mconcat rest)逐行解读其工作原理匹配开标签pSatisfy $ matchTagOpen figure []确认当前标签是figure并取出属性列表attrList。二分支解析核心是parser的定义——Left $ pInTags figcaption block把figcaption内的块解析结果标记为Left候选标题Right $ block把其他普通块标记为Right主体内容。这就是用例一/三中图片Image进入主体、而figcaption进入标题的结构来源。收集与切分manyTill parser (pCloses tag | eof)持续按上述二分支解析直到/figure或文件结束partitionEithers把Left所有标题块与Right所有主体块分到两个列表。合并标题B.simpleCaption (mconcat captions)把所有figcaption块按出现顺序拼接为一个Caption。源码注释 Concatenate all captions together 表明即使一个figure内出现多个figcaption也会被合并进同一个标题而不是报错或丢弃。构建结果B.figureWith (toAttr attrList)把figure自身的属性id、class、key-values完整保留到Figure的attr字段主体部分mconcat rest合并所有非标题块。从源码结构还可以推断两个边界行为其一若figure内既有figcaption又有多个普通块普通块会全部进入body其二若figcaption出现在主体块之后解析器依然能正确将其归入标题因为它只按“是否为 figcaption”分流不依赖位置。六、simpleCaption与标题结构的关系pFigure使用B.simpleCaption构造标题这与 Pandoc 中表格Table、DocBook、JATS 等读取器构造标题的方式一致参见 src/Text/Pandoc/Readers/DocBook.hs、src/Text/Pandoc/Readers/JATS.hs、src/Text/Pandoc/Readers/HTML/Table.hs。simpleCaption生成Caption Nothing blocks这正是三个用例中Caption Nothing [...]的由来——Nothing表示“无短标题”。也就是说从 HTML 的figcaption解析出来的标题始终是完整标题Pandoc 不会为它自动生成短标题。在 AST 层面Figure作为块类型同样参与其他模块的处理例如 src/Text/Pandoc/Shared.hs 第 904 行 的blockToInlines (Figure _ _ body)在需要把块转行内时只取Figure的主体而舍弃标题——这为理解“Figure 被内联化时的行为”提供了依据。而在写出writer方向LaTeX 写出器src/Text/Pandoc/Writers/LaTeX.hs、Docx 写出器src/Text/Pandoc/Writers/Docx/OpenXML.hs等都会读取stInFigure状态来生成对应的figure环境说明Figure块是贯穿读写两端的一等公民。七、如何在本地复现与扩展该命令测试test/command/4183.md是命令测试command test格式每个以 包裹的代码块包含一条 shell 命令以%开头、通过标准输入以^D结束送入的输入内容以及期望的 stdout 输出。要复现第一个用例只需在仓库根目录执行pandoc -f html -t native然后粘贴figure img srcfoo altbar /figure并以 Ctrl-D^D结束输入即可得到与用例一完全一致的 native 输出。其余两个用例同理。这种交互式验证方式正是 Pandoc 命令测试的设计初衷测试文件中的命令与人工敲入的命令完全等价。如果你想扩展测试可以参考同一目录下其他.md文件的写法新建一个test/command/编号.md在其中写入% pandoc ...命令块与期望输出然后通过测试套件入口 test/Tests/Command.hs 统一运行比对。可覆盖的场景包括figure idfig1 classwide带属性时的Figure (fig1, [wide], [])形态figcaption内同时出现多个段落、列表或代码块的复杂排版figure内出现多个普通块如图片加说明段落时的body结构嵌套figureHTML5 不允许但解析器行为可通过测试固定下来。八、小结通过 test/command/4183.md 这一组命令测试可以完整掌握 Pandoc HTML 读取器对figure/figcaption的解析语义输入特征AST 结果无figcaptionCaption Nothing []img的alt进入Image的inlinesfigcaption内含divDiv块整体进入Caption的块列表figcaption内含pemPara [ Emph ... ]进入Caption行内格式保留img无altImage的inlines为空[]figure带属性属性经toAttr保留到Figure的attr其底层统一由 HTML.hs 的pFigure实现figcaption按块解析并归入标题、其余块归入主体、多个标题块按序拼接。无论你是要理解 Pandoc 的 native AST、编写基于Figure的过滤器还是为 HTML 转 Markdown/LaTeX 的流程排查图片标题问题上述用例与源码都可以作为直接可复现、可引用的依据。【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考