背景

在某些场景下,我们需要将 PDF 文件的内容转换为图片,然后将图片返回给前端进行展示,比如下面情况:

java 将pdf的图片提取为文字_Image

红色方框所示区域为图片,所以进行PDF 转图片的操作就有必要了。

实现思路

  • 对文件扩展名进行校验
  • 读取PDF文件内容
  • 将文件内容渲染为BufferedImage对象
  • 保存图片文件

具体代码

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;

import static com.xjl.common.constant.EscapeCharacterEnum.ESCAPE_CHARACTER_WINDOWS_O_LINUX;
import static com.xjl.common.constant.ExtensionConstant.PPDF;
import static com.xjl.common.constant.ExtensionConstant.PPNG;

/**
 * PDF转图片工具类
 *
 * @author b16mt
 */
public class PdfToImageUtil {

    /**
     * 图片保存性能
     * 高性能:BINARY,损失清晰度,获得转换速度与存储空间
     * 中性能:GRAY,平衡决定,在转换速度与存储空间里得到新的体验
     * 低性能:RGB,损失转换速度与存储空间,获得更好地可视程度和清晰度
     */
    private static final ImageType IMAGE_SAVING_PERFORMANCE_LOWER = ImageType.RGB;
    private static final ImageType IMAGE_SAVING_PERFORMANCE_MIDDLE = ImageType.GRAY;
    private static final ImageType IMAGE_SAVING_PERFORMANCE_SENIOR = ImageType.BINARY;


    /**
     * 图片保存精度
     * 高精度:300dpi,存储的图像会消耗过高的存储空间和转换时间
     * 中精度:150~200dpi,推荐方案,平衡决定
     * 低精度:100dpi,存储的图像会相对模糊
     */
    private static final int IMAGE_SAVING_DEFINITION_LOWER = 100;
    private static final int IMAGE_SAVING_DEFINITION_MIDDLE = 150;
    private static final int IMAGE_SAVING_DEFINITION_SENIOR = 300;


    /**
     * 执行后续操作前的校验
     *
     * @param imgInputPath  包含要处理的PDF文件的目录路径
     * @param imgOutputPath 保存生成图像的目录路径
     */
    public static String oneVerificationExecution(String imgInputPath, String imgOutputPath) {
        // 创建输入和输出目录的File对象
        File inputFolder = new File(imgInputPath);

        // 仅处理PDF文件(文件扩展名以“.pdf”结尾)
        if (inputFolder.isFile() && inputFolder.getName().toLowerCase().endsWith(PPDF)) {
            // 调用convertPdfToImages方法,将PDF文件转换为图像
            return convertPdfToImages(inputFolder, imgOutputPath);
        }
        return null;
    }


    /**
     * 将PDF文件转换为一系列图像
     *
     * @param pdfFile    要转换为图像的输入PDF文件
     * @param savePath  生成图像的保存目录
     */
    private static String convertPdfToImages(File pdfFile, String savePath) {
        // 从提供的文件加载PDF文档
        try (PDDocument document = PDDocument.load(pdfFile)){
            // 为文档创建PDFRenderer以渲染页面
            PDFRenderer pdfRenderer = new PDFRenderer(document);

            // 以150 DPI和 GRAY 图像类型呈现页面为 BufferedImage
            BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, IMAGE_SAVING_DEFINITION_SENIOR, IMAGE_SAVING_PERFORMANCE_MIDDLE);

            // 生成全局唯一文件名
            // 可以使用uuid
            SnowflakeFileGeneratorUtil generator = new SnowflakeFileGeneratorUtil(1, 1);
            // 为当前页面的图像生成唯一的输出文件名
            String outputFileName = "image_" + generator.generateUniqueId() + PPNG;
            File outputFile = new File(savePath, outputFileName);

            // 将呈现的图像保存为PNG文件
            ImageIO.write(bufferedImage, "PNG", outputFile);

            return savePath + ESCAPE_CHARACTER_WINDOWS_O_LINUX.getSymbol() + outputFileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

解释

校验PDF文件是否合法(主要判断是否为pdf扩展名的文件),这里可以自己去自定义其他校验规则

/**
     * 执行后续操作前的校验
     *
     * @param imgInputPath  包含要处理的PDF文件的目录路径
     * @param imgOutputPath 保存生成图像的目录路径
     */
    public static String oneVerificationExecution(String imgInputPath, String imgOutputPath) {
        // 创建输入和输出目录的File对象
        File inputFolder = new File(imgInputPath);

        // 仅处理PDF文件(文件扩展名以“.pdf”结尾)
        if (inputFolder.isFile() && inputFolder.getName().toLowerCase().endsWith(PPDF)) {
            // 调用convertPdfToImages方法,将PDF文件转换为图像
            return convertPdfToImages(inputFolder, imgOutputPath);
        }
        return null;
    }

读取PDF文件内容,使用try...catch包围确保资源释放

// 从提供的文件加载PDF文档
        try (PDDocument document = PDDocument.load(pdfFile)){
            // 为文档创建PDFRenderer以渲染页面
            PDFRenderer pdfRenderer = new PDFRenderer(document);

            ...
            ...

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

将文件内容渲染为BufferedImage对象,可以根据具体业务需求选择不同性能、精度的图片:

  • 高性能:IMAGE_SAVING_PERFORMANCE_LOWER,损失清晰度,获得转换速度与存储空间
  • 中性能:IMAGE_SAVING_PERFORMANCE_MIDDLE,平衡决定,在转换速度与存储空间里得到新的体验
  • 低性能:IMAGE_SAVING_PERFORMANCE_SENIOR,损失转换速度与存储空间,获得更好地可视程度和清晰度
  • 高精度:IMAGE_SAVING_DEFINITION_LOWER,存储的图像会消耗过高的存储空间和转换时间
  • 中精度:IMAGE_SAVING_DEFINITION_MIDDLE,推荐方案,平衡决定
  • 低精度:IMAGE_SAVING_DEFINITION_SENIOR,存储的图像会相对模糊

性能越高 + 精度越低转换的速度就会越快,反之就会越慢

// 以150 DPI和 GRAY 图像类型呈现页面为 BufferedImage
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, IMAGE_SAVING_DEFINITION_SENIOR, IMAGE_SAVING_PERFORMANCE_MIDDLE);

输出图片,推荐png格式,当然也可以自己自定义其他格式

// 将呈现的图像保存为PNG文件
ImageIO.write(bufferedImage, "PNG", outputFile);

一行代码调用

两个参数:

  • pdfInputPath pdf输入路径
  • imgSavePath 图片保存路径
oneVerificationExecution(pdfInputPath, imgSavePath);

可以使用String接收返回值,返回值为文件存储路径(文件是本地化存储)