在Linux上如何获取Word文档的页数?

11
我看到了这个问题PHP-获取Word文档中的页面数。我也需要确定给定word文件(doc/docx)的页数。我尝试调查phplivedocx/ZF(@hobodave在原帖回答中链接了这些),但我手脚麻木。我也不能使用任何外部网络服务(如DOC2PDF网站,然后计算PDF版本中的页数之类的...)。

简而言之:是否有任何PHP代码(使用ZF或其他PHP工具, 排除COM对象或其他执行文件,例如'AbiWord'; 我正在使用共享Linux服务器,没有exec或类似功能),可以找到Word文件的页数?

编辑:即将支持的Word版本是Microsoft Word 2003和2007。


1
你指的是msword文件的哪种文件格式标准?如果您想获得具体的答案,请添加规范。 - hakre
4个回答

22

获取docx文件的页数非常容易:

function get_num_pages_docx($filename)
{
    $zip = new ZipArchive();

    if($zip->open($filename) === true)
    {  
        if(($index = $zip->locateName('docProps/app.xml')) !== false)
        {
            $data = $zip->getFromIndex($index);
            $zip->close();

            $xml = new SimpleXMLElement($data);
            return $xml->Pages;
        }

        $zip->close();
    }

    return false;
}

对于97-2003格式来说,确定页数确实具有挑战性,但绝非不可能。页数存储在文档的SummaryInformation部分中,但由于文件的OLE格式,这使得查找变得很麻烦。结构被定义得非常彻底(尽管我认为做得不好)这里和更简单的这里。今天我花了一个小时看了这个内容,但是进展甚微!(这不是我习惯处理的抽象层次),但输出十六进制以更好地理解结构:

function get_num_pages_doc($filename) 
{
    $handle = fopen($filename, 'r');
    $line = @fread($handle, filesize($filename));

    echo '<div style="font-family: courier new;">';

        $hex = bin2hex($line);
        $hex_array = str_split($hex, 4);
        $i = 0;
        $line = 0;
        $collection = '';
        foreach($hex_array as $key => $string)
        {
            $collection .= hex_ascii($string);
            $i++;

            if($i == 1)
            {
                echo '<b>'.sprintf('%05X', $line).'0:</b> ';
            }

            echo strtoupper($string).' ';

            if($i == 8)
            {
                echo ' '.$collection.' <br />'."\n";
                $collection = '';
                $i = 0;

                $line += 1;
            }
        }

    echo '</div>';

    exit();
}

function hex_ascii($string, $html_safe = true)
{
    $return = '';

    $conv = array($string);
    if(strlen($string) > 2)
    {
        $conv = str_split($string, 2);
    }

    foreach($conv as $string)
    {
        $num = hexdec($string);

        $ascii = '.';
        if($num > 32)
        {   
            $ascii = unichr($num);
        }

        if($html_safe AND ($num == 62 OR $num == 60))
        {
            $return .= htmlentities($ascii);
        }
        else
        {
            $return .= $ascii;
        }
    }

    return $return;
}

function unichr($intval)
{
    return mb_convert_encoding(pack('n', $intval), 'UTF-8', 'UTF-16BE');
}

这将输出代码,其中您可以找到以下部分:

007000: 0500 5300 7500 6D00 6D00 6100 7200 7900 ..S.u.m.m.a.r.y.
007010: 4900 6E00 6600 6F00 7200 6D00 6100 7400 I.n.f.o.r.m.a.t.
007020: 6900 6F00 6E00 0000 0000 0000 0000 0000 i.o.n...........
007030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 

这将允许您查看引用信息,例如:

007040: 2800 0201 FFFF FFFF FFFF FFFF FFFF FFFF (...ÿÿÿÿÿÿÿÿÿÿÿÿ
007050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
007060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
007070: 0000 0000 2500 0000 0010 0000 0000 0000 ....%...........

这将允许您确定所描述的属性:

_ab = ("SummaryInformation") 
_cb = 0028
_mse = 02 (STGTY_STREAM) 
_bflags = 01 (DE_BLACK) 
_sidLeftSib = FFFF FFFF 
_sidRightSib = FFFF FFFF (none) 
_sidChild = FFFF FFFF (n/a for STGTY_STREAM) 
_clsid = 0000 0000 0000 0000 0000 0000 0000 0000 (n/a) 
_dwUserFlags = 0000 0000 (n/a) 
_time[0] = CreateTime = 0000 0000 0000 0000 (n/a) 
_time[1] = ModifyTime = 0000 0000 0000 0000 (n/a)
_startSect = 0000 0000 
_ulSize = 0000 1000 
_dptPropType = 0000 (n/a)

这将帮助你找到相关的代码部分,解包它并获取页面编号。当然,这是我没有时间去做的困难部分,但应该为您指明正确的方向。

微软并不让它变得容易!


规范文件链接已失效:其他可用链接包括 - 复合文件二进制格式Microsoft Office Word 97-2007 二进制文件格式(.doc)规范 - Orbling
页数在那个文档的第120页 - 规范中,它存储在文档属性(标签:DOP)中 - 在偏移46(x2E),一个int(2字节)中,属性名称为cPg - 反映了最后计算的计数。因此,过程是在文件表中查找DOP,然后从该表的第42个字节中获取整数。 - Orbling
关于复合文件二进制格式 - 在Open Office上有一个更好的解释文件:http://www.openoffice.org/sc/compdocfileformat.pdf - 这个文件有一个非常有用的示例部分(第8节),可以帮助理解这个混乱的东西,本质上是一个糟糕的文件系统在一个文件中。 - Orbling
大部分读取Compound File Binary格式的工作都在这里完成:https://code.google.com/p/binary-compound-file-reader/基本上,你只需要在文件上打开一个文件流,用它创建一个OLEFile,从中可以提取分配表,如果你能从头文件中获取目录扇区,就可以提取那个,条目为128字节。按照上面的答案格式 - "WordDocument"的起始扇区应该将您带入FIB,偏移量402是包含“Table”流中DOP位置的长整型。 - Orbling
我的docx文件名是UTF8,但zipArchive在打开这个DOCX时出现了问题。https://stackoverflow.com/questions/45154025/php-ziparchive-dont-support-utf8-files-for-open?noredirect=1#comment77280333_45154025 - user3770797
显示剩余2条评论

3

请查看来自微软codeplex的PhpWord ... "http://phpword.codeplex.com/

它可以让你在PHP中打开和读取Word格式文件,并进行任何所需处理。


不要认为它可以处理旧的文件格式,除非用户安装了兼容性包,但你无法强制用户这样做,所以它在这里很遗憾地没有用处... - Paul Norman
phpword codeplex的链接失效了吗? - Oliver M Grech

2

要使用PHP获取doc、docx、ppt和pptx的元数据属性,例如页面数、幻灯片数,我按照以下过程进行了操作,并且它非常成功,我很高兴。下面是我遵循的过程,希望对某些人有所帮助。

Download and configure Apache Tika.

完成后,您可以尝试执行以下命令,它将提供有关文件的所有元数据。

java -jar tika-app-1.5.jar -m test.docx
java -jar tika-app-1.5.jar -m test.doc
java -jar tika-app-1.5.jar -m test.pptx
java -jar tika-app-1.5.jar -m test.ppt

一旦测试完毕,您可以在PHP脚本中执行此命令。谢谢。

-1

除了使用Abiword或OpenOffice以外?不可能的 - 页面数量将取决于单词/字母数量、所使用的字体、对齐和字距、边距大小、行间距、段落间距、段落数量、列数、图形/嵌入式对象的大小、页面/列断点和页面边距。

您需要的是一种可以理解所有这些内容的工具。

即使您使用OpenOffice或Abiword,重新排版文本也可能会改变页面数。实际上,在某些情况下,在MSWord的不同实例中打开同一文档可能会有所不同。

您可能能够做到的最好的方法可能是基于文档表示的统计方法 - 但仍然会看到巨大的差异。


1
我已经使用7zip打开了2003文件(.doc)和2007文件(.docx)。在2007提取的文件中,我发现XML文件(docProps/app.xml),其中明确包括页面数(<Pages>5</Pages>)。在2003中,我没有找到XMLs,但是可以在Windows资源管理器中查看文件属性,在摘要选项卡的高级部分中查看页面数。我现在无法测试它,但我相信这些数据不是即时计算的,而是以某种方式明确地封装在组合的Word文件中。实际上,这个数字正是我需要的。 - Yaakov Shoham
我的docx文件名是UTF8,但zipArchive在打开这个DOCX文件时有问题。https://stackoverflow.com/questions/45154025/php-ziparchive-dont-support-utf8-files-for-open?noredirect=1#comment77280333_45154025 - user3770797

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接