|
@@ -0,0 +1,682 @@
|
|
|
|
|
+package shop.alien.util.pdf;
|
|
|
|
|
+
|
|
|
|
|
+import org.apache.pdfbox.pdmodel.PDDocument;
|
|
|
|
|
+import org.apache.pdfbox.pdmodel.PDPage;
|
|
|
|
|
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
|
|
|
|
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
|
|
|
|
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
|
|
|
|
+
|
|
|
|
|
+import javax.imageio.ImageIO;
|
|
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
|
|
+import java.io.*;
|
|
|
|
|
+import java.net.HttpURLConnection;
|
|
|
|
|
+import java.net.URL;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 图片转PDF工具类
|
|
|
|
|
+ * 支持单张图片转PDF、多张图片合并为一个PDF
|
|
|
|
|
+ * 支持从本地文件、URL、字节数组转换
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author system
|
|
|
|
|
+ * @since 2026-01-05
|
|
|
|
|
+ */
|
|
|
|
|
+public class ImageToPdfUtil {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将单张图片文件转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imagePath 图片文件路径
|
|
|
|
|
+ * @param pdfPath PDF文件保存路径
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageToPdf(String imagePath, String pdfPath) {
|
|
|
|
|
+ return imageToPdf(new File(imagePath), new File(pdfPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将单张图片文件转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageFile 图片文件
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageToPdf(File imageFile, File pdfFile) {
|
|
|
|
|
+ if (imageFile == null || !imageFile.exists()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片文件不存在: " + (imageFile != null ? imageFile.getPath() : "null"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (InputStream imageStream = new FileInputStream(imageFile)) {
|
|
|
|
|
+ return imageToPdf(imageStream, pdfFile);
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("读取图片文件失败: " + imageFile.getPath(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片输入流转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageStream 图片输入流
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageToPdf(InputStream imageStream, File pdfFile) {
|
|
|
|
|
+ if (imageStream == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片输入流不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (pdfFile == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("PDF文件不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保PDF文件目录存在
|
|
|
|
|
+ File parentDir = pdfFile.getParentFile();
|
|
|
|
|
+ if (parentDir != null && !parentDir.exists()) {
|
|
|
|
|
+ parentDir.mkdirs();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument()) {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("无法读取图片,可能不是有效的图片格式");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建PDF页面,使用图片的尺寸
|
|
|
|
|
+ PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(), bufferedImage.getHeight()));
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.save(pdfFile);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片URL转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageUrl 图片URL
|
|
|
|
|
+ * @param pdfPath PDF文件保存路径
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageUrlToPdf(String imageUrl, String pdfPath) {
|
|
|
|
|
+ return imageUrlToPdf(imageUrl, new File(pdfPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片URL转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageUrl 图片URL
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageUrlToPdf(String imageUrl, File pdfFile) {
|
|
|
|
|
+ if (imageUrl == null || imageUrl.trim().isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片URL不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ HttpURLConnection connection = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ URL url = new URL(imageUrl);
|
|
|
|
|
+ connection = (HttpURLConnection) url.openConnection();
|
|
|
|
|
+ connection.setConnectTimeout(10000); // 10秒连接超时
|
|
|
|
|
+ connection.setReadTimeout(30000); // 30秒读取超时
|
|
|
|
|
+ connection.setRequestMethod("GET");
|
|
|
|
|
+ connection.setRequestProperty("User-Agent", "Mozilla/5.0");
|
|
|
|
|
+
|
|
|
|
|
+ InputStream imageStream = connection.getInputStream();
|
|
|
|
|
+ try {
|
|
|
|
|
+ return imageToPdf(imageStream, pdfFile);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (imageStream != null) {
|
|
|
|
|
+ imageStream.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("从URL下载图片失败: " + imageUrl, e);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (connection != null) {
|
|
|
|
|
+ connection.disconnect();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片字节数组转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageBytes 图片字节数组
|
|
|
|
|
+ * @param pdfPath PDF文件保存路径
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageBytesToPdf(byte[] imageBytes, String pdfPath) {
|
|
|
|
|
+ return imageBytesToPdf(imageBytes, new File(pdfPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片字节数组转换为PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageBytes 图片字节数组
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageBytesToPdf(byte[] imageBytes, File pdfFile) {
|
|
|
|
|
+ if (imageBytes == null || imageBytes.length == 0) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片字节数组不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ByteArrayInputStream imageStream = new ByteArrayInputStream(imageBytes);
|
|
|
|
|
+ try {
|
|
|
|
|
+ return imageToPdf(imageStream, pdfFile);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ try {
|
|
|
|
|
+ imageStream.close();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ // ByteArrayInputStream的close()实际上不会抛出异常,但为了编译通过需要捕获
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片合并为一个PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imagePaths 图片文件路径列表
|
|
|
|
|
+ * @param pdfPath PDF文件保存路径
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imagesToPdf(List<String> imagePaths, String pdfPath) {
|
|
|
|
|
+ return imagesToPdfFromPaths(imagePaths, new File(pdfPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片合并为一个PDF(从文件路径)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imagePaths 图片文件路径列表
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imagesToPdfFromPaths(List<String> imagePaths, File pdfFile) {
|
|
|
|
|
+ if (imagePaths == null || imagePaths.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片路径列表不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<File> imageFiles = new ArrayList<>();
|
|
|
|
|
+ for (String imagePath : imagePaths) {
|
|
|
|
|
+ imageFiles.add(new File(imagePath));
|
|
|
|
|
+ }
|
|
|
|
|
+ return imagesToPdfFromFiles(imageFiles, pdfFile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片合并为一个PDF(从文件对象)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageFiles 图片文件列表
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imagesToPdfFromFiles(List<File> imageFiles, File pdfFile) {
|
|
|
|
|
+ if (imageFiles == null || imageFiles.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片文件列表不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (pdfFile == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("PDF文件不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保PDF文件目录存在
|
|
|
|
|
+ File parentDir = pdfFile.getParentFile();
|
|
|
|
|
+ if (parentDir != null && !parentDir.exists()) {
|
|
|
|
|
+ parentDir.mkdirs();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument()) {
|
|
|
|
|
+ for (File imageFile : imageFiles) {
|
|
|
|
|
+ if (imageFile == null || !imageFile.exists()) {
|
|
|
|
|
+ continue; // 跳过不存在的文件
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (FileInputStream imageStream = new FileInputStream(imageFile)) {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ continue; // 跳过无法读取的图片
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建PDF页面,使用图片的尺寸
|
|
|
|
|
+ PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(), bufferedImage.getHeight()));
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ // 记录错误但继续处理其他图片
|
|
|
|
|
+ System.err.println("处理图片失败: " + imageFile.getPath() + ", 错误: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (document.getNumberOfPages() == 0) {
|
|
|
|
|
+ throw new RuntimeException("没有成功添加任何图片到PDF");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.save(pdfFile);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("多张图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片URL合并为一个PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageUrls 图片URL列表
|
|
|
|
|
+ * @param pdfPath PDF文件保存路径
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageUrlsToPdf(List<String> imageUrls, String pdfPath) {
|
|
|
|
|
+ return imageUrlsToPdf(imageUrls, new File(pdfPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片URL合并为一个PDF
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageUrls 图片URL列表
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageUrlsToPdf(List<String> imageUrls, File pdfFile) {
|
|
|
|
|
+ if (imageUrls == null || imageUrls.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片URL列表不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保PDF文件目录存在
|
|
|
|
|
+ File parentDir = pdfFile.getParentFile();
|
|
|
|
|
+ if (parentDir != null && !parentDir.exists()) {
|
|
|
|
|
+ parentDir.mkdirs();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument()) {
|
|
|
|
|
+ for (String imageUrl : imageUrls) {
|
|
|
|
|
+ if (imageUrl == null || imageUrl.trim().isEmpty()) {
|
|
|
|
|
+ continue; // 跳过空的URL
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ URL url = new URL(imageUrl);
|
|
|
|
|
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
|
|
|
+ connection.setConnectTimeout(10000);
|
|
|
|
|
+ connection.setReadTimeout(30000);
|
|
|
|
|
+ connection.setRequestMethod("GET");
|
|
|
|
|
+ connection.setRequestProperty("User-Agent", "Mozilla/5.0");
|
|
|
|
|
+
|
|
|
|
|
+ InputStream imageStream = connection.getInputStream();
|
|
|
|
|
+ try {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ continue; // 跳过无法读取的图片
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建PDF页面,使用图片的尺寸
|
|
|
|
|
+ PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(), bufferedImage.getHeight()));
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (imageStream != null) {
|
|
|
|
|
+ imageStream.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ // 记录错误但继续处理其他图片
|
|
|
|
|
+ System.err.println("处理图片URL失败: " + imageUrl + ", 错误: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (document.getNumberOfPages() == 0) {
|
|
|
|
|
+ throw new RuntimeException("没有成功添加任何图片到PDF");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.save(pdfFile);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("多张图片URL转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片转换为字节数组
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param bufferedImage 图片对象
|
|
|
|
|
+ * @return 图片字节数组
|
|
|
|
|
+ */
|
|
|
|
|
+ private static byte[] imageToByteArray(BufferedImage bufferedImage) throws IOException {
|
|
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
|
+ String format = "png"; // 默认使用PNG格式
|
|
|
|
|
+ ImageIO.write(bufferedImage, format, baos);
|
|
|
|
|
+ return baos.toByteArray();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片输入流转换为PDF字节数组(内存方式,不保存文件)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageStream 图片输入流
|
|
|
|
|
+ * @return PDF字节数组
|
|
|
|
|
+ */
|
|
|
|
|
+ public static byte[] imageToPdfBytes(InputStream imageStream) {
|
|
|
|
|
+ if (imageStream == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片输入流不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument();
|
|
|
|
|
+ ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream()) {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("无法读取图片,可能不是有效的图片格式");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建PDF页面,使用图片的尺寸
|
|
|
|
|
+ PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(), bufferedImage.getHeight()));
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将PDF保存到字节数组
|
|
|
|
|
+ document.save(pdfOutputStream);
|
|
|
|
|
+ return pdfOutputStream.toByteArray();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将图片输入流转换为PDF字节数组(A4页面大小,内存方式)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageStream 图片输入流
|
|
|
|
|
+ * @return PDF字节数组
|
|
|
|
|
+ */
|
|
|
|
|
+ public static byte[] imageToPdfA4Bytes(InputStream imageStream) {
|
|
|
|
|
+ if (imageStream == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片输入流不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument();
|
|
|
|
|
+ ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream()) {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("无法读取图片,可能不是有效的图片格式");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建A4页面
|
|
|
|
|
+ PDPage page = new PDPage(PDRectangle.A4);
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算图片在A4页面上的尺寸(保持宽高比)
|
|
|
|
|
+ float pageWidth = PDRectangle.A4.getWidth();
|
|
|
|
|
+ float pageHeight = PDRectangle.A4.getHeight();
|
|
|
|
|
+ float imageWidth = bufferedImage.getWidth();
|
|
|
|
|
+ float imageHeight = bufferedImage.getHeight();
|
|
|
|
|
+
|
|
|
|
|
+ // 计算缩放比例,使图片适应A4页面
|
|
|
|
|
+ float scaleX = pageWidth / imageWidth;
|
|
|
|
|
+ float scaleY = pageHeight / imageHeight;
|
|
|
|
|
+ float scale = Math.min(scaleX, scaleY);
|
|
|
|
|
+
|
|
|
|
|
+ float scaledWidth = imageWidth * scale;
|
|
|
|
|
+ float scaledHeight = imageHeight * scale;
|
|
|
|
|
+
|
|
|
|
|
+ // 居中显示
|
|
|
|
|
+ float x = (pageWidth - scaledWidth) / 2;
|
|
|
|
|
+ float y = (pageHeight - scaledHeight) / 2;
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, x, y, scaledWidth, scaledHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将PDF保存到字节数组
|
|
|
|
|
+ document.save(pdfOutputStream);
|
|
|
|
|
+ return pdfOutputStream.toByteArray();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将单张图片转换为PDF(A4页面大小,图片自适应)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imagePath 图片文件路径
|
|
|
|
|
+ * @param pdfPath PDF文件保存路径
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageToPdfA4(String imagePath, String pdfPath) {
|
|
|
|
|
+ return imageToPdfA4(new File(imagePath), new File(pdfPath));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将单张图片转换为PDF(A4页面大小,图片自适应)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageFile 图片文件
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imageToPdfA4(File imageFile, File pdfFile) {
|
|
|
|
|
+ if (imageFile == null || !imageFile.exists()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片文件不存在: " + (imageFile != null ? imageFile.getPath() : "null"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保PDF文件目录存在
|
|
|
|
|
+ File parentDir = pdfFile.getParentFile();
|
|
|
|
|
+ if (parentDir != null && !parentDir.exists()) {
|
|
|
|
|
+ parentDir.mkdirs();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument()) {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageFile);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("无法读取图片,可能不是有效的图片格式");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建A4页面
|
|
|
|
|
+ PDPage page = new PDPage(PDRectangle.A4);
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算图片在A4页面上的尺寸(保持宽高比)
|
|
|
|
|
+ float pageWidth = PDRectangle.A4.getWidth();
|
|
|
|
|
+ float pageHeight = PDRectangle.A4.getHeight();
|
|
|
|
|
+ float imageWidth = bufferedImage.getWidth();
|
|
|
|
|
+ float imageHeight = bufferedImage.getHeight();
|
|
|
|
|
+
|
|
|
|
|
+ // 计算缩放比例,使图片适应A4页面
|
|
|
|
|
+ float scaleX = pageWidth / imageWidth;
|
|
|
|
|
+ float scaleY = pageHeight / imageHeight;
|
|
|
|
|
+ float scale = Math.min(scaleX, scaleY);
|
|
|
|
|
+
|
|
|
|
|
+ float scaledWidth = imageWidth * scale;
|
|
|
|
|
+ float scaledHeight = imageHeight * scale;
|
|
|
|
|
+
|
|
|
|
|
+ // 居中显示
|
|
|
|
|
+ float x = (pageWidth - scaledWidth) / 2;
|
|
|
|
|
+ float y = (pageHeight - scaledHeight) / 2;
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, x, y, scaledWidth, scaledHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.save(pdfFile);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片合并为一个PDF(A4页面大小,每张图片一页,图片自适应)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageFiles 图片文件列表
|
|
|
|
|
+ * @param pdfFile PDF文件
|
|
|
|
|
+ * @return 是否转换成功
|
|
|
|
|
+ */
|
|
|
|
|
+ public static boolean imagesToPdfA4(List<File> imageFiles, File pdfFile) {
|
|
|
|
|
+ if (imageFiles == null || imageFiles.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片文件列表不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (pdfFile == null) {
|
|
|
|
|
+ throw new IllegalArgumentException("PDF文件不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保PDF文件目录存在
|
|
|
|
|
+ File parentDir = pdfFile.getParentFile();
|
|
|
|
|
+ if (parentDir != null && !parentDir.exists()) {
|
|
|
|
|
+ parentDir.mkdirs();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument()) {
|
|
|
|
|
+ float pageWidth = PDRectangle.A4.getWidth();
|
|
|
|
|
+ float pageHeight = PDRectangle.A4.getHeight();
|
|
|
|
|
+
|
|
|
|
|
+ for (File imageFile : imageFiles) {
|
|
|
|
|
+ if (imageFile == null || !imageFile.exists()) {
|
|
|
|
|
+ continue; // 跳过不存在的文件
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (FileInputStream imageStream = new FileInputStream(imageFile)) {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ continue; // 跳过无法读取的图片
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建A4页面
|
|
|
|
|
+ PDPage page = new PDPage(PDRectangle.A4);
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 计算图片在A4页面上的尺寸(保持宽高比)
|
|
|
|
|
+ float imageWidth = bufferedImage.getWidth();
|
|
|
|
|
+ float imageHeight = bufferedImage.getHeight();
|
|
|
|
|
+
|
|
|
|
|
+ // 计算缩放比例,使图片适应A4页面
|
|
|
|
|
+ float scaleX = pageWidth / imageWidth;
|
|
|
|
|
+ float scaleY = pageHeight / imageHeight;
|
|
|
|
|
+ float scale = Math.min(scaleX, scaleY);
|
|
|
|
|
+
|
|
|
|
|
+ float scaledWidth = imageWidth * scale;
|
|
|
|
|
+ float scaledHeight = imageHeight * scale;
|
|
|
|
|
+
|
|
|
|
|
+ // 居中显示
|
|
|
|
|
+ float x = (pageWidth - scaledWidth) / 2;
|
|
|
|
|
+ float y = (pageHeight - scaledHeight) / 2;
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, x, y, scaledWidth, scaledHeight);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ // 记录错误但继续处理其他图片
|
|
|
|
|
+ System.err.println("处理图片失败: " + imageFile.getPath() + ", 错误: " + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (document.getNumberOfPages() == 0) {
|
|
|
|
|
+ throw new RuntimeException("没有成功添加任何图片到PDF");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ document.save(pdfFile);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("多张图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将多张图片输入流合并为一个PDF字节数组(内存方式,不保存文件)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imageStreams 图片输入流列表
|
|
|
|
|
+ * @return PDF字节数组
|
|
|
|
|
+ */
|
|
|
|
|
+ public static byte[] imagesToPdfBytes(List<InputStream> imageStreams) {
|
|
|
|
|
+ if (imageStreams == null || imageStreams.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException("图片输入流列表不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try (PDDocument document = new PDDocument();
|
|
|
|
|
+ ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream()) {
|
|
|
|
|
+
|
|
|
|
|
+ for (InputStream imageStream : imageStreams) {
|
|
|
|
|
+ if (imageStream == null) {
|
|
|
|
|
+ continue; // 跳过空的输入流
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ BufferedImage bufferedImage = ImageIO.read(imageStream);
|
|
|
|
|
+ if (bufferedImage == null) {
|
|
|
|
|
+ continue; // 跳过无法读取的图片
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建PDF页面,使用图片的尺寸
|
|
|
|
|
+ PDPage page = new PDPage(new PDRectangle(bufferedImage.getWidth(), bufferedImage.getHeight()));
|
|
|
|
|
+ document.addPage(page);
|
|
|
|
|
+
|
|
|
|
|
+ // 将图片添加到PDF页面
|
|
|
|
|
+ PDImageXObject pdImage = PDImageXObject.createFromByteArray(
|
|
|
|
|
+ document, imageToByteArray(bufferedImage), "image");
|
|
|
|
|
+
|
|
|
|
|
+ try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
|
|
|
|
+ contentStream.drawImage(pdImage, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ // 记录错误但继续处理其他图片
|
|
|
|
|
+ System.err.println("处理图片输入流失败: " + e.getMessage());
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ // 关闭输入流
|
|
|
|
|
+ if (imageStream != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ imageStream.close();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ // 忽略关闭异常
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (document.getNumberOfPages() == 0) {
|
|
|
|
|
+ throw new RuntimeException("没有成功添加任何图片到PDF");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将PDF保存到字节数组
|
|
|
|
|
+ document.save(pdfOutputStream);
|
|
|
|
|
+ return pdfOutputStream.toByteArray();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ throw new RuntimeException("多张图片转PDF失败", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|