java使用pdfbox实现将pdf页面转化为图片并实现图片自定义压缩和转化为webp格式

1.导入maven依赖

    <!-- webp-imageio 依赖 -->
        <dependency>
            <groupId>org.sejda.imageio</groupId>
            <artifactId>webp-imageio</artifactId>
            <version>0.1.6</version>
        </dependency>

        <!-- Thumbnailator -->
        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>0.4.8</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>3.0.5</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2.实现代码

import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * PDF文件转图片工具类,提供可配置的图片转换功能
 */
@Slf4j
public class PdfImageConverter {
	
	// 默认DPI值
	private static final float DEFAULT_DPI = 600f;
	// 默认输出格式
	private static final String DEFAULT_FORMAT = "png";
	
	/**
	 * 将PDF文件转换为图片
	 *
	 * @param pdfFilePath   PDF文件的绝对路径
	 * @param dpi           渲染图片的DPI,默认为600
	 * @param outputPath    图片保存的路径,为空则保存在PDF文件所在目录
	 * @param compress      是否需要压缩图片
	 * @param quality       压缩质量 (0.0-1.0),仅在compress为true时有效
	 * @param convertToWebP 是否需要转换为WebP格式
	 * @return 生成的图片文件路径列表
	 */
	public List<String> convertPdfToImages(String pdfFilePath, Float dpi, String outputPath, boolean compress, Float quality, boolean convertToWebP) {
		// 参数检查与默认值设置
		if (dpi == null || dpi <= 0) {
			dpi = DEFAULT_DPI;
		}
		if (compress && (quality == null || quality < 0 || quality > 1)) {
			throw new IllegalArgumentException("压缩质量必须在0.0到1.0之间");
		}
		// 设置输出路径
		String finalOutputPath = determineOutputPath(pdfFilePath, outputPath);
		List<String> generatedFiles = new ArrayList<>();
		try {
			// 确保输出目录存在
			ensureDirectoryExists(finalOutputPath);
			// 加载PDF文档
			File pdfFile = new File(pdfFilePath);
			if (!pdfFile.exists()) {
				throw new IllegalArgumentException("PDF文件不存在: " + pdfFilePath);
			}
			try (PDDocument document = Loader.loadPDF(pdfFile)) {
				PDFRenderer renderer = new PDFRenderer(document);
				renderer.setSubsamplingAllowed(false); // 保持高质量,不允许子采样
				int pageCount = document.getNumberOfPages();
				for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
					// 渲染图像
					BufferedImage renderedImage = renderer.renderImageWithDPI(pageIndex, dpi, ImageType.ARGB);
					// 保存图片
					String baseName = getBaseFileName(pdfFile) + "_page" + (pageIndex + 1);
					String filePath;
					if (convertToWebP) {
						// 转换为WebP格式
						filePath = saveAsWebP(renderedImage, finalOutputPath, baseName, compress, quality);
					} else {
						// 保存为PNG或压缩的PNG
						filePath = saveImage(renderedImage, finalOutputPath, baseName, compress, quality);
					}
					generatedFiles.add(filePath);
					log.info("生成图片: {}", filePath);
				}
			}
		} catch (IOException e) {
			log.error("转换PDF到图片失败", e);
			throw new RuntimeException("转换PDF到图片失败: " + e.getMessage(), e);
		}
		return generatedFiles;
	}
	
	/**
	 * 确定输出路径,如果未提供则使用PDF文件所在目录
	 *
	 * @param pdfFilePath 原PDF文件路径
	 * @param outputPath  指定的输出路径(可能为空)
	 * @return 最终输出路径
	 */
	private String determineOutputPath(String pdfFilePath, String outputPath) {
		if (outputPath == null || outputPath.trim().isEmpty()) {
			// 使用PDF文件所在目录作为输出目录
			File pdfFile = new File(pdfFilePath);
			return pdfFile.getParent() + File.separator + getBaseFileName(pdfFile) + "_images";
		}
		return outputPath;
	}
	
	/**
	 * 获取不带扩展名的文件名
	 */
	private String getBaseFileName(File file) {
		String fileName = file.getName();
		int dotIndex = fileName.lastIndexOf('.');
		return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
	}
	
	/**
	 * 确保目录存在,如不存在则创建
	 */
	private void ensureDirectoryExists(String directoryPath) throws IOException {
		Path path = Paths.get(directoryPath);
		if (!Files.exists(path)) {
			Files.createDirectories(path);
		}
	}
	
	/**
	 * 保存图片(PNG或压缩的PNG)
	 */
	private String saveImage(BufferedImage image, String outputPath, String baseName, boolean compress, Float quality) throws IOException {
		String filePath = outputPath + File.separator + baseName + "." + DEFAULT_FORMAT;
		
		if (compress) {
			saveCompressedImage(image, filePath, quality);
		} else {
			ImageIO.write(image, DEFAULT_FORMAT, new File(filePath));
		}
		
		return filePath;
	}
	
	/**
	 * 保存压缩图片
	 */
	private void saveCompressedImage(BufferedImage image, String filePath, float quality) throws IOException {
		Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(DEFAULT_FORMAT);
		if (!writers.hasNext()) {
			throw new IllegalStateException("没有找到" + DEFAULT_FORMAT + "格式的图片写入器");
		}
		
		ImageWriter writer = writers.next();
		try (ImageOutputStream outputStream = new FileImageOutputStream(new File(filePath))) {
			writer.setOutput(outputStream);
			
			ImageWriteParam param = writer.getDefaultWriteParam();
			if (param.canWriteCompressed()) {
				param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
				param.setCompressionQuality(quality);
			}
			writer.write(null, new IIOImage(image, null, null), param);
		} finally {
			writer.dispose();
		}
	}
	
	/**
	 * 保存为WebP格式
	 * 使用WebpConverter转换为WebP格式
	 */
	private String saveAsWebP(BufferedImage image, String outputPath, String baseName, boolean compress, Float quality) throws IOException {
		// 先保存为PNG
		String pngPath = saveImage(image, outputPath, baseName, compress, quality);
		
		// 检查WebP支持
		if (!WebpConverter.isWebpSupported()) {
			log.warn("WebP格式不受支持,返回原PNG图片路径。请确保webp-imageio依赖已正确添加到项目中");
			return pngPath;
		}
		// 设置WebP输出路径
		String webpPath = outputPath + File.separator + baseName + ".webp";
		
		// 使用WebpConverter进行转换
		boolean success = WebpConverter.convertToWebP(image, webpPath, quality != null ? quality : 0.8f);
		
		if (success) {
			// 转换成功后可以选择删除原PNG文件
			// new File(pngPath).delete();
			return webpPath;
		} else {
			log.warn("WebP转换失败,返回原PNG图片路径");
			return pngPath;
		}
	}
}

3.示例用法

import lombok.extern.slf4j.Slf4j;

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

/**
 * PdfImageConverter使用示例
 */
@Slf4j
public class PdfImageConverterDemo {
	
	/**
	 * 使用示例:简单转换,使用默认DPI和无压缩
	 *
	 * @param pdfFilePath PDF文件路径
	 */
	public static void simpleConversion(String pdfFilePath) {
		PdfImageConverter converter = new PdfImageConverter();
		List<String> generatedFiles = converter.convertPdfToImages(pdfFilePath,
				null,  // 使用默认DPI (600)
				null,  // 使用PDF所在目录
				false, // 不压缩
				null,  // 无压缩质量
				false  // 不转换为WebP
		);
		log.info("生成了{}个图片文件:", generatedFiles.size());
		for (String file : generatedFiles) {
			log.info("- {}", file);
		}
	}
	
	/**
	 * 使用示例:自定义DPI和输出路径
	 *
	 * @param pdfFilePath PDF文件路径
	 * @param dpi         渲染DPI
	 * @param outputPath  输出路径
	 */
	public static void customDpiAndPath(String pdfFilePath, float dpi, String outputPath) {
		PdfImageConverter converter = new PdfImageConverter();
		List<String> generatedFiles = converter.convertPdfToImages(pdfFilePath,
				dpi,  // 自定义DPI
				outputPath, // 自定义输出路径
				false,      // 不压缩
				null,       // 无压缩质量
				false       // 不转换为WebP
		);
		log.info("使用DPI={}生成了{}个图片文件在 {}", dpi, generatedFiles.size(), outputPath);
	}
	
	/**
	 * 使用示例:带压缩的转换
	 *
	 * @param pdfFilePath PDF文件路径
	 * @param quality     压缩质量 (0.0-1.0)
	 */
	public static void compressedConversion(String pdfFilePath, float quality) {
		PdfImageConverter converter = new PdfImageConverter();
		List<String> generatedFiles = converter.convertPdfToImages(pdfFilePath, null,   // 使用默认DPI
				null,   // 使用PDF所在目录
				true,   // 启用压缩
				quality,// 压缩质量
				false   // 不转换为WebP
		);
		log.info("生成了{}个压缩图片文件 (质量={})", generatedFiles.size(), quality);
	}
	
	/**
	 * 使用示例:转换为WebP格式
	 * 使用webp-imageio库实现WebP转换
	 *
	 * @param pdfFilePath PDF文件路径
	 * @param quality     压缩质量 (0.0-1.0)
	 */
	public static void webpConversion(String pdfFilePath, float quality) {
		// 首先检查WebP支持
		if (!WebpConverter.isWebpSupported()) {
			log.warn("WebP格式不受支持,请确保webp-imageio依赖已正确添加到项目中。跳过WebP转换示例。");
			return;
		}
		
		PdfImageConverter converter = new PdfImageConverter();
		List<String> generatedFiles = converter.convertPdfToImages(pdfFilePath, null,   // 使用默认DPI
				null,   // 使用PDF所在目录
				true,   // 启用压缩
				quality,// 压缩质量
				true    // 转换为WebP
		);
		
		log.info("生成了{}个图片文件 (质量={})", generatedFiles.size(), quality);
	}
	
	/**
	 * 使用示例:完整配置转换
	 *
	 * @param pdfFilePath PDF文件路径
	 */
	public static void fullConfigurationExample(String pdfFilePath) {
		// 检查WebP支持
		boolean useWebp = WebpConverter.isWebpSupported();
		if (!useWebp) {
			log.warn("WebP格式不受支持,将使用PNG格式替代");
		}
		
		PdfImageConverter converter = new PdfImageConverter();
		
		// 设置自定义输出路径 (使用与PDF相同的文件名创建一个子目录)
		File pdfFile = new File(pdfFilePath);
		String outputPath = pdfFile.getParent() + File.separator + pdfFile.getName().replaceAll("\\.pdf$", "") + "_output";
		
		// 使用完整配置转换
		List<String> generatedFiles = converter.convertPdfToImages(pdfFilePath, 300f,       // 自定义DPI为300
				outputPath, // 自定义输出路径
				true,       // 启用压缩
				0.85f,      // 压缩质量 (85%)
				useWebp     // 根据支持情况决定是否转换为WebP
		);
		
		String format = useWebp ? "WebP" : "PNG";
		log.info("全配置模式下生成了{}个{}图片文件", generatedFiles.size(), format);
	}
	
	
	public static void main(String[] args) {
		String pdfFilePath = "D:\\work\\demo\\pdf\\2025\\06\\09\\SL2504240275-10536-订餐卷-(90×54)2盒双名片覆膜1K1M.pdf";
		File pdfFile = new File(pdfFilePath);
		
		// 运行几个示例
		try {
			log.info("示例1: 简单转换");
			simpleConversion(pdfFilePath);
			
			log.info("\n示例2: 自定义DPI和路径");
			customDpiAndPath(pdfFilePath, 300f, pdfFile.getParent() + File.separator + "custom_output");
			
			log.info("\n示例3: 压缩转换");
			compressedConversion(pdfFilePath, 0.7f);
			
			log.info("\n示例4: WebP转换");
			webpConversion(pdfFilePath, 0.85f);
			
			log.info("\n示例5: 完整配置示例");
			fullConfigurationExample(pdfFilePath);
			
		} catch (Exception e) {
			log.error("转换过程中发生错误", e);
		}
	}
}

注意:要使用pdfbox3.05版本,之前使用pdfbox3.0.3版本,3.0.3版本存在一个bug,会将pdf的透明部分渲染成黑色,导致生成的图片颜色与本来的pdf颜色相差比较大,特别是pdf是白色透明背景,生成的图片会变成黑色背景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值