JAVA导出pdf模版

XML 复制代码
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>html2pdf</artifactId>
            <version>5.0.0</version>
        </dependency>

            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itext7-core</artifactId>
                <version>8.0.0</version>
            </dependency>

word模版

java的ftl文件 也就是先写好html然后后缀改成 ftl 用freeMarker渲染

模版文件 在资源里边

java用代码熏染

java 复制代码
package com.mxbc.mos.utils;

import cn.hutool.core.io.resource.ResourceUtil;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.properties.TextAlignment;
import com.mxbc.mos.vo.CreateTaxFreeVo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;


@Slf4j
public class PDFTemplateUtil {
    public static void main(String[] args) throws Exception {
        ByteArrayOutputStream baos = null;
        try {
            CreateTaxFreeVo createTaxFreeVo = new CreateTaxFreeVo();
            createTaxFreeVo.setShopCode("门店编码");
            createTaxFreeVo.setShopName("门店名称");
            createTaxFreeVo.setCustomer("签约名称");
            createTaxFreeVo.setNaxNo("税号");
            createTaxFreeVo.setOrderDate("订单日期");
            createTaxFreeVo.setOrderNumber("订单号");
            createTaxFreeVo.setWarehouse("发货仓");
            createTaxFreeVo.setCurrency("币种");

            createTaxFreeVo.setTotalPrice("总价");
            createTaxFreeVo.setDiscountPrice("折扣额");
            createTaxFreeVo.setNoTaxTotalPrice("不含税总额");
            createTaxFreeVo.setTaxPrice("税额");
            createTaxFreeVo.setTaxTotalPrice("含税总价");

            List<CreateTaxFreeVo.MaterialDetail> materialDetails = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                CreateTaxFreeVo.MaterialDetail materialDetail = new CreateTaxFreeVo.MaterialDetail();
                materialDetail.setMaterialName("物料名称");
                materialDetail.setSpecification("规格");
                materialDetail.setMaterialCount("数量");
                materialDetail.setMaterialUnit("单位");
                materialDetail.setMaterialPrice("价格");
                materialDetail.setMaterialTotalPrice("总价");

                materialDetails.add(materialDetail);
            }
            createTaxFreeVo.setMaterialVos(materialDetails);


            PDFTemplateUtil.createPDF(createTaxFreeVo, "id_tax_free_order.ftl", Files.newOutputStream(Paths.get("C:\\Users\\yuanhui\\Desktop\\1.pdf")));
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("导出失败:" + e.getMessage());
        } finally {
            if (baos != null) {
                baos.close();
            }
        }
    }

    public static final String ENCODING = "UTF-8";

    public static void createPDF(Object data, String templateFileName, OutputStream outputStream) throws Exception {
        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(new Version(2, 3, 32));
        // 设置模板的编码格式
        cfg.setEncoding(Locale.CHINA, ENCODING);
        cfg.setClassForTemplateLoading(PDFTemplateUtil.class, "/template");
        // 获取模板文件
        Template template = cfg.getTemplate(templateFileName, ENCODING);
        StringWriter writer = new StringWriter();
        // 将数据输出到html中
        template.process(data, writer);
        writer.flush();
        String html = writer.toString();

        ConverterProperties converterProperties = new ConverterProperties();
        FontProvider fp = new FontProvider();
        fp.addStandardPdfFonts();
        //中文
        fp.addFont(IOUtils.toByteArray(ResourceUtil.getStream("template/font/STSong-Light.ttf")));
        //自定义字体
        fp.addFont(IOUtils.toByteArray(ResourceUtil.getStream("template/font/AlibabaSansThai-Rg.ttf")));
//            // .ttf 字体所在目录
//        fp.addDirectory(ResourceUtil.getResource("template/font").getPath());
        converterProperties.setFontProvider(fp);
        // html中使用的图片等资源目录(图片也可以直接用url或者base64格式而不放到资源里)
        //converterProperties.setBaseUri(TEMPLATE_PATH + File.separator + "img");

        PdfWriter pdfWriter = new PdfWriter(outputStream);
        PdfDocument pdfDocument = new PdfDocument(pdfWriter);
        pdfDocument.setDefaultPageSize(PageSize.A4);
        pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new PageMarker());
        HtmlConverter.convertToPdf(html, pdfDocument, converterProperties);
        //HtmlConverter.convertToPdf(html, pdfWriter);
    }

    @AllArgsConstructor
    public static class PageMarker implements IEventHandler {
        @Override
        public void handleEvent(Event event) {
            final PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
            final PdfDocument pdfDoc = docEvent.getDocument();
            final PdfPage page = docEvent.getPage();
            final Rectangle pageSize = page.getPageSize();
            final int pageNumber = pdfDoc.getPageNumber(page);

            final Document doc = new Document(pdfDoc);
            Paragraph p = new Paragraph(pageNumber + "/" + pdfDoc.getNumberOfPages());
            p.setFontSize(7f);
            p.setBold();
            p.setFontColor(new DeviceRgb(22, 28, 39));
            p.setFixedPosition(pageSize.getWidth() / 2 - 25, 20, 50);
            p.setTextAlignment(TextAlignment.CENTER);

            doc.add(p);
        }
    }
}

pdf合并

java 复制代码
package com.mxbc.mos.utils;

import cn.hutool.core.date.DateUtil;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.properties.AreaBreakType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;


public class PDFUtil {
    private static final Logger logger = LoggerFactory.getLogger(PDFUtil.class);

    /**
     * 本地测试用
     * 将给定List集合中的pdf文档,按照顺序依次合并,生成最终的目标PDF文档
     *
     * @param pdfPathLists 待合并的PDF文档路径集合,可以是本地PDF文档,也可以是网络上的PDF文档
     * @param destPath     目标合并生成的PDF文档路径
     */
    public static boolean mergeMultiplePdfs(List<String> pdfPathLists, String destPath) {
        try {
            int size = pdfPathLists.size();
            byte[] pdfData = getPdfBytes(pdfPathLists.get(0));
            for (int i = 1; i < size; i++) {
                pdfData = mergePdfBytes(pdfData, getPdfBytes(pdfPathLists.get(i)));
            }
            if (pdfData != null) {
                FileOutputStream fis = new FileOutputStream(destPath);
                fis.write(pdfData);
                fis.close();
            }
            return true;
        } catch (Exception e) {
            logger.error("合并PDF异常:", e);
        }
        return false;
    }

    /**
     * 多个pdf合并 可以传url
     *
     * @param pdfPathOrFileUrlLists 待合并的PDF文档路径集合,可以是本地PDF文档,也可以是网络上的PDF文档
     * @return 字节数组 方便上传阿里云等云资源
     */
    public static byte[] mergeMultiplePdfs(List<String> pdfPathOrFileUrlLists) {
        if (pdfPathOrFileUrlLists == null || pdfPathOrFileUrlLists.isEmpty()) {
            return null;
        }

        byte[] pdfData;
        try {
            int size = pdfPathOrFileUrlLists.size();
            pdfData = getPdfBytes(pdfPathOrFileUrlLists.get(0));
            for (int i = 1; i < size; i++) {
                pdfData = mergePdfBytes(pdfData, getPdfBytes(pdfPathOrFileUrlLists.get(i)));
            }
        } catch (Exception e) {
            logger.error("合并PDF异常:", e);
            return null; // 返回 null 以指示异常
        }
        return pdfData; // 返回合并后的PDF字节数组
    }


    /**
     * 将给定集合中的图片合并到一个pdf文档里面
     *
     * @param imagePathList 图片路径集合
     * @param destPath      合并之后的PDF文档
     */
    public static boolean mergeImagesToPdf(List<String> imagePathList, String destPath) {
        try {
            PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destPath));
            Document document = new Document(pdfDocument);
            if (imagePathList != null && imagePathList.size() > 0) {
                int size = imagePathList.size();
                for (int i = 0; i < size; i++) {
                    String imgPath = imagePathList.get(i);
                    ImageData imageData;
                    if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) {
                        imageData = ImageDataFactory.create(new URL(imgPath));
                    } else {
                        imageData = ImageDataFactory.create(imgPath);
                    }
                    Image image = new Image(imageData);
                    /*
                        设置旋转的弧度值,默认是逆时针旋转的。
                        弧度、角度换算公式:
                        1° = PI / 180°
                        1 rad = 180° / PI
                    */
                    image.setRotationAngle(-Math.PI / 2); // 顺时针旋转90°
                    // 设置图片自动缩放,即:图片宽高自适应
                    image.setAutoScale(true);
                    document.add(image);
                    if (i != size - 1) {
                        // 最后一页不需要新增空白页
                        document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
                    }
                }
            }
            pdfDocument.close();
            return true;
        } catch (Exception e) {
            logger.error("合并图片到PDF异常:", e);
        }
        return false;
    }

    /**
     * 基于内存中的字节数组进行PDF文档的合并
     *
     * @param firstPdf  第一个PDF文档
     * @param secondPdf 第二个PDF文档
     */
    private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) throws IOException {
        if (firstPdf == null || secondPdf == null) {
            return null;
        }

        // 创建字节数组,基于内存进行合并
        // 合并的pdf文件对象
        // 合并对象
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             PdfDocument destDoc = new PdfDocument(new PdfWriter(baos));
             PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));
             PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)))) {

            PdfMerger merger = new PdfMerger(destDoc);
            merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());
            merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());

            // 关闭文档流
            merger.close();
            return baos.toByteArray();
        } catch (IOException e) {
            throw new IOException("Error merging PDF documents", e);
        }
    }

    /**
     * 将pdf文档转换成字节数组
     *
     * @param pdf PDF文档路径
     * @return 返回对应PDF文档的字节数组
     */
    private static byte[] getPdfBytes(String pdf) throws Exception {
        if (StringUtils.isBlank(pdf)) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        InputStream is;
        if (pdf.startsWith("http://") || pdf.startsWith("https://")) {
            is = new URL(pdf).openStream();
        } else {
            is = new FileInputStream(pdf);
        }
        byte[] data = new byte[2048];
        int len;
        while ((len = is.read(data)) != -1) {
            out.write(data, 0, len);
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws FileNotFoundException {
        // 图片合并之后生成的PDF路径
        //String imagePath = "C:\\Users\\yuanhui\\Desktop\\marge.pdf";
        //List<String> imageList = new ArrayList<>();
        //imageList.add("https://mxpos-sg.oss-ap-southeast-1.aliyuncs.com/app/mos/order/invoice/booking/test/INV-230700003.pdf");
        //imageList.add("https://mxpos-sg.oss-ap-southeast-1.aliyuncs.com/app/mos/order/invoice/booking/test/INV-230700003.pdf");
        // 先合并图片
        //PDFUtil.mergeImagesToPdf(imageList, imagePath);

        long startTime = System.currentTimeMillis();
        System.out.println("pdf合并程序开始时间" + DateUtil.now());
        // 在合并PDF
        String destPath = "C:/Users/yuanhui/Desktop/marge.pdf";
        List<String> pdfPath = new ArrayList<>();
        pdfPath.add("https://mxpos-sg.oss-ap-southeast-1.aliyuncs.com/app/mos/order/invoice/booking/test/INV-230700003.pdf");
        PDFUtil.mergeMultiplePdfs(pdfPath, destPath);

        long endTime = System.currentTimeMillis();
        System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
        System.out.println("pdf程序合并结束时间:" + DateUtil.now());
    }
}

成品

相关推荐
希忘auto3 小时前
详解Redis的常用命令
redis·1024程序员节
yaosheng_VALVE19 小时前
探究全金属硬密封蝶阀的奥秘-耀圣控制
运维·eclipse·自动化·pyqt·1024程序员节
dami_king19 小时前
SSH特性|组成|SSH是什么?
运维·ssh·1024程序员节
一个通信老学姐6 天前
专业125+总分400+南京理工大学818考研经验南理工电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节
sheng12345678rui6 天前
mfc140.dll文件缺失的修复方法分享,全面分析mfc140.dll的几种解决方法
游戏·电脑·dll文件·dll修复工具·1024程序员节
huipeng9267 天前
第十章 类和对象(二)
java·开发语言·学习·1024程序员节
earthzhang20217 天前
《深入浅出HTTPS》读书笔记(19):密钥
开发语言·网络协议·算法·https·1024程序员节
爱吃生蚝的于勒8 天前
计算机基础 原码反码补码问题
经验分享·笔记·计算机网络·其他·1024程序员节
earthzhang20218 天前
《深入浅出HTTPS》读书笔记(20):口令和PEB算法
开发语言·网络协议·算法·https·1024程序员节
一个通信老学姐8 天前
专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节