使用PDFBox从PDF中提取图像

31

我正在尝试使用pdfbox从pdf中提取图像。示例pdf在这里

但我只得到空白图像。

我正在尝试的代码:

public static void main(String[] args) {
   PDFImageExtract obj = new PDFImageExtract();
    try {
        obj.read_pdf();
    } catch (IOException ex) {
        System.out.println("" + ex);
    }

}

 void read_pdf() throws IOException {
    PDDocument document = null; 
    try {
        document = PDDocument.load("C:\\Users\\Pradyut\\Documents\\MCS-034.pdf");
    } catch (IOException ex) {
        System.out.println("" + ex);
    }
    List pages = document.getDocumentCatalog().getAllPages();
    Iterator iter = pages.iterator(); 
    int i =1;
    String name = null;

    while (iter.hasNext()) {
        PDPage page = (PDPage) iter.next();
        PDResources resources = page.getResources();
        Map pageImages = resources.getImages();
        if (pageImages != null) { 
            Iterator imageIter = pageImages.keySet().iterator();
            while (imageIter.hasNext()) {
                String key = (String) imageIter.next();
                PDXObjectImage image = (PDXObjectImage) pageImages.get(key);
                image.write2file("C:\\Users\\Pradyut\\Documents\\image" + i);
                i ++;
            }
        }
    }

}
感谢(Thanks)。

PDF包含JBIG2编码图像。我不确定pdfBox是否支持这些图像。 - mark stephens
我能在这个应用程序中使用 jbig2-imageio 库吗?将其作为库 jar 或 classpath 添加到应用程序中是否可行? - Pradyut Bhattacharya
我在测试你的代码时遇到了问题:"UnsupportedOper"。 - user1209671
你找到了如何解码JBIG2图像的答案吗? - WelcomeTo
@PradyutBhattacharya,你找到了使用jbig2-imageio解码JBIG2图像的解决方案吗?谢谢。 - WelcomeTo
8个回答

35

以下是使用PDFBox 2.0.1的代码,可获取PDF中所有图像的列表。与其他代码不同的是,它会递归遍历文档,而不是尝试从顶层获取图像。

public List<RenderedImage> getImagesFromPDF(PDDocument document) throws IOException {
        List<RenderedImage> images = new ArrayList<>();
    for (PDPage page : document.getPages()) {
        images.addAll(getImagesFromResources(page.getResources()));
    }

    return images;
}

private List<RenderedImage> getImagesFromResources(PDResources resources) throws IOException {
    List<RenderedImage> images = new ArrayList<>();

    for (COSName xObjectName : resources.getXObjectNames()) {
        PDXObject xObject = resources.getXObject(xObjectName);

        if (xObject instanceof PDFormXObject) {
            images.addAll(getImagesFromResources(((PDFormXObject) xObject).getResources()));
        } else if (xObject instanceof PDImageXObject) {
            images.add(((PDImageXObject) xObject).getImage());
        }
    }

    return images;
}

这应该被标记为正确答案。我在Java11上使用PDFBox 2.0.12时成功了。 如果有人在使用此方法时遇到jpeg2000错误,请查看https://github.com/jai-imageio/jai-imageio-jpeg2000 - ThetaSinner
@Matt 你只考虑了 xObjectPDFormXObject 或者 PDImageXObject 的情况。但是根据 Javadoc 中的说明,它也可能是 PDPostScriptXObject。难道 PDPostScriptXObject 里面还可能包含更多的图片吗? - Joe7

11

对于PDFBox 2.0.1版本,pudaykiran的答案需要进行一些修改,因为有些API已经被更改。

public static void testPDFBoxExtractImages() throws Exception {
    PDDocument document = PDDocument.load(new File("D:/Temp/Test.pdf"));
    PDPageTree list = document.getPages();
    for (PDPage page : list) {
        PDResources pdResources = page.getResources();
        for (COSName c : pdResources.getXObjectNames()) {
            PDXObject o = pdResources.getXObject(c);
            if (o instanceof org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject) {
                File file = new File("D:/Temp/" + System.nanoTime() + ".png");
                ImageIO.write(((org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject)o).getImage(), "png", file);
            }
        }
    }
}

太好了!对我有用。但是在我的情况下,需要添加jai-imageio-jpeg2000依赖项来转换jpeg2000图像。 - Victoria Agafonova

11

下面的GetImagesFromPDF Java类会获取文件04-Request-Headers.pdf中的所有图像,并将这些文件保存到目标文件夹PDFCopy中。

import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;

@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
public class GetImagesFromPDF {
    public static void main(String[] args) {
        try {
            String sourceDir = "C:/PDFCopy/04-Request-Headers.pdf";// Paste pdf files in PDFCopy folder to read
            String destinationDir = "C:/PDFCopy/";
            File oldFile = new File(sourceDir);
            if (oldFile.exists()) {
            PDDocument document = PDDocument.load(sourceDir);

            List<PDPage> list = document.getDocumentCatalog().getAllPages();

            String fileName = oldFile.getName().replace(".pdf", "_cover");
            int totalImages = 1;
            for (PDPage page : list) {
                PDResources pdResources = page.getResources();

                Map pageImages = pdResources.getImages();
                if (pageImages != null) {

                    Iterator imageIter = pageImages.keySet().iterator();
                    while (imageIter.hasNext()) {
                        String key = (String) imageIter.next();
                        PDXObjectImage pdxObjectImage = (PDXObjectImage) pageImages.get(key);
                        pdxObjectImage.write2file(destinationDir + fileName+ "_" + totalImages);
                        totalImages++;
                    }
                }
            }
        } else {
            System.err.println("File not exists");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

有没有一种方法可以对此进行排序或确保它是串行完成的? - Ian
@Ian,你想按顺序获取图像吗? - UdayKiran Pulipati
@COBRA.cH,请告诉我你正在尝试哪个版本? - UdayKiran Pulipati
我在使用pdfbox 2.0.25时没有找到pdResources.getImages()这个表达式。 - André Rodrigues de Sousa

2
只需在路径末尾添加.jpeg
image.write2file("C:\\Users\\Pradyut\\Documents\\image" + i + ".jpeg");

那对我来说可以。

1
您可以使用PDPage.convertToImage()函数将PDF页面转换为BufferedImage。接下来,您可以使用BufferedImage创建图像。
有关更多详细信息,请使用以下参考: 并且不要忘记在PDPage类中查找PDPage.convertToImage()函数。

1
这是@Matt答案的Kotlin版本。
fun <R> PDResources.onImageResources(block: (RenderedImage) -> (R)): List<R> =
        this.xObjectNames.flatMap {
            when (val xObject = this.getXObject(it)) {
                is PDFormXObject -> xObject.resources.onImageResources(block)
                is PDImageXObject -> listOf(block(xObject.image))
                else -> emptyList()
            }
        }

您可以像这样在PDPage资源上使用它:

page.resources.onImageResources { image ->
    Files.createTempFile("image", "xxx").also { path-> 
        if(!ImageIO.write(it, "xxx", file.toFile()))
            IllegalStateException("Couldn't write image to file")
    }
}

其中"xxx"是您所需的格式(如"jpeg"


1

对于想要复制和粘贴这个现成代码的人来说

import org.apache.pdfbox.contentstream.PDFStreamEngine;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

public class ExtractImagesUseCase extends PDFStreamEngine{
    private final String filePath;
    private final String outputDir;

    // Constructor
    public ExtractImagesUseCase(String filePath,
                                String outputDir){
        this.filePath = filePath;
        this.outputDir = outputDir;
    }

    // Execute
    public void execute(){
        try{
            File file = new File(filePath);
            PDDocument document = PDDocument.load(file);

            for(PDPage page : document.getPages()){
                processPage(page);
            }

        }catch(IOException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void processOperator(Operator operator, List<COSBase> operands) throws IOException{
        String operation = operator.getName();

        if("Do".equals(operation)){
            COSName objectName = (COSName) operands.get(0);
            PDXObject pdxObject = getResources().getXObject(objectName);

            if(pdxObject instanceof PDImageXObject){
                // Image
                PDImageXObject image = (PDImageXObject) pdxObject;
                BufferedImage bImage = image.getImage();

                // File
                String randomName = UUID.randomUUID().toString();
                File outputFile = new File(outputDir,randomName + ".png");

                // Write image to file
                ImageIO.write(bImage, "PNG", outputFile);

            }else if(pdxObject instanceof PDFormXObject){
                PDFormXObject form = (PDFormXObject) pdxObject;
                showForm(form);
            }
        }

        else super.processOperator(operator, operands);
    }
}

演示

public class ExtractImageDemo{
    public static void main(String[] args){
        String filePath = "C:\\Users\\John\\Downloads\\Documents\\sample-file.pdf";
        String outputDir = "C:\\Users\\John\\Downloads\\Documents\\Output";

        ExtractImagesUseCase useCase = new ExtractImagesUseCase(
                filePath,
                outputDir
        );
        useCase.execute();
    }
}

如果我理解正确,这个例子和Matt的回答的区别是这个例子会在内存中渲染文档,也就是处理所有的操作符,而不像Matt的方法。对吗? - undefined

0

不要调用

image.write2file("C:\\Users\\Pradyut\\Documents\\image" + i);

您可以使用ImageIO.write()静态方法,以您需要的任何格式将RGB图像写出。在这里,我使用了PNG格式:

File outputFile = new File( "C:\\Users\\Pradyut\\Documents\\image" + i + ".png");
ImageIO.write( image.getRGBImage(), "png", outputFile);

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