1. 先别急着“转格式”:Word 和 FTL 差着整整一个“世界观”
先说一个很多人忽略的事实:Word 根本不能“直接转成 FTL”,网上那些教程第一步就叫你另存为 Word 2003 XML,其实绕了一个大弯。
FTL 是什么?它是 Java 模板引擎 FreeMarker 的模板文件,本质是一段带占位符的纯文本。FreeMarker 拿到这份文本之后,会把它当成字符串逐字逐句地读,遇到 ${变量} 就替换成数据,遇到 <#if>、<#list> 就执行逻辑,最终拼出一份完整内容。所以 FTL 本身不关心你生成的是 Word、HTML、邮件还是普通 txt,它只负责“把文本算出来”。
但 Word 文档不是这种脾气。早期的 .doc 是私有二进制格式,你用记事本打开就是一堆乱码;后来的 .docx 虽然本质上是一堆 XML,但它被压缩包外壳包着,里面是 word/document.xml、word/styles.xml、word/media/ 等一大堆文件。FreeMarker 可不会自己拆压缩包,更不能理解 Word 的排版模型。如果直接把 .docx 改名成 .ftl 丢给 FreeMarker 渲染,结果往往不是 Word 乱码,就是渲染出来一堆无法识别的二进制内容。
理解了这层,再去搜“Word 转 FTL”就不会被绕晕了。所谓“Word 转 FTL”,准确描述是:选一种 Word 能打开、同时又是纯文本/可被 FreeMarker 处理的中间载体,把 Word 里的内容改造成带 FTL 标签的模板。为什么都在用 Word 2003 XML?因为它是这个场景里出现最早、资料最多、也最容易用文本编辑器直接上手的一种载体。
1.1 FTL 的本质是一张带占位符的白纸,它只认文本不认排版
你可以把 FTL 模板想象成一张写满了句子、中间留了很多空槽的答题卡:
code复制尊敬的${customerName}:
您有一条新的告警工单,编号为${orderNo}。
当后端把 customerName、orderNo 填充进去,这张纸就变成了完整内容。FTL 的规则非常简单,变量用 ${} 包裹,逻辑标签用 <#xxx> 包裹,没有别的高深机制。
恰恰是这份“简单”,让所有希望把 Word 当模板的人踩进了同一个坑:Word 是富文本,它内部不仅记录“我写了什么字”,还记录“每个字是什么字体、什么颜色、有没有加粗、段落间距多少、当前是第几节”,这些信息以非常复杂的结构藏在文件里。FTL 没有能力理解这套结构,它只能机械地把模板文本输出。想让它生成 Word,唯一办法是:确保你输出的文本本身就是 Word 能认识的 XML 源码。
换句话说,Word 2003 XML 这个格式之所以能当 FTL 的“内容载体”,是因为它让 Word 内容和 FTL 处在了同一个维度上——模板里写的每一段、每一行、每一个表格,在保存成 Word 2003 XML 后都是一个一个带 <w:...> 标签的纯文字节点,可以直接用查找替换往里塞变量,渲染出来后再让 Word 自己打开。
1.2 doc 是二进制、docx 是压缩包,都没法直接拿来当文本改
很多人第一次手动改 Word 模板,会下意识把 .docx 改名成 .zip,解压出 document.xml,然后把里面需要动态变化的文字替换成 ${xxx} 再打包回去。这条路不是完全走不通,但它有一个致命问题:你解压出来的 XML 只是文档的“正文部分”,样式、页眉页脚、主题、兼容性设置全部分布在其他文件里。一旦你用文本编辑器手动改了 document.xml,却漏掉 styles.xml 或 [Content_Types].xml 之间的关联,重新打包后 Word 就会立刻弹“文件已损坏,是否尝试修复”。
同时,直接改 document.xml 的体验非常差,因为 Word 保存 docx 时会插入大量辅助节点。比如你用 Word 打了一段“客户名称:张三”,打开 document.xml 可能看到:
xml复制<w:p>
<w:r>
<w:t>客户名称:</w:t>
</w:r>
<w:proofErr w:type="spellStart"/>
<w:r>
<w:t>张</w:t>
</w:r>
<w:r>
<w:t>三</w:t>
</w:r>
<w:proofErr w:type="spellEnd"/>
</w:p>
一个字被拆成两段 run,中间夹着拼写检查标记。你要把“张三”整体替换成 ${customerName},靠普通查找替换根本做不到,因为你得先想清楚哪些碎片能拼成一句话、中间的 proofErr 要不要删。这也是为什么很多人在 docx 直接改模板时,改出来的变量要么断成两截,要么渲染后句子莫名其妙多了空格。
而 Word 2003 XML 是另外一种存在。它把所有内容摊在一个扁平的单文件 XML 里,样式和正文都在同一个文件内,你不需要解压、不需要知道压缩包内部依赖。虽然它也逃不掉 Word 会拆 run 的老毛病,但至少你用文本编辑器打开它,看到的是一整段可读的 XML 流,搜内容、找节点、做替换都方便得多。
1.3 “另存为一个 XML”不是目的,真正的目的是让 FTL 能输出 Word 源码
顺着上面两条继续推,你会发现一个重要结论:Word 转 FTL 整个过程,本质上是把“用 Word 看到的排版界面”翻译成“一段带动态占位符的 WordprocessingML XML 源码”。
Word 2003 XML 的源码长什么样?一段包含“您好”两字的段落,保存后大概是:
xml复制<w:document xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
<w:body>
<w:p>
<w:pPr>...</w:pPr>
<w:r>
<w:rPr>...</w:rPr>
<w:t>您好</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
这段 XML 看起来像是给程序员看的天书,但它确实是 Word 自己能直接打开的文件格式。所以只要你用 FTL 把这段 XML 中的“您好”换成“您好,${name}”,FreeMarker 渲染后输出的依然是一段合法的 WordprocessingML,Word 自然能打开。
想通这一层后,再回头看网上教程,你会发现它们让你“另存为 Word 2003 XML、改后缀 .ftl”的真正价值,是在帮你找一个**既能在 Word 里可视化编辑、又能让 FreeMarker 直接操作的文本化中间格式*
