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是白色透明背景,生成的图片会变成黑色背景。