PDFBox从没有密码加密的PDF中提取空白

时间:2018-08-27 22:55:11

标签: java encryption pdfbox

我正在使用PDFBox从表单中提取文本,并且我有一个未使用密码加密的PDF,但PDFBox表示已加密。我怀疑某种Adobe“功能”,因为当我打开它时说(安全),而其他我没有问题的PDF却没有。 isEncrypted()返回true,因此,尽管没有密码,但似乎已受到某种保护。

我怀疑它没有正确解密,因为它能够提取表单的文本提示,但不能提取响应本身。在下面的代码中,它从示例PDF中提取了Address (Street Name and Number)City,但没有提取它们之间的响应。

我正在使用PDFBox 2.0,但我也尝试过1.8。

我尝试了可以​​为PDFBox找到的每种解密方法,包括不赞成使用的方法(为什么不这样做)。我得到的结果与根本不尝试解密的结果相同,只是地址和城市提示。

由于PDF确实是他们的噩梦,因此该PDF可能是以某种非标准的方式创建的。感谢您在识别此问题并再次采取行动方面的帮助。

Sample PDF

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import java.io.File;
import org.apache.pdfbox.pdmodel.PDPage;
import java.awt.Rectangle;
import java.util.List;


class Scratch {

    private static float pwidth;
    private static float pheight;

    private static int widthByPercent(double percent) {
        return (int)Math.round(percent * pwidth);
    }

    private static int heightByPercent(double percent) {
        return (int)Math.round(percent * pheight);
    }

    public static void main(String[] args) {
        try {
            //Create objects
            File inputStream = new File("ocr/TestDataFiles/i-9_08-07-09.pdf");

            PDDocument document = PDDocument.load(inputStream);

            // Try every decryption method I've found
            if(document.isEncrypted()) {

                // Method 1
                document.decrypt("");

                // Method 2
                document.openProtection(new StandardDecryptionMaterial(""));

                // Method 3
                document.setAllSecurityToBeRemoved(true);

                System.out.println("Removed encryption");
            }

            PDFTextStripperByArea stripper = new PDFTextStripperByArea();

            //Get the page with data on it
            PDPageTree allPages = document.getDocumentCatalog().getPages();
            PDPage page = allPages.get(3);

            pheight = page.getMediaBox().getHeight();
            pwidth = page.getMediaBox().getWidth();

            Rectangle LastName = new Rectangle(widthByPercent(0.02), heightByPercent(0.195), widthByPercent(0.27), heightByPercent(0.1));
            stripper.addRegion("LastName", LastName);
            stripper.setSortByPosition(true);
            stripper.extractRegions(page);
            List<String> regions = stripper.getRegions();

            System.out.println(stripper.getTextForRegion("LastName"));

        } catch (Exception e){
            System.out.println(e.getMessage());
        }
    }
}

1 个答案:

答案 0 :(得分:2)

Brunos注释解释了为什么即使您不需要输入密码也要对PDF进行加密:

  

可以使用两个密码来加密PDF:用户密码和所有者密码。当使用用户密码加密PDF时,如果不输入该密码,则无法在PDF查看器中打开该文档。仅使用所有者密码加密PDF时,每个人都可以不使用该密码打开PDF,但是可能会有一些限制。您可以识别使用所有者密码加密的PDF,因为它们在Adobe Reader中提到了“已保护”。

您的PDF仅使用所有者密码加密,即用户密码为空。因此,您可以在PDFBox版本中使用空密码""对其进行解密:

document.decrypt("");

(顺便说一下,此“方法1”与您的“方法2”完全相同

document.openProtection(new StandardDecryptionMaterial(""));

加上一些异常包装。)


Tilman的注释暗示了您不检索表单值的原因:您的代码使用PDFTextStripperByArea进行文本提取,但是此文本提取仅提取固定页面内容,而不是该页面上浮动的注释的内容。

您要提取的内容是其小部件为注释的表单域的内容。

蒂尔曼的提议

doc.getDocumentCatalog().getAcroForm().getField("form1[0].#subform[3].address[0]").getValueAsString()

显示在这种情况下如何提取您知道名称"form1[0].#subform[3].address[0]"的表单域的值。如果您不知道要从中提取内容的字段的名称,则PDAcroForm返回的doc.getDocumentCatalog().getAcroForm()对象还有许多其他方法可以访问字段内容。


顺便说一句, AcroForm 定义中的字段名称,例如"form1[0].#subform[3].address[0]"表示PDF的另一个特色:它实际上包含两个表单定义,核心PDF AcroForm 定义和更独立的 XFA 定义。两者描述相同的视觉形式。这样的PDF表单称为混合PDF表单

混合表单的优点是可以使用仅了解 AcroForm 表单(基本上是Adobe以外的所有软件)的PDF工具查看和填写它们,而具有XFA支持的PDF工具(基本上仅Adobe的软件)可以使用其他XFA功能。

混合表单的缺点是,如果使用不具有XFA支持的工具来填写它们,则仅 AcroForm 信息会被更新,而XFA信息则保持不变。因此,混合文档可以在同一字段中包含不同的数据...