如何使用过滤器“[/ FlateDecode,/ RunLengthDecode]”

时间:2018-01-20 11:41:26

标签: itext

几年以来,我已经成功地从PDF中提取图像了。 我使用itextsharp来做到这一点。 我得到一个PdfImageObject并获得过滤器。 这个过滤器大多是“/ FlateDecode”。 在这种情况下,我使用pdf.PdfReader.FlateDecode(bytes,True)来解码原始字节。

但是最近我遇到了带有滤镜的PdfImageObjects的pdf:“[/ FlateDecode,/ RunLengthDecode]”。

所以我猜原始字节必须解码两次!?!?

我在互联网上找到了/ RunLengthDecode部分的一些代码: https://github.com/kusl/itextsharp/blob/master/tags/iTextSharp_5_4_5/src/core/iTextSharp/text/pdf/FilterHandlers.cs

我尝试在图像上应用两种解码选项。 First / FlateDecode然后/ RunLengthDecode。 第二个是/ RunLengthDecode,然后是/ FlateDecode。

但/ RunLengthDecode代码在两个场景中都给出了错误。

1 个答案:

答案 0 :(得分:0)

这实际上不是问题的答案,而是对导致这个问题的问题的分析。

在对该问题的评论中,结果发现iText中的错误是OP尝试手动过滤原始流并提取图像的原因:某些图像是在小错误的情况下提取的。 OP将有问题的图像识别为具有过滤器[/FlateDecode, /RunLengthDecode]的图像。

错误

有问题的错误确实是iText对 RunLengthDecode 过滤器的实现,此处来自iText for .Net 5.5.x:

private class Filter_RUNLENGTHDECODE : IFilterHandler {

    public byte[] Decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) {
     // allocate the output buffer
        MemoryStream baos = new MemoryStream();
        sbyte dupCount = -1;
        for (int i = 0; i < b.Length; i++){
            dupCount = (sbyte)b[i];
            if (dupCount == -128) break; // this is implicit end of data

            if (dupCount >= 0 && dupCount <= 127){
                int bytesToCopy = dupCount+1;
                baos.Write(b, i, bytesToCopy);
                i+=bytesToCopy;
            } else {
                // make dupcount copies of the next byte
                i++;
                for (int j = 0; j < 1-(int)(dupCount);j++){ 
                    baos.WriteByte(b[i]);
                }
            }
        }
        return baos.ToArray();
    }
}

更确切地说是这一行:

                baos.Write(b, i, bytesToCopy);

它应该在 索引bytesToCopy之后复制下一个i字节 - 在索引i处有计数值毕竟 - 但此命令从 索引bytesToCopy开始复制下一个i字节 。因此,对于每次运行的字节,一旦iText复制,首先复制计数字节,然后复制运行的最后一个字节。

相反,该行应为

                baos.Write(b, i+1, bytesToCopy);

对位图图像的示例效果

由于重复字节的运行被正确提取,即使对于长的,非重复的运行,也有许多正确的字节(在一个位置上),提取的图像iText只是看起来有点错误,例如:< / p>

图片损坏:

damaged image

未损坏的图片:

undamaged image

漏洞的普遍性

这个bug多年来一直在iText 5.x for .Net中。此外,它已经存在于iText 5.x for Java中多年,并且仍然是,例如,这里来自当前的5.5.13-SNAPSHOT:

private static class Filter_RUNLENGTHDECODE implements FilterHandler{

    public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) throws IOException {
     // allocate the output buffer
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte dupCount = -1;
        for(int i = 0; i < b.length; i++){
            dupCount = b[i];
            if (dupCount == -128) break; // this is implicit end of data

            if (dupCount >= 0 && dupCount <= 127){
                int bytesToCopy = dupCount+1;
                baos.write(b, i, bytesToCopy);
                i+=bytesToCopy;
            } else {
                // make dupcount copies of the next byte
                i++;
                for(int j = 0; j < 1-(int)(dupCount);j++){ 
                    baos.write(b[i]);
                }
            }
        }

        return baos.toByteArray();
    }
}

并在iText 7中,例如这里来自Java的当前7.1.2-SNAPSHOT:

public class RunLengthDecodeFilter implements IFilterHandler {

    @Override
    public byte[] decode(byte[] b, PdfName filterName, PdfObject decodeParams, PdfDictionary streamDictionary) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte dupCount;
        for (int i = 0; i < b.length; i++) {
            dupCount = b[i];
            if (dupCount == (byte) 0x80) { // this is implicit end of data
                break;
            }
            if (dupCount >= 0) {
                int bytesToCopy = dupCount + 1;
                baos.write(b, i, bytesToCopy);
                i += bytesToCopy;
            } else {                // make dupcount copies of the next byte
                i++;
                for (int j = 0; j < 1 - (int) (dupCount); j++) {
                    baos.write(b[i]);
                }
            }
        }
        return baos.toByteArray();
    }
}

很可能这个错误可能会持续很长时间,因为 RunLengthDecode 过滤器几乎没有被使用多年。