Word:三行Python代碼實(shí)現(xiàn)OMML原生公式轉(zhuǎn)換)
用LaTeX寫了六七年論文最讓我頭疼的從來不是排版而是每次投稿或返修時(shí)被編輯一句“請(qǐng)?zhí)峁¦ord版本”打回原形。公式在LaTeX里明明整潔漂亮一復(fù)制進(jìn)Word要么變成一堆亂碼符號(hào)要么就是純文本形式的“a/b√c”完全沒有學(xué)術(shù)排版該有的樣子。后來我研究出一個(gè)用Python轉(zhuǎn)換公式的套路核心處理邏輯只有三行就能把LaTeX公式變成Word原生公式對(duì)象OMML雙擊能編輯、編號(hào)能對(duì)齊、格式不散架。這篇文章把整個(gè)思路、代碼和踩過的坑都整理出來希望能幫到所有在LaTeX和Word之間反復(fù)橫跳的學(xué)術(shù)人。1. 先搞清楚LaTeX公式為什么進(jìn)不了Word1.1 學(xué)術(shù)寫作中最常見的時(shí)間黑洞不少人在論文協(xié)作時(shí)都經(jīng)歷過這個(gè)場(chǎng)景你的正文、圖表、參考文獻(xiàn)全在LaTeX里導(dǎo)師或期刊卻要求提交docx。有人選擇硬著頭皮用Word重排一遍公式全部重新錄入一篇論文光公式就要折騰兩三天。也有人嘗試把LaTeX源碼直接復(fù)制到Word結(jié)果編輯器里出現(xiàn)一堆反斜杠指令比如\frac{a}在Word里完全是死代碼根本渲染不成數(shù)學(xué)公式。問題的本質(zhì)在于兩套體系背后的數(shù)學(xué)排版語(yǔ)言不一樣。LaTeX公式底層是用TeX語(yǔ)法描述的符號(hào)指令Word原生公式則使用OMMLOffice Math Markup Language這種XML格式存儲(chǔ)。兩邊沒有直接的“復(fù)制粘貼”通道所以才出現(xiàn)了公式整體遷移時(shí)數(shù)量多、格式亂、效率低的問題。我之前也試過MathType確實(shí)能把Word公式和LaTeX互轉(zhuǎn)但一個(gè)公式一個(gè)公式地點(diǎn)鼠標(biāo)數(shù)量一多照樣崩潰。后來我開始研究Python生態(tài)里的轉(zhuǎn)換鏈路試圖用腳本批量處理這才發(fā)現(xiàn)這件事完全可以自動(dòng)化。1.2 三行Python代碼到底走了哪條路先給結(jié)論我用到的核心工具鏈?zhǔn)莑atex2mathml庫(kù)加Word自帶的MML2OMML.XSL樣式表。技術(shù)路線是這樣的第一步用latex2mathml把LaTeX公式字符串轉(zhuǎn)換成MathML格式的XML數(shù)據(jù)第二步借助Microsoft Office安裝目錄下的MML2OMML.XSL樣式表用lxml庫(kù)做一次XSLT變換把MathML再轉(zhuǎn)換成OMML格式第三步通過python-docx的底層XML接口把OMML節(jié)點(diǎn)插入Word段落的XML樹里。聽到XML、XSLT、OMML這些詞可能會(huì)覺得復(fù)雜但實(shí)際用Python封裝起來核心邏輯就是三行代碼。往后需要轉(zhuǎn)換時(shí)只需要把公式字符串傳進(jìn)來Word里就會(huì)生成一個(gè)原生公式對(duì)象這個(gè)對(duì)象可以雙擊編輯、可以右擊切換顯示樣式跟你在Word里用“插入→公式”創(chuàng)建的公式完全一樣。1.3 這篇內(nèi)容適合誰(shuí)參考如果你屬于下面任何一種情況這篇文章可以幫你省下大量時(shí)間平時(shí)用LaTeX寫作但投稿、交作業(yè)、交課題材料時(shí)需要Word版本需要把一批老文檔里的LaTeX公式遷移到Word里手動(dòng)重錄實(shí)在太多想搭建一個(gè)從LaTeX/Markdown到Word的自動(dòng)化轉(zhuǎn)換流程比如配合Pandoc、自動(dòng)化腳本使用對(duì)python-docx的底層有好奇想知道Word公式對(duì)象是怎么在XML里存著的。我的文章里會(huì)給出完整可運(yùn)行的腳本思路也會(huì)把公式編號(hào)、批量替換、符號(hào)兼容這些周邊問題一起聊透。2. 動(dòng)手前要把Python環(huán)境收拾利索2.1 Python環(huán)境怎么裝比較省心如果你已經(jīng)有Python環(huán)境直接跳過這一步。還沒有的話我的建議是安裝Python 3.8以上版本推薦3.10或3.11。去Python官網(wǎng)下載對(duì)應(yīng)系統(tǒng)的安裝包時(shí)有一個(gè)非常關(guān)鍵的勾選項(xiàng)“Add Python to PATH”這個(gè)一定記得勾上否則后面在命令行里運(yùn)行python命令大概率會(huì)提示找不到。裝完以后在終端運(yùn)行python --version能輸出版本號(hào)就說明基礎(chǔ)環(huán)境沒問題。實(shí)際項(xiàng)目里我建議創(chuàng)建一個(gè)獨(dú)立虛擬環(huán)境不要讓各種庫(kù)污染全局環(huán)境。創(chuàng)建命令很簡(jiǎn)單python -m venv latex2wordWindows下進(jìn)入虛擬環(huán)境latex2word\Scripts\activateLinux或macOS下則是source latex2word/bin/activate虛擬環(huán)境的好處是即使某個(gè)庫(kù)依賴發(fā)生了沖突刪掉這個(gè)文件夾重新來一遍就行不會(huì)把系統(tǒng)Python搞得一團(tuán)糟。2.2 需要安裝哪些庫(kù)核心依賴有三個(gè)python-docx負(fù)責(zé)創(chuàng)建和操作Word文檔的Python庫(kù)latex2mathml負(fù)責(zé)把LaTeX公式指令解析成MathML XMLlxml負(fù)責(zé)處理XML的解析和XSLT變換。安裝命令一次搞定pip install python-docx latex2mathml lxml如果下載速度慢可以臨時(shí)用國(guó)內(nèi)鏡像源。我實(shí)測(cè)下來清華源的兼容性不錯(cuò)pip install -i https://pypi.tuna.tsinghua.edu.cn/simple python-docx latex2mathml lxmllatex2mathml這個(gè)庫(kù)其實(shí)是把數(shù)學(xué)公式解析成Content MathML結(jié)構(gòu)的安裝版本號(hào)建議保持最新。lxml庫(kù)在Windows安裝時(shí)一般有現(xiàn)成的wheel包也不會(huì)太折騰。2.3 找到Word自帶的那張“翻譯對(duì)照表”接下來要找到Office安裝目錄下的MML2OMML.XSL文件。這是Office自帶的一個(gè)樣式表文件作用是把MathML詞典翻譯成Word能識(shí)別的OMML數(shù)學(xué)標(biāo)記。常見路徑是C:\Program Files\Microsoft Office\root\Office16\MML2OMML.XSLOffice 2016對(duì)應(yīng)Office16Office 2019、Microsoft 365也基本都在這個(gè)目錄。如果你沒找到可以用一個(gè)小腳本搜索一下import glob paths glob.glob(rC:\Program Files\Microsoft Office\**\MML2OMML.XSL, recursiveTrue) print(paths)我這個(gè)腳本在Microsoft 365環(huán)境下跑出來路徑就是C:\Program Files\Microsoft Office\root\Office16\MML2OMML.XSL。如果還是沒有直接去辦公軟件安裝目錄里手動(dòng)搜MML2OMML.XSL也行。Mac版本的Office也內(nèi)置了類似文件但路徑不太一樣用Finder搜索或者配合mdfind查找即可。這個(gè)文件是后面轉(zhuǎn)換的關(guān)鍵我一般會(huì)把它復(fù)制到項(xiàng)目里一個(gè)名為assets的目錄避免不同電腦上路徑不一樣導(dǎo)致腳本報(bào)錯(cuò)。2.4 環(huán)境驗(yàn)證小腳本庫(kù)和XSL文件都就緒后可以先跑一個(gè)最簡(jiǎn)單的驗(yàn)證腳本確保鏈路是通的from latex2mathml.converter import convert mathml_str convert(r\frac{1}{2}) print(mathml_str[:100])如果輸出類似math xmlns...開頭的XML字符串那么第一步就通了。接下來就可以進(jìn)入正式轉(zhuǎn)換環(huán)節(jié)。3. 核心代碼逐行拆解3.1 三行核心代碼的背后先展示完整可運(yùn)行的最小版本我這里為了方便演示把XSLT的路徑放在一個(gè)變量里然后寫了一個(gè)很短的核心函數(shù)import re from lxml import etree from docx import Document from latex2mathml.converter import convert XSL_PATH rC:\Program Files\Microsoft Office\root\Office16\MML2OMML.XSL def latex_to_omml(latex_str, xsl_pathXSL_PATH): mathml convert(latex_str) # 第一步LaTeX轉(zhuǎn)MathML transform etree.XSLT(etree.parse(xsl_path)) # 第二步載入XSLT規(guī)則 omml transform(etree.fromstring(mathml)) # 第三步MathML轉(zhuǎn)OMML return omml.getroot() doc Document() paragraph doc.add_paragraph() paragraph._p.append(latex_to_omml(r\frac{a} \sqrt{c})) doc.save(公式測(cè)試.docx)嚴(yán)格來說核心邏輯確實(shí)只有三個(gè)動(dòng)作轉(zhuǎn)成MathML、用XSLT轉(zhuǎn)換成OMML、把OMML節(jié)點(diǎn)掛到段落XML里。這三行代碼就是標(biāo)題里說的“三行代碼”的精髓。但要在實(shí)際工程里用還需要我后面提到的輔助函數(shù)和異常處理。3.2 每一行到底完成了什么工作要我拆開細(xì)說的話第一步latex2mathml.converter.convert負(fù)責(zé)把類似\frac{a}這樣的LaTeX源碼解析成結(jié)構(gòu)化的MathML XML這一步非常關(guān)鍵因?yàn)镸athML是描述數(shù)學(xué)結(jié)構(gòu)的中性標(biāo)記語(yǔ)言既不偏袒LaTeX也不偏袒Word。第二步里etree.XSLT是lxml庫(kù)提供的XSLT變換引擎。MML2OMML.XSL不是隨便找的規(guī)則文件它是微軟官方提供的XSLT樣式表內(nèi)部定義了一整套把MathML結(jié)構(gòu)映射成OMML結(jié)構(gòu)的規(guī)則等于說翻譯工作由微軟自己完成了我們只是用Python調(diào)用了一下非??煽俊5谌絧aragraph._p.append(omml)是把OMML節(jié)點(diǎn)作為子節(jié)點(diǎn)添加到Word段落的XML中。這里的_p是python-docx暴露出來的內(nèi)部XML元素接口直接操作XML雖然看起來有點(diǎn)“硬核”但這是唯一能讓W(xué)ord識(shí)別公式對(duì)象的方式。用python-docx庫(kù)提供的普通添加段落接口只能插入文字配不出公式對(duì)象。3.3 行內(nèi)公式和獨(dú)立公式怎么插論文里的公式分兩種場(chǎng)景一種混在正文文字里比如“當(dāng)(a0)時(shí)函數(shù)單調(diào)遞增”另一種是獨(dú)占一行、居中的大公式。兩種場(chǎng)景處理方式略有區(qū)別。行內(nèi)公式的處理方式是先添加一個(gè)包含前導(dǎo)文字的段落再把公式OMML節(jié)點(diǎn)插到文字節(jié)點(diǎn)后面緊接著再追加后續(xù)文字from docx.enum.text import WD_ALIGN_PARAGRAPH p doc.add_paragraph() p.add_run(當(dāng) ) p._p.append(latex_to_omml(ra 0)) p.add_run( 時(shí)函數(shù)單調(diào)遞增。)獨(dú)立公式則簡(jiǎn)單設(shè)置段落對(duì)齊方式為居中段落里只放公式就行p doc.add_paragraph() p.alignment WD_ALIGN_PARAGRAPH.CENTER p._p.append(latex_to_omml(r\int_0^1 f(x)\,dx \frac{\pi}{2}))我調(diào)試時(shí)發(fā)現(xiàn)一個(gè)細(xì)節(jié)行內(nèi)公式如果前后不加空格Word里渲染出來的公式會(huì)和文字貼得很緊所以在公式前后補(bǔ)了一個(gè)字符級(jí)的空格顯示效果好很多。3.4 為什么不用pandoc就夠了很多人這時(shí)候會(huì)問Pandoc一條命令就能把LaTeX轉(zhuǎn)成Word為什么還要寫Python腳本。我的體會(huì)是Pandoc適合“整篇文檔從LaTeX到Word的一次性轉(zhuǎn)換”但它的強(qiáng)項(xiàng)是Markdown這種簡(jiǎn)單結(jié)構(gòu)化文檔遇到復(fù)雜模板、自定制樣式、需要把公式插入到已有Word文檔的某個(gè)具體位置時(shí)Pandoc的靈活性就不夠了。Python方案的優(yōu)勢(shì)在于可以嵌入到自動(dòng)化流程里。比如我在做文獻(xiàn)綜述時(shí)論文基本盤用docx公式部分單獨(dú)維護(hù)一個(gè)LaTeX片段文件通過腳本批量生成新的docx版本整個(gè)流程能跑通。Pandoc更像家庭搬家服務(wù)一次搬完P(guān)ython方案更像工具箱哪里需要修哪里。4. 從單個(gè)公式到整篇論文批量轉(zhuǎn)換實(shí)操4.1 自動(dòng)抽取LaTeX源碼中的公式塊真實(shí)場(chǎng)景下你手上可能是一整個(gè).tex文件里面夾雜著$...$行內(nèi)公式、\[...\]獨(dú)立公式、\begin{equation}...\end{equation}編號(hào)公式。這時(shí)候一個(gè)個(gè)復(fù)制顯然不現(xiàn)實(shí)我用正則一次性把它們?nèi)珦瞥鰜韎mport re with open(paper.tex, r, encodingutf-8) as f: tex f.read() # 匹配 $$...$$ 和 \[...\] 和 \begin{equation}...\end{equation} pattern re.compile(r\$\$(.*?)\$\$|\\\[(.*?)\\\]|\\begin\{equation\}(.*?)\\end\{equation\}, re.S) matches pattern.findall(tex) formulas [] for m in matches: formula next(x for x in m if x) formulas.append(formula.strip())這里有個(gè)容易踩的坑LaTeX公式里常會(huì)有換行、對(duì)齊符、百分號(hào)%等直接塞進(jìn)XSLT轉(zhuǎn)換時(shí)會(huì)遇到解析錯(cuò)誤。我一般會(huì)在正則提取之后做一個(gè)簡(jiǎn)單的清洗比如去掉公式內(nèi)部的%注釋但保留下劃線把\left(、\right)這類自動(dòng)調(diào)整括號(hào)的命令原樣保留因?yàn)閘atex2mathml支持這些命令。4.2 在已有Word文檔里做“原地替換”另一個(gè)高頻需求是你已經(jīng)有一份Word文檔里面有占位符或者大段LaTeX源碼想直接替換成真正的公式對(duì)象。思路是先讀取Word文檔遍歷所有段落如果發(fā)現(xiàn)段落文本里包含$$...$$這樣的模式就把這一段清空把文本切成三段前文中、公式、后文中。doc Document(draft.docx) pattern re.compile(r(\$\$.*?\$\$), re.S) for idx, para in enumerate(doc.paragraphs): if $$ not in para.text: continue # 記錄舊段落然后清空它 target para._p for child in list(target): target.remove(child) parts pattern.split(para.text) for part in parts: if part.startswith($$) and part.endswith($$): target.append(latex_to_omml(part[2:-2].strip())) else: if part: new_run para.add_run(part)需要注意target.remove(child)清空時(shí)要遍歷副本列表不能在遍歷原列表時(shí)直接刪除這是python-docx的常見坑。替換完以后Word文檔里原來那些LaTeX代碼會(huì)變成一個(gè)個(gè)可編輯的公式雙擊就能用Word的公式編輯器修改。4.3 公式編號(hào)、居中和制表位對(duì)齊學(xué)術(shù)論文的公式經(jīng)常要帶右對(duì)齊的編號(hào)比如“1”。在LaTeX里這是\begin{equation}自動(dòng)完成的在Word里通常用制表位實(shí)現(xiàn)左對(duì)齊位置放公式右對(duì)齊位置放編號(hào)。用python-docx處理時(shí)需要先給段落設(shè)置一個(gè)制表位from docx.enum.text import WD_TAB_ALIGNMENT from docx.shared import Cm from docx.oxml.ns import qn from docx.oxml import OxmlElement def add_tab_stop(paragraph, position_cm, alignmentWD_TAB_ALIGNMENT.RIGHT): pPr paragraph._p.get_or_add_pPr() tab_stops OxmlElement(w:tabs) tab OxmlElement(w:tab) tab.set(qn(w:val), right) tab.set(qn(w:pos), str(int(position_cm * 567))) tab_stops.append(tab) pPr.append(tab_stops) p doc.add_paragraph() add_tab_stop(p, position_cm15.5) p.add_run(\t) p._p.append(latex_to_omml(rE mc^2)) p.add_run(\t) p.add_run((1))這套操作模擬了Word里“先輸入公式再按Tab鍵跳轉(zhuǎn)到右邊界輸入編號(hào)”的流程。實(shí)際打印出來公式居中偏左、編號(hào)最右效果和期刊排版要求基本一致。我的經(jīng)驗(yàn)是如果統(tǒng)一使用Word的制表位要小心默認(rèn)段落有一個(gè)首行縮進(jìn)這也會(huì)影響對(duì)齊建議把段落的縮進(jìn)清掉。5. 常見報(bào)錯(cuò)與使用避坑指南5.1latex2mathml不認(rèn)識(shí)的宏命令怎么辦實(shí)際用的時(shí)候你會(huì)發(fā)現(xiàn)latex2mathml的宏支持雖然相當(dāng)全但仍然有盲區(qū)。常見的\mathbb、\boldsymbol、\text{}都能識(shí)別但一些冷門宏或自定義宏就不行了比如\qed、\varnothing的某些變體或者\(yùn)substack這種高級(jí)排版命令。我的排查思路分兩步。第一步先單獨(dú)把這個(gè)公式拿出來轉(zhuǎn)換測(cè)試確認(rèn)是哪一行拋異常第二步檢查報(bào)錯(cuò)信息如果指向某個(gè)宏無法識(shí)別直接做字符串替換def clean_latex(formula): formula formula.replace(r\mathbb{R}, r\mathbf{R}) formula formula.replace(r\boldsymbol, r\mathbf) return formula不過需要注意\mathbb{R}替換成\mathbf{R}之后字體風(fēng)格會(huì)發(fā)生改變黑板體的R和粗體的R在學(xué)術(shù)語(yǔ)境下含義不同替換前要想清楚。另一個(gè)常用方案是手動(dòng)修改latex2mathml的符號(hào)表但那個(gè)太傷筋動(dòng)骨除非你有極其復(fù)雜的特殊符號(hào)需求否則不建議動(dòng)。5.2 公式在Word里顯示成域代碼或亂碼有次我?guī)屯罗D(zhuǎn)換公式代碼跑通了Word里卻看到{EMBED Equation.DSMT4}這樣的域代碼或者一堆灰色代碼而不是公式圖形。這個(gè)問題通常不是Python代碼導(dǎo)致的而是Word里的“域”顯示設(shè)置問題。Word默認(rèn)會(huì)把公式以域結(jié)果的形式顯示但如果你開啟了“顯示域代碼”選項(xiàng)公式所在位置就會(huì)露出代碼本身。解決方式是用快捷鍵AltF9切換域的顯示狀態(tài)。如果讀者拿到文檔后還是看不到先檢查一下“文件→選項(xiàng)→高級(jí)→顯示文檔內(nèi)容→域代碼”這個(gè)開關(guān)。我在生成文檔時(shí)習(xí)慣加一段Word宏設(shè)置把這個(gè)開關(guān)強(qiáng)制關(guān)掉這在批量分發(fā)給別人時(shí)特別重要Sub DisableFieldCodeDisplay() ActiveWindow.View.ShowFieldCodes False End Sub5.3 矩陣、分段函數(shù)、特殊符號(hào)怎么處理矩陣和分段函數(shù)是論文里最常見的復(fù)雜結(jié)構(gòu)。實(shí)測(cè)發(fā)現(xiàn)latex2mathml對(duì)\begin{pmatrix}...\end{pmatrix}和\begin{cases}...\end{cases}的支持都不錯(cuò)轉(zhuǎn)換出來后在Word里能正常顯示大括號(hào)和行列布局。以分段函數(shù)為例formula rf(x) \begin{cases} x^2, x \geq 0 \\ -x, x 0 \end{cases} omml latex_to_omml(formula)這個(gè)公式轉(zhuǎn)換后放到Word里行內(nèi)的“cases花括號(hào)”會(huì)渲染成Word公式編輯器里的分段函數(shù)結(jié)構(gòu)可以編輯不是一張圖片這是我特別滿意的地方。但有一個(gè)符號(hào)類問題要留心\left( \right.這種“隱形括號(hào)”在Word里有時(shí)會(huì)渲染成多余的點(diǎn)或空格。這種問題一般無法根治我處理時(shí)會(huì)把\left.和\right.手動(dòng)刪掉或替換成普通括號(hào)只損失一點(diǎn)視覺上的自動(dòng)拉伸效果。5.4 公式字體和大小怎么統(tǒng)一Word公式對(duì)象的字號(hào)默認(rèn)繼承段落的字號(hào)設(shè)置。如果整篇文檔是五號(hào)字公式也會(huì)跟隨變化這點(diǎn)比LaTeX舒服不用額外設(shè)置。但如果你從別的文檔復(fù)制公式過來可能會(huì)遇到公式字號(hào)不統(tǒng)一的情況。我的處理方式是在生成公式后給段落設(shè)置一個(gè)統(tǒng)一的字體和字號(hào)。公式本身由Word管理但段落屬性可以控制基線style doc.styles[Normal] style.font.name Times New Roman style.font.size Pt(12)公式里出現(xiàn)的普通文本部分比如中文文字在Word里可能默認(rèn)用宋體渲染這一點(diǎn)我認(rèn)為是Word的默認(rèn)行為不必刻意修改。如果你有強(qiáng)迫癥希望公式里的中文也統(tǒng)一成黑體可以考慮在公式里加\text{...}但latex2mathml對(duì)中文\text{}的支持有時(shí)候不穩(wěn)定我一般建議公式里盡量避免出現(xiàn)中文需要解釋文字時(shí)放在正文里說。5.5 期刊要求MathType格式時(shí)怎么辦有的期刊要求公式必須用MathType尤其理工科國(guó)內(nèi)期刊這個(gè)需求很常見。嚴(yán)格意義上Word原生公式和MathType格式不互通但MathType插件支持直接在Word里選擇“轉(zhuǎn)換公式”把已有Word原生公式批量轉(zhuǎn)成MathType格式。我的經(jīng)驗(yàn)是先用自己的腳本生成Word原生公式再打開MathType插件的“Convert Equations”功能選擇Word OMML equations作為轉(zhuǎn)換來源一次性轉(zhuǎn)成MathType格式就行。不過這里有版本兼容問題。老版本MathType比如6.x對(duì)新版Word的原生公式支持不好經(jīng)常找不到可轉(zhuǎn)換的公式。這種情況下我建議換用較新的MathType 7.x版本或者改用Word自帶的公式編輯器別再折騰MathType。真遇到必須用MathType的老舊環(huán)境最穩(wěn)妥的辦法還是腳本直接生成MathType格式的公式域但這條路工程成本較高按需選擇。5.6 公式從圖片轉(zhuǎn)成Word時(shí)是否也能用這套流程搜索“公式圖片轉(zhuǎn)word”的人很多其實(shí)和本文路線是平行的。如果手上公式不是LaTeX源碼而是圖片流程是先做OCR識(shí)別再用識(shí)別出的LaTeX轉(zhuǎn)成Word。我在Windows上試過Mathpix的API把公式圖片轉(zhuǎn)成LaTeX代碼準(zhǔn)確率相當(dāng)高。拿到LaTeX代碼后走我這套轉(zhuǎn)換鏈路即可。如果你不想付費(fèi)也可以用開源的pix2tex效果也不錯(cuò)只是環(huán)境配置略微難受??傮w思路就是“圖片→LaTeX→MathML→OMML→Word公式”四步鏈路對(duì)大批量老論文的公式提取非常有用。6. 批量生成整篇文檔時(shí)我的一些心得6.1 域名安全與宏設(shè)置Word文檔里嵌入公式對(duì)象之后有時(shí)候會(huì)觸發(fā)“宏安全”或“外部?jī)?nèi)容”提醒。這不一定是因?yàn)楣接袉栴}而是OMML作為擴(kuò)展字段被Word識(shí)別成了潛在的外部?jī)?nèi)容。批量生成文檔給多個(gè)協(xié)作者用的時(shí)候我建議關(guān)閉宏或使用Word的受保護(hù)視圖。不過這又會(huì)讓接收方打開文檔時(shí)看到一片提示信息體驗(yàn)很差。我的實(shí)際做法是腳本生成后的文檔先用自己的Word打開一次確認(rèn)沒有出現(xiàn)任何“無法打開”“域錯(cuò)誤”的提示再發(fā)出去。這一步雖然老套但能擋掉90%的問題。6.2 制表位和首行縮進(jìn)的隱形沖突公式編號(hào)那一步最容易被忽略的是“首行縮進(jìn)”。Word默認(rèn)正文段落沒有首行縮進(jìn)但論文模板往往設(shè)置了“首行縮進(jìn)2字符”。如果公式段落繼承了這一設(shè)置你會(huì)發(fā)現(xiàn)公式整體右移了編號(hào)不再頂格靠右。解決方法是給公式段單獨(dú)設(shè)置paragraph_format.first_line_indent 0或者直接在生成段落時(shí)不繼承模板樣式。這個(gè)小坑我記憶猶新后來養(yǎng)成了每次生成公式段都顯式寫一次縮進(jìn)設(shè)置的習(xí)慣再也沒出過問題。6.3 公式多的時(shí)候別一次性塞進(jìn)一個(gè)段落一個(gè)自然段里塞七八個(gè)行內(nèi)公式Word的渲染性能會(huì)明顯下降。我在做一份長(zhǎng)公式較多的數(shù)理課程講義時(shí)一份文檔里有幾百個(gè)公式對(duì)象用Word打開要等七八秒。后來我把連續(xù)文本拆成多段每段最多三四個(gè)行內(nèi)公式打開速度明顯提升。對(duì)讀者來說閱讀體驗(yàn)也更舒服畢竟一段密密麻麻全是公式的論文誰(shuí)看了都頭大。6.4 版本兼容低版本W(wǎng)ord打不開高版本公式最后提醒一個(gè)低頻但致命的問題如果你用的是Microsoft 365或Word 2021生成出的OMML結(jié)構(gòu)可能帶一些新特性發(fā)給用Word 2010的同事對(duì)方可能打不開或者公式顯示異常。應(yīng)對(duì)方案很簡(jiǎn)單盡量在“兼容模式”下驗(yàn)證文檔或者在對(duì)方用Word 2013以上的環(huán)境測(cè)試后再發(fā)送。論文投遞場(chǎng)景下編輯部要求不高但正規(guī)返修時(shí)公式格式是評(píng)審的一部分這一點(diǎn)千萬(wàn)不要輕視。如果條件允許我建議在生成文檔后用LibreOffice做一次打開測(cè)試它能從側(cè)面驗(yàn)證OMML結(jié)構(gòu)是否基本標(biāo)準(zhǔn)。大多數(shù)情況下Word能打開、LibreOffice也能打開這份文檔就比較安全了。7. 最后分享一點(diǎn)我的真實(shí)體會(huì)整套方案跑通之后我最大的感受是LaTeX公式轉(zhuǎn)Word這件事技術(shù)上并不是什么“魔法”本質(zhì)就是兩種數(shù)學(xué)標(biāo)記語(yǔ)言的翻譯。真正難的是在轉(zhuǎn)換前后的工程細(xì)節(jié)路徑怎么找、正則怎么抽、字號(hào)怎么統(tǒng)一、編號(hào)怎么對(duì)齊、發(fā)給別人能不能正常顯示。這些零碎問題不解決哪怕核心代碼只有三行用起來依然會(huì)到處碰壁。如果只能給你一個(gè)建議那就是先拿一兩個(gè)典型公式跑通最小閉環(huán)再擴(kuò)展到整篇文檔。別一上來就指望腳本能處理所有復(fù)雜結(jié)構(gòu)先保證常規(guī)公式準(zhǔn)確再逐步補(bǔ)充異常處理這套工具才能在學(xué)術(shù)寫作里真正幫上忙。