精准计算Word文档页数的PHP类
WordPageCounter 文档
概述
WordPageCounter 是一个 PHP 类,用于计算 Microsoft Word (.docx) 文档的页数。它通过解析文档的 XML 结构和内容来估算页数,而不是依赖文档元数据中的页数信息(因为元数据可能不准确)。
功能特性
-
支持 .docx 格式的 Word 文档
-
两种页数计算方式:
-
从文档属性 (app.xml) 中直接读取页数(如果存在且有效)
-
基于文档内容和格式进行页数估算
-
-
考虑多种文档元素:
-
页面大小和方向
-
页边距
-
段落间距
-
字体大小
-
分页符和分节符
-
多栏布局
-
类结构
属性
-
$filePath
: 文档文件路径 -
$zipArchive
: ZipArchive 实例,用于处理 docx 文件 -
$documentXml
: 文档主体内容 (word/document.xml) 的 SimpleXMLElement 对象 -
$appXml
: 文档属性 (docProps/app.xml) 的 SimpleXMLElement 对象 -
$stylesXml
: 样式定义 (word/styles.xml) 的 SimpleXMLElement 对象 -
$sectionProperties
: 文档各节的页面属性数组 -
$styles
: 文档中定义的样式集合
主要方法
__construct(string $filePath)
构造函数,初始化 WordPageCounter 实例并加载文档内容。
getPageCount(): int
获取文档页数的主要方法。首先尝试从 app.xml 中获取页数,如果不可用则基于内容计算。
initialize(): void
初始化方法,加载并解析文档内容。执行以下操作:
-
检查文件存在性和格式
-
打开 docx 文件作为 zip 存档
-
加载文档主体、属性和样式 XML
-
提取样式信息和节属性
辅助方法
-
parseXml(string $xmlContent): SimpleXMLElement
- 解析 XML 字符串 -
extractStyles(): void
- 从 styles.xml 提取样式信息 -
extractSectionProperties(): void
- 从 document.xml 提取节属性 -
calculatePageCountFromDocument(): int
- 基于内容计算页数 -
calculateParagraphHeight(DOMXPath, DOMElement, int): int
- 计算段落高度
技术细节
页数计算逻辑
-
从 app.xml 获取(如果存在且有效):
-
直接读取
<Pages>
节点的值
-
-
基于内容计算:
-
遍历文档中的所有段落
-
检测分页符和分节符
-
根据页面大小、边距和内容高度计算页数
-
考虑段落间距、字体大小和行距
-
单位系统
使用 Word 内部的 "twips" 单位(1 twip = 1/1440 英寸)进行计算。
默认值
当某些属性未定义时使用以下默认值:
-
页面大小:A4 (11906 × 16838 twips)
-
页边距:1 英寸 (1440 twips)
-
行距:12 磅 (240 twips)
-
字体大小:12 磅 (240 twips)
使用示例
php
try {$counter = new WordPageCounter('example.docx');$pageCount = $counter->getPageCount();echo "The document has approximately {$pageCount} pages."; } catch (\Exception $e) {echo "Error: " . $e->getMessage(); }
注意事项
-
仅支持 .docx 格式(基于 Open XML 的 Word 文档)
-
基于内容的页数计算是估算值,可能与 Word 实际显示的页数有差异
-
复杂的文档元素(如表格、图片、浮动对象)可能影响计算准确性
-
需要 PHP 的 Zip 和 XML 扩展支持
异常处理
类可能抛出以下异常:
-
RuntimeException
: 文件不存在、无法打开或 XML 解析失败 -
InvalidArgumentException
: 文件不是 .docx 格式
详细代码如下:
<?php
declare(strict_types=1);namespace WordPageCounter;use ZipArchive;
use SimpleXMLElement;
use DOMDocument;
use DOMXPath;/*** WordPageCounter类用于精确计算Word文档的页数* 通过解析Word文档的XML结构,考虑文本、图片、表格、表单和Visio对象等因素*/
class WordPageCounter
{private string $filePath; // Word文档文件路径private ?ZipArchive $zipArchive = null; // 用于处理docx文件的ZipArchive对象private ?SimpleXMLElement $documentXml = null; // document.xml内容private ?SimpleXMLElement $appXml = null; // app.xml内容(包含页数信息)private ?SimpleXMLElement $stylesXml = null; // styles.xml内容(包含样式信息)private ?SimpleXMLElement $headerXml = null; // header.xml内容(页眉)private ?SimpleXMLElement $footerXml = null; // footer.xml内容(页脚)private array $sectionProperties = []; // 文档各节的属性(页面大小、边距等)private array $styles = []; // 文档样式定义private array $mediaFiles = []; // 文档中的媒体文件(图片等)private array $objects = []; // 文档中的嵌入对象(表单、Visio等)/*** 构造函数,初始化Word页数计算器* @param string $filePath Word文档路径*/public function __construct(string $filePath){$this->filePath = $filePath;$this->initialize();}/*** 初始化函数,加载Word文档并解析其内容*/private function initialize(): void{// 检查文件是否存在if (!file_exists($this->filePath)) {throw new \RuntimeException("File not found: {$this->filePath}");}// 检查文件是否为docx格式if (strtolower(pathinfo($this->filePath, PATHINFO_EXTENSION)) !== 'docx') {throw new \InvalidArgumentException("Only .docx files are supported");}// 禁用libxml错误输出,改为内部处理libxml_use_internal_errors(true);// 打开docx文件(实际上是zip压缩包)$this->zipArchive = new ZipArchive();if ($this->zipArchive->open($this->filePath) !== true) {throw new \RuntimeException("Failed to open the docx file as a zip archive");}// 加载并解析document.xml(文档主要内容)$documentXmlContent = $this->zipArchive->getFromName('word/document.xml');if ($documentXmlContent === false) {throw new \RuntimeException("Failed to extract document.xml from the docx file");}$this->documentXml = $this->parseXml($documentXmlContent);// 加载并解析app.xml(包含文档元数据,可能包含页数)$appXmlContent = $this->zipArchive->getFromName(