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)文章!
- Springboot整合itext實現(xiàn)PDF文件合并
- SpringBoot3集成iText實現(xiàn)PDF導(dǎo)出功能
- SpringBoot集成iTextPDF的實例
- SpringBoot整合iText7導(dǎo)出PDF及性能優(yōu)化方式
- SpringBoot集成itext實現(xiàn)html轉(zhuǎn)PDF
- SpringBoot集成itextpdf實現(xiàn)根據(jù)模板動態(tài)生成PDF
- SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印
- SpringBoot集成itext導(dǎo)出PDF的過程
相關(guān)文章
SpringBoot+Elasticsearch實現(xiàn)數(shù)據(jù)搜索的方法詳解
Elasticsearch是一個基于Lucene的搜索服務(wù)器。它提供了一個分布式多用戶能力的全文搜索引擎,基于RESTful?web接口。本文將利用SpringBoot整合Elasticsearch實現(xiàn)海量級數(shù)據(jù)搜索,需要的可以參考一下2022-05-05springboot配置請求超時時間(Http會話和接口訪問)
本文主要介紹了springboot配置請求超時時間,包含Http會話和接口訪問兩種,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07使用Java通過OAuth協(xié)議驗證發(fā)送微博的教程
這篇文章主要介紹了使用Java通過OAuth協(xié)議驗證發(fā)送微博的教程,使用到了新浪微博為Java開放的API weibo4j,需要的朋友可以參考下2016-02-02