在 PHP 中从 /Filter /FlateDecode PDF 流中提取数据

Posted

技术标签:

【中文标题】在 PHP 中从 /Filter /FlateDecode PDF 流中提取数据【英文标题】:Data extraction from /Filter /FlateDecode PDF stream in PHP 【发布时间】:2012-07-28 16:57:55 【问题描述】:

我无法解密流中的数据,例如:

    56 0 obj 
    << /Length 1242 /Filter /FlateDecode >>
    stream
    x]êΩnƒ ÑûbÀKq¬æ\âê¢....(whole binary is omitted)
    endstream
    endobj

我尝试隔离文件和二进制字符串中的二进制内容 (x]êΩnƒ ÑûbÀKq¬æ\âê¢....)。解码功能gzinflate($encripted_data) 向我发送解码错误,我认为这是因为编码内容没有“放气”左右。

在 PDF Reference v 1.7,(六版)的第 67 页,我发现 /FlateDecode 过滤器的描述为:...解压缩使用 zlib/deflate 压缩方法编码的数据,再现原始文本或二进制数据

我需要真正的原始解决方案,也就是 php 函数或/和算法,如何处理这个“\FlateDecoded”流。

谢谢!

【问题讨论】:

你需要这个函数只用于选定的对象还是所有压缩流(和所有压缩方案)? 亲爱的库尔特!我很高兴知道如何处理各种过滤器,例如:ASCIIHexDecode、ASCII85Decode、LZWDecode、RunLengthDecode、CCITTFaxDecode、JBIG2Decode、DCTDecode、JPXDecode、Crypt,但在现实生活中,FlateDecode 是 PDF 文件中最常用的,它是由“打印到 PDF...”-s))) 生成,现在我真的需要处理这个单一的过滤器。 你说'我认为这是因为编码的内容没有“放气”左右'。 -- 这就是为什么我在回答中给了你关于qpdf 的提示。您可以(至少)使用它来验证或伪造您自己的努力,即使事实证明它不符合您的直接要求。您的 56 0 obj-object 也可以是任何东西。如果您不知道它在 PDF 中的哪个位置被引用为 56 0 R,则无法知道它是 ICC 配置文件、字体、图像、某些页面内容还是其他... 亲爱的库尔特!可能是 qpdf 是 taks 的好解决方案,就像这样,但不幸的是,qpdf 是“shell”或命令行解决方案。不是我的情况。 Bitte verzeih mir! Danke für die Hinweise! 【参考方案1】:

由于您没有告诉您是只需要访问 一个 解压缩流还是需要解压所有流,所以我建议您使用一个简单的命令行工具,它可以一次性完成完整 PDF:Jay Berkenbilt 的 qpdf

命令行示例:

 qpdf --qdf --object-streams=disable in.pdf out.pdf

然后可以在文本编辑器中检查out.pdf(只有嵌入的 ICC 配置文件、图像和字体仍然可以是二进制的)。

qpdf 还会自动重新排序对象并以标准化方式显示 PDF 语法(并在评论中告诉您解压缩对象的原始对象 ID 是什么)。

如果您需要重新压缩文件(可能在您编辑后),只需运行以下命令:

 qpdf out-edited.pdf out-recompressed.pdf

(您可能会看到一些警告消息,告诉您该实用程序正在尝试修复损坏的文件......)

qpdf 是多平台的,available from Sourceforge

【讨论】:

如何重新压缩pdf文件,例如修改未压缩文件中的文本后? 谢谢。它重新压缩原始文件。但是,在adobe reader中打开后,出现如下错误:“此文档在Adobe Reader中启用了扩展功能。该文档自创建以来已更改,不再使用扩展功能。请联系作者获取原文本文件的版本。” PDF 表单中有可填写的字段。有没有办法在没有 Adob​​e Reader 的情况下修改 PDF 文件以引发上述错误?因为 Adob​​e 在重新压缩后禁用可填写字段。 在解压和解压文件后,输出文件与原始输入不同。两个不应该一样吗? @KemalDağ:使用 QPDF 解压缩和重新压缩所有 PDF 对象不会完全恢复原始 PDF。 QPDF 正在提供 PDF 的“内容保留” 转换。正如我所说,解压缩还“重新排序对象”“以标准化方式显示 PDF 语法”。重新压缩后,它不会恢复对象的原始顺序(不同的顺序不会改变页面的可见呈现内容)。 @KemalDağ:不幸的是,Adobe 的 PDF 软件使用了一些专有的“扩展功能”用于可填写的字段(这种类型的文档称为 PDF 表单 )。基本上,这些文档需要 Adob​​e(私有)签名密钥,因此只能由有权访问该密钥的人处理。破解和重复使用该密钥是违法的。【参考方案2】:
header('Content-Type: text');           // I going to download the result of decoding
$n = "binary_file.bin";                 // decoded part in file in a directory
$f = @fopen($n, "rb");                  // now file is mine
$c = fread($f, filesize($n));           // now I know all about it 
$u = @gzuncompress($c);                 // function, exactly fits for this /FlateDecode filter
$out = fopen("php://output", "wb");     // ready to output anywhere
fwrite($out, $u);                       // output to downloadable file

铃儿响!叮铃铃!...

gzuncompress() - 解决方案

【讨论】:

【参考方案3】:

姗姗来迟,但有人可能会觉得它有帮助。在这种情况下: > 你所需要的只是将隔离的二进制字符串(基本上是“stream”和“endstream”之间的所有内容)传递给zlib.decompress:

import zlib
stream = b"êΩnƒ ÑûbÀKq¬æ\âê"  # binary stream here
data = zlib.decompress(stream) # Here you have your clean decompressed stream

但是,如果您的 PDF 对象中有 /DecodeParms,事情就会变得复杂。您将需要 /Predictor 值和列号。最好使用 PyPDF2。

【讨论】:

问题是要求PHP,这个解决方案建议使用Python。这不是一个很好的选择。无论如何,这对您来说可能是显而易见的,但对其他人来说并非如此:您需要传递 streamendstream 之间的所有内容除了前导和尾随 EOL 标记。【参考方案4】:

我刚用过

import de.intarsys.pdf.filter.FlateFilter;

来自 jpod / 源伪造 而且效果很好

FlateFilter filter = new FlateFilter(null);
byte[] decoded = filter.decode(bytes, start, end - start);

字节直接来自 pdf 文件

【讨论】:

以上是关于在 PHP 中从 /Filter /FlateDecode PDF 流中提取数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在 WAMPServer 中从 Windows 命令行运行 PHP

为啥不能在 PHP 中从抽象类中调用抽象函数?

在 PHP 中从文本到图像

在 PHP 中从 JSON 中提取数据失败

在 AJAX 调用中从 PHP 返回 JSON 对象?

在php中从秒计算日期