
Pandoc ICML 输出中嵌套元素与 ID 锚点的正确渲染5541-nesting 命令测试深度解析【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc导读本文以 pandoc 仓库中的命令测试 test/command/5541-nesting.md 为线索深入解析 ICMLAdobe InCopy Interchange写器如何处理嵌套的 div / span / img 元素及其 id 属性哪些 id 会转换为 ICML 的HyperlinkTextDestination锚点图片如何在嵌套结构中渲染为Rectangle对象以及 Standalone 模式输出的完整骨架。读完本文你将掌握 pandoc ICML 写器的核心结构、锚点与链接的生成机制并学会用仓库自带的命令测试框架验证写器行为、将输出导入 InDesign 使用。一、认识命令测试与 5541 系列测试pandoc 使用一种命令测试command test的 golden 测试机制来验证命令行行为规则定义在 test/Tests/Command.hs 中。一个典型的测试文件是 Markdown 代码块内部格式如下% pandoc -f markdown -t latex *hi* ^D \emph{hi}以%开头的一行是待执行的命令Tests.Command.hs中的dropPercent负责解析之后到^D之前的内容会作为 stdin 传给该命令^D之后的内容是期望的 stdout 输出逐行比对compareValues若期望有 stderr需以2前缀标注若期望非零退出码以 状态码结尾。test/command/目录下所有.md文件都会被扫描并注册为测试组tests函数通过getDirectoryContents command收集文件名中的数字编号通常对应 GitHub issue / PR 编号。5541-nesting.md与同批的5541-localLink.md、5541-urlLink.md共同验证了 ICML 写器对链接与锚点的处理其中5541-nesting.md专门针对元素嵌套场景。二、测试用例逐步拆解1. 命令与输入该测试的命令与输入如下% pandoc -f html -t icml -s div idblockId div idblockId2 span idinlineId img idinlineId2 srclalune.jpg / /span /div /div要点-f html以 HTML 读取器解析输入此场景用 HTML 最能体现id 属性透传-t icml目标格式为 Adobe InCopy ICML-sstandalone 模式输出完整文档骨架XML 声明、样式组、Story 等输入是三层嵌套外层div#blockId→ 内层div#blockId2→span#inlineId→img#inlineId2图片引用测试夹具 test/command/lalune.jpg。2. Standalone 输出的骨架期望输出的整体结构如下?xml version1.0 encodingUTF-8 standaloneyes? ?aid style50 typesnippet readerVersion6.0 featureSet513 product8.0(370) ? ?aid SnippetTypeInCopyInterchange? Document DOMVersion8.0 Selfpandoc_doc RootCharacterStyleGroup Selfpandoc_character_styles ... /RootCharacterStyleGroup RootParagraphStyleGroup Selfpandoc_paragraph_styles ... /RootParagraphStyleGroup RootTableStyleGroup Selfpandoc_table_styles ... /RootTableStyleGroup RootCellStyleGroup Selfpandoc_cell_styles ... /RootCellStyleGroup Story Selfpandoc_story TrackChangesfalse StoryTitle AppliedTOCStylen AppliedNamedGridn StoryPreference OpticalMarginAlignmenttrue OpticalMarginSize12 / !-- 正文 -- /Story /Document这个骨架对应 pandoc 的 ICML 默认模板 data/templates/default.icml由 ICML.hs 中的writeICML通过renderTemplate渲染出来。骨架中的几个细节值得注意默认段落样式$ID/NormalParagraphStyle带有SpaceBefore6 SpaceAfter6和一组TabList首个制表位Position10这是 ICML 输出的默认段落间距空名段落样式ParagraphStyle SelfParagraphStyle/ Name LeftIndent0基于 NormalParagraphStyle 派生。由于本例中 div 没有指定custom-style正文段落使用了这个空名样式对应输出中ParagraphStyleRange AppliedParagraphStyle正文对齐注释期望输出中保留了源码注释!-- body needs to be non-indented, otherwise code blocks are indented too far --说明 ICML 写器对代码块的缩进有特殊要求。3. 正文区嵌套元素如何被渲染正文只有一个段落是嵌套渲染的最终产物ParagraphStyleRange AppliedParagraphStyle CharacterStyleRange AppliedCharacterStyle$ID/NormalCharacterStyle HyperlinkTextDestination SelfHyperlinkTextDestination/#inlineId NameDestination DestinationUniqueKey1 / Content /Content /CharacterStyleRange CharacterStyleRange AppliedCharacterStyle$ID/NormalCharacterStyle Rectangle Selfuec StrokeWeight0 ItemTransform1 0 0 1 75 -75 Properties PathGeometry GeometryPathType PathOpenfalse PathPointArray PathPointType Anchor-75 -75 ... / PathPointType Anchor-75 75 ... / PathPointType Anchor75 75 ... / PathPointType Anchor75 -75 ... / /PathPointArray /GeometryPathType /PathGeometry /Properties Image Selfue6 ItemTransform1 0 0 1 -75 -75 Properties Profile typestring$ID/Embedded/Profile GraphicBounds Left0 Top0 Right150 Bottom150 / /Properties Link Selfueb LinkResourceURIfile:lalune.jpg / /Image /Rectangle /CharacterStyleRange CharacterStyleRange AppliedCharacterStyle$ID/NormalCharacterStyle HyperlinkTextDestination SelfHyperlinkTextDestination/#inlineId NameDestination DestinationUniqueKey1 / Content /Content /CharacterStyleRange /ParagraphStyleRange逐层解读输入元素id输出表现div#blockId、div#blockId2blockId、blockId2完全不出现——div 只贡献样式前缀id 被丢弃span#inlineIdinlineId其 id 透传给所有行内子元素生成HyperlinkTextDestination/#inlineIdimg#inlineId2inlineId2渲染为RectangleImage但自身 id 不作为锚点HyperlinkTextDestination/#inlineId出现两次恰好夹住图片对象这是因为 span 内的换行缩进被 HTML 读取器解析为空白字符Content /Content而空白内容同样继承了 span 的 id从而各自生成一个目标锚点。这正是该测试名为 nesting 的原因——它锁定了嵌套元素 id 锚点 图片这一组合场景的精确输出防止写器改动破坏结构。三、源码级原理ICML 写器如何工作ICML 写器位于 src/Text/Pandoc/Writers/ICML.hs入口为writeICML :: PandocMonad m WriterOptions - Pandoc - m Text。1. ID 合法性预处理writeICML的第一步是对文档调用ensureValidXmlIdentifiers定义于 src/Text/Pandoc/Writers/Shared.hs。它会把不以字母开头的 id加上id_前缀并同步修正以#开头的内部链接目标确保生成的 ICML/XML 标识符合法ensureValidXmlIdentifiers :: Pandoc - Pandoc ensureValidXmlIdentifiers walk fixLinks . walkAttr fixIdentifiers本例中的inlineId已以字母开头因此原样保留为HyperlinkTextDestination/#inlineId。2. Div只贡献样式不保留 id从源码结构看blockToICML对Div的处理ICML.hs只读取属性键值对中的custom-styleblockToICML opts style (Div (_ident, _, kvs) lst) let dynamicStyle maybeToList $ lookup dynamicStyleKey kvs in blocksToICML opts (dynamicStyle style) lst_ident被显式忽略这就是blockId、blockId2不出现在输出中的原因。当没有custom-style时dynamicStyle为空列表最终parStyle计算出的样式字符串为空产生AppliedParagraphStyle。3. Spanid 透传与锚点生成inlineToICML对Span的处理ICML.hs把ident传给inlinesToICML随后每个行内子元素在charStyle中调用makeContentmakeContent :: Text - Doc Text - Doc Text makeContent ident cont | isEmpty cont empty | not (Text.null ident) makeLinkDest ident cont | otherwise inTagsSimple Content $ flush cont其中makeLinkDestICML.hs把 id 格式化为# 空格替换为连字符的HyperlinkTextDestination。这就是span#inlineId→HyperlinkTextDestination/#inlineId的直接来源也解释了为何每个非空子内容都会生成一个目标。4. 图片Rectangle 中的嵌入图像图片走imageICMLICML.hs通过fetchItem获取图片、imageSize解析尺寸失败时报告CouldNotDetermineImageSize并使用默认值计算半宽hw ow / 2、hh oh / 2和缩放矩阵scale生成四角PathPointType构成矩形轮廓非 data URI 的图片生成Link LinkResourceURIfile:...data URIbase64则内嵌为Contents的 CDATA对应 issue #8398 的修复可通过图片属性object-style名称指定对象样式源码中applyObjectStyle lookup objectStyleKey kvs输出AppliedObjectStyleObjectStyle/名称。期望输出中GraphicBounds Right150 Bottom150、锚点±75、ItemTransform1 0 0 1 75 -75说明lalune.jpg被按 150pt 见方的尺寸渲染。注意img#inlineId2的inlineId2在imageICML中并未被使用——图片锚点语义与 span 不同这正是嵌套场景中容易混淆的地方。5. 链接与目标与其他 5541 测试对照5541-nesting.md验证的是锚点目的地HyperlinkTextDestination的生成而同一批测试还覆盖了另外两种链接形态test/command/5541-localLink.md验证内部链接。标题段落如Header 1通过makeLinkDest生成HyperlinkTextDestination/#header-1正文中的[links to](#header-1)生成HyperlinkTextSource Selfhtss-1并在文档末尾由hyperlinksToDoc汇总输出Hyperlink Selfuf-1 ... DestinationHyperlinkTextDestination/#header-1{#spanner}这种显式 id 的 span 同理。链接在WriterState的links字段中按出现顺序编号link_id 1 n。test/command/5541-urlLink.md验证外部 URL 链接。URL 目标输出为HyperlinkURLDestination并通过escapeColons把:转义为%3a如HyperlinkURLDestination/https%3a//www.pandoc.org源码注释说明这是为了规避 InDesign CS6 对含多个冒号 URL 的崩溃问题。6. 样式收集机制writeICML内部维护WriterStateICML.hs包含blockStyles、inlineStyles、objectStyles、links等集合。渲染过程中parStyle把段落样式名按 串联如嵌套引用的Blockquote Paragraph并注册到blockStylescharStyle把字符样式注册到inlineStyles渲染结束后parStylesToDoc、charStylesToDoc、objectStylesToDoc、hyperlinksToDoc分别生成RootParagraphStyleGroup等样式声明区。因此期望输出中的样式组并非模板写死而是依据正文实际用到的样式动态生成——这也是为什么5541-nesting.md的输出中只有 Normal 段落样式与空名段落样式。四、如何复现与验证1. 手工复现在仓库根目录下用构建出的pandoc二进制即可复现注意图片相对路径以test/command/为基准cd test/command pandoc -f html -t icml -s EOF div idblockId div idblockId2 span idinlineId img idinlineId2 srclalune.jpg / /span /div /div EOF输出应与 test/command/5541-nesting.md 中^D之后的内容一致。2. 运行测试套件命令测试由 test/Tests/Command.hs 驱动通过execTest执行test-pandoc --emulate pandoc ...即用测试包装程序模拟 pandoc 命令行。可用项目标准方式运行测试例如cabal test pandoc --test-options-p 5541当实际输出与期望不符时compareValues会输出--- test/command/文件与 命令格式的统一差异getDiff便于精确定位回归点。3. 输出物在 InDesign 中的使用ICML 是 InCopyInDesign 的配套文字处理组件的交换格式模块注释ICML.hs明确说明ICML 是压缩包 IDML 格式中可独立使用的 XML 子集可在 InDesign 中通过File → Place置入。生成后段落/字符样式会出现在 InDesign 的样式面板中可手动调整或从模板加载定义图片上的object-styleMANUAL.txt 也做了说明允许在 InDesign 中统一定制图片对象样式示例见 test/command/11498.md其-s输出中带有RootObjectStyleGroup与ObjectStyle SelfObjectStyle/myStyle。五、小结5541-nesting.md虽只是一个十几行的 golden 测试却精确锁定了 ICML 写器在嵌套结构下的三组关键行为div 的 id 被丢弃只通过custom-style影响段落样式链span 的 id 逐层透传给行内子内容生成HyperlinkTextDestination锚点含空白内容故可能出现多个相同目标图片渲染为RectangleImage尺寸由图片元数据换算为点值且不消费自身 id。结合 src/Text/Pandoc/Writers/ICML.hs 的源码、test/Tests/Command.hs 的测试框架以及同批的5541-localLink/5541-urlLink测试你可以完整理解 pandoc 到 InCopy/InDesign 工作流中的锚点、链接、样式与图片语义进而在实际排版项目中安全地定制与排查 ICML 输出。【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考