前言
由于医疗报销需要,必须大电子票夹(微信小程序)中的发票导出来,打印为纸质版提供给保险公司。目前总结出来最简单的做法是,将需要打印的发票,选中发送到电子邮件中。收到的是一个压缩包,内容为每张发票的pdf文件。要打印这些太麻烦了,还浪费纸。
方案
将压缩包解压,然后将解压出来的pdf中发票内容提取为图片,将图片3张合成一张,最后将所有合成的图片生成pdf打印。以下程序,最后输出的是一个pdf文件。打印的时候需要注意的是,缩放选项,选择【适合纸张大小】
环境
JDK8
itextpdf:5.5.13.3
pdfbox:2.0.17
poi-ooxml:5.2.3
hutool-all:5.8.11
源码
java
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.io.FileUtils;
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 org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* @author dzh
* @since 2025/1/3
*/
public class PdfImageUtils {
// 将压缩包中pdf的发票内容提取出来,然后3张为一页,拼接为pdf,方便打印
public static void main(String[] args) {
String filePath = "C:/temp/abc.zip";
File unzipPath = new File(filePath + "_unzip");
File imageFile = new File(filePath + "_img");
File mergeFile = new File(filePath + "_merge");
// 解压
File file = ZipUtil.unzip(new File(filePath), unzipPath);
System.out.println(file.getAbsolutePath());
// 转换为图片
dealFiles(file, (f) -> {
toImage(f.getAbsolutePath(), imageFile.getAbsolutePath() + "/" + f.getName() + ".png");
});
FileUtil.del(unzipPath);
// 图片3张合并一张
imageMerge(imageFile, mergeFile, 3);
FileUtil.del(imageFile);
imageToPdf(filePath + ".pdf", mergeFile);
FileUtil.del(mergeFile);
}
public static void imageToPdf(String pdfPath, File imageBaseDir) {
try (PDDocument document = new PDDocument()) {
File[] files = imageBaseDir.listFiles();
if (files == null) {
return;
}
for (File file : files) {
PDImageXObject pdImage = PDImageXObject.createFromFileByExtension(file, document);
PDRectangle mediaBox = new PDRectangle(pdImage.getWidth(), pdImage.getHeight());
PDPage page = new PDPage(mediaBox);
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
// 将图片绘制到PDF页面上
contentStream.drawImage(pdImage, 0, 0);
contentStream.close();
}
document.save(pdfPath);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void dealFiles(File file, Consumer<File> consumer) {
if (file.isDirectory()) {
for (File f : Objects.requireNonNull(file.listFiles())) {
dealFiles(f, consumer);
}
} else {
consumer.accept(file);
}
}
public static void toImage(String pdfFilePath, String outputImagePath) {
try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {
if (!document.isEncrypted()) {
PDFRenderer pdfRenderer = new PDFRenderer(document);
// 获取第一页的图片
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 300, ImageType.RGB);
// 保存图片
File output = new File(outputImagePath);
FileUtils.createParentDirectories(output);
ImageIO.write(bufferedImage, "PNG", output);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void imageMerge(File dir, File mergeFile, int size) {
File[] files = dir.listFiles();
if (files == null) {
return;
}
List<File> fileList = Arrays.stream(files).collect(Collectors.toList());
imageMerge(fileList, mergeFile, size);
}
private static void imageMerge(List<File> files, File mergeFile, int size) {
File baseFile = files.get(0).getParentFile();
String basePath = baseFile.getAbsolutePath();
files.sort((a, b) -> b.getName().compareTo(a.getName()));
List<List<File>> partition = ListUtils.partition(files, size);
String imageDic = mergeFile.getAbsolutePath() + "/";
for (int i = 0; i < partition.size(); i++) {
File imageFile = new File(imageDic + i + ".png");
try {
FileUtils.createParentDirectories(imageFile);
} catch (Exception e) {
// ignore
}
imageMerge(partition.get(i), imageFile.getAbsolutePath());
}
}
private static void imageMerge(List<File> files, String target) {
try {
BufferedImage[] images = new BufferedImage[files.size()];
for (int i = 0; i < files.size(); i++) {
images[i] = ImageIO.read(files.get(i));
}
int width = images[0].getWidth();
int height = Arrays.stream(images).mapToInt(BufferedImage::getHeight).sum();
// 创建一个新的图片,宽度是两张图片的宽度之和,高度取最大值
BufferedImage concatenatedImage = new BufferedImage(
width,
height,
BufferedImage.TYPE_INT_RGB);
// 画图
Graphics2D g2d = concatenatedImage.createGraphics();
int startY = 0;
for (BufferedImage image : images) {
g2d.drawImage(image, 0, startY, null);
startY += image.getHeight();
g2d.drawLine(0, startY, image.getWidth(), startY += 3);
}
g2d.dispose();
// 保存拼接后的图片
File output = new File(target);
FileUtils.createParentDirectories(output);
ImageIO.write(concatenatedImage, "png", output);
} catch (IOException e) {
e.printStackTrace();
}
}
}