java中pdf模版填充表單踩坑實(shí)戰(zhàn)記錄(itextPdf、openPdf、pdfbox)
準(zhǔn)備Pdf模版
Adobe Acrobat Dc打開pdf模版,點(diǎn)擊準(zhǔn)備表單出現(xiàn)自動(dòng)識別的表單域,如果需要自定義表單域可點(diǎn)擊頂欄標(biāo)記位置自定義表單位置。

雙擊表單域,出現(xiàn)表單設(shè)置框,可設(shè)置表單字段名稱(與代碼相關(guān)),位置、以及外觀字體以及大小等(默認(rèn)值),最后點(diǎn)擊保存


方法1:itextpdf7填充表單
(1)加入依賴
<dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>7.1.19</version> <type>pom</type> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.1.19</version> <scope>pom</scope> </dependency>
(2)代碼
public void exportContractPdf(EnterpriseInfoVo params, HttpServletResponse response) {
String inputFileName = "template/test.pdf";//模版路徑
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ClassPathResource classPathResource = new ClassPathResource(inputFileName);
try (InputStream inputStream = classPathResource.getInputStream();PdfReader reader = new PdfReader(inputStream);
PdfWriter writer = new PdfWriter(baos);
PdfDocument pdfDoc = new PdfDocument(reader, writer)) {
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
ClassPathResource fontResource = new ClassPathResource("ttf/STSong.ttf"); //字體路徑
InputStream fontStream = fontResource.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] data1= new byte[1024];
int nRead;
while ((nRead = fontStream.read(data1, 0, data1.length)) != -1) {
buffer.write(data1, 0, nRead);
}
buffer.flush();
byte[] fontBytes = buffer.toByteArray();
PdfFont font = PdfFontFactory.createFont(
fontBytes,
PdfEncodings.IDENTITY_H,
true
);
form.getFormFields().values().forEach(field -> field.setFont(font));
Map<String, Object> data = new HashMap<>();
data.put("param1", params.getParam1());
data.put("param2",params.getParam2());
for (Map.Entry<String, Object> entry : data.entrySet()) {
PdfFormField field = form.getField(entry.getKey());
if (field != null) {
field.setValue(entry.getValue().toString());
}
}
form.flattenFields();
pdfDoc.close();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" +
Optional.ofNullable(enterpriseInfoVo.getContract_id())
.orElse(URLEncoder.encode(enterpriseInfoVo.getFirm_name(), StandardCharsets.UTF_8.name())) + ".pdf");
response.getOutputStream().write(baos.toByteArray());
} catch (Exception e) {
//異常處理
}
}
(3)遇到的問題
本來這個(gè)方法沒有任何問題,但是 由于突然要將系統(tǒng)遷移到另一個(gè)服務(wù)器,部署上去后 PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
這行報(bào)錯(cuò) com/itextpdf/io/font/cmap/UniJIS-UTF16-H was not found.該路徑下沒有這文件但是font-asian有這個(gè)文件,本地運(yùn)行正常,按照網(wǎng)上的方法加入itext-asian依賴,或者自己導(dǎo)入resource資源都試過了,還是不行。研究了很久正式服就是走不通,但是之前服務(wù)器的正式服能就走,搗鼓很久一無所獲,所以決定換方法2。
方法2:pdfbox填充表單
(1)加入依賴
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.24</version> </dependency>
(2)代碼
public void exportContractPdf( EnterpriseInfoVo params, HttpServletResponse response) throws ParseException {
String inputFileName = "template/test.pdf";
try (PDDocument document = PDDocument.load(new ClassPathResource(inputFileName).getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
InputStream fontStream = new ClassPathResource("ttf/STSong.ttf").getInputStream();
PDType0Font font = PDType0Font.load(document, fontStream, false);
PDResources resources = new PDResources();
resources.put(COSName.getPDFName("F1"), font);
form.setDefaultResources(resources);
Map<String, Object> data = new HashMap<>();
data.put("param1", params.getParam1());
data.put("param2", params.getParam2());
for (PDField field : form.getFields()) {
if (data.containsKey(field.getFullyQualifiedName())) {
if (field instanceof PDTextField) {
PDTextField textField = (PDTextField) field;
textField.setDefaultAppearance("/F1 10 Tf 0 g"); // 可能導(dǎo)致文件過大
String fullyQualifiedName = field.getFullyQualifiedName();
Object o = data.get(fullyQualifiedName);
textField.setValue(o.toString()); // 設(shè)置字段值
textField.setReadOnly(true); // 將字段設(shè)置為只讀
}
}
}
form.flatten();
document.save(baos);
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" +
Optional.ofNullable(params.getId())
.orElse(URLEncoder.encode(enterpriseInfoVo.getName(), StandardCharsets.UTF_8.name())) + ".pdf");
response.getOutputStream().write(baos.toByteArray());
} catch (Exception e) {
//異常處理
}
}
(3)遇到的問題
由于業(yè)務(wù)需要下載協(xié)議和預(yù)覽協(xié)議,移動(dòng)端在線預(yù)覽和下載時(shí)出現(xiàn)亂碼情況,暫時(shí)沒有解決問題。于是又試了方法3。
方法3:openpdf填充表單
(1)加入依賴
<dependency> <groupId>com.github.librepdf</groupId> <artifactId>openpdf</artifactId> <version>1.3.26</version> </dependency>
(2)代碼
public void exportContractPdf(EnterpriseInfoVo params, HttpServletResponse response) throws ParseException {
String inputFileName = "template/test.pdf";
try (InputStream templateStream = new ClassPathResource(inputFileName).getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
com.lowagie.text.pdf.PdfReader reader = new com.lowagie.text.pdf.PdfReader(templateStream);
PdfStamper stamper = new PdfStamper(reader, baos);
AcroFields form = stamper.getAcroFields();
BaseFont font = BaseFont.createFont(
new ClassPathResource("ttf/STSong.ttf").getPath(),
BaseFont.IDENTITY_H,
BaseFont.EMBEDDED
);
Map<String, Object> data = new HashMap<>();
data.put("param1", params.getParam1());
data.put("param2", params.getParam2());
for (Object fieldName : form.getFields().keySet()) {
if (data.containsKey(fieldName.toString())) {
form.addSubstitutionFont(font);
// form.setFieldProperty(fieldName.toString(), "textfont", font, null); //這兩行代碼加入后直接從200k激增到7.5M
// form.setFieldProperty(fieldName.toString(),"textsize",Float.valueOf(10f),null);
Object value = data.get(fieldName);
form.setField(fieldName.toString(), value.toString());
form.setFieldProperty(fieldName.toString(), "setfflags", PdfFormField.FF_READ_ONLY, null);
}
}
stamper.setFormFlattening(true);
stamper.close();
reader.close();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" +
Optional.ofNullable(params.getId())
.orElse(URLEncoder.encode(params.getName(), StandardCharsets.UTF_8.name())) + ".pdf");
response.getOutputStream().write(baos.toByteArray());
} catch (Exception e) {
//異常處理
}
}
(3)遇到的問題
剛開始導(dǎo)出模版出現(xiàn)填充文字大小不一,字體樣式不統(tǒng)一的情況,所以使用了// form.setFieldProperty(fieldName.toString(), "textfont", font, null); //這兩行代碼加入后直接從200k激增到7.5M
// form.setFieldProperty(fieldName.toString(),"textsize",Float.valueOf(10f),null);這兩行代碼的加入直接導(dǎo)致一頁的pdf文件從200k到7.5M,查找原因說是字體文件重復(fù)嵌入,試了很多方法總是出現(xiàn)這樣那樣的問題,所以到方法4。
方法4:itextpdf5填充表單
(1)加入依賴
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.13.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>wiki.xsx</groupId>
<artifactId>x-easypdf-pdfbox</artifactId>
<version>2.11.6</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.1.22</version>
<exclusions>
<exclusion>
<artifactId>itextpdf</artifactId>
<groupId>com.itextpdf</groupId>
</exclusion>
</exclusions>
</dependency>
(2)代碼
public void exportContractPdf(EnterpriseInfoVo params, HttpServletResponse response) {
String inputFileName = "template/test.pdf";
try {
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" +
Optional.ofNullable(params.getId())
.orElse(URLEncoder.encode(params.getName(), StandardCharsets.UTF_8.name())) + ".pdf");
Map<String, String> data = new HashMap<>();
data.put("param1", params.getParam1());
data.put("param2", params.getParam2());
XEasyPdfHandler.Document
.load( new ClassPathResource(inputFileName).getInputStream())
.formFiller()
.setFontPath(getTempFontPath("ttf/STSong.ttf"))
.enableCompress()
.enableFixForm()
.enableReadOnly()
.fill(data)
.finish(response.getOutputStream());
} catch (Exception e) {
//異常處理
}
}(3)遇到的問題
剛開始出現(xiàn).fill(data)這里報(bào)空指針異常,但是data里每個(gè)都有值,針對每個(gè)值一一排查發(fā)現(xiàn)竟然有的值能填充成功有的值不行,一次排查后發(fā)現(xiàn)跟模版的表單有問題,最終針對pdf模版的表單域刪除重新創(chuàng)建就可以了。但是上線到測試服后才發(fā)現(xiàn)又有問題聯(lián)想瀏覽器預(yù)覽的pdf表單填的都是空值,下載又可以了。我不死心又拿著新建表單域的pdf模板去試方法2和方法3發(fā)現(xiàn)兩個(gè)方法的毛病解決了,所以最終我使用的方法2。至于方法1和方法4的問題實(shí)在沒有時(shí)間來驗(yàn)證和排查了。
總結(jié)
到此這篇關(guān)于java中pdf模版填充表單踩坑實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)java pdf模版填充表單內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目實(shí)戰(zhàn)之加載和讀取資源文件
在項(xiàng)目的開發(fā)中,我們知道的是SpringBoot框架大大減少了我們的配置文件,但是還是留下了一個(gè)application.properties文件讓我們可以進(jìn)行一些配置,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目實(shí)戰(zhàn)之加載和讀取資源文件的相關(guān)資料,需要的朋友可以參考下2021-10-10
SpringSecurity 自定義認(rèn)證登錄的項(xiàng)目實(shí)踐
本文主要介紹了SpringSecurity 自定義認(rèn)證登錄的項(xiàng)目實(shí)踐,以手機(jī)驗(yàn)證碼登錄為例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
Java中Prime算法的原理與實(shí)現(xiàn)詳解
Prime算法是一種窮舉查找算法來從一個(gè)連通圖中構(gòu)造一棵最小生成樹。本文主要為大家介紹了Java中Prime算法的原理與實(shí)現(xiàn),感興趣的可以學(xué)習(xí)一下2022-07-07
詳解Java中CountDownLatch異步轉(zhuǎn)同步工具類
今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識,文章圍繞著CountDownLatch異步轉(zhuǎn)同步工具類展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
springBoot啟動(dòng)報(bào)錯(cuò)log4j沖突的解決方案
這篇文章主要介紹了springBoot啟動(dòng)報(bào)錯(cuò)log4j沖突的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。2021-07-07
Java簡單實(shí)現(xiàn)session保存到redis的方法示例
這篇文章主要介紹了Java簡單實(shí)現(xiàn)session保存到redis的方法,結(jié)合實(shí)例形式分析了Java將session存入redis緩存服務(wù)器的相關(guān)設(shè)置、實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下2018-05-05

