試 5367 解讀腳注編號(hào)與排序的修復(fù)實(shí)現(xiàn))
Pandoc 表格腳注的 LaTeX 輸出從回歸測(cè)試 5367 解讀腳注編號(hào)與排序的修復(fù)實(shí)現(xiàn)【免費(fèi)下載鏈接】pandocUniversal markup converter項(xiàng)目地址: https://gitcode.com/gh_mirrors/pa/pandoc導(dǎo)讀本篇文章以 pandoc 官方回歸測(cè)試用例 test/command/5367.md 為核心深入剖析 pandoc 將 Markdown 中帶有腳注footnote的表格轉(zhuǎn)換為 LaTeX 時(shí)如何處理表格標(biāo)題caption、表頭header與單元格cell中的腳注。讀者將掌握 pandoc 的 golden test 格式、腳注在longtable環(huán)境中的輸出形態(tài)以及 LaTeX writer 中外部腳注收集 延遲輸出這一核心機(jī)制的工作原理并了解該機(jī)制在 Beamer 等特殊格式下的分支處理邏輯。一、回歸測(cè)試 5367 的由來(lái)與價(jià)值test/command/5367.md 是 pandoc 測(cè)試套件中的一個(gè)典型 golden test 文件其編號(hào)對(duì)應(yīng) GitHub issue #5367。在 changelog.md 的 pandoc 2.8 版本記錄中可以找到這次修復(fù)的官方說明Fix footnotes in table caption and cells (#5367). This fixes a bug wherein footnotes appeared in the wrong order, and with duplicate numbers, when in table captions and cells. We now use regular\footnotecommands, even in the table caption and the minipages containing cells. Apparently longtable knows how to handle this.也就是說該修復(fù)之前位于表格標(biāo)題、表頭與單元格中的腳注會(huì)出現(xiàn)順序錯(cuò)亂和編號(hào)重復(fù)兩個(gè)典型缺陷修復(fù)之后pandoc 直接在這些位置生成普通的\footnote命令交由 LaTeX 的longtable環(huán)境自行處理腳注編號(hào)。而 test/command/5367.md 正是這一修復(fù)的回歸測(cè)試防止未來(lái)改動(dòng)重新引入該缺陷。二、讀懂 golden test 文件格式pandoc 的命令行回歸測(cè)試位于 test/command/ 目錄采用統(tǒng)一的輸入輸出約定test/command/5367.md 完整展示了這一格式% pandoc -t latex hello[^1] : Sample table.[^2] ----------- Fruit[^3] ----------- Bans[^4] ----------- dolly[^5] [^1]: doc footnote [^2]: caption footnote [^3]: header footnote [^4]: table cell footnote [^5]: doc footnote ^D hello\footnote{doc footnote} ... dolly\footnote{doc footnote}其結(jié)構(gòu)分為三段第一行% pandoc -t latex指定要執(zhí)行的命令行等價(jià)于運(yùn)行pandoc -t latex輸入為 Markdown輸出目標(biāo)格式為 LaTeX中間輸入?yún)^(qū)以^DEOF 標(biāo)記結(jié)尾的 Markdown 文檔本體末尾輸出區(qū)pandoc 轉(zhuǎn)換后應(yīng)產(chǎn)生的精確期望輸出。測(cè)試框架實(shí)現(xiàn)于 test/Tests/Command.hs會(huì)執(zhí)行第一行命令并將實(shí)際輸出與文件末尾的期望輸出逐字節(jié)比對(duì)任何差異都會(huì)導(dǎo)致測(cè)試失敗。因此該文件不僅是一份可運(yùn)行的轉(zhuǎn)換示例更是一份輸入 Markdown → 期望 LaTeX的權(quán)威映射。三、測(cè)試輸入中的腳注分布場(chǎng)景測(cè)試輸入在一份很短的文檔里刻意覆蓋了表格中可能出現(xiàn)的全部腳注位置共 5 處腳注標(biāo)記出現(xiàn)位置腳注文本對(duì)應(yīng)輸出位置[^1]正文段落hellodoc footnote文檔體腳注[^2]表格標(biāo)題captioncaption footnote\caption內(nèi)[^3]表頭單元格Fruitheader footnote表頭minipage內(nèi)[^4]表格數(shù)據(jù)單元格Banstable cell footnote表格行內(nèi)[^5]正文段落dollydoc footnote文檔體腳注其中[^3]與[^4]的位置是關(guān)鍵腳注位于longtable環(huán)境的minipage內(nèi)部而minipage中的\footnote默認(rèn)會(huì)變成帶編號(hào)的腳注標(biāo)記但不進(jìn)入頁(yè)面底部這正是舊實(shí)現(xiàn)容易出錯(cuò)的原因。測(cè)試特意在正文hello、dolly也放置腳注用于驗(yàn)證表格內(nèi)外腳注的整體排序與編號(hào)連續(xù)性。四、期望輸出逐段解析4.1 表格標(biāo)題中的腳注\caption[Sample table.]{Sample table.\footnote{caption footnote}}\tabularnewline標(biāo)題被生成為帶短標(biāo)題[Sample table.]的\caption腳注文本caption footnote以普通\footnote命令內(nèi)聯(lián)在標(biāo)題文字內(nèi)部。這與修復(fù)前腳注被移到表格外部、編號(hào)錯(cuò)亂的行為形成鮮明對(duì)比。4.2 表頭中的腳注\begin{minipage}[b]{\linewidth}\centering Fruit\footnote{header footnote} \end{minipage} \\表頭單元格被渲染為minipage腳注直接以\footnote{header footnote}形式嵌在單元格文字中。值得注意的細(xì)節(jié)是longtable會(huì)對(duì)跨頁(yè)表格重復(fù)輸出表頭\endfirsthead/\endhead機(jī)制在重復(fù)的表頭部分第二次及之后出現(xiàn)的表頭中腳注被替換為Fruit{}——即重復(fù)表頭中不再重復(fù)輸出腳注命令避免同一腳注在每一頁(yè)重復(fù)出現(xiàn)\endfirsthead \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering Fruit{} \end{minipage} \\ \midrule\noalign{} \endhead4.3 單元格與正文中的腳注數(shù)據(jù)單元格腳注同樣以內(nèi)聯(lián)形式輸出Bans\footnote{table cell footnote} \\而正文段落中的兩個(gè)腳注則輸出為文檔體的普通腳注hello\footnote{doc footnote} ... dolly\footnote{doc footnote}最終5 個(gè)腳注在 LaTeX 源中保持與輸入完全一致的先后順序編號(hào)將由 LaTeX 編譯器按出現(xiàn)順序統(tǒng)一分配徹底消除了舊實(shí)現(xiàn)中表格內(nèi)腳注被提前收集、導(dǎo)致編號(hào)重復(fù)或錯(cuò)序的問題。五、源碼級(jí)實(shí)現(xiàn)剖析外部腳注機(jī)制回歸測(cè)試背后是 LaTeX writer 一套精心設(shè)計(jì)的外部腳注收集機(jī)制。其核心入口位于 src/Text/Pandoc/Writers/LaTeX/Table.hs-- See #5367 -- footnotehyper/footnote dont work in beamer, -- so we need to produce the notes outside the table... if float || beamer then ($$) $ withExternalNotes renderTable * getAccumulatedNotes else renderTable這段代碼的含義是當(dāng)表格帶有float類浮動(dòng)表格環(huán)境或目標(biāo)格式為 Beamer 時(shí)整個(gè)表格的渲染過程被包裹在withExternalNotes中渲染完畢后通過getAccumulatedNotes把收集到的腳注追加在表格環(huán)境之后輸出。5.1 腳注如何被收集而非直接輸出withExternalNotes定義于 src/Text/Pandoc/Writers/LaTeX/Util.hs的作用是在渲染期間把 writer 狀態(tài)中的stExternalNotes標(biāo)志置為True使腳注處理分支發(fā)生切換。在 src/Text/Pandoc/Writers/LaTeX.hs 中可以看到這個(gè)分支externalNotes - gets stExternalNotes if externalNotes then do modify $ \st - st{ stNotes noteContents : stNotes st } return \\footnotemark{} else return $ \\footnote beamerMark braces noteContents也就是說在外部腳注模式下腳注不立即輸出而是在腳注位置僅輸出一個(gè)占位標(biāo)記\footnotemark{}把腳注正文壓入狀態(tài)棧stNotes待表格渲染完畢后由getAccumulatedNotessrc/Text/Pandoc/Writers/LaTeX/Util.hs統(tǒng)一取出、清空并在表格之后輸出。而普通的表格路徑非 float、非 Beamer則直接走常規(guī)\footnote內(nèi)聯(lián)輸出。這與 changelog 中we now use regular\footnotecommands... longtable knows how to handle this的描述完全吻合——longtable對(duì)表格內(nèi)的\footnote有原生支持因此對(duì)最常見的普通長(zhǎng)表格場(chǎng)景無(wú)需特殊處理。5.2 重復(fù)表頭中的腳注去重前面提到重復(fù)表頭中腳注被替換為Fruit{}其實(shí)現(xiàn)位于 src/Text/Pandoc/Writers/LaTeX/Table.hs 的tableToLaTeXLongtablefirsthead - mkHead thead let removeNote (Note _) Span (, [], []) [] removeNote x x repeated - mkHead (walk removeNote thead) return $ capt \\tabularnewline $$ firsthead $$ \\endfirsthead $$ repeated代碼用walk removeNote對(duì)重復(fù)表頭做了一次 AST 變換把所有Note內(nèi)聯(lián)元素替換為空Span從而保證腳注命令只出現(xiàn)在首次表頭\endfirsthead之前跨頁(yè)重復(fù)的表頭中不再重復(fù)產(chǎn)生腳注。這正是測(cè)試期望輸出中Fruit{}的來(lái)源也是防止同一腳注在每頁(yè)重復(fù)出現(xiàn)的關(guān)鍵細(xì)節(jié)。5.3 非表格場(chǎng)景的常規(guī)腳注在文檔體的普通塊序列中blockListToLaTeXsrc/Text/Pandoc/Writers/LaTeX.hs會(huì)在每個(gè)塊輸出后把當(dāng)前累積的腳注一并追加輸出在外部腳注模式下則跳過追加交由表格渲染完成后的getAccumulatedNotes統(tǒng)一處理。這套狀態(tài)棧累積 延遲沖洗的設(shè)計(jì)保證了腳注無(wú)論出現(xiàn)在正文、列表、標(biāo)題還是表格內(nèi)最終都以正確的先后順序輸出。六、測(cè)試的運(yùn)行與驗(yàn)證方式讀者可自行復(fù)現(xiàn)該測(cè)試。在完成 pandoc 構(gòu)建后通過make test或在倉(cāng)庫(kù)根目錄執(zhí)行測(cè)試套件即可運(yùn)行全部 command 測(cè)試若只想針對(duì)本用例驗(yàn)證可將文件中的輸入?yún)^(qū)% pandoc -t latex之后的 Markdown至^D前保存為臨時(shí)文件并執(zhí)行pandoc -t latex input.md將輸出與 test/command/5367.md 末尾的期望輸出對(duì)比。若二者一致說明當(dāng)前版本的 LaTeX writer 對(duì)表格內(nèi)腳注的處理符合預(yù)期。同時(shí)將同一輸入改用-t beamer輸出可以觀察到表格渲染走withExternalNotes分支后腳注被收集到表格外部輸出、并以.-[frame]等 Beamer 覆蓋語(yǔ)法標(biāo)記見 src/Text/Pandoc/Writers/LaTeX.hs呈現(xiàn)的差異。七、小結(jié)test/command/5367.md 看似只是一個(gè)幾十行的轉(zhuǎn)換示例實(shí)則濃縮了 pandoc 關(guān)于表格腳注這一邊角場(chǎng)景的完整工程決策行為約定表格標(biāo)題、表頭、單元格內(nèi)的腳注以普通\footnote內(nèi)聯(lián)輸出交由longtable處理缺陷防護(hù)重復(fù)表頭通過 AST 變換剔除腳注避免跨頁(yè)重復(fù)特殊分支浮動(dòng)表格與 Beamer 下切換到腳注收集 延遲輸出機(jī)制規(guī)避\footnote在這些環(huán)境中的失效問題。對(duì)于希望深入 pandoc LaTeX 輸出的讀者建議按 test/command/ 目錄的編號(hào)順序 golden test并結(jié)合 src/Text/Pandoc/Writers/LaTeX.hs 與 src/Text/Pandoc/Writers/LaTeX/Table.hs 對(duì)照研讀可以快速建立起測(cè)試即規(guī)范的源碼閱讀路徑?!久赓M(fèi)下載鏈接】pandocUniversal markup converter項(xiàng)目地址: https://gitcode.com/gh_mirrors/pa/pandoc創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考