欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程

 更新時間:2024年09月03日 09:20:18   作者:長不大搬不完磚  
由于最近開發(fā)的項目需要用到打印單據(jù),就在網(wǎng)上找了一下方案,反反復(fù)復(fù),都沒有找到合適的,借鑒了網(wǎng)上資源,使用itext5、itext7的工具包,所以本文介紹了SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程,需要的朋友可以參考下

前言

由于最近開發(fā)的項目需要用到打印單據(jù),就在網(wǎng)上找了一下方案,反反復(fù)復(fù),都沒有找到合適的,借鑒了網(wǎng)上資源,使用itext5、itext7的工具包,自己寫了一個通用的pdf表單填充工具,簡單易用,支持單頁、分頁單據(jù)打印,當(dāng)然還有更多的定制化,需要大家自行擴展。

一、設(shè)計表單

1.使用excel設(shè)計了一個pdf單據(jù),因為比較整齊設(shè)計方便,如下圖:

在這里插入圖片描述

2.使用免費工具PDFgear將excel文件轉(zhuǎn)為pdf文件,如下圖:

在這里插入圖片描述

3.使用免費Adobe Acrobat DC工具編輯pdf文件

打開轉(zhuǎn)換好的pdf文件,選擇工具欄右邊的編輯表單,然后拖動頂部欄的表單域設(shè)計屬性,屬性值和和代碼填充的要一致,字體下拉最后面的,不然你打印出來的單據(jù)字體會很難看,當(dāng)然也可以在程序代碼里去設(shè)計字體,一般設(shè)計好就夠用了,如果沒有特定要求的話。

ps:有些可能編輯表單,使用默認(rèn)的,有時候可能顯示不了數(shù)據(jù),這個把這個表單域刪除了,重新添加一個即可。

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

OK,表單設(shè)計好,下面就是編碼啦

二、項目搭建

我這里是使用阿里云官方提供快速創(chuàng)建腳手架的在線網(wǎng)站生成了一個springboot項目,項目結(jié)構(gòu)圖如下:

在這里插入圖片描述

三、引入依賴

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13</version>
</dependency>

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.5</version>
    <type>pom</type>
</dependency>

四、編寫工具類PdfItext7Util代碼

package com.example.pdfdemo.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.example.pdfdemo.form.PdfFormTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
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 lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>pdfItext7工具類</p>
 *
 * @author aliqingge
 * @date 2024/8/30 14:57
 */
@Slf4j
public class PdfItext7Util {

    /**
     * 每頁顯示數(shù),可自行修改
     */
    public static final int PAGE_SIZE = 12;

    /**
     * 臨時文件路徑
     */
    public final static String FOLDER = "/tempPdfFile";

    /**
     * 文件路徑
     */
    public final static String FOLDER_PATH = FOLDER + "/";


    /**
     * 填充表單
     *
     * @param template 模版對象
     * @return 填充表單文件路徑
     */
    public static String fillTemplate(PdfFormTemplate template) {
        Assert.notNull(template, "模版對象不能為空");

        // 創(chuàng)建臨時文件夾
        File file = new File(FOLDER);
        if (!file.exists() && !file.isDirectory()) {
            file.setWritable(true, false);
            file.mkdirs();
        }

        // 合并后生成文件的目標(biāo)路徑
        String destPdfPath = StringUtils.join(
                template.isGenerateToLocalFileFlag() ? template.getLocalFileStoragePath() : FOLDER_PATH,
                System.currentTimeMillis(),
                template.getTemplateFileName(),
                ".pdf"
        );

        // 獲取pdf字節(jié)數(shù)組列表
        List<byte[]> pdfByteList = getPdfByteList(template);
        // 合并文件
        mergePdf(pdfByteList, destPdfPath);

        return destPdfPath;
    }


    /**
     * 填充表單,并返回字節(jié)數(shù)組
     *
     * @param template 模版對象
     * @return 填充表單文件路徑
     */
    public static byte[] fillAndGetPdfByte(PdfFormTemplate template) {
        Assert.notNull(template, "模版對象不能為空");
        return mergePdf(getPdfByteList(template));
    }

    /**
     * 獲取pdf字節(jié)數(shù)組列表
     *
     * @param template
     * @return
     */
    private static List<byte[]> getPdfByteList(PdfFormTemplate template) {
        List<byte[]> pdfByteList = Lists.newArrayList();
        // 數(shù)據(jù)分頁
        List<List<Map<String, String>>> pageList = Lists.partition(template.getPageDataList(), PAGE_SIZE);
        int pageTotal = pageList.size();
        for (int i = 0; i < pageList.size(); i++) {
            // 生成每頁pdf的字節(jié)數(shù)組
            byte[] currentPagePdf = createCurrentPagePdf(template, pageList.get(i), (i + 1), pageTotal);
            pdfByteList.add(currentPagePdf);
        }
        return pdfByteList;
    }

    /**
     * 生成當(dāng)前頁pdf字節(jié)數(shù)組
     *
     * @param template
     * @param currentPageDataList
     * @param currentPageNo
     * @param pageTotal
     * @return
     */
    private static byte[] createCurrentPagePdf(PdfFormTemplate template, List<Map<String, String>> currentPageDataList, int currentPageNo, int pageTotal) {
        PdfDocument pdfDoc = null;
        try {
            // 輸出流用于存儲填充好數(shù)據(jù)的PDF文件
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 打開PDF模板文件
            PdfReader reader = new PdfReader(template.getTemplatePath());
            // 創(chuàng)建PDF寫入器
            PdfWriter writer = new PdfWriter(bos);
            // 創(chuàng)建PDF文檔對象,連接讀取器和寫入器
            pdfDoc = new PdfDocument(reader, writer);
            // 設(shè)置默認(rèn)頁面大小為A4
            pdfDoc.setDefaultPageSize(PageSize.A4);
            // 獲取PDF表單
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
            // 設(shè)置頁碼
            PdfFormField formField = form.getField(template.getCustomPageNoFiledName());
            if (formField != null) {
                formField.setValue(StringUtils.join(currentPageNo, "/", pageTotal));
            }

            Map<String, String> baseDataMap = template.getBaseDataMap();
            if (CollUtil.isNotEmpty(baseDataMap)) {
                // 填充基本信息
                for (String dataKey : baseDataMap.keySet()) {
                    PdfFormField field = form.getField(dataKey);
                    if (field != null) {
                        field.setValue(baseDataMap.get(dataKey));
                    }
                }
            }

            if (CollUtil.isNotEmpty(currentPageDataList)) {
                int i = 1;
                // 填充分頁數(shù)據(jù)
                for (Map<String, String> currentPageDataMap : currentPageDataList) {
                    // 填充序號, 可以自定義屬性,從外部傳入
                    PdfFormField noFormField = form.getField(StringUtils.join("no", i));
                    if (noFormField != null) {
                        noFormField.setValue(String.valueOf((currentPageNo - 1) * PAGE_SIZE + i));
                    }

                    // 填充其他數(shù)據(jù)
                    Set<Map.Entry<String, String>> entries = currentPageDataMap.entrySet();
                    int finalNo = i;
                    entries.forEach(entry -> {
                        PdfFormField field = form.getField(StringUtils.join(entry.getKey(), finalNo));
                        if (field != null) {
                            field.setValue(entry.getValue());
                        }
                    });
                    i++;
                }
            }

            // 設(shè)置表單不可編輯
            form.flattenFields();

            // 可以自定義字體
                /*PdfFont pdfFont = loadPdfFont();
                if (pdfFont != null) {
                    // 給表單域字段設(shè)置字體和大小
                    Map<String, PdfFormField> allFormFields = form.getFormFields();
                    for (String formFiled : allFormFields.keySet()) {
                        if (template.getFontSize() > 0) {
                            allFormFields.get(formFiled).setFontAndSize(pdfFont, template.getFontSize());
                        } else {
                            allFormFields.get(formFiled).setFont(pdfFont);
                        }
                    }
                }*/

            pdfDoc.close();

            return bos.toByteArray();
        } catch (IOException e) {
            log.error("createCurrentPagePdf 填充PDF表單失敗", e);
            throw new RuntimeException(e);
        } finally {
            if (pdfDoc != null) {
                pdfDoc.close();
            }
        }
    }

    /**
     * 合并pdf文件
     *
     * @param pdfByteList 待合并PDF文檔字節(jié)集合
     * @param destPath 生成合并PDF目標(biāo)文檔路徑
     */
    public static void mergePdf(List<byte[]> pdfByteList, String destPath) {
        try {
            int size = pdfByteList.size();
            byte[] pdfData = pdfByteList.get(0);
            for (int i = 1; i < size; i++) {
                pdfData = mergePdfBytes(pdfData, pdfByteList.get(i));
            }

            if (pdfData != null) {
                FileOutputStream fis = new FileOutputStream(destPath);
                fis.write(pdfData);
                fis.close();
            }

        } catch (Exception e) {
            log.error("mergePdf 合并PDF異常", e);
            throw new RuntimeException("合并PDF異常");
        }
    }


    /**
     * 合并pdf文件
     *
     * @param pdfByteList 待合并PDF文檔字節(jié)集合
     */
    public static byte[] mergePdf(List<byte[]> pdfByteList) {
        try {
            int size = pdfByteList.size();
            byte[] pdfData = pdfByteList.get(0);
            for (int i = 1; i < size; i++) {
                pdfData = mergePdfBytes(pdfData, pdfByteList.get(i));
            }
            return pdfData;
        } catch (Exception e) {
            log.error("mergePdf 合并PDF異常", e);
            throw new RuntimeException("合并PDF異常");
        }
    }

    /**
     * 基于內(nèi)存中的字節(jié)數(shù)組進行PDF文檔的合并
     *
     * @param firstPdf 第一個PDF文檔字節(jié)數(shù)組
     * @param secondPdf 第二個PDF文檔字節(jié)數(shù)組
     */
    @SneakyThrows
    private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) {
        if (firstPdf == null || secondPdf == null) {
            return null;
        }

        // 1.創(chuàng)建字節(jié)數(shù)組,基于內(nèi)存進行合并
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PdfDocument destDoc = new PdfDocument(new PdfWriter(os));

        // 2.合并的pdf文件對象
        PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));
        PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)));

        // 3.合并對象
        PdfMerger merger = new PdfMerger(destDoc);
        log.info("mergePdfBytes 合并PDF文檔,第一個PDF文檔頁數(shù):{}, 第二個PDF文檔頁數(shù):{}", firstDoc.getNumberOfPages(), secondDoc.getNumberOfPages());
        merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());
        merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());
        log.info("mergePdfBytes 合并PDF文檔成功");

        // 4.關(guān)閉文檔流
        merger.close();
        firstDoc.close();
        secondDoc.close();
        destDoc.close();

        return os.toByteArray();
    }

    /**
     * 判斷當(dāng)前系統(tǒng)是否是Windows系統(tǒng)
     *
     * @return true:Windows系統(tǒng),false:Linux系統(tǒng)
     */
    public static boolean isWindowsSystem() {
        String property = System.getProperty("os.name").toLowerCase();
        return property.contains("windows");
    }

    /**
     * 加載字體
     * @return pdf字體
     */
    public static PdfFont loadPdfFont(String fontName) {
        PdfFont pdfFont = null;
        try {
            if (StrUtil.isBlank(fontName)) {
                return pdfFont;
            }

            // 自行擴展

            String fontStr = isWindowsSystem() ? "/fonts/STSONG.TTF" : "/fonts/extend/simsun.ttc,0";
            pdfFont = PdfFontFactory.createFont(fontStr, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);
        } catch (IOException e) {
            log.error("loadPdfFont 無法加載字體", e);
        }
        return pdfFont;
    }


    public static void main(String[] args) {
        // 構(gòu)建基本信息
        Map<String, String> baseDataMap = Maps.newHashMap();
        baseDataMap.put("orderNo", "X202408301625");
        baseDataMap.put("memberName", "張三");
        baseDataMap.put("memberPhone", "18888888888");
        baseDataMap.put("orderDate", LocalDateTimeUtil.format(LocalDate.now(), DatePattern.NORM_DATE_PATTERN));

        baseDataMap.put("remark", "我是備注呀");
        baseDataMap.put("auditUserName", "李四");
        baseDataMap.put("totalAmount", "¥182.25");

        List<Map<String, String>> pageDataList = Lists.newArrayList();
        for (int i = 1; i < 15; i++) {
            Map<String, String> pageDataMap = Maps.newHashMap();
            BigDecimal price = BigDecimal.valueOf(i * Math.random()).setScale(2, RoundingMode.HALF_UP);
            pageDataMap.put("goodsName", StringUtils.join("商品名稱", i));
            pageDataMap.put("price", StringUtils.join("¥", price));
            pageDataMap.put("totalAmount", StringUtils.join("¥", price));
            pageDataMap.put("address", StringUtils.join("廣東省廣州市越秀區(qū)北京路", i, "號"));
            pageDataList.add(pageDataMap);
        }

        // 構(gòu)建業(yè)務(wù)數(shù)據(jù)
            /*PdfFormTemplate formTemplate = PdfFormTemplate.builder()
                    .baseDataMap(baseDataMap)
                    .pageDataList(pageDataList)
                    .customPageNoFiledName("pageNo")
                    .templatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\telephone\\出庫單據(jù)模版form.pdf")
                    .templateFileName("pdf表單模版")
                    .build();*/

        PdfFormTemplate formTemplate = new PdfFormTemplate()
                .setBaseDataMap(baseDataMap)
                .setPageDataList(pageDataList)
                // 本地測試是絕對路徑、發(fā)布web環(huán)境就相對路徑
                .setTemplatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\template\\出庫單據(jù)模版form.pdf")
                .setTemplateFileName("pdf表單模版");

        String pdfPath = fillTemplate(formTemplate);
        // byte[] andGetPdfByte = fillAndGetPdfByte(formTemplate);
        log.info("PDF路徑:{}", pdfPath);
    }


}

五、編輯工具類PdfItext5Util代碼

package com.example.pdfdemo.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.example.pdfdemo.form.PdfFormTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;

/**
 * <p>pdfItext5工具類</p>
 *
 * @author aliqingge
 * @date 2024/8/30 18:38
 */
@Slf4j
public class PdfItext5Util {

    /**
     * 每頁顯示數(shù),可自行修改
     */
    public static final int PAGE_SIZE = 12;

    /**
     * 臨時文件路徑
     */
    public final static String FOLDER = "/tempPdfFile";

    /**
     * 文件路徑
     */
    public final static String FOLDER_PATH = FOLDER + "/";

    /**
     * 獲取pdf字節(jié)數(shù)組列表
     *
     * @param template
     * @return
     */
    private static String createPdf(PdfFormTemplate template) throws Exception {
        List<PdfReader> pdfReaderList = new ArrayList<>(Collections.emptyList());

        // 數(shù)據(jù)分頁
        List<List<Map<String, String>>> pageList = Lists.partition(template.getPageDataList(), PAGE_SIZE);
        int pageTotal = pageList.size();
        for (int i = 0; i < pageList.size(); i++) {
            // 生成每頁pdf的字節(jié)數(shù)組
            PdfReader currentPagePdf = createCurrentPagePdf(template, pageList.get(i), (i + 1), pageTotal);
            pdfReaderList.add(currentPagePdf);
        }

        // 合并后生成文件的目標(biāo)路徑
        String destPdfPath = StringUtils.join(
                template.isGenerateToLocalFileFlag() ? template.getLocalFileStoragePath() : FOLDER_PATH,
                System.currentTimeMillis(),
                template.getTemplateFileName(),
                ".pdf"
        );

        //輸出流
        FileOutputStream outputStream = new FileOutputStream(destPdfPath);
        Document document = new Document(new PdfReader(pdfReaderList.get(0)).getPageSize(1));
        PdfSmartCopy copy = new PdfSmartCopy(document, outputStream);
        document.open();
        //合并所有分頁pdf
        for (int k = 1; k <= pdfReaderList.size(); k++) {
            PdfReader reader = pdfReaderList.get(k - 1);
            document.newPage();
            PdfImportedPage page = copy.getImportedPage(reader, 1);
            copy.addPage(page);
        }
        copy.close();
        outputStream.close();

        return destPdfPath;
    }

    /**
     * 獲取分頁pdf
     *
     * @param template pdf表單模版
     * @param currentTableData 當(dāng)前頁表格數(shù)據(jù)
     * @param currentPage 當(dāng)前頁
     * @param pageCount 總頁數(shù)
     * @return
     * @throws Exception
     */
    /**
     * 生成當(dāng)前頁pdf字節(jié)數(shù)組
     *
     * @param template
     * @param currentPageDataList
     * @param currentPageNo
     * @param pageTotal
     * @return
     */
    private static PdfReader createCurrentPagePdf(PdfFormTemplate template, List<Map<String, String>> currentPageDataList, int currentPageNo, int pageTotal) throws Exception {
        PdfReader templateReader;
        ByteArrayOutputStream bos;
        //讀取pdf模板
        templateReader = new PdfReader(template.getTemplatePath());
        bos = new ByteArrayOutputStream();
        PdfStamper stamper = new PdfStamper(templateReader, bos);
        AcroFields form = stamper.getAcroFields();

        // 自定義字體設(shè)置
        /*BaseFont baseFont;
        if (isWindowsSystem()) {
            baseFont = BaseFont.createFont("/fonts/STSONG.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } else {
            baseFont = BaseFont.createFont("/fonts/extend/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        }
        form.setSubstitutionFonts(Lists.newArrayList(baseFont));*/

        // 設(shè)置頁碼
        form.setField(template.getCustomPageNoFiledName(), StringUtils.join(currentPageNo, "/", pageTotal));
        // 填充基本信息
        Map<String, String> baseDataMap = template.getBaseDataMap();
        if (CollUtil.isNotEmpty(baseDataMap)) {
            for (String dataKey : baseDataMap.keySet()) {
                form.setField(dataKey, baseDataMap.get(dataKey));
            }
        }

        if (CollUtil.isNotEmpty(currentPageDataList)) {
            int i = 1;
            // 填充分頁數(shù)據(jù)
            for (Map<String, String> currentPageDataMap : currentPageDataList) {
                // 填充序號, 可以自定義屬性,從外部傳入
                form.setField("no", String.valueOf((currentPageNo - 1) * PAGE_SIZE + i));
                // 填充其他數(shù)據(jù)
                Set<Map.Entry<String, String>> entries = currentPageDataMap.entrySet();
                int finalNo = i;
                entries.forEach(entry -> {
                    try {
                        form.setField(StringUtils.join(entry.getKey(), finalNo), entry.getValue());
                    } catch (Exception e) {
                        log.error("填充表單數(shù)據(jù)異常", e);
                    }
                });
                i++;
            }
        }

        /*Map<String, AcroFields.Item> formFields = form.getFields();
        for (String key : formFields.keySet()) {
            form.setFieldProperty(key, "textfont", baseFont, null);
            form.setFieldProperty(key, "textsize", 8f, null);
        }*/

        String pagePdfPath = FOLDER_PATH + System.currentTimeMillis() + ".pdf";
        FileOutputStream outputStream = new FileOutputStream(pagePdfPath);
        //如果為false那么生成的PDF文件還能編輯,一定要設(shè)為true
        stamper.setFormFlattening(true);
        stamper.close();
        Document doc = new Document();
        PdfSmartCopy copy = new PdfSmartCopy(doc, outputStream);
        doc.open();
        PdfReader reader = new PdfReader(bos.toByteArray());
        PdfImportedPage importPage = copy.getImportedPage(reader, 1);
        copy.addPage(importPage);
        doc.close();
        outputStream.close();
        return reader;
    }

    /**
     * 判斷當(dāng)前系統(tǒng)是否是Windows系統(tǒng)
     *
     * @return
     */
    public static boolean isWindowsSystem() {
        String property = System.getProperty("os.name").toLowerCase();
        return property.contains("windows");
    }


    public static void main(String[] args) throws Exception {

        // 構(gòu)建基本信息
        Map<String, String> baseDataMap = Maps.newHashMap();
        baseDataMap.put("orderNo", "X202408301625");
        baseDataMap.put("memberName", "張三");
        baseDataMap.put("memberPhone", "18888888888");
        baseDataMap.put("orderDate", LocalDateTimeUtil.format(LocalDate.now(), DatePattern.NORM_DATE_PATTERN));

        baseDataMap.put("remark", "我是備注呀");
        baseDataMap.put("auditUserName", "李四");
        baseDataMap.put("totalAmount", "¥182.25");

        List<Map<String, String>> pageDataList = Lists.newArrayList();
        for (int i = 1; i < 15; i++) {
            Map<String, String> pageDataMap = Maps.newHashMap();
            BigDecimal price = BigDecimal.valueOf(i * Math.random()).setScale(2, RoundingMode.HALF_UP);
            pageDataMap.put("goodsName", StringUtils.join("商品名稱", i));
            pageDataMap.put("price", StringUtils.join("¥", price));
            pageDataMap.put("totalAmount", StringUtils.join("¥", price));
            pageDataMap.put("address", StringUtils.join("廣東省廣州市越秀區(qū)北京路", i, "號"));
            pageDataList.add(pageDataMap);
        }

        // 構(gòu)建業(yè)務(wù)數(shù)據(jù)
        /*PdfFormTemplate formTemplate = PdfFormTemplate.builder()
                .baseDataMap(baseDataMap)
                .pageDataList(pageDataList)
                .customPageNoFiledName("pageNo")
                .templatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\telephone\\出庫單據(jù)模版form.pdf")
                .templateFileName("pdf表單模版")
                .build();*/

        PdfFormTemplate formTemplate = new PdfFormTemplate()
                .setBaseDataMap(baseDataMap)
                .setPageDataList(pageDataList)
                .setTemplatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\template\\出庫單據(jù)模版form.pdf")
                .setTemplateFileName("測試pdf表單模版");

        String pdf = createPdf(formTemplate);
        log.info("pdf文件路徑: {}", pdf);


    }

}

六、統(tǒng)一模版代碼類說明

統(tǒng)一模板類,對業(yè)務(wù)數(shù)據(jù)、模板、字體等屬性進行封裝,要對哪個pdf模版進行填充數(shù)據(jù),入口處只需按照模版要求組裝業(yè)務(wù)數(shù)據(jù),調(diào)用填充接口即可,無需過多操作,當(dāng)然很多操作還可以自行擴展。

package com.example.pdfdemo.form;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * <p>pdf表單模版對象</p>
 *
 * @author aliqingge
 * @date 2024/8/30 14:57
 */
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class PdfFormTemplate implements Serializable {

    private static final long serialVersionUID = 3935687509620254261L;

    /**
     * 基本數(shù)據(jù)
     */
    private Map<String, String> baseDataMap;

    /**
     * 分頁數(shù)據(jù)集合
     */
    private List<Map<String, String>> pageDataList;

    /**
     * 自定義頁碼字段名,默認(rèn)pageNumber
     */
    private String customPageNoFiledName = "pageNo";

    /**
     * 填充模版路徑
     */
    private String templatePath;

    /**
     * 模版文件名, 默認(rèn)pdf表單模版
     */
    private String templateFileName = "pdf表單模版";

    /**
     * 字體大小,默認(rèn)8f
     */
    private float fontSize = 8f;

    /**
     * 字體名稱,默認(rèn)宋體
     */
    private String fontName;

    /**
     * 是否生成到本地文件,默認(rèn)生成
     */
    private boolean generateToLocalFileFlag = true;

    /**
     * 本地文件存儲路徑
     */
    private String localFileStoragePath = "C:\\tempPdfFile\\";

}

七、填充pdf表單核心代碼

1.填充入口

 /**
     * 填充表單
     *
     * @param template 模版對象
     * @return 填充表單文件路徑
     */
    public static String fillTemplate(PdfFormTemplate template) {
        Assert.notNull(template, "模版對象不能為空");

        // 創(chuàng)建臨時文件夾
        File file = new File(FOLDER);
        if (!file.exists() && !file.isDirectory()) {
            file.setWritable(true, false);
            file.mkdirs();
        }

        // 合并后生成文件的目標(biāo)路徑
        String destPdfPath = StringUtils.join(
                template.isGenerateToLocalFileFlag() ? template.getLocalFileStoragePath() : FOLDER_PATH,
                System.currentTimeMillis(),
                template.getTemplateFileName(),
                ".pdf"
        );

        // 獲取pdf字節(jié)數(shù)組列表
        List<byte[]> pdfByteList = getPdfByteList(template);
        // 合并文件
        mergePdf(pdfByteList, destPdfPath);

        return destPdfPath;
    }

2.填充模版

	/**
	 * 獲取pdf字節(jié)數(shù)組列表
	 *
	 * @param template
	 * @return
	 */
	private static List<byte[]> getPdfByteList(PdfFormTemplate template) {
	    List<byte[]> pdfByteList = Lists.newArrayList();
	    // 數(shù)據(jù)分頁
	    List<List<Map<String, String>>> pageList = Lists.partition(template.getPageDataList(), PAGE_SIZE);
	    int pageTotal = pageList.size();
	    for (int i = 0; i < pageList.size(); i++) {
	        // 生成每頁pdf的字節(jié)數(shù)組
	        byte[] currentPagePdf = createCurrentPagePdf(template, pageList.get(i), (i + 1), pageTotal);
	        pdfByteList.add(currentPagePdf);
	    }
	    return pdfByteList;
	}
	
	/**
     * 生成當(dāng)前頁pdf字節(jié)數(shù)組
     *
     * @param template
     * @param currentPageDataList
     * @param currentPageNo
     * @param pageTotal
     * @return
     */
    private static byte[] createCurrentPagePdf(PdfFormTemplate template, List<Map<String, String>> currentPageDataList, int currentPageNo, int pageTotal) {
        PdfDocument pdfDoc = null;
        try {
            // 輸出流用于存儲填充好數(shù)據(jù)的PDF文件
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 打開PDF模板文件
            PdfReader reader = new PdfReader(template.getTemplatePath());
            // 創(chuàng)建PDF寫入器
            PdfWriter writer = new PdfWriter(bos);
            // 創(chuàng)建PDF文檔對象,連接讀取器和寫入器
            pdfDoc = new PdfDocument(reader, writer);
            // 設(shè)置默認(rèn)頁面大小為A4
            pdfDoc.setDefaultPageSize(PageSize.A4);
            // 獲取PDF表單
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
            // 設(shè)置頁碼
            PdfFormField formField = form.getField(template.getCustomPageNoFiledName());
            if (formField != null) {
                formField.setValue(StringUtils.join(currentPageNo, "/", pageTotal));
            }

            Map<String, String> baseDataMap = template.getBaseDataMap();
            if (CollUtil.isNotEmpty(baseDataMap)) {
                // 填充基本信息
                for (String dataKey : baseDataMap.keySet()) {
                    PdfFormField field = form.getField(dataKey);
                    if (field != null) {
                        field.setValue(baseDataMap.get(dataKey));
                    }
                }
            }

            if (CollUtil.isNotEmpty(currentPageDataList)) {
                int i = 1;
                // 填充分頁數(shù)據(jù)
                for (Map<String, String> currentPageDataMap : currentPageDataList) {
                    // 填充序號, 可以自定義屬性,從外部傳入
                    PdfFormField noFormField = form.getField(StringUtils.join("no", i));
                    if (noFormField != null) {
                        noFormField.setValue(String.valueOf((currentPageNo - 1) * PAGE_SIZE + i));
                    }

                    // 填充其他數(shù)據(jù)
                    Set<Map.Entry<String, String>> entries = currentPageDataMap.entrySet();
                    int finalNo = i;
                    entries.forEach(entry -> {
                        PdfFormField field = form.getField(StringUtils.join(entry.getKey(), finalNo));
                        if (field != null) {
                            field.setValue(entry.getValue());
                        }
                    });
                    i++;
                }
            }

            // 設(shè)置表單不可編輯
            form.flattenFields();
            pdfDoc.close();

            return bos.toByteArray();
        } catch (IOException e) {
            log.error("createCurrentPagePdf 填充PDF表單失敗", e);
            throw new RuntimeException(e);
        } finally {
            if (pdfDoc != null) {
                pdfDoc.close();
            }
        }
    }

八、效果圖

在這里插入圖片描述

在這里插入圖片描述

以上就是SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot itext填充pdf及導(dǎo)出的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot+Elasticsearch實現(xiàn)數(shù)據(jù)搜索的方法詳解

    SpringBoot+Elasticsearch實現(xiàn)數(shù)據(jù)搜索的方法詳解

    Elasticsearch是一個基于Lucene的搜索服務(wù)器。它提供了一個分布式多用戶能力的全文搜索引擎,基于RESTful?web接口。本文將利用SpringBoot整合Elasticsearch實現(xiàn)海量級數(shù)據(jù)搜索,需要的可以參考一下
    2022-05-05
  • springboot配置請求超時時間(Http會話和接口訪問)

    springboot配置請求超時時間(Http會話和接口訪問)

    本文主要介紹了springboot配置請求超時時間,包含Http會話和接口訪問兩種,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • Java struts2捕獲404錯誤的方法匯總

    Java struts2捕獲404錯誤的方法匯總

    這篇文章主要為大家詳細(xì)匯總了Java struts2捕獲404錯誤的常用方法,感興趣的小伙伴們可以參考一下
    2016-05-05
  • 使用Java通過OAuth協(xié)議驗證發(fā)送微博的教程

    使用Java通過OAuth協(xié)議驗證發(fā)送微博的教程

    這篇文章主要介紹了使用Java通過OAuth協(xié)議驗證發(fā)送微博的教程,使用到了新浪微博為Java開放的API weibo4j,需要的朋友可以參考下
    2016-02-02
  • Java進階知識之反射的概念與獲取方法

    Java進階知識之反射的概念與獲取方法

    這篇文章主要給大家介紹了關(guān)于Java進階知識之反射的概念與獲取方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Java實現(xiàn)布隆過濾器的示例詳解

    Java實現(xiàn)布隆過濾器的示例詳解

    布隆過濾器(Bloom?Filter)是1970年由布隆提出來的,實際上是由一個很長的二進制數(shù)組+一系列hash算法映射函數(shù),用于判斷一個元素是否存在于集合中。本文主要介紹了Java實現(xiàn)布隆過濾器的示例代碼,希望對大家有所幫助
    2023-03-03
  • Java如何判斷一個url是否有效

    Java如何判斷一個url是否有效

    URL類提供了一個openConnection()方法,可以打開一個連接并返回一個URLConnection對象,該對象可以用來檢查連接是否有效,這篇文章主要介紹了Java判斷一個url是否有效,需要的朋友可以參考下
    2023-09-09
  • Spring Boot日志控制詳解

    Spring Boot日志控制詳解

    這篇文章主要為大家詳細(xì)介紹了Spring Boot日志控制的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 告訴你為什么?ThreadLocal?可以做到線程隔離

    告訴你為什么?ThreadLocal?可以做到線程隔離

    對于 ThreadLocal 我們都不陌生,它的作用如同它的名字用于存放線程本地變量,這篇文章主要介紹了為什么?ThreadLocal?可以做到線程隔離,需要的朋友可以參考下
    2022-07-07
  • 淺談Java自定義注解相關(guān)知識

    淺談Java自定義注解相關(guān)知識

    今天帶大家來學(xué)習(xí)Java注解的相關(guān)知識,文中對自定義注解作了非常詳細(xì)的介紹,對正在學(xué)習(xí)Java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05

最新評論