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());
    }
}

成品

相关推荐
学传打活4 小时前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活5 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16813 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心15 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552721 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552722 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝23 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552723 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552724 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节