当长度未知时,是否可以使用libtiff解码CCITT编码的数据?

时间:2016-10-08 01:00:41

标签: pdf tiff

在这个问题的答案中:c++ decode CCITT encoded images in pdfs

指出libtiff可用于解码CCITT编码的图像。当然,我们必须预先添加一个TIFF标头,以使CCITT流成为有效的TIFF文件。

但是,PDF文件中的某些图像是内联图像,虽然给出了宽度,高度和位深度,但未给出它们的长度。读取PDF的程序应该解码CCITT流,读取(宽度*高度*深度)位的解码数据,以及读取数据后的任何位置,这是内联图像的结束。然后它应该继续下一页标记命令,依此类推。

这带来了一个问题。 TIFF图像文件目录必须指定图像数据的每个条带中有多少字节,但在我们解码之前我们不知道编码数据的实际属于多少字节,但是我们不能不使用libtiff来解码图像...

有没有办法在这里使用libtiff,还是我们需要自定义CCITT过滤器代码?

1 个答案:

答案 0 :(得分:4)

严格来说(是否可以使用libtiff ......?),。它涉及一些黑客攻击,但不是太多。

事实:数据将由一个条带组成,因为没有任何偏移信息,因此我们唯一的偏移量为零。我们只需要阅读条带。

事实:这个数据是W * H 1位深像素矩阵的压缩。

步骤1:估计压缩流的最大可能长度。这大约是W * H的15%,即W = 1000而H = 1000则得到150000字节。该值始终更多而不是实际值。如果我们通过找到合适的EI终端图像标签得到更好的估计,那就更好了,但不是必需的。

第2步:构建“虚拟”TIF文件。这将由49 49 2a 00 AA BB CC DD形式的标题组成,其中0xDDCCBBAA是估计长度加上8;然后是我们估计的数据流;然后是TIFF目录。

第3步:TIFF目录将始终具有相同的结构;其中的一些值是偏移量,并且通常取决于IFD位置0xDDCCBBAA。引用TIFF6规范(请注意,字节顺序相反 - 摩托罗拉,而非英特尔端):

TIFF 6.0 Specification Final—June 3, 1992                         20

Putting it all together (along with a couple of less-important fields that are discussed
later), a sample bilevel image file might contain the following fields

A Sample Bilevel TIFF File

Offset Description Value
(hex) (numeric values are expressed in hexadecimal notation)
Header:
0000 Byte Order     4D4D 
0002 42             002A
0004 1st IFD offset 00000014
IFD:
0014 Number of Directory Entries 000C
0016 NewSubfileType              00FE 0004 00000001 00000000
0022 ImageWidth                  0100 0004 00000001 000007D0
002E ImageLength                 0101 0004 00000001 00000BB8
003A Compression                 0103 0003 00000001 8005 0000
0046 PhotometricInterpretation   0106 0003 00000001 0001 0000
0052 StripOffsets                0111 0004 000000BC 000000B6(*1)
005E RowsPerStrip                0116 0004 00000001 00000010
006A StripByteCounts             0117 0003 000000BC 000003A6(*2)
0076 XResolution                 011A 0005 00000001 00000696(*3)
0082 YResolution                 011B 0005 00000001 0000069E(*4)
008E Software                    0131 0002 0000000E 000006A6(*5)
009A DateTime                    0132 0002 00000014 000006B6(*6)
00A6 Next IFD offset             00000000
Values longer than 4 bytes:
(*1) StripOffsets Offset0        00000008
(*2) StripByteCounts Count0
(*3) XResolution 0000012C 00000001
(*4) YResolution 0000012C 00000001
(*5) Software “PageMaker 4.0”
(*6) DateTime “1988:02:18 13:59:59”

在上面,0xDDCCBBAA实际上是0014,其他所有偏移都是如此。

我使用ImageMagick生成的单条TIFFG4图像进行了一些测试,tiffcp编辑为1条CCITT格式。那里的标题略有不同(我没有看到规范中应该有的软件和日期时间标签)。否则它会检查。

我们现在有一个损坏的 TIFF图像,其中包含一个超长条带,并且它在内存中。

使用TIFFClientOpen,我们可以access it as if it was a disk image

尝试读取第一个条带现在将导致错误并且程序中止:

TIFFFillStrip: Read error on strip 0; got 143151 bytes, expected 762826.

通过使用TIFFSetErrorHandlerTIFFSetErrorHandlerExt,我们设置自己拦截此错误并解析它,从而恢复143151信息,而不是中止。

我们需要向TIFFClientOpen提供回调,但它们都非常简单:

TIFFReadWriteProc readproc(h, *ptr, n) // copy n bytes from FakeBuffer+pos into ptr, update pos to pos + n, ignore h.
TIFFReadWriteProc writeproc            // Throw an error. We don't write
TIFFSeekProc seekproc                  // update pos appropriately
TIFFCloseProc closeproc                // do nothing
TIFFSizeProc sizeproc                  // return total buffer size
TIFFMapFileProc mapproc                // Set to NULL
TIFFUnmapFileProc unmapproc            // Set to NULL

处理确实很尴尬和复杂,但至于可行性,它 可以完成。

我已经用C语言运行测试,手工提取我在网上找到的内嵌图像BI / ID / EI PDF中的CCITT流,并按上述方式读取它。

如果我有一种确定正确的EI的确定方法 - 我已经挖掘a message by Tilman Hausherr解释为了识别EI之后识别有效PDF操作员的黑客行为,这让我觉得可能不是很多更好的方法 - 我总是可以估计正确的偏移量,并直接从PDF中生成正确且可读的TIFF文件,甚至根本不涉及libtiff。

相关问题