java springboot 生成pdf 親測可用 附源碼

實(shí)驗(yàn)時間: 2020-09-23
代碼: https://github.com/mefly521/springbootAllinone spring-boot-pdf-template模塊

本文介紹將一個制作好的模板自動填充上動態(tài)內(nèi)容的方法.
網(wǎng)上有好多方法沒有一一實(shí)驗(yàn),因?yàn)橛行┎荒芸缙脚_,有些需要裝dll 或其它第三方組件所以將其排除.

只介紹我實(shí)驗(yàn)成功的2種方法 :
方法1生成速度比較快,但需要下載 Adobe Acrobat dc 編輯模板,
方法2 為word 模板,先將word模板的動態(tài)內(nèi)容填充后,再將word 轉(zhuǎn)成 pdf.缺點(diǎn)是速度比較慢,將近10秒

1 pdf模板生成

下載 Adobe Acrobat dc
http://www.downza.cn/soft/20562.html

用dc 打開pdf , 編寫靜態(tài)文字


image.png

再點(diǎn)準(zhǔn)備表單
將文本框放上去, 將編輯名稱, 名稱是后面用于替換的key


image.png

引用的包

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.8.0</version>
        </dependency>

        <!-- itextpdf 依賴包 -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.10</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

代碼

package com.demo.controller;

import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.util.*;

/**
 * 生成pdf的工具類
 */
public class PdfUtils
{
    public static void main(String[] args) throws IOException
    {
        //Map中Key對應(yīng)PDF表單中的fieldNames赫舒,Value則是你想填充的值
        HashMap map = new HashMap<String, String>();
        map.put("number","2020");
        map.put("sex","On");    //Value為On 則勾選這個復(fù)選框
        map.put("test","On");   //Value為On 則勾選這個復(fù)選框
        map.put("fill_1","被檢查單位123232");
        map.put("fill_18","有一小鳥首装,它的家搭在最高的樹枝上跪帝,它的羽毛還未豐滿印蔬,不能要飛能庆,每日只在家里嘰嘰地叫著派哲,和兩只老鳥說著話兒偎血,他們都是覺得非常的快樂。這一天早晨雕沉,它醒了集乔,那兩個老鳥都找食物去了。一看見火紅的太陽坡椒,它們又害怕了扰路,因?yàn)樘柼罅耍鼈冇挚匆娨豢脴渖系囊黄么蟮臉淙~倔叼,樹葉上又有站著一只小鳥汗唱,正在吃害蟲,害蟲吃了很多樹葉丈攒,讓大樹不能長大哩罪,大樹是我們的好朋友,每一棵樹都產(chǎn)生氧氣巡验,讓我們每一個人呼吸际插。這時老鳥馬上飛過去,與小鳥一起吃害蟲显设,吃得飽飽的框弛,并為民除害。");
        String sourceFile = "D:\\temp\\01\\p22.pdf"; //原文件路徑
        String targetFile = "D:\\temp\\01\\output.pdf";     //目標(biāo)文件路徑
        PdfUtils.genPdf(map,sourceFile,targetFile);
    }

    /**
     * @param map 需要填充的字段
     * @param sourceFile  原文件路徑
     * @param targetFile  目標(biāo)文件路徑
     * @throws IOException
     */
    public static void genPdf(HashMap map, String sourceFile, String targetFile) throws IOException {
        File templateFile = new File(sourceFile);
        fillParam(map, FileUtils.readFileToByteArray(templateFile), targetFile);
    }

    /**
     * Description: 使用map中的參數(shù)填充pdf捕捂,map中的key和pdf表單中的field對應(yīng) <br>
     * @author mk
     * @Date 2018-11-2 15:21 <br>
     * @Param
     * @return
     */
    public static void fillParam(Map<String, String> fieldValueMap, byte[] file, String contractFileName) {
        FileOutputStream fos = null;
        try {

            fos = new FileOutputStream(contractFileName);
            PdfReader reader = null;
            PdfStamper stamper = null;
            BaseFont base = null;
            try {
                reader = new PdfReader(file);
                stamper = new PdfStamper(reader, fos);
                stamper.setFormFlattening(true);
                base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); //簡體中文字體
                //base = BaseFont.createFont("MHei-Medium", "UniCNS-UCS2-H", BaseFont.NOT_EMBEDDED);  //繁體中文字體
                AcroFields acroFields = stamper.getAcroFields();
                for (String key : acroFields.getFields().keySet()) {
                    acroFields.setFieldProperty(key, "textfont", base, null);
                    acroFields.setFieldProperty(key, "textsize", new Float(9), null);   //字體大小
                }
                if (fieldValueMap != null) {
                    for (String fieldName : fieldValueMap.keySet())
                    {
                        if (StringUtils.isNotBlank(fieldValueMap.get(fieldName)))
                        {
                            //獲取map中key對應(yīng)的Value是否為On瑟枫,若是則勾選復(fù)選框
                            if (fieldValueMap.get(fieldName).equals("On") || fieldValueMap.get(fieldName) == "On")
                            {
                                acroFields.setField(fieldName, fieldValueMap.get(fieldName),true);
                            }else
                            {
                                acroFields.setField(fieldName, fieldValueMap.get(fieldName));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (stamper != null) {
                    try {
                        stamper.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                if (reader != null) {
                    reader.close();
                }
            }

        } catch (Exception e) {
            System.out.println("填充參數(shù)異常");
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(fos);
        }
    }


    /**
     * Description: 獲取pdf表單中的fieldNames<br>
     * @author mk
     * @Date 2018-11-2 15:21 <br>
     * @Param
     * @return
     */
    public static Set<String> getTemplateFileFieldNames(String pdfFileName) {
        Set<String> fieldNames = new TreeSet<String>();
        PdfReader reader = null;
        try {
            reader = new PdfReader(pdfFileName);
            Set<String> keys = reader.getAcroFields().getFields().keySet();
            for (String key : keys) {
                int lastIndexOf = key.lastIndexOf(".");
                int lastIndexOf2 = key.lastIndexOf("[");
                fieldNames.add(key.substring(lastIndexOf != -1 ? lastIndexOf + 1 : 0, lastIndexOf2 != -1 ? lastIndexOf2 : key.length()));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                reader.close();
            }
        }

        return fieldNames;
    }


    /**
     * Description: 讀取文件數(shù)組<br>
     * @author mk
     * @Date 2018-11-2 15:21 <br>
     * @Param
     * @return
     */
    public static byte[] fileBuff(String filePath) throws IOException {
        File file = new File(filePath);
        long fileSize = file.length();
        if (fileSize > Integer.MAX_VALUE) {
            //System.out.println("file too big...");
            return null;
        }
        FileInputStream fi = new FileInputStream(file);
        byte[] file_buff = new byte[(int) fileSize];
        int offset = 0;
        int numRead = 0;
        while (offset < file_buff.length && (numRead = fi.read(file_buff, offset, file_buff.length - offset)) >= 0) {
            offset += numRead;
        }
        // 確保所有數(shù)據(jù)均被讀取
        if (offset != file_buff.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }
        fi.close();
        return file_buff;
    }

    /**
     * Description: 合并pdf <br>
     * @author mk
     * @Date 2018-11-2 15:21 <br>
     * @Param
     * @return
     */
    public static void mergePdfFiles(String[] files, String savepath) {
        Document document = null;
        try {
            document = new Document(); //默認(rèn)A4大小
            PdfCopy copy = new PdfCopy(document, new FileOutputStream(savepath));
            document.open();
            for (int i = 0; i < files.length; i++) {
                PdfReader reader = null;
                try {
                    reader = new PdfReader(files[i]);
                    int n = reader.getNumberOfPages();
                    for (int j = 1; j <= n; j++) {
                        document.newPage();
                        PdfImportedPage page = copy.getImportedPage(reader, j);
                        copy.addPage(page);
                    }
                } finally {
                    if (reader != null) {
                        reader.close();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //關(guān)閉PDF文檔流,OutputStream文件輸出流也將在PDF文檔流關(guān)閉方法內(nèi)部關(guān)閉
            if (document != null) {
                document.close();
            }

        }
    }
}


2 先成word 再轉(zhuǎn)成pdf

制作一個word 模板再使用 poi-tl (http://deepoove.com/poi-tl/)這個組件填充內(nèi)容
再將word 轉(zhuǎn)成pdf (具體看這篇文章 https://blog.csdn.net/hunwanjie/article/details/97135633,親測可用)
但是測試后發(fā)現(xiàn)轉(zhuǎn)換時間比較長, 用了將近10秒,考慮到性能問題,暫時將第2種設(shè)為備選.
優(yōu)先使用第一種,除非找不到編輯pdf 的軟件 .

Java使用itext 根據(jù)PDF模板進(jìn)行填充并生成_weixin_43910274的博客-CSDN博客
https://blog.csdn.net/weixin_43910274/article/details/104916106?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末指攒,一起剝皮案震驚了整個濱河市慷妙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌允悦,老刑警劉巖膝擂,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡猿挚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門驶鹉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绩蜻,“玉大人,你說我怎么就攤上這事室埋“炀” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵姚淆,是天一觀的道長孕蝉。 經(jīng)常有香客問我,道長腌逢,這世上最難降的妖魔是什么降淮? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮搏讶,結(jié)果婚禮上佳鳖,老公的妹妹穿的比我還像新娘。我一直安慰自己媒惕,他們只是感情好系吩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妒蔚,像睡著了一般穿挨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肴盏,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天科盛,我揣著相機(jī)與錄音,去河邊找鬼菜皂。 笑死土涝,一個胖子當(dāng)著我的面吹牛幌墓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播常侣,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼蜡饵,長吁一口氣:“原來是場噩夢啊……” “哼胳施!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤焦辅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筷登,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剃根,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡前方,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年狈醉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惠险。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡苗傅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出班巩,到底是詐尸還是另有隱情渣慕,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布抱慌,位于F島的核電站摇庙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏遥缕。R本人自食惡果不足惜卫袒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望单匣。 院中可真熱鬧夕凝,春花似錦、人聲如沸户秤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸡号。三九已至转砖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鲸伴,已是汗流浹背府蔗。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汞窗,地道東北人姓赤。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像仲吏,于是被迫代替她去往敵國和親不铆。 傳聞我的和親對象是個殘疾皇子蝌焚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345