0

我正在使用 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
        }
    }
}
4

1 回答 1

1

当 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。第一个输出代码是10 位长应是创建表条目 511 之后的位长,对于 11 (1023) 和 12 (2047) 位也是如此。代码不得超过 12 位;因此,条目 4095 是 LZW 表的最后一个条目。

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

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

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

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

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

话虽如此,iText 的LZWDecoder方法decode可以通过null测试得到强化,至少在 的else分支中if (code < tableIndex),并且行为更优雅,或者抛出更具描述性的异常,或者如果没有留下太多输入位,甚至默默地忽略该问题。

于 2013-06-21T10:34:54.627 回答