我创建了一个带有一个图像字段的示例PDF表单。我正在尝试使用PDFBox将图像设置为字段。
我看到PDFBox将此类字段视为PDPushButton
的实例,但是我看不到此类的接口公开了处理图像的方法...
可以使用注释中的URL下载示例PDF。
怎么办?
编辑:
这是我到目前为止正在做的事情:
PDDocument pdfDocument = null;
PDAcroForm acroForm = pdfDocument.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
PDPushButton field = (PDPushButton) acroForm.getField("test");
PDImageXObject pdImageXObject = PDImageXObject.createFromFile("my_img.png", pdfDocument);
List<PDAnnotationWidget> widgets = field.getWidgets();
/*
* The field may appear multiple times in the document, I would like to repeat that for every widget (occurence).
*/
for(PDAnnotationWidget widget : widgets) {
PDRectangle rectangle = widget.getRectangle();
//PDAppearanceDictionary appearanceDict = widget.getAppearance();
/*
* In my case, when the image is not set with Acrobat DC, appearanceDict is null.
*/
/*
* Create the appearance stream and fill it with the image.
*/
PDAppearanceStream pdAppearanceStream = new PDAppearanceStream(pdfDocument);
pdAppearanceStream.setResources(new PDResources());
try (PDPageContentStream pdPageContentStream = new PDPageContentStream(pdfDocument, pdAppearanceStream)) {
pdPageContentStream.drawImage(pdImageXObject, rectangle.getLowerLeftX(), rectangle.getLowerLeftY(), pdImageXObject.getWidth(), pdImageXObject.getHeight());
}
pdAppearanceStream.setBBox(new PDRectangle(rectangle.getWidth(), rectangle.getHeight()));
/*
* Create the appearance dict with only one appearance (default) and set the appearance to the widget.
*/
PDAppearanceDictionary appearanceDict = new PDAppearanceDictionary();
appearanceDict.setNormalAppearance(pdAppearanceStream);
widget.setAppearance(appearanceDict);
}
}
ByteArrayOutputStream outStr = new ByteArrayOutputStream();
pdfDocument.save(outStr);
pdfDocument.close();
但是,使用Acrobat Reader生成的PDF不会显示任何图像。
答案 0 :(得分:1)
首先,pdf标准ISO 32000-2根本没有指定“图像字段”。某些专有的pdf生成器/编辑器(特别是Adobe产品)正在使用JavaScript来使按钮字段的操作类似于GUI中图像的表单字段,尤其是在其自己的pdf查看器中。但是,这些按钮是按钮,而不是图像表单字段。因此,
PDFBox将此类字段视为
PDPushButton
的实例,但我看不到此类的接口公开了处理图像的方法...
但是,如果您确实需要像图像字段之类的东西,则无需寻找另一种解决方案来模拟图像字段,只需遵循Adobe的指导。只需知道,只有一个模拟图像字段。
要用图像填充按钮字段,可以将Renat Gatin中出现的AcroFormPopulator
his answer个问题用于自己的问题"How to insert image programmatically in to AcroForm field using java PDFBox?"。
但是请注意,将其应用于示例文件后,它会揭示PDFBox表单拼合代码中的错误。因此,您应该停用AcroFormPopulator
中的表格拼合,即删除其中的acroForm.flatten()
。
存在问题的错误是由于缺少转换:如果将XObject用作表单字段的外观,则按其规范将其边界框中的所有内容自动移入注释矩形: >
1。必须使用 Matrix 转换外观的边界框(由其 BBox 条目指定),以生成具有任意方向的四边形。 变形的外观框是包含此四边形的最小的直立矩形。
2。必须计算一个矩阵 A ,该矩阵可以缩放并平移转换后的外观框,使其与注释矩形(由 Rect 条目指定)的边缘对齐。 A 映射左下角( x 和 y 坐标最小的角)和右上角(具有转换后的外观框的最大 x 和 y 坐标)到注释矩形的相应角。
3。 矩阵应与 A 连接以形成矩阵 AA ,该矩阵在默认用户空间中从外观的坐标系映射到注释的矩形
(ISO 32000-2,第12.5.5节外观流,算法:外观流)
当从页面内容引用展平后的XObject时,查看器不再自动确定并应用此转换 AA ,因此必须由展平器显式添加
显然,扁平化PDFBox表单至少不会创建该 AA 矩阵,特别是它假定边框左下角始终位于坐标系的原点。
在您的示例PDF中并非如此,因此此处的展平有效地将展平的图像字段按钮移出屏幕。
PS:这种情况比预期的还要怪异,PDAcroForm.flatten(List<PDField>, boolean)
确实尝试确定是否需要对先前外观XObject进行平移或缩放,并在认为必要时添加了转换,但是
1。在检查PDAcroForm.resolveNeedsTranslation(PDAppearanceStream)
中是否需要翻译时,它实际上检查外观XObject的表单XObject资源。当且仅当其中有一个XObject的边界框且两个锚点坐标都不为0时,才假定不需要平移。 —这是一个非常奇怪的测试,适当的测试必须检查外观XObject本身的边界框,而不是它包含的XObjects形式。在示例文档中,外观XObject不包含任何形式的XObject,因此会自动假定需要进行翻译。
2。添加平移变换时,它再次忽略外观XObject的边界框,并进行平移,就好像外观XObject锚点位于坐标系原点。 —在示例文档中,这完全是不够的,因为边界框锚点已经定位在很远的距离,导致将其重新定位到距原点所需距离的两倍。
3。在检查PDAcroForm.resolveNeedsScaling(PDAppearanceStream)
中是否需要翻译时,它实际上会检查是否包含所包含的任意XObject,并假定如果存在这样的包含XObject,则需要缩放。 —在示例文档中有一个图像XObject,因此假定缩放是必需的……很奇怪。
这3个细节完全没有意义。 (好吧,可能有一些示例文档偶然地为它们带来了预期的结果,但是总的来说这是胡说八道。)
答案 1 :(得分:0)
事实是,我需要插入一个代表 签名...所以,如果有其他方法可以做到,我将很高兴 听到。事实是,我不需要(甚至我想) 签名的数字属性,我只对 图片。
您知道您可以直接将图像添加到PDF文件吗? 但是,如果您需要签名,那么我会说使用签名表单字段。 请勿滥用其他任何字段。而且,如果您真的不需要签名的功能,那么请在整理文档后保留图像,但签名消失了。但是我们的许多客户一开始都希望这样,但是经过一番询问和考虑之后,他们决定使用真实签名...
这是您需要做的:
在字段上签名(或在步骤1中一步完成)
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(new FileInputStream("YourPathToAKeyStore.p12"), "pw".toCharArray());
File documentFile = new File("YourPdf.pdf");
CreateVisibleSignature signing = new CreateVisibleSignature(keystore,"pw".toCharArray());
FileInputStream imageStream = new FileInputStream("imageOfTheSignature.png");
signing.setVisibleSignDesigner(-85, imageStream);
imageStream.close();
signing.setVisibleSignatureProperties("name", "location", "Security", true);
signing.setExternalSigning(externalSig);
signing.signPDF(documentFile,new File("signed.pdf"),null,"SignatureField10");
<optionally>
展平文档