【发布时间】:2016-07-19 17:55:47
【问题描述】:
我们偶尔会遇到一些非常大的 PDF,其中包含整页高分辨率图像(文档扫描的结果)。例如,我有一个包含 3500 多张图像的 1.7GB PDF。加载文档大约需要 50 秒,但计算图像大约需要 15 分钟。
我确定这是因为图像字节是作为 API 调用的一部分读取的。有没有办法在不实际读取图像字节的情况下提取图像计数?
PDFBox 版本:2.0.2
示例代码:
@Test
public void imageCountIsCorrect() throws Exception {
PDDocument pdf = readPdf();
try {
assertEquals(3558, countImages(pdf));
// assertEquals(3558, countImagesWithExtractor(pdf));
} finally {
if (pdf != null) {
pdf.close();
}
}
}
protected PDDocument readPdf() throws IOException {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
FileInputStream stream = new FileInputStream("large.pdf");
PDDocument pdf;
try {
pdf = PDDocument.load(stream, MemoryUsageSetting.setupMixed(1024 * 1024 * 250));
} finally {
stream.close();
}
stopWatch.stop();
log.info("PDF loaded: time={}s", stopWatch.getTime() / 1000);
return pdf;
}
protected int countImages(PDDocument pdf) throws IOException {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
int imageCount = 0;
for (PDPage pdPage : pdf.getPages()) {
PDResources pdResources = pdPage.getResources();
for (COSName cosName : pdResources.getXObjectNames()) {
PDXObject xobject = pdResources.getXObject(cosName);
if (xobject instanceof PDImageXObject) {
imageCount++;
if (imageCount % 100 == 0) {
log.info("Found image: #" + imageCount);
}
}
}
}
stopWatch.stop();
log.info("Images counted: time={}s,imageCount={}", stopWatch.getTime() / 1000, imageCount);
return imageCount;
}
如果我将 countImages 方法更改为依赖 COSName,计数会在不到 1 秒的时间内完成,但我对依赖名称前缀有点不确定。这似乎是 pdf 编码器的副产品,而不是 PDFBox(我在他们的代码中找不到对它的任何引用):
if (cosName.getName().startsWith("QuickPDFIm")) {
imageCount++;
}
【问题讨论】:
-
附带说明,您的代码只计算每页的即时位图图像 resources。它既不包含内联图像,也不包含 xobject 或模式中包含的图像。另一方面,图像资源不需要在页面上使用。因此,有时您还会计算太多图像。对于通用解决方案,您需要考虑内容流。
-
啊,这可以解释我在使用 PDFGraphicsStreamEngine 的自定义实现来计算图像时发现的图像计数之间的一些不一致。我将深入研究该代码以找出我缺少的内容。谢谢!
-
我的想法是修改 ExtractImages 示例并删除所有创建图像对象的内容,并使用
DrawObject extends GraphicsOperatorProcessor处理器调用addOperator(new DrawObject());,如果它是图像,则不会创建 xobjects 但会遵循表格。参见 org.apache.pdfbox.contentstream.operator.DrawObject 的源码。 -
我明白了...我最初的失败是使用 NOOP 方法扩展 PDFGraphicsStreamEngine,但 drawImage 增加了计数。看起来我应该使用您所描述的 addOperator 扩展 PDFStreamEngine 。感谢您朝正确的方向轻推!
-
根据您的反馈添加了答案。谢谢!