ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

信创环境下WordPress公式乱码?从OMML到LaTeX的全链路解决方案

信创环境下WordPress公式乱码?从OMML到LaTeX的全链路解决方案 上个月我把博客从原来的服务器迁移到信创环境之后绝大多数页面都恢复得不错唯独那些包含Word公式的文章惨不忍睹。标题和正文都正常公式区域要么变成一行奇怪符号要么重复、错位、直接缺块。我一开始以为是主题问题后来才明白这其实是一个从文档格式到Web渲染的全链路问题。今天想把这套处理过程整理出来给同样在信创环境下用WordPress处理Word公式的朋友做个参考。先说结论Word公式到了WordPress里会乱根本原因不是信创环境太差而是Word、WordPress和浏览器三者对“公式”的理解根本不一致。Word自带的是OMML格式WordPress默认不会解析公式浏览器只认MathML或者经过MathJax/KaTeX渲染后的HTMLCSS。这中间缺了好几个转换环节所以格式兼容无从谈起。加上信创环境下常用的是国产操作系统和国产浏览器字体、内核、插件生态都有自己的脾气问题就更容易被放大。我踩完这些坑之后把整套流程固化了下来包括怎么批量转格式、怎么在WordPress里渲染LaTeX公式、怎么离线部署前端资源以及遇到乱码和行内公式顶格怎么修。这篇就当是一个完整的实操记录适合正在做WordPress建站、内容迁移或者刚把站点搬到信创环境的站长参考。1. 为什么Word公式到了WordPress里会变成一团乱麻想要解决问题先别急着装插件。先弄明白Word公式在电脑上显示得好好的为什么一搬到网页上就现原形。1.1 公式的三种“身份证”OMML、MathML与LaTeX很多人不知道Word里的公式并不是一张图而是一段结构化的公式对象。Word默认使用OMMLOffice Math Markup Language这种基于XML的数学公式描述格式。OMML被设计用来在Office内部交换数学公式比如上标、下标、根式、分式都是通过特定的XML节点表示。浏览器那边完全不是这套体系。现代浏览器并没有把OMML纳入标准Web的数学显示标准是MathML一种专门用于数学公式的标记语言。HTML5甚至把MathML列为内建支持的元素只是各家的实现程度不一样。除开放标准之外还有一套被数学社区广泛使用的LaTeX语法它不是标记语言而是基于TeX排版系统的一套命令式语法比如用\frac{a}{b}表示分数用\sqrt{x}表示根式。所以你的Word文档里有一个公式实际数据可能是OMML你打开浏览器想显示它浏览器需要的是MathML或者可以由MathJax解析的LaTeX而WordPress本身只是一套PHP内容管理系统它只负责把文章内容存储成HTML放进数据库不会主动把OMML“翻译”成网页能理解的东西。这三者之间的差异就是格式兼容问题的根源。你从Word复制出来的公式粘贴到WordPress后台时编辑器保存下来的内容可能是一堆无效的OMML节点或者被清空的标签。页面展示的时候浏览器碰到无法识别的节点就会忽略结果要么什么都不显示要么只显示残缺符号。1.2 WordPress的渲染管线到底缺了什么WordPress传统上由经典编辑器负责内容录入现在则默认使用古腾堡块编辑器。这两个编辑器对公式的支持都相当有限古腾堡虽然支持插入自定义HTML和代码块但并不会自动把Word公式转成可渲染的格式。如果你在Word里复制一段公式直接粘贴到古腾堡段落块中编辑器通常会把公式中的结构信息清洗掉。因为浏览器粘贴板里提供的是带格式的HTML或纯文本Word在复制时甚至可能提供多种格式的快照而浏览器选择的那一份并不适合公式保存。公式对象被转换之后有可能变成图片的引用地址有可能丢失全部排版信息。WordPress主题和插件的存在增加了变量。很多主题为了显示效果会加载自己的CSS重置默认样式比如给img设置max-width: 100%给p设置line-height。公式一旦被转成图片很容易被这种全局样式影响造成缩放变形。公式一旦以文本符号形式残留在段落里又会被主题的字体和行高控制出现行内公式顶到上方或者行距忽大忽小的问题。所以WordPress缺的不是“公式功能”本身而是一条完整的转换链路把Word公式从OMML转成Web可渲染格式然后再把渲染工具MathJax或KaTeX嵌入到站点环境中并保证主题和编辑器不去破坏它。1.3 信创环境把什么矛盾放大了信创环境的特殊性在于整套基础软件都在国产化链路里。你可能会用麒麟操作系统或者统信UOS作为桌面系统用WPS替代Office用国产浏览器访问WordPress后台服务器端用的是国产数据库和PHP环境。单看每一项都问题不大但它们凑在一起之后公式兼容的矛盾会被放大。首先是办公软件层面。Word公式在微软Office里保存为OMML没有太多问题但很多信创办公场景中用户用的是WPS。WPS对Word文档有比较好的兼容性但在公式编辑、尤其是复杂公式对象的内部表示上和原生Office存在细微差异。用WPS打开一份包含大量公式的docx再另存偶尔会出现公式对象轻微变形或者被转换为图片的情况。这份文档随后被导入WordPress后问题就跟着带过来了。其次是浏览器层面。国产浏览器大多基于Chromium内核对MathML的支持并不完整有些版本甚至默认不开启MathML支持。你可以用MathJax把这些公式统一走HTMLCSS渲染绕开MathML的原生差异但这又要求你正确配置MathJax的资源加载。还有一个更现实的问题很多信创环境是内网隔离或者半隔离状态服务器部署在私有云里前端页面挂着公网CDN的MathJax脚本文件在测试环境中能正常显示到了生产环境却加载不出来公式区域留白一大片。这不是公式格式的错而是资源部署链路没有跟着一起“信创化”。后面的实操部分我会专门讲离线部署就是为了避免这个坑。2. 迁移前先想清楚三条路线按需求选在动手批量处理之前必须先想清楚你希望公式在浏览器里以什么形态呈现。我试过三条路线各有各的好处也各有各的闹心之处。2.1 公式转图片最省事但也最“死”最早期的做法就是把Word公式截图或导出成PNG图片然后在WordPress里插入图片。这个方法确实简单公式长什么样图片里就是什么样对浏览器、主题、设备基本没有要求。但缺点几乎是致命的公式导入后变成静态图片如果发现某个系数写错了需要回到Word或WPS里修改原公式、重新导出图片、重新上传替换。图片在Retina屏和高分屏上还会发虚放大后边缘模糊。而且图片的基线、行内对齐很难控制经常出现公式跟文字上下不齐的情况。这条路只适合文章数量极少、公式改动频率很低的场景。如果你的博客有几十上百篇带公式的文章我不建议一开始就全量转图片否则后期维护会变成一场灾难。2.2 转MathML跟浏览器最合拍但还是要处理细节MathML是W3C的标准数学标记语言浏览器原生支持不需要额外加载JavaScript库。在信创环境的国产浏览器上只要内核版本不是太老MathML的基础元素多数能显示出来。方法是把Word公式转成MathML嵌入到WordPress文章HTML中。实际操作中Pandoc可以把docx里的公式输出为MathML效果大致可用。但命令行转换出来的MathML往往冗长而且部分MathML元素在老旧Chromium内核中支持不完整比如矩阵、大括号跨行排版容易错位。更麻烦的是WordPress古腾堡编辑器在保存内容时不会主动清理MathML但如果你把MathML粘贴到可视化编辑器里浏览器会尝试解析它反而容易把部分标签吃掉。正确做法是切换到代码编辑器或自定义HTML块中粘贴。这要求你对手写HTML有一定熟悉度否则在复杂文章中找公式位置非常痛苦。2.3 转LaTeX技术类博客的最优解我个人最推荐我个人最推荐把Word公式转成LaTeX语法然后在前端用MathJax或KaTeX渲染。LaTeX语法本身占用空间小语义清晰便于在数据库里存储和检索后期如果要批量查找某个公式也很容易。MathJax渲染出来的公式质量非常高分式、根式、求和符号都能保持原有的排版逻辑。Pandoc在转换Word公式时默认可以把OMML转成LaTeX而且相似度较高至少我能遇到的绝大多数公式都能转换成功。行内公式会放在两个美元符号之间块级公式会放在双美元符号或[...]之间。WordPress里只要加载MathJax这些语法就会被渲染成可视化公式。这个方案的另一个好处是转换后的文章仍然保留着公式的“语义”你可以继续针对公式做全文搜索也可以在未来站点换皮肤、换平台时把这些LaTeX代码原样迁移到新系统不丢失公式信息。相比图片方案这是一条更可持续的路。这里放一张三条路线的对比表方便你按场景判断方案维护成本显示效果兼容性是否支持全文检索适合场景公式转图片高改一次要重新出图一般高分辨率下发虚最好任何设备都能看不支持公式极少、不再改动MathML中需处理标签细节良依赖浏览器内核较好老旧内核存在风险支持对交互要求高、偏好原生标准LaTeXMathJax低改语法即可优排版效果接近TeX佳脚本统一渲染支持技术类博客、文档站、内容长期维护3. 我的实操路径从Word文档到WordPress页面选定走LaTeXMathJax之后我完整跑通了一条流程。下面按我实际操作时的顺序把每一步的关键节点和参数都写清楚你照着走基本不会跑偏。3.1 工具清单与实际选择在信创环境下我常用的办公套件是WPS它会直接影响到后续处理。因为我的原始文档大多来自Windows环境下的Microsoft Office也可能被同事在WPS里编辑过。第一步不是急着安装转换工具而是先检查docx文件里的公式是否为可转换的OMML对象而不是已经被WPS转成图片的静态对象。检查方法不复杂用解压软件打开docx文件找到word/document.xml搜索“m:oMath”如果有命中的节点说明公式还保留为可编辑的公式对象。如果搜索不到只看到大量图片引用说明原文档里的公式已经被变成图片了后续转换需要先脱机识别或者只能按图片处理了。批量转换我选择Pandoc它可以在Linux和Windows命令行下运行。我服务器上跑的是Linux下的Pandoc桌面端也用了一份。Pandoc对docx的OMML支持比较成熟转换出来的LaTeX质量高于我试过的其他工具。公式本身极其复杂且包含矩阵、多行编号的场景Pandoc可能会出错但可以作为批量处理的起点。3.2 用Pandoc把docx转成带公式的HTML我的转换思路不是直接从docx转WordPress而是先用Pandoc转成标准的HTML再把HTML内容导入WordPress。这样能同时处理正文结构和公式语法减少手动复制粘贴的损耗。最基本的转换命令是这样pandoc input.docx -o output.html --mathjax这个命令会把docx里的公式全部转成LaTeX并在生成的HTML头部引入MathJax脚本。不过我不建议直接使用它生成的HTML作为最终文件因为引入的MathJax地址还是线上CDN在信创内网环境可能用不了。我更常用的做法是先指定不引入脚本只把公式语法提取出来pandoc input.docx -o output.html --mathjax --no-highlight生成的HTML里行内公式会被包裹为\(...\)块级公式会被包裹为\[...\]。这是标准的LaTeX数学定界符。为了在WordPress里更稳妥地渲染我会在后续处理中把\(替换为$把\[替换为$$。MathJax默认支持这两种写法而且我自己在Markdown迁移过来的旧文章里也习惯用美元符号统一格式便于维护。如果你希望保留文档中的表格、图片和列表结构可以直接把output.html里的body内容复制到WordPress自定义HTML块中。但我通常不会一股脑全粘贴因为WordPress主题的CSS和Pandoc生成的HTML类名往往不一致直接粘贴容易造成内容与样式冲突。我一般只提取段落、标题和公式内容在WordPress后台重新组织版面。另外对于包含大量公式的文章我建议先用Pandoc做一个“转换测试”把文档里公式数量统计一下看看有没有Pandoc无法识别的地方。可以在命令行直接指定输出格式为“json”然后把非数学部分的输出内容检索一遍但平时我懒得这样细查直接在HTML文件里搜索\(和\[的数量再跟原文档里的公式数量比对数量对得上基本就放心了。3.3 在WordPress里正确粘贴公式内容WordPress后台粘贴公式内容最忌讳就是直接贴到可视化段落块里。古腾堡编辑器会把粘贴进来的文本做过滤很多标签会被自动去掉代码块外的LaTeX定界符也可能被转义。我试过几次之后总结出了一套稳定做法。对于一篇文章里公式特别多的情况我会在文章编辑页面插入多个“自定义HTML”块每个块对应文章的一部分。先把Pandoc转换出的正文段落和公式代码放进这个HTML块再把旁边的段落文本复制到正常的段落块中。这样做的好处是公式代码不会被编辑器的格式化逻辑污染而正文部分仍然可以利用WordPress的经典样式。如果你在编辑器里看到公式代码被自动转换成了类似$这种实体字符说明粘贴源文本时被转义了。解决办法是多用“代码编辑器”视图也就是古腾堡右上角三个点菜单里的代码编辑器直接在HTML源码层操作。确认所有$符号都保持原样发布后再刷新页面看渲染效果。还有一种常见情况是你用的是经典小工具或第三方页面构建器。主题自带的页面构建器为了安全性会过滤掉script标签导致MathJax加载失败。这种情况下不要试图在页面内容里写script引入MathJax应该把MathJax的加载脚本放到主题的footer或者header中保证全站统一加载。3.4 离线部署MathJax彻底摆脱外部CDN我在前文反复强调离线部署因为信创环境下的网络访问并不总是一帆风顺尤其是部署在内网机房的WordPress外网加载脚本很容易超时或被拦截。MathJax脚本如果加载不出来页面上的$...$就只是一串字符公式自然全部裸奔。我的处理方式是把MathJax下载到服务器本地静态目录。假设WordPress安装在/var/www/html我会创建一个/var/www/html/wp-content/mathjax/目录把MathJax的es5打包文件放到里面。然后在主题的functions.php或自定义子主题的functions.php中加入加载脚本的代码。具体代码可以写成这样add_action(wp_enqueue_scripts, function () { wp_enqueue_script( mathjax-local, /wp-content/mathjax/tex-mml-chtml.js, array(), 3.2.2, true ); });如果你不想改主题文件也可以直接在主题的header.php里手动加入script标签但要注意别在页面构建器管理的页面里重复加载。加载位置我建议放在页面底部也就是footer区域因为MathJax只会在文档内容渲染完毕后再执行不会阻塞首屏。离线部署之后MathJax的配置同样要放在本地。比如我希望支持行内$定界符并禁止在Markdown链接等文本中误触发就在MathJax配置里明确设置script window.MathJax { tex: { inlineMath: [[$, $], [\\(, \\)]], displayMath: [[$$, $$], [\\[, \\]]] }, svg: { fontCache: global } }; /script用SVG输出而不是HTML-CSS输出这个取舍我在下一节详细说。先记住离线脚本本地配置是整个信创环境下公式显示稳定性的关键。4. 信创环境下的特殊适配细节如果只是把公式成功显示出来那这关算过了。但我在实际使用中又遇到了不少信创环境特有的问题不专门处理的话公式显示依旧不稳定。4.1 国产操作系统上字体缺失会怎么影响公式信创环境的桌面端多为Linux内核的国产操作系统预装字体和Windows差别很大。Windows上有宋体、微软雅黑等字体而很多国产系统自带的是文泉驿、思源黑体等开源字体。Word公式在转换前如果依赖了特殊的数学字体在Linux系统下打开可能显示为方块或者缺字符这一层问题会直接影响转换质量。在我自己的迁移过程中遇到最典型的情况是原始Word文档使用“Cambria Math”作为公式字体。在Windows上没问题但在统信UOS上打开时系统会自动替换字体结果WPS显示出来的公式有些字符错位。如果这时候直接复制内容再转换得到的LaTeX源代码也可能带有错误字符。解决办法是在信创环境下先不要直接编辑原始docx先用Pandoc在命令行下转换这样能跳过字体渲染环节直接从底层读取OMML。Pandoc读取的是Office XML中的公式节点而不是画面上看到的字形因此字体缺失对转换结果影响较小。如果你的文档已经被WPS打开并保存过字体替换信息可能已经写进了文档属性里甚至导致部分复杂公式被降级为OMML不兼容的结构。这种情况我会重新拿一份原始文档在Windows上另存一遍后再转到信创环境处理避免二次污染。4.2 浏览器差异不是公式代码的问题是渲染引擎的事很多信创用户使用的浏览器是国产浏览器常见的有奇安信、360安全浏览器、龙芯浏览器等它们大多基于Chromium内核但版本跨度很大。老版本的Chromium对MathML的原生支持停留在实验阶段而MathJax升级到3.x之后对浏览器版本也有一定要求。用MathJax 3渲染LaTeX时底层默认使用HTML-CSS输出处理器在渲染过程中会基于页面字体和CSS进行计算。如果浏览器禁用了WebGL相关功能或者页面使用了混合内容策略限制了本地脚本执行MathJax可能退回到降级模式部分符号显示成方框。我最终选择将MathJax的输出改为SVG模式。SVG模式一次渲染生成对应的矢量图形对浏览器的计算压力更小也不依赖WebGL和字体检测结果。对于学术文章这种公式数量很大的页面SVG格式还保证了放大缩小不变形。配置方式是在MathJax配置项里加入svg: { fontCache: global }然后在加载脚本前设置window.MathJax {...}。这样输出的公式不再是HTML字符拼接而是一张完整的SVG对象兼容性明显提升。4.3 服务器端组件选择对公式显示的隐性影响公式显示问题有时候并不在前端而在后端。WordPress动态页面如果开启了PHP缓存插件页面第一次生成时会保存一份静态HTML文件。如果第一次访问时MathJax脚本还没加载成功缓存文件里的公式源码就已经被保存成未经渲染的状态之后所有访问者看到的都会是那一份坏缓存。我在信创环境下部署了Batcache和Redis对象缓存之后遇到一个奇怪现象文章发布时公式正确过了一段时间再刷新公式又变成LaTeX源码。排查了半天才发现是页面缓存的锅。处理办法是把动态页面的MathJax配置独立出来做成静态文件。这样即使页面被缓存公式源码依然是保存好的LaTeX文本不会因为渲染延迟被缓存污染。服务器端还要注意PHP关闭了短标签支持时?和? ?的解析会有隐患但这不是公式问题的重点。真正的重点在于公式源码最后是存进数据库的纯文本PHP端不要对它做HTML转义。我在WordPress的wpautop函数中遇到过几次问题它会对文本段落自动添加p标签有时候会把公式前后的换行符改成p标签导致$$...$$中间出现HTML节点MathJax匹配失败。对策是给包含公式的区块设置一个不会触发wpautop的渲染方式。最简单的办法是把公式放在自定义HTML块里或者使用一个支持禁用wpautop的插件。如果文章数量少直接在模板中给对应body元素加上类名再用CSS强制清除受影响的空白间距但不推荐作为通用方案。5. 常见问题与排查记录最后把这一个月里遇到的典型问题和排查思路整理成速查表以后重新部署的时候直接照着看就行。5.1 乱码和“源代码裸露”是怎么回事公式区域直接显示$符号和LaTeX命令说明MathJax没有加载成功。第一步先打开浏览器开发者工具查看控制台里有没有MathJax相关脚本的404报错。有报错就检查脚本路径和WordPress当前站点地址是否一致尤其是迁移后站点URL发生了变化的场景。如果脚本加载正常但公式依然裸露大概率是MathJax的裁剪配置问题。3.x版本的MathJax支持组件化加载默认的tex-mml-chtml.js文件体积不小但有些精简配置只加载了部分扩展。检查是否有配置项把行内识别符关闭了或者由于页面中出现了$符号但前后没有空格MathJax不认为它是一个公式的起始。再者部分安全插件会对页面中的JavaScript做过滤比如Wordfence或All in One Security的防火墙选项会把内联脚本当做潜在攻击拦截。如果页面所有内联脚本都被移除了MathJax配置代码自然失效。这种情况把MathJax的配置代码升级为外部JS文件然后再设置安全插件白名单。5.2 行内公式挤成一团、行距异常怎么调行内公式默认使用vertical-align: middle会让公式和文字的中心线对齐。但Word里生成的公式在转换后经常出现整体偏高或者偏低跟行内文字不在一条线上。解决方法是给公式外层容器增加CSS属性强制设置为vertical-align: -0.3em或根据实际情况微调。行距异常更多是字体设置的问题。有些国产浏览器默认渲染line-height为1.5或者更高公式中的分式、根式的高度会超过普通文字行高导致行与行之间被撑开。用MathJax时可以在CSS里将包含公式的段落行高设为一个固定的数值比如1.6倍并且不要给公式所在块额外设置边距。如果你发现公式渲染后文字被往上顶像热词里说的“word公式后输入内容靠上”多半是公式被当成了inline-block元素并且在段落内有一个默认的vertical-align: baseline。这时候给.MathJax_SVG或mjx-container加上vertical-align: middle; display: inline-table;一般能解决。5.3 图片公式发虚、被拉伸怎么办如果部分公式在转换过程中已经被转成了图片那么后续无论怎么调MathJax都帮不上忙。图片公式发虚通常是因为图片原始分辨率太低。在Word中导出公式图片时尽量用WPS自带的高清截图或office的“复制为图片”功能设置缩放比例不低于200%再导入WordPress。被拉伸的问题则来自WordPress自带的响应式图片机制。主题设置里可能默认要求图片自适应宽度公式图片是一个窄长条被拉成100%容器宽度后自然模糊变形。解决方案是在该图片的外部包一层div并给它一个合适的width或者直接给img添加style样式限制最大宽度为原始宽度。对于已经上传且发虚的公式图片我后来用脚本批量检查media库里的图片宽高比把明显过宽过窄的图片找出来人工替换。虽然没有自动化做到全量重导出但至少能把影响降到最低。6. 结合我的经验再说几句在信创环境下做WordPress的公式兼容本质上是一个跨格式、跨编辑器的数据转换问题并不只是装个插件那么简单。关键是把转换链路理清楚Word公式到LaTeXLaTeX到MathJaxMathJax离线部署到信创网络。每一环都稳住了页面公式才能长期稳定显示。我后来回头复盘发现自己最初犯的错误就是把所有希望寄托在WordPress插件市场上。试了好几个公式插件有的在Windows上表现正常一放到国产浏览器环境下脚本加载不了有的直接跟主题冲突公式区域样式错乱。反倒是回到最稳定的PandocMathJax组合一步一个脚印解决问题。有时候越朴素的方案越靠得住尤其是在基础软件生态不完全一致的信创环境里。最后再分享一个小细节发布公式文章之后建议用手机上的非Chrome内核浏览器再刷一遍看看行内公式、块级公式、分式缩放表现如何。桌面端显示没问题移动端经常因为字体和渲染宽度再次翻车。公式兼容这件事没有“一次配置一劳永逸”的说法多留几条适配余量后面会省心很多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进