我正在使用iText v5.4.2。我正在尝试从PDF文件解析图像。对于某些PDF文件中的某些图像,我得到了NullPointerException。带有一个“故障”图像的PDF文件可以在这里下载:https://dl.dropboxusercontent.com/u/3585277/LZW_Error.pdf

这是一个简单的演示:


public class LZWDecodeDemo {

    public static void main(String[] args) throws Exception {
        LZWDecodeDemo demo = new LZWDecodeDemo();
        demo.parseImages();
    }

    private void parseImages() throws Exception {
        String pathToPdf = "C:\\temp\\LZW_Error.pdf";
        PdfReader reader = new PdfReader(pathToPdf);
        PdfReaderContentParser parser = new PdfReaderContentParser(reader);
        ImageRenderListener imageRenderListener = new ImageRenderListener();
        parser.processContent(1, imageRenderListener);
    }

    private class ImageRenderListener implements RenderListener {

        public ImageRenderListener() {
            //
        }

        public void beginTextBlock() {
            // nothing
        }

        public void endTextBlock() {
            // nothing
        }

        public void renderImage(ImageRenderInfo imageRenderInfo) {
            try {
                PdfImageObject image = imageRenderInfo.getImage();
                System.out.println("Rendered image :" + image);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void renderText(TextRenderInfo arg0) {
            // nothing
        }
    }
}

最佳答案

当LZW输出位长度增加时,恰好发生图像数据结尾时,可以在示例文件中观察到该问题:

在图像/ Im3的情况下,最后一个携带图像数据的代码导致了第511个LZW表条目的创建,这意味着以下代码应使用10位进行编码。不幸的是,后面的EOD(数据结束)标记仅使用9位进行编码。

iText正确解码了下一个代码(即使用10位,流中的下一个位为0位),因此看到514而不是257(这是EOD标记值),并尝试使用表条目号514 NPE发生;毕竟第511条只是刚刚添加的...

可能是因为编码器(知道它位于图像数据的末尾)根本没有在最后一个代码之后创建表项,所以可能会发生这种情况。因此,它没有看到达到表长度触发器,只是忘记使用10位。

对此,规范非常清楚。 ISO 32000-1中的第7.4.4.2节“ LZW编码的详细信息”:


使用LZW压缩方法编码的数据应由9到12位长的代码序列组成。每个代码应代表输入数据的单个字符(0–255),清除表标记(256),EOD标记(257)或代表在输入中先前遇到的多字符序列的表条目(258或更高)。

最初,代码长度应为9位,并且LZW表应仅包含258个固定代码的条目。随着编码的进行,条目应追加到表中,使新代码与越来越长的输入字符序列相关联。编码器和解码器应维护该表的相同副本。

每当编码器和解码器都独立(但同步)意识到当前代码长度不再足以表示表中的条目数时,它们应将每个代码的位数增加1。创建表条目511之后,应为10位长,同样对于11(1023)和12(2047)位也应如此。代码不得超过12位;因此,条目4095是LZW表的最后一个条目。

编码器应执行以下步骤序列以生成每个输出代码:

a)累积一个或多个输入字符的序列,该序列与表中已经存在的序列匹配。为了获得最大压缩,编码器会寻找最长的序列。

b)发出与该序列相对应的代码。

c)为第一个未使用的代码创建一个新的表条目。它的值是在步骤(a)中找到的顺序,后跟下一个输入字符。


因此,即使在发出最后一个输入字符的代码之后,也必须创建一个表项。并且,如果该表条目是数字511,则随后的第一个输出代码(即EOD标记)必须为10位长。

话虽这么说,iText的LZWDecoder方法decode可以通过null测试得到加强,至少在elseif (code < tableIndex)分支中,并且行为更优雅,要么抛出更具描述性的异常,要么默默地忽略如果没有太多输入位的问题。

07-26 04:24