尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

pandoc LaTeX 输出的 `?``/`!`` 连字(ligature)问题与修复:从 test/command/5407.md 解读 `{\kern0pt}` 的插入机制

pandoc LaTeX 输出的 `?``/`!`` 连字(ligature)问题与修复:从 test/command/5407.md 解读 `{\kern0pt}` 的插入机制 pandoc LaTeX 输出的?/! 连字ligature问题与修复从 test/command/5407.md 解读{\kern0pt}的插入机制【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc导读本文以 pandoc 仓库中的黄金回归测试用例 test/command/5407.md 为主线深入讲解 LaTeX 输出路径中一个容易踩坑的细节当正文文本以问号?或感叹号!紧接一个左引号时TeX 会把? 与! 组合排版成倒置的问号¿和感叹号¡。文章将完整解读该测试文件的输入/输出结构、pandoc 命令行测试golden test的编写与运行机制并溯源到 LaTeX 转义核心实现 中stringToLaTeX的逐字符转义逻辑帮助你理解 pandoc 如何用{\kern0pt}精确阻断这类连字以及该机制适用的前提条件smart 扩展开启、纯文本上下文。一、测试用例的原始内容与逐行解读test/command/5407.md 全文是一个被三重反引号包裹的代码块内容如下% pandoc -t latex --wrappreserve hi there?“ hi there!“ hi there?‘ hi there!‘ hi there! ^D hi there?{\kern0pt} hi there!{\kern0pt} hi there?{\kern0pt} hi there!{\kern0pt} hi there!这是一个典型的 pandoc 命令行测试用例可以拆解为三个部分部分内容含义命令行% pandoc -t latex --wrappreserve以 LaTeX 为输出格式--wrappreserve保证输出不自动换行便于逐行比对标准输入hi there?“等 5 行文本以^D结束模拟终端输入的 EOF 结束符期望输出hi there?{\kern0pt} 等 5 行与输入一一对应的 LaTeX 转义结果注意输入文本中紧跟问号/感叹号的是两个真正的 Unicode 左引号字符“U201C 左双引号与‘U2018 左单引号。它们在 pandoc 的 LaTeX 输出中分别被渲染为 TeX 反引号序列与 。于是hi there?“→hi there?若不处理TeX 会将其排成 ¿倒置问号连字hi there!“→hi there!若不处理会排成 ¡倒置感叹号hi there?‘→hi there?hi there!‘→hi there!最后一行hi there!后面没有引号原样输出。期望输出中凡是可能形成连字的位置都被插入了{\kern0pt}零字距 kern用于阻断 TeX 连字最终得到 hi there?{\kern0pt} 这样的安全序列。这个测试用例对应的是 GitHub issue #5407pandoc 从 Markdown默认开启 smart 扩展转换到 LaTeX 时不应“无意中”制造出倒置问号/感叹号。二、命令行测试golden test的格式与执行机制test/command/目录下存放大量以 issue 编号命名的.md文件每个文件里的每一个代码块都是一条独立的命令测试。解析逻辑位于 test/Tests/Command.hsextractCommandTestCommand.hs用 pandoc 自身的 Markdown 读取器把测试文件解析成代码块逐个构造测试用例runCommandTestCommand.hs按行解析代码块以%开头的行为命令行dropPercent去除%及随后的空格Command.hs遇到单独的^D行之前的所有行为标准输入break (^D)^D之后、单独.行之前的内容为期望输出实际执行时用execTest运行 pandoc 并把输出与期望值比对不一致则报告 diff测试名即为文件名例如#5407。也就是说5407.md本质上是把“复现 issue 5407 的最小命令”固化成了可自动回归的测试。你完全可以手工复现这条命令观察修复前后的行为差异% pandoc -t latex --wrappreserve hi there?“ hi there!“ hi there?‘ hi there!‘ hi there!三、问题本质TeX 的? 与! 连字在 TeX 排版体系中反引号与某些字符组合会触发“连字”ligature?组合被渲染为倒置问号 ¿!组合被渲染为倒置感叹号 ¡两个连字符--会形成 en-dash---形成 em-dash。这在西班牙语等使用倒置标点的语境里是 TeX 的刻意设计配合fontenc/T1 编码但对于从 Markdown 转换而来的普通英文文本则是灾难用户明明写的是“hi there?”加一个左引号编译出来却变成“hi there¿”。pandoc 在 changelog.mdpandoc 2.7.22019-04-05中明确记录了这次修复LaTeX writer: Avoid inadvertently creating? or! ligatures (#5407). These are upside down ? and !, resp.中文含义即避免无意间制造? 或! 连字它们分别对应倒置的 ? 和 !。四、源码级实现stringToLaTeX的“向后看”转义修复的核心位于 src/Text/Pandoc/Writers/LaTeX/Util.hs 的stringToLaTeX。该函数用foldr从右向左逐字符转义字符串stringToLaTeX context zs do opts - gets stOptions ... return $ T.pack $ foldr (go opts context) mempty $ T.unpack zs关键点在于foldr的处理方向当处理到某个字符x时累加器xs中存放的已经是其右侧所有字符转义后的输出。因此go里的守卫可以直接检查xs的开头是否是反引号从而实现“预览右侧转义结果”的效果? | ligatures - -- avoid ? ligature case xs of :_ - emits ?{\\kern0pt} -- se #10610 _ - emitc x ! | ligatures - -- avoid ! ligature case xs of :_ - emits !{\\kern0pt} _ - emitc x见 Util.hs。结合测试用例可以完整还原执行链路输入行hi there?“中最右侧的“先被转义为见 \x201C | ligatures - emitquote [Util.hs](https://link.gitcode.com/i/ba8fa6ddb24b70289e5d7ccdb58fdbc4#L146)随后处理到?时右侧输出以 开头命中 :_ 分支于是输出 ?{\kern0pt}。两个字符最终拼接为 ?{\kern0pt}与 5407.md 的期望输出完全一致。其中ligatures的定义限定了修复的触发条件let ligatures isEnabled Ext_smart opts ctx TextStringUtil.hs。它包含两个硬性前提smart 扩展必须开启Ext_smart定义于 src/Text/Pandoc/Extensions.hs是 Markdown 输入的默认扩展之一负责智能引号、撇号、省略号、连字符转换。只有在 smart 开启时pandoc 才会把引号字符输出为 TeX 反引号序列也因此才需要防范随之而来的连字上下文必须是普通文本StringContext区分TextString、URLString、CodeStringUtil.hs。在 URL 上下文中反引号会被转义为\%60Util.hs在代码上下文中反引号会被转义为\textasciigraveUtil.hs均不会产生 TeX 连字自然无需干预。五、同一模式下的其他连字防护?/!的处理并非孤例stringToLaTeX中应用了相同的“检查右侧转义输出”模式来防护多种 TeX 连字连字符- - case xs of (-:_) - emits -\\/Util.hs当右侧输出以-开头时插入\/防止相邻连字符形成--en-dash引号之间的薄空格emitquote在引号后紧跟另一个引号时插入\,薄空格Util.hs防止、等组合被错误合并\kern0pt的同类用法\x200B零宽空格字符在输出中同样被映射为\hspace{0pt}Util.hs与{\kern0pt}一样都是通过插入零宽度元素来阻断 TeX 的自动合字。从源码结构看该修复是LaTeX 输出路径专属的ConTeXt 写手的转义函数escapeCharForConTeXtsrc/Text/Pandoc/Writers/ConTeXt.hs虽然同样用ligatures isEnabled Ext_smart opts控制--/---/的转换但并未出现{\kern0pt}形式的?/!特殊处理——这与两种排版引擎对引号字符的处理方式不同有关。六、测试的维护与回归价值5407.md这类文件的价值在于把一次 bug 修复固化为永久回归测试它验证了“输入含?/!加左引号时LaTeX 输出必须插入{\kern0pt}”这一行为契约它同时覆盖了单引号与双引号、问号与感叹号的四种组合以及一个无引号的对照行hi there!保证修复不过度作用任何未来改动如果破坏了这一转义逻辑例如改变了foldr方向、关闭了 smart 判断、调整了引号转义序列#5407测试都会立即失败并给出精确 diff。如果你在本地构建了 pandoccabal build或stack build也可以通过运行测试套件如cabal test中的 command 测试组来执行该用例或者直接用发布版/自建二进制手工执行上文的管道命令观察输出中{\kern0pt}是否存在。理解了这个测试你就掌握了 pandoc 命令行 golden test 的读写方式也明白了 LaTeX 输出中“字符转义 连字防护”这一层容易被忽略但至关重要的工程细节。参考路径速查测试用例test/command/5407.md测试解析与执行test/Tests/Command.hsLaTeX 转义核心实现src/Text/Pandoc/Writers/LaTeX/Util.hssmart 扩展定义src/Text/Pandoc/Extensions.hs修复记录changelog.mdpandoc 2.7.2ConTeXt 转义对照实现src/Text/Pandoc/Writers/ConTeXt.hs【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表