从PDF中提取不可选择的内容

3
我正在使用Apache PDFBox从PDF文件中提取页面,但我找不到一种方法来提取不可选择的内容(文本或图像)。对于PDF文件内可选择的内容,没有问题。
请注意,所涉及的PDF文件没有任何限制可以复制其内容,至少从我在文件的“文档限制摘要”中看到的情况来看:它们都允许“内容复制”和“用于辅助功能的内容复制”!在同一个PDF文件中,有一些内容是可选择的,而其他部分则不是。发生的情况是,提取的页面带有“空洞”,即它们只有PDF的可选择部分。然而,在MS Word中,如果我将PDF作为对象添加,整个PDF页面的内容就会出现!因此,我希望能够使用PDFBox lib或任何其他Java lib来做到这一点!
以下是我用来将PDF页面转换为图像的代码:
private void convertPdfToImage(File pdfFile, int pdfId) throws IOException {
   PDDocument document = PDDocument.loadNonSeq(pdfFile, null);
   List<PDPage> pdPages = document.getDocumentCatalog().getAllPages();
   for (PDPage pdPage : pdPages) { 
       BufferedImage bim = pdPage.convertToImage(BufferedImage.TYPE_INT_RGB, 300);
       ImageIOUtil.writeImage(bim, TEMP_FILEPATH + pdfId + ".png", 300);
   }
   document.close();
}

有没有办法使用Apache PDFBox库(或其他类似的库)从PDF中提取不可选择的内容?还是根本不可能实现?如果确实如此,那为什么呢?
非常感谢您的任何帮助!
编辑:我正在使用Adobe Reader作为PDF查看器和PDFBox v1.8。这是一个示例PDF:https://dl.dropboxusercontent.com/u/2815529/test.pdf

不,那不是问题所在,有关PDF的文件没有任何限制复制内容,至少从我在文件的“文档限制摘要”中看到的情况来看。 - Dr Jorge
1
检查权限显然是我做的第一件事,只有世界上最笨的人才会在没有检查的情况下发布这样的SO问题。尽管如此,我编辑了我的问题,指出我已经进行了检查。 - Dr Jorge
2
我问这个问题是因为你没有提到这个事实,而且这里问的问题有一半可以在一分钟内通过谷歌搜索或者阅读文档解决。不是针对你个人 :) - user3707125
我知道我的声望不高,但我想相信那些问愚蠢问题的人比我声望还要低得多:P 无论如何,感谢您的评论。 - Dr Jorge
2
我刚刚看了一下你的样本文件。确实,在两种情况下,图像被嵌入到图案中,这使得它们在Adobe Reader中无法选择,并且不能被标准的文本/图像提取解析器提取。这并不意味着它们无法被提取,只是需要在这里进行一些编码。我今天不在办公室,但我会在明天的某个时候查看这个问题。 - mkl
显示剩余8条评论
1个回答

5

所谓的两张图片,右上角的fischer标志和稍微向下的小草图,都是通过用平铺图案填充页面上的一个区域来绘制,这个图案在其内容流中又会绘制相应的图像。

Adobe Reader不允许选择图案的内容,而自动图像提取器通常也不会遍历Pattern资源树。

PDFBox 1.8.10

您可以使用PDFBox相对容易地构建图案图像提取器,例如针对PDFBox 1.8.10:

public void extractPatternImages(PDDocument document, String fileNameFormat) throws IOException
{
    List<PDPage> pages = document.getDocumentCatalog().getAllPages();
    if (pages == null)
        return;

    for (int i = 0; i < pages.size(); i++)
    {
        String pageFormat = String.format(fileNameFormat, "-" + i + "%s", "%s");
        extractPatternImages(pages.get(i), pageFormat);
    }
}

public void extractPatternImages(PDPage page, String pageFormat) throws IOException
{
    PDResources resources = page.getResources();
    if (resources == null)
        return;
    Map<String, PDPatternResources> patterns = resources.getPatterns();

    for (Map.Entry<String, PDPatternResources> patternEntry : patterns.entrySet())
    {
        String patternFormat = String.format(pageFormat, "-" + patternEntry.getKey() + "%s", "%s");
        extractPatternImages(patternEntry.getValue(), patternFormat);
    }
}

public void extractPatternImages(PDPatternResources pattern, String patternFormat) throws IOException
{
    COSDictionary resourcesDict = (COSDictionary) pattern.getCOSDictionary().getDictionaryObject(COSName.RESOURCES);
    if (resourcesDict == null)
        return;
    PDResources resources = new PDResources(resourcesDict);
    Map<String, PDXObject> xObjects = resources.getXObjects();
    if (xObjects == null)
        return;

    for (Map.Entry<String, PDXObject> entry : xObjects.entrySet())
    {
        PDXObject xObject = entry.getValue();
        String xObjectFormat = String.format(patternFormat, "-" + entry.getKey() + "%s", "%s");
        if (xObject instanceof PDXObjectForm)
            extractPatternImages((PDXObjectForm)xObject, xObjectFormat);
        else if (xObject instanceof PDXObjectImage)
            extractPatternImages((PDXObjectImage)xObject, xObjectFormat);
    }
}

public void extractPatternImages(PDXObjectForm form, String imageFormat) throws IOException
{
    PDResources resources = form.getResources();
    if (resources == null)
        return;
    Map<String, PDXObject> xObjects = resources.getXObjects();
    if (xObjects == null)
        return;

    for (Map.Entry<String, PDXObject> entry : xObjects.entrySet())
    {
        PDXObject xObject = entry.getValue();
        String xObjectFormat = String.format(imageFormat, "-" + entry.getKey() + "%s", "%s");
        if (xObject instanceof PDXObjectForm)
            extractPatternImages((PDXObjectForm)xObject, xObjectFormat);
        else if (xObject instanceof PDXObjectImage)
            extractPatternImages((PDXObjectImage)xObject, xObjectFormat);
    }

    Map<String, PDPatternResources> patterns = resources.getPatterns();

    for (Map.Entry<String, PDPatternResources> patternEntry : patterns.entrySet())
    {
        String patternFormat = String.format(imageFormat, "-" + patternEntry.getKey() + "%s", "%s");
        extractPatternImages(patternEntry.getValue(), patternFormat);
    }
}

public void extractPatternImages(PDXObjectImage image, String imageFormat) throws IOException
{
    image.write2OutputStream(new FileOutputStream(String.format(imageFormat, "", image.getSuffix())));
}

(ExtractPatternImages.java)

我将它应用到了你提供的样例PDF中,如下所示:

public void testtestDrJorge() throws IOException
{
    try (InputStream resource = getClass().getResourceAsStream("testDrJorge.pdf"))
    {
        PDDocument document = PDDocument.load(resource);
        extractPatternImages(document, "testDrJorge%s.%s");;
    }
}

(ExtractPatternImages.java)

得到了两个图片:

  • `testDrJorge-0-R15-R14.png

    testDrJorge-0-R15-R14.png

  • testDrJorge-0-R38-R37.png

    testDrJorge-0-R38-R37.png

这些图片失去了它们的红色部分。这很可能是因为PDFBox 1.x.x版本没有适当地支持CMYK图像提取,参考PDFBOX-2128 (不正确支持CMYK图像),而你的图片是CMYK格式的。

PDFBox 2.0.0 发布候选版

我将代码更新至PDFBox 2.0.0(目前仅作为发布候选版):

public void extractPatternImages(PDDocument document, String fileNameFormat) throws IOException
{
    PDPageTree pages = document.getDocumentCatalog().getPages();
    if (pages == null)
        return;

    for (int i = 0; i < pages.getCount(); i++)
    {
        String pageFormat = String.format(fileNameFormat, "-" + i + "%s", "%s");
        extractPatternImages(pages.get(i), pageFormat);
    }
}

public void extractPatternImages(PDPage page, String pageFormat) throws IOException
{
    PDResources resources = page.getResources();
    if (resources == null)
        return;
    Iterable<COSName> patternNames = resources.getPatternNames();

    for (COSName patternName : patternNames)
    {
        String patternFormat = String.format(pageFormat, "-" + patternName + "%s", "%s");
        extractPatternImages(resources.getPattern(patternName), patternFormat);
    }
}

public void extractPatternImages(PDAbstractPattern pattern, String patternFormat) throws IOException
{
    COSDictionary resourcesDict = (COSDictionary) pattern.getCOSObject().getDictionaryObject(COSName.RESOURCES);
    if (resourcesDict == null)
        return;
    PDResources resources = new PDResources(resourcesDict);
    Iterable<COSName> xObjectNames = resources.getXObjectNames();
    if (xObjectNames == null)
        return;

    for (COSName xObjectName : xObjectNames)
    {
        PDXObject xObject = resources.getXObject(xObjectName);
        String xObjectFormat = String.format(patternFormat, "-" + xObjectName + "%s", "%s");
        if (xObject instanceof PDFormXObject)
            extractPatternImages((PDFormXObject)xObject, xObjectFormat);
        else if (xObject instanceof PDImageXObject)
            extractPatternImages((PDImageXObject)xObject, xObjectFormat);
    }
}

public void extractPatternImages(PDFormXObject form, String imageFormat) throws IOException
{
    PDResources resources = form.getResources();
    if (resources == null)
        return;
    Iterable<COSName> xObjectNames = resources.getXObjectNames();
    if (xObjectNames == null)
        return;

    for (COSName xObjectName : xObjectNames)
    {
        PDXObject xObject = resources.getXObject(xObjectName);
        String xObjectFormat = String.format(imageFormat, "-" + xObjectName + "%s", "%s");
        if (xObject instanceof PDFormXObject)
            extractPatternImages((PDFormXObject)xObject, xObjectFormat);
        else if (xObject instanceof PDImageXObject)
            extractPatternImages((PDImageXObject)xObject, xObjectFormat);
    }

    Iterable<COSName> patternNames = resources.getPatternNames();

    for (COSName patternName : patternNames)
    {
        String patternFormat = String.format(imageFormat, "-" + patternName + "%s", "%s");
        extractPatternImages(resources.getPattern(patternName), patternFormat);
    }
}

public void extractPatternImages(PDImageXObject image, String imageFormat) throws IOException
{
    String filename = String.format(imageFormat, "", image.getSuffix());
    ImageIOUtil.writeImage(image.getOpaqueImage(), "png", new FileOutputStream(filename));
}

看起来有所改善... ;)

  • testDrJorge-0-COSName{R15}-COSName{R14}.png

    testDrJorge-0-COSName{R15}-COSName{R14}.png

  • testDrJorge-0-COSName{R38}-COSName{R37}.png

    testDrJorge-0-COSName{R38}-COSName{R37}.png


仅仅为了完整起见:如果您将整个文件转换为RGB,图像是否正确提取?(令人惊讶的是,我的Acrobat Pro会丢弃右上角的标志,但确实将第二个图像转换为RGB。) - Jongware
1
@Jongware 请参考我的编辑,使用PDFBox 2.0.0,两个图案都可以正确提取... ;) - mkl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接