在icepdf中限制渲染图像大小

时间:2016-12-09 12:10:15

标签: icepdf

在将一堆PDF渲染到图像时,icepdf似乎随机冒出一个OutOfMemoryError。试图追踪这一点我发现了两件事:

  1. 靠近OOM,它呈现了一个A0页面或类似的大文档页面
  2. 使用eclipse内存分析器,我在内存中找到了1 / 2GB图像。
  3. 这表明将输出图像大小限制为可管理的内容。我想知道最简单的方法是什么?

    我查看了icepdf的Page对象,但强烈建议只使用Page.BOUNDARY_CROPBOX,其他用途似乎没有记录在Javadoc中。

    如何限制Document.getPageImage的输出图像大小或我可以用什么其他方法来阻止OOM(除了增加Xmx,我可以' t)。降低图像质量是一种选择。但它应该仅适用于"超大"图像,而不是所有图像。

    我已尝试使用Document.paintPage()使用预定义图像,但这还不够。

    Debug最后允许我放大有问题的文档。我得到一个日志:

    2016-12-09T14:23:35Z    DEBUG   class org.icepdf.core.pobjects.Document 1       MEMFREE: 712484296 of 838860800
    2016-12-09T14:23:35Z    DEBUG   class org.icepdf.core.pobjects.Document 1       LOADING: ..../F1-2.pdf
    2016-12-09T14:23:37Z    WARN    class org.icepdf.core.pobjects.graphics.ScaledImageReference    1       Error loading image: 9 0 R Image stream= {Type=XObject, Length=8 0 R, Filter=FlateDecode, ColorSpace=DeviceGray, Decode=[1, 0], Height=18676, Width=13248, Subtype=Image, BitsPerComponent=1, Name=Im1}  9 0 R
    

    所以这将是Height = 18676,Width = 13248这真的很大。

    我想OOM在加载图像时已经发生了,所以后来的缩放并没有帮助。此外,似乎财产org.icepdf.core.imageReference=scaled没有及早发现。

    对我来说,忽略像这样的超大图像会好的。有机会吗?

1 个答案:

答案 0 :(得分:1)

图像加载是解码PDF内容时迄今为止最耗费内存的内存任务。在这个时候,没有一种方法可以关闭真正大图像的图像加载,但是如果你想实现这一点,我会给你一些代码提示。

ImageReferenceFactory.java类是系统属性org.icepdf.core.imageReference后面的工厂,您将看到getImageReferenced()的默认值是ImageStreamReference。您可以创建一个新的ImageReference类型,如下所示:

public static org.icepdf.core.pobjects.graphics.ImageReference
getImageReference(ImageStream imageStream, Resources resources, GraphicsState graphicsState,
                  Integer imageIndex, Page page) {
    switch (scaleType) {
        case SCALED:
            return new ScaledImageReference(imageStream, graphicsState, resources, imageIndex, page);
        case SMOOTH_SCALED:
            return new SmoothScaledImageReference(imageStream, graphicsState, resources, imageIndex, page);
        case MIP_MAP:
            return new MipMappedImageReference(imageStream, graphicsState, resources, imageIndex, page);
        case SKIP_LARGE:
            return new SkipLargeImageReference(imageStream, graphicsState, resources, imageIndex, page);
        default:
            return new ImageStreamReference(imageStream, graphicsState, resources, imageIndex, page);
    }
}

接下来,您可以使用新的ImageStreamReference课程扩展课程SkipLargeImageReference。然后重写call()方法,如下所示,它将跳过在定义的MAX_SIZE上加载任何图像。

public BufferedImage call() {
    BufferedImage image = null;
    if (imageStream.getWidth() < MAX_SIZE && imageStream.getHeight() < MAX_SIZE){
        long start = System.nanoTime();
        try {
            image = imageStream.getImage(graphicsState, resources);
        } catch (Throwable e) {
            logger.log(Level.WARNING, "Error loading image: " + imageStream.getPObjectReference() +
                    " " + imageStream.toString(), e);
        }
        long end = System.nanoTime();
        notifyImagePageEvents((end - start));
        return image;
    }
    return null;
}

侧面说明:要最小化解码图像所需的内存量,请确保使用org.icepdf.core.imageReference=default,因为这只会解码图像一次。 org.icepdf.core.imageReference=scaled实际上将以完整大小解码图像,然后执行可以创建非常大的内存峰值的比例。我们正在试验NIO的直接ByteBuffers,它看起来很有希望将解码内存的使用从堆中移除,所以希望将来会有所改善。