☰
PHPWord 0.8.0 版本解析:模板引擎、表格行克隆与排版能力全面升级
2026/9/28 6:37:45 网站建设 项目流程
  • 后端

【免费下载链接】PHPWord

A pure PHP library for reading and writing word processing documents

项目地址:https://gitcode.com/gh_mirrors/ph/PHPWord
点击查看免费下载

PHPWord 是一个纯 PHP 实现的、用于读取和写入字处理文档的开源库。0.8.0 版本发布于 2014 年 3 月 15 日,是该项目早期演进中极具分量的一个里程碑:它合并了大量来自社区的改进,并在本版本中引入单元测试,使代码覆盖率达到了 90%。本文以 docs/changes/0.x/0.8.0.md 发布说明为骨架,结合当前仓库源码,逐条还原该版本引入的模板处理、表格、段落、字体、节(Section)、脚注与读取器等能力,帮助读者理解这些特性的设计意图与今天在仓库中的实现形态。

版本背景:社区驱动的一次大版本汇聚

0.8.0 的发布说明明确提到,该版本"合并了大量来自社区的改进",并且"在本版本中引入单元测试,代码覆盖率达到了 90%"。这意味着从 0.8.0 开始,PHPWord 不再只靠手工验证,而是建立了可持续的回归测试体系——当前仓库中的tests/PhpWordTests/目录正是这套体系的延续,例如 TemplateProcessorTest.php 覆盖了模板处理的各类场景。

从版本号跨度看,0.8.0 的特性列表几乎横跨了 PHPWord 的核心能力面:模板(Template)、Word2007 写入器、表格行、字体、段落、节(Section)、脚注(Footnote)、读取器(Reader)与图片处理。下面按这些领域逐一展开。

模板引擎:从占位符替换到整表行克隆

0.8.0 在模板处理上贡献了最多特性,这些能力在今天的TemplateProcessor类(src/PhpWord/TemplateProcessor.php)中均有完整对应实现。

saveAs():直接把模板处理结果落盘为文件

此前模板处理结果只能以临时文件形式保存,0.8.0 提供了saveAs($fileName)方法(由 @RomanSyroeshko 贡献,#56、#57),允许把生成的模板文件直接保存为用户指定的文件名。

从源码看(src/PhpWord/TemplateProcessor.php#L1040-L1056),该方法的实现先调用内部save()生成临时文件,再通过copy()复制到目标路径并删除临时文件。源码注释特别说明:不使用rename()是因为它在 Windows 平台上会丢失文件所有权信息,导致用户打开文件时出现"拒绝访问"错误——这是一个值得注意的跨平台细节。

实际用法:

$templateProcessor = new PhpOffice\PhpWord\TemplateProcessor('template.docx'); $templateProcessor->setValue('name', 'PHPWord'); $templateProcessor->saveAs('output.docx');

setValue() 的替换次数限制

setValue()在 0.8.0 中新增了第三参数$limit,用于限制对模板变量执行替换的次数(@RomanSyroeshko,#52、#53、#85)。

当前源码中该参数默认值为常量MAXIMUM_REPLACEMENTS_DEFAULT = -1(表示不限制,见 src/PhpWord/TemplateProcessor.php#L35、#L326)。当模板中同一个宏出现多次、而你只想替换其中某几次时,这个限制参数就非常有用。同时setValues()也支持把限制透传给内部逐个调用的setValue()(#L368-L373)。

// 只替换前 2 次出现的 ${city},其余保持占位符原样 $templateProcessor->setValue('city', 'Shanghai', 2);

需要留意:setValue()在替换前会自动补齐宏的定界符(${与},见 #L98-L100 与ensureMacroCompleted()),并对替换值做 UTF-8 编码转换与回车符处理,因此直接传入普通字符串即可。

applyXslStyleSheet():用 XSL 样式表改写模板

0.8.0 允许对模板应用 XSL 样式表(@RomanSyroeshko,#46、#47、#83),这为模板的批量改写提供了强大手段。

从 src/PhpWord/TemplateProcessor.php#L240-L252 的实现看,该方法接收一个DOMDocument形式的 XSL 样式表,通过XSLTProcessor导入并可选地设置参数($xslOptions与$xslOptionsUri),随后对文档的页眉(headers)、正文主体(main part)和页脚(footers)三部分分别执行转换。仓库测试文件tests/PhpWordTests/_files/xsl/下提供了passthrough.xsl与remove_tables_by_needle.xsl两个示例样式表,前者透传 XML,后者可依据指定条件移除表格——可直接作为编写自定义 XSL 的参考起点。

方法注释给出重要警告:该方法不对 XSL 样式表的输出逻辑做任何推断,务必保证输出正确转义,否则可能产生损坏的文档。

cloneRow():动态克隆表格行

0.8.0 引入的"在模板文档中即时克隆表格行"(@jeroenmoors,#44、#88)是模板功能中最实用的特性之一,对应今日的cloneRow($search, $numberOfClones)(src/PhpWord/TemplateProcessor.php#L762-L806)与后续补充的cloneRowAndSetValues()(#L878)。

其原理是:在文档 XML 中找到包含目标宏的那一行(findRowStart()/findRowEnd()定位<w:tr>边界),提取整行 XML,然后按克隆份数对行内的宏变量进行带编号的复制,再重新拼回文档主体。实现中还特别处理了跨行合并单元格(w:vMerge)的场景,避免克隆破坏合并结构。

仓库示例 samples/Sample_07_TemplateCloneRow.php 完整演示了两种用法:

// 方式一:先克隆 10 行,再逐个给编号变量赋值 $templateProcessor->cloneRow('rowValue', 10); $templateProcessor->setValue('rowValue#1', 'Sun'); $templateProcessor->setValue('rowValue#2', 'Mercury'); // ... rowValue#3 ~ #10 // 方式二:一次克隆并赋值(适合结构化数据) $values = [ ['userId' => 1, 'userFirstName' => 'James', 'userName' => 'Taylor', 'userPhone' => '+1 428 889 773'], // ... ]; $templateProcessor->cloneRowAndSetValues('userId', $values);

克隆后,原模板中的rowValue会被编号为rowValue#1、rowValue#2……,之后即可用setValue('rowValue#N', ...)逐个填充。这是生成发票明细、人员名单等重复表格行场景的标准做法。

顺带修复:含&的替换值破坏模板

0.8.0 还修复了"向模板中写入包含&的值会破坏模板"的问题(@SiebelsTim,#51)。这一问题的根源在于模板本质是 XML 文档,&等字符必须按 XML 规则转义。当前setValue()实现中,当Settings::isOutputEscapingEnabled()开启时会通过Xml转义器处理替换值(src/PhpWord/TemplateProcessor.php#L346-L349),配合ensureUtf8Encoded()与回车符转换,共同保证替换后文档的 XML 合法性。

表格:表头行重复、跨页断行与百分比宽度

0.8.0 的表格增强主要由 @ivanlanin 贡献(#48、#86):

  • 表头行重复(Repeat as header row):让指定表格行在跨页时自动作为表头重复出现,对应Row元素的 header 相关属性;
  • 允许行跨页断行(allow row to break across pages):控制单元格内容较多时是否允许行在页面边界被拆分;
  • 表格宽度支持百分比:表宽不再局限于绝对尺寸,可用百分比定义,对应Table的宽度设置。

这些能力在 src/PhpWord/Element/Row.php、src/PhpWord/Element/Table.php 以及样式类 src/PhpWord/Style/Table.php 中均有对应实现与取值校验,实际使用时把宽度值传入表格样式数组即可让 Word 按相对比例渲染。

段落与排版:悬挂缩进、分页控制与 Tab 停靠位

0.8.0 在段落层面引入了多项与 Word 对齐的能力(@ivanlanin,#48、#86、#87、#92):

悬挂缩进(Hanging paragraph)

悬挂缩进即首行不缩进、后续行缩进的排版方式,常用于项目符号列表与参考文献。当前仓库中该能力由 src/PhpWord/Style/Indentation.php 的hanging属性承载,读取器端也会将其映射为 OOXML 的w:ind w:hanging(见 src/PhpWord/Reader/Word2007/AbstractPart.php#L712)。HTML 转 Word 时,无序/有序列表的悬挂缩进正是借助该机制实现的(src/PhpWord/Shared/Html.php#L617-L641)。

段落分页控制(Pagination)

widowControl(孤行控制)、keepNext(与下段同页)、keepLines(段内不跨页)以及pageBreakBefore(段前分页)四项分页属性,分别对应 src/PhpWord/Style/Paragraph.php 中的四个布尔字段(#L113-L134),默认值分别为true(孤行控制开启)、false、false、false。这些开关可显著改善长文档的排版质量:

$phpWord->addParagraphStyle('pagination', [ 'widowControl' => true, // 防止段落首行/末行单独出现在页面边缘 'keepNext' => true, // 保持与下一段落在同一页 'keepLines' => true, // 段落行不跨页拆分 'pageBreakBefore' => false, // 段前是否强制分页 ]);

setTabs() 与行高方法

  • setTabs()允许为段落设置自定义 Tab 停靠位(@ivanlanin,#92),用于对齐多列文本;
  • 行高方法用于镜像 Word 中的行高设置(@gabrielbull),对应Paragraph样式的lineHeight属性(该属性在 src/PhpWord/Shared/Html.php#L1242 中注释为"乘以默认行高的倍数,如 1、1.5 等")。

节(Section):多栏排版、分节符与页码

0.8.0 为 Section 增加了三类重要能力(@ivanlanin,#48、#86;@gabrielbull):

  • 多栏排版(Multicolumn):在 src/PhpWord/Style/Section.php 中对应colsNum(栏数,默认常量DEFAULT_COLUMN_COUNT)与colsSpace(栏间距)两个属性;
  • 分节符(Section break):对应同一文件的breakType属性,可取值包括nextPage、nextColumn、continuous、evenPage、oddPage五种(#L124-L136),覆盖了从"下一页分节"到"连续分节""奇偶页分节"的全部 Word 分节形态;
  • 页面页码(Page numbering):由pageNumberingStart属性承载(#L103-L108),允许设置节内起始页码,配合页眉页脚中的页码字段实现分节页码控制。

此外 0.8.0 还支持页眉/页脚高度(@JillElaine,#5):headerHeight与footerHeight在读取器端对应 OOXML 的w:pgMar w:header与w:pgMar w:footer(见 src/PhpWord/Reader/Word2007/Document.php#L118-L119)。

字体:上标/下标、东亚字体与内部单位重构

上标与下标(Superscript / Subscript)

@ivanlanin(#48、#86)为字体增加了上标、下标支持,对应 src/PhpWord/Style/Font.php 的superScript/subScript布尔属性(#L142)。读写两侧均已打通:Word2007 读取器将其映射为w:vertAlign的superscript/subscript取值(src/PhpWord/Reader/Word2007/AbstractPart.php#L773-L774),HTML 转换器中<sup>/<sub>标签也会自动设置这两个属性(src/PhpWord/Shared/Html.php#L227-L228)。

东亚字体风格(East Asian font style)

@jhfangying(#111、#118)为中日韩等东亚文字增加了字体风格支持。当前PhpWord对象提供setDefaultAsianFontName()方法用于设置默认东亚字体(src/PhpWord/PhpWord.php#L273),仓库还保留了 samples/Sample_10_EastAsianFontStyle.php 示例演示其用法,处理中文、日文等文档时尤为实用。

PHPWord_Style_Font 重构与"内部使用磅而非半磅"

0.8.0 对PHPWord_Style_Font进行了重构(@ivanlanin,#93),核心变化是内部统一使用"磅(points)"作为字号单位,仅在写出 XML 时转换为半磅(halfpoints)。这消除了此前混合单位的混乱,也让 src/PhpWord/Shared/Converter.php 中的单位转换成为统一的换算枢纽——该文件定义了英寸、厘米、像素、磅、Twip、EMU 之间的全套换算常量与静态方法,例如pointToTwip()(#L209-L212),换算依据INCH_TO_TWIP = 1440、INCH_TO_POINT = 72等标准比例。

图片:格式检测与远程图片支持

0.8.0 在图片处理上有三项改进:

  • 用 exif_imagetype 检测图片格式(@gabrielbull,#114):不再依赖扩展名判断图片类型,从源码结构看这与Shared/Validate.php、Shared/Drawing.php的图片处理逻辑相配合,提高了对伪装扩展名图片的识别准确度;
  • 允许远程图片(@ivanlanin,#122):当allow_url_open = on时支持插入远程图片,适合从 URL 直接加载图片素材的场景;
  • 图片后换行管理(@bskrtich,#6、#66、#84):可管理图片插入后的换行行为。

TextBreak、TextRun 与脚注

  • TextBreak 支持字体与段落样式(@ivanlanin,#18):换行符本身也可携带字体样式与段落样式,使换行后的格式控制更精细;
  • TextRun 内允许 TextBreak(@bskrtich,#109):TextRun容器内可以插入文本换行,这在 src/PhpWord/Element/AbstractContainer.php 的容器能力定义(@method void addTextBreak(...),#L33)中有明确体现;
  • TextRun 在 ODT 与 RTF 上的基础支持(@ivanlanin,#99):同一段 TextRun 内容可以跨 Word2007、ODT、RTF 三种格式写出,仓库中对应 src/PhpWord/Writer/ODText/Element/TextRun.php 与 src/PhpWord/Writer/RTF/Element/TextRun.php。

基础脚注支持(Basic footnote support)

@deds(#16)引入了基础脚注支持,这在当前仓库中已经发展为一个相对完整的功能面:

  • 容器层:AbstractContainer声明了@method Footnote addFootnote(mixed $pStyle = null)(src/PhpWord/Element/AbstractContainer.php#L36),脚注可以挂在Section、TextRun、Cell、ListItemRun等容器上(#L255);
  • 脚注内容本身是类似 TextRun 的元素集合,可包含文本、链接、图片、对象与手动换行;
  • 复杂类型 src/PhpWord/ComplexType/FootnoteProperties.php 配合 src/PhpWord/SimpleType/NumberFormat.php 可设置脚注编号格式(如带圆圈的数字序号)。

仓库示例 samples/Sample_06_Footnote.php 展示了完整用法:

$textrun = $section->addTextRun($paragraphStyleName); $textrun->addText('这段文字后跟一个脚注。'); $footnote = $textrun->addFootnote(); $footnote->addText('脚注内容与 TextRun 一样,可以包含多种元素。'); $footnote->addLink('https://github.com/PHPOffice/PHPWord', '链接', $linkFontStyleName); $footnote->addImage('earth.jpg', ['width' => 18, 'height' => 18]); // 设置脚注编号格式为带圆圈十进制数字 $footnoteProperties = new FootnoteProperties(); $footnoteProperties->setNumFmt(NumberFormat::DECIMAL_ENCLOSED_CIRCLE); $section->setFootnoteProperties($footnoteProperties);

读取器与写入器基础设施

0.8.0 还引入了一条重要主线:Word2007 基础读取器(@ivanlanin,#104)。至此 PHPWord 从"只能写"开始走向"既能写也能读",当前仓库的 src/PhpWord/Reader/ 目录下已扩展出 Word2007、ODText、RTF、HTML、MsDoc、WPS 等多个读取器,Word2007读取器内部通过AbstractPart以声明式数组映射 OOXML 元素(本版本涉及的superScript、subScript、indentHanging、headerHeight、footerHeight等均在 src/PhpWord/Reader/Word2007/AbstractPart.php 与 src/PhpWord/Reader/Word2007/Document.php 中有对应规则),配合 tests/PhpWordTests/Reader/Word2007/ 下的测试与 tests/PhpWordTests/_files/documents/reader.docx 等样例文档进行验证。

与之配套的写入器侧,0.8.0 也加入了XMLWriter 兼容性选项设置(@bskrtich,#103),对应 src/PhpWord/Settings.php 中的 XMLWriter 兼容性开关,用于处理某些环境下 XML 写出兼容性问题。

Bugfix 回顾:本轮修复清单

除上述功能外,0.8.0 还修复了一批影响实际使用的问题:

问题贡献者影响
单元格样式异常(cell styling)@gabrielbull修复表格单元格样式应用
单元格内的列表项异常(list items inside of cells)@gabrielbull修复表格单元格内使用列表的渲染
模板值包含&破坏文档@SiebelsTim(#51)保证替换值的 XML 合法转义
README.md 示例损坏@Progi1984(#89)修复文档中的示例代码
centimetersToPixels()换算错误@ivanlanin(#94)修正厘米到像素的换算,见 src/PhpWord/Shared/Drawing.php#L120
多节文档中非全部节含脚注时 DOCX 损坏@ivanlanin(#125)修复 Word 报告 DOCX 损坏的问题

其中"单元格样式""单元格内列表"两项修复直接关系到表格在复杂排版下的正确性;脚注与多节相关的损坏修复则保证了一个常见场景——文档含多个节、且并非每个节都有脚注时——生成的 DOCX 能被 MS Word 正常打开。

小结:0.8.0 在 PHPWord 演进中的位置

从功能密度看,0.8.0 几乎为后续所有主流能力铺设了地基:

  • 模板方向:saveAs()、带限制的setValue()、XSL 应用与cloneRow()构成了今天TemplateProcessor的核心骨架,其中行克隆已成为生成批量表格文档的标配手段;
  • 排版方向:悬挂缩进、分页控制、多栏节、分节符、页眉页脚高度与页码,让纯 PHP 生成接近 Word 原生排版效果成为可能;
  • 字体与格式方向:上标/下标、东亚字体、内部单位统一为磅,配合Converter的换算体系,奠定了跨写入器一致性的基础;
  • 读写闭环:Word2007 基础读取器的出现,加上 TextRun 在 ODT/RTF 的写出支持,标志着多格式读写路线图的启动。

对于希望深入源码的读者,建议按以下顺序阅读:先看 src/PhpWord/TemplateProcessor.php 理解模板核心流程,再对照 samples/Sample_07_TemplateCloneRow.php 与 tests/PhpWordTests/TemplateProcessorTest.php 验证行克隆行为,最后以 src/PhpWord/Style/Paragraph.php、src/PhpWord/Style/Section.php 为入口梳理排版属性与 OOXML 的映射关系。发布说明全文见 docs/changes/0.x/0.8.0.md。

  • 后端

【免费下载链接】PHPWord

A pure PHP library for reading and writing word processing documents

项目地址:https://gitcode.com/gh_mirrors/ph/PHPWord
点击查看免费下载
上一篇:苹果生态全平台制霸:Firebase iOS SDK多平台开发终极指南
下一篇:3步掌握webMAN-MOD:PS3游戏加载与网络管理完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询