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(