设置图像表单字段

时间:2019-01-07 20:05:17

标签: pdf pdfbox

我创建了一个带有一个图像字段的示例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不会显示任何图像。

我的目标是从this PDF开始并使用PDFBox获取this PDF

2 个答案:

答案 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. Create a keystore(最佳使用pkcs12)
  2. 向您的PDF添加signature field
  3. 在字段上签名(或在步骤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");
    
  4. <optionally>展平文档