【问题标题】:PDFBox - "saveIncremental" after inserting second image doesn't workPDFBox - 插入第二张图像后的“saveIncremental”不起作用
【发布时间】:2019-05-07 14:14:48
【问题描述】:

我在使用 PDFBox 时遇到问题。我在 PDF 中有一个空白页,我想在其中插入图像。因为我也使用签名的 PDF,所以所有更改都必须保存为“saveIncremental”。

当我只插入一张图片时,一切都很好(图片已插入)。当我尝试在此 PDF 中插入另一个图像时,它尚未插入,并且在 Adob​​e Acrobat Reader 中打开时显示“此页面存在错误。Adobe 可能无法正确显示该页面...”。

奇怪的事情 - 当 PDF 不仅是空白页,而且例如带有图像的空白页,一切都很好(第一个和第二个图像已使用 saveIncremental 正确插入)。

图片插入和保存代码:

PDImageXObject pdImage = PDImageXObject.createFromFile(tmpSig.getFileName(), doc);
PDPageContentStream contentStream = new PDPageContentStream(doc, tmpPage, PDPageContentStream.AppendMode.APPEND, true, true);
contentStream.drawImage(pdImage, finalX, (finalPageHeight - finalY - finalHeight), finalWidth, finalHeight);
contentStream.close();

// update before save
tmpPage.getCOSObject().setNeedToBeUpdated(true);
tmpPage.getResources().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);

// save
doc.saveIncremental(new FileOutputStream(pdfFile));

所有可用的文件here

使用 PDFBox 版本 2.0.7,但我也尝试了最新的 (2.0.15),但没有帮助。

感谢所有的想法!


编辑: 我试图像这样更新 XObject 和资源(在注释“保存前更新”下添加了这段代码):

pdImage.getCOSObject().setNeedToBeUpdated(true);
PDResources pdResources = tmpPage.getResources();
for (COSName name : pdResources.getXObjectNames()) {
    pdResources.getXObject(name).getCOSObject().setNeedToBeUpdated(true);
}

问题依然存在,没有任何改变...

【问题讨论】:

  • 您只显示添加单个图像的代码。如何添加两个图像?我的假设是问题在于您没有将特定的 XObject 资源字典标记为已更新,而只是将通用 Resources 字典标记为。
  • 是的。使用 PDFDebugger 打开 blank-inserted-one-saveIncremental-OK-inserted-second-PROBLEM.pdf 会显示一条日志消息“缺少 XObject:Im2”。在 2.0.15 中,如果您还没有阅读 saveIncremental() 的 javadoc,请花时间阅读。
  • @mkl 我不会同时插入两个图像。我插入一张图片并保存文档(使用上面的代码,一切正常)。然后我使用这个文档并在其中添加另一个图像(使用上面的代码但是这个文档不正确,第二个图像丢失了)。
  • @TilmanHausherr 我意识到图像在 PDF 文件中完全丢失,但为什么?为什么第一次正确插入第一张图像,但当我尝试添加第二张图像时,它丢失了?当我实际使用相同的代码时,我做错了什么?为什么当原始 PDF 不是空白但有图像时它会起作用?
  • 可能是因为第一次,图像字典是新的。请尝试 mkl 写的内容。

标签: java pdf pdfbox


【解决方案1】:

除了您已标记为已更新的词典之外

tmpPage.getCOSObject().setNeedToBeUpdated(true);
tmpPage.getResources().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getPages().getCOSObject().setNeedToBeUpdated(true);
doc.getDocumentCatalog().getCOSObject().setNeedToBeUpdated(true);

请将资源字典中的 XObject 条目标记为已更新:

tmpPage.getResources().getCOSObject().getCOSDictionary(COSName.XOBJECT).setNeedToBeUpdated(true);

您想知道为什么在添加第一张图片时不需要这样做吗?

在原始 PDF 中,资源字典中还没有 XObject 条目。因此,它是重新生成的,因此隐式标记为更新。

你想知道为什么在添加到已经有图像的文件时不需要这样做吗?

在其他文件中,资源字典中的 XObject 条目是直接对象,即它立即包含在资源字典中。

4 0 obj
<<
  /Type /Page
  /Resources <<
    /ProcSets [/PDF /Text /ImageB /ImageC /ImageI]
    /ExtGState <</G3 5 0 R /gs2 6 0 R /gs3 7 0 R>>
    /XObject <</Im1 8 0 R /Im2 9 0 R>>
  >>
  /MediaBox [0 0 611.03998 864.95996]
  /Contents [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
  /StructParents 0
  /Parent 2 0 R
>> 
endobj

因此,每当写入资源字典的新副本时,也会隐式写入 XObject 条目的新副本。

在 PDFBox 在资源字典中创建 XObject 条目的文件中,PDFBox 将其创建为间接对象,即在资源字典 XObject 中仅映射对对象编号的引用,并且在具有该编号的对象中可以找到实际的条目字典。

2 0 obj
<<
  /Type /Page
  /Resources <<
    /ProcSets [/PDF /Text /ImageB /ImageC /ImageI]
    /ExtGState <</G3 3 0 R>>
    /XObject 7 0 R
  >>
  /MediaBox [0 0 611.03998 864.95996]
  /Contents [8 0 R 4 0 R 9 0 R]
  /StructParents 0
  /Parent 5 0 R
>>
endobj
7 0 obj
<<
  /Im1 10 0 R
>> 
endobj

因此,当写入资源字典的新副本时,在这种情况下不会写入 XObject 条目字典的隐式新副本。


顺便说一句,您当前的方法对您的任务没有帮助

因为我也处理签名的 PDF,所以所有更改都必须保存为“saveIncremental”。

向页面内容添加图像是不允许对已签名 PDF 进行的更改,因此 Adob​​e Reader 仍会指示您的签名无效。有关签名后允许和不允许的更改的摘要,请查看 this answer 及其引用的文档。

您应该尝试在注释中添加图像。

【讨论】:

  • 是的,解决了我的问题,非常感谢!!这个问题已经让我发疯了几天......也感谢您的整个解释!
猜你喜欢
  • 1970-01-01
  • 2021-07-03
  • 2018-04-19
  • 1970-01-01
  • 2012-07-08
  • 2022-10-04
  • 2014-04-22
  • 2013-01-18
  • 1970-01-01
相关资源
最近更新 更多