精准计算Word文档页数的PHP类

发布于:2025-08-11 ⋅ 阅读:(10) ⋅ 点赞:(0)

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

初始化方法,加载并解析文档内容。执行以下操作:

  1. 检查文件存在性和格式

  2. 打开 docx 文件作为 zip 存档

  3. 加载文档主体、属性和样式 XML

  4. 提取样式信息和节属性

辅助方法
  • parseXml(string $xmlContent): SimpleXMLElement - 解析 XML 字符串

  • extractStyles(): void - 从 styles.xml 提取样式信息

  • extractSectionProperties(): void - 从 document.xml 提取节属性

  • calculatePageCountFromDocument(): int - 基于内容计算页数

  • calculateParagraphHeight(DOMXPath, DOMElement, int): int - 计算段落高度

技术细节

页数计算逻辑

  1. 从 app.xml 获取(如果存在且有效):

    • 直接读取 <Pages> 节点的值

  2. 基于内容计算

    • 遍历文档中的所有段落

    • 检测分页符和分节符

    • 根据页面大小、边距和内容高度计算页数

    • 考虑段落间距、字体大小和行距

单位系统

使用 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();
}

注意事项

  1. 仅支持 .docx 格式(基于 Open XML 的 Word 文档)

  2. 基于内容的页数计算是估算值,可能与 Word 实际显示的页数有差异

  3. 复杂的文档元素(如表格、图片、浮动对象)可能影响计算准确性

  4. 需要 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(

网站公告

今日签到

点亮在社区的每一天
去签到