发票打印更方便

前言

由于医疗报销需要,必须大电子票夹(微信小程序)中的发票导出来,打印为纸质版提供给保险公司。目前总结出来最简单的做法是,将需要打印的发票,选中发送到电子邮件中。收到的是一个压缩包,内容为每张发票的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();
        }
    }
}
相关推荐
Atlim5 分钟前
maven多模块项目编译一直报Failure to find com.xxx.xxx:xxx-xxx-xxx:pom:1.0-SNAPSHOT in问题
java·开发语言·maven
敲代码养活全家5 分钟前
基于Elasticsearch8的向量检索实现相似图形搜索
java·elasticsearch
咔咔库奇12 分钟前
HarmonyOS开发:传参方式
java·华为·harmonyos
ss27316 分钟前
基于SpringBoot实现的保障性住房管理系统
java·spring boot·后端
ccmjga19 分钟前
升级 Spring Boot 3 配置讲解 — JDK 23 会给 SpringBoot 带来什么特性?
java·spring boot·后端·spring·gradle·spring security
福大大架构师每日一题22 分钟前
42.2 告警触发trigger模块单点问题和高可用解决方案
java·linux·服务器·prometheus
Thomas灬Wade41 分钟前
jdk8升级JDK21(Springboot2.7.18升级Springboot3.4.0)
java·spring boot
武汉万象奥科43 分钟前
Linux文件系统的安全保障---Overlayroot!
java·服务器·前端
rgrgrwfe1 小时前
【Tomcat】
java·tomcat
易雪寒1 小时前
Java大厂面试题之10种分布式ID的生成方案
java·开发语言·分布式