Java PDF生成方案之iText

 

我是不會告訴你,關注公衆號《andyqian》,回覆pdf,會給你整個工程代碼的!html

前言

  這是一篇之前的文章,最近作了優化處理,決定分享在這裏,沒有時間閱讀全文的童鞋,可直接拖到最後一章節。進入主題,今天介紹的是電子憑證(pdf)生成的解決方案,會從幾個經常使用的工具來介紹,也會對比一下幾者之間的性能。linux

iText是什麼?

在官網中 http://itextpdf.com/描述:windows

iText, the world's preferred PDF library,iText is a software developer toolkit that allows users to integrate PDF functionalities within their applications, processes or productsapp

iText,是世界上首選的PDF庫,iText是一個軟件開發人員工具包,容許用戶將PDF功能集成到其餘應用程序,流程或者產品中。 其特色有:maven

  1. 支持表格
  2. 圖片,
  3. 定製字體
  4. 支持合併pdf 等。

準備工做

在使用iTex時,咱們須要添加Maven依賴,以下:ide

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

入門實例

代碼:工具

/**
     * 生成pdf文件
     */
    @Test
    public void testCreatePdf(){
        try{
        // 1. new Document
        Document document = new Document();
        PdfWriter.getInstance(document, new FileOutputStream(DEST));
        // 2. 打開document
        document.open();
        // 3. 添加內容
        document.add(new Paragraph("hello world!"));
        // 4. 關閉 (若是未關閉則會生成無效的pdf文件)
        document.close();
        }catch(DocumentException ex){
            ex.printStackTrace();
        }catch (FileNotFoundException ex){
            ex.printStackTrace();
        }
    }

效果:
helloWorld性能

iText字體

iText內置支持多種字體,咱們能夠經過FontFactory類找到iText的內置字體:字體

Courier;
Courier-Bold;
Courier-Oblique;
Courier-BoldOblique;
Helvetica;
Helvetica-Bold;
Helvetica-Oblique;
Helvetica-BoldOblique;
Symbol;
Times;
Times-Roman;
Times-Bold;
Times-Italic;
Times-BoldItalic;
ZapfDingbats;

一樣,iText也容許自定義字體,itext默認字體不支持中文,這時,咱們就能夠經過自定義字體來解決這個問題,代碼以下所示:優化

@Test
    public void testChineseFontPdf(){
        try {
            //1. new document
            Document document = new Document();
            PdfWriter.getInstance(document, new FileOutputStream("/tmp/pdf/2017/1.pdf"));
            //2. open document
            document.open();
            BaseFont bf = BaseFont.createFont(path()+"fonts/SIMKAI.TTF", BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
            //3. 註冊字體
            Font font = new Font(bf,30);
            //3. 添加段落,並設置字體
            document.add(new Paragraph("hello world(中文,)",font));
            //4. close document
            document.close();
        }catch(FileNotFoundException ex){
            ex.printStackTrace();
        }catch(DocumentException ex){
            ex.printStackTrace();
        }catch(IOException ex){
            ex.printStackTrace();
        }
    }
    
 /**
     * 獲取資源的路徑
     * @return
     */
    private String path(){
        String path = this.getClass().getResource("/").getPath();
        return path;
    }

效果以下: 中文

表格

一般咱們會在pdf文件中生成表格,來展現數據,在iText中也是很是方便的,代碼以下:

/**
     * 生成表格
     */
    @Test
    public void testTablePdf(){
        try {
            //1.new document
            Document document = new Document();
            PdfWriter.getInstance(document,new FileOutputStream("/tmp/pdf/2017/1.pdf"));
            //2 open document
            document.open();
            //3.添加pdf tables 3表示列數,
            PdfPTable pdfPTable = new PdfPTable(3);
            // cell表示單元格,(12表示12個單元格,3列,12個單元格就造成來一個4行3列的表格)
            for (int i = 0; i < 12; i++) {
                pdfPTable.addCell("cell" + i);
            }
            document.add(pdfPTable);
            //4. 關閉document
            document.close();
        }catch(DocumentException ex){
            ex.printStackTrace();
        }catch(FileNotFoundException ex){
            ex.printStackTrace();
        }
    }

生成效果以下所示:
表格

圖片

代碼:

/**
     * 生成image
     */
    @Test
    public void testImagePdf(){
        try {
            //1.new document
            Document document = new Document();
            PdfWriter.getInstance(document,new FileOutputStream("/tmp/pdf/2017/1.pdf"));
            //2 open document
            document.open();
            document.add(new Paragraph("Hello world!"));
            //3.添加image
            Image image = Image.getInstance(path()+"/images/timg.jpg");
            image.scaleAbsolute(PageSize.A4.rotate());
            document.add(image);
            //4. 關閉document
            document.close();
        }catch(DocumentException ex){
            ex.printStackTrace();
        }catch(FileNotFoundException ex){
            ex.printStackTrace();
        }catch (IOException ex){
            ex.printStackTrace();
        }
    }

效果:
表格

html轉換pdf

咱們在實際項目中,須要將html格式的文檔轉換爲pdf工具,在iText中,默認是不直接支持html格式文檔的,咱們能夠藉助com.itextpdf.tool來生成 首先添加maven

<dependency>
      <groupId>com.itextpdf.tool</groupId>
      <artifactId>xmlworker</artifactId>
      <version>5.5.11</version>
    </dependency>

代碼以下所示:

/**
 * author: andy
 * date: 17-5-30
 * blog: www.andyqian.com
 * version: 0.0.1
 * description: 生成html格式內容的工具類
 */
public class CreateHtmlPdfTest {

    /**
     * html內容路徑
     */
    private static final String HTML_PATH= "/html/helloworld.html";
    private static final String FONT_PATH="/fonts/SIMKAI.TTF";

    /**
     * 生成pdf格式的內容
     */
    @Test
    public void testHtml(){
        try {
            //1. new document
            Document document = new Document();
            PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("/tmp/pdf/2017/1.pdf"));
            //2.open document
            document.open();
            //3. 設置字體
            XMLWorkerFontProvider xmlWorkerFontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
            xmlWorkerFontProvider.register(getContextPath()+FONT_PATH);
            //4. 文件
            FileInputStream fileInputStream = new FileInputStream(getContextPath()+HTML_PATH);
            XMLWorkerHelper.getInstance().parseXHtml(writer, document,fileInputStream, Charset.forName("UTF-8"),xmlWorkerFontProvider);
            //3. close document
            document.close();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    /**
     * 獲取上下文路徑
     * @return
     */
    private String getContextPath(){
        return this.getClass().getResource("/").getPath();
    }
}

html代碼內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>標題</title>
</head>
<style>
    *{
        font-family: KaiTi_GB2312;
    }
</style>
<body>
    hello world! XMLWorkerHelper [這是帶有中文的html格式內容]
</body>
</html>

代碼結構爲:
項目結構效果以下所示:
帶中文的html

7.iText+Freemarker

首先添加Freemarker Maven依賴以下所示:

<dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
      <version>2.3.23</version>
    </dependency>

代碼以下所示:

/**
 * author: andy
 * date: 17-5-30
 * blog: www.andyqian.com
 * version: 0.0.1
 * description: 經過feemarker工具類生成pdf
 */
public class CreateFeemarkerPdfTest {

    private static final String FONT_PATH="/fonts/SIMKAI.TTF";
    private static final String HTML_PATH="/html/feemarker.html";

    @Test
    public void testCreatePdf(){
            try {
                //1. new document
                Document document = new Document();
                PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("/tmp/pdf/2017/1.pdf"));
                //2.open document
                document.open();
                //3. 設置字體
                XMLWorkerFontProvider xmlWorkerFontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
                xmlWorkerFontProvider.register(getContextPath()+FONT_PATH);

                //4. 設置模板內容
                Map<String,Object> params = new HashMap<String,Object>();
                params.put("name","鞠騫");
                params.put("career","軟件開發");
                params.put("blog","http://www.andyqian.com");
                String content = getFreeMarkerText(htmlContent(),params);
                //4. 文件
                InputStream inputStream = new ByteArrayInputStream(content.getBytes("utf-8"));
                XMLWorkerHelper.getInstance().parseXHtml(writer, document,inputStream, Charset.forName("UTF-8"),xmlWorkerFontProvider);
                //3. close document
                document.close();
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }

    /**
     * 獲取上下文路徑
     * @return
     */
    private String getContextPath(){
        return this.getClass().getResource("/").getPath();
    }

    /**
     * freemarker模板方法
     * @param templateTxt 模板文本
     * @param map  模板參數
     * @return
     * @throws Exception
     */
    public static String getFreeMarkerText(String templateTxt, Map<String, Object> map) throws Exception {
        String result = null;
        Configuration config = new Configuration(Configuration.VERSION_2_3_23);
        try {
            StringTemplateLoader e = new StringTemplateLoader();
            e.putTemplate("t", templateTxt);
            config.setTemplateLoader(e);
            config.setDefaultEncoding("UTF-8");
            Template template = config.getTemplate("t", "UTF-8");
            StringWriter out = new StringWriter();
            template.process(map, out);
            result = out.toString();
            return result;
        } catch (IOException iex) {
            throw new Exception("獲取freemark模版出錯", iex);
        } catch (TemplateException ex) {
            throw new Exception("freemark模版處理異常", ex);
        }
    }

    /**
     * 獲取html內容
     * @return
     */
    private String htmlContent(){
        String result = "";
        try {
            FileInputStream fileInputStream = new FileInputStream(getContextPath() + HTML_PATH);
            int len=0;
            byte[] array = new byte[1024];
            StringBuffer stringBuffer = new StringBuffer();
            while((len=fileInputStream.read(array))!=-1){
                stringBuffer.append(new String(array,0,len));
            }
            result = stringBuffer.toString();
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return result;
    }
}

效果以下:
freemarker

其實上面,在windows平臺下仍是有些問題的,字體路徑會加載不到,處理方案,請看下一章節。

福利時間 | 現成代碼

我是不會告訴你,關注公衆號《andyqian》,回覆pdf,會給你整個工程代碼的!

若是想拷貝代碼直接使用,也可使用下面代碼

public class PdfUtils {

        private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);

        /**
         * 經過html生成文件
         * @param htmlContent  html格式內容
         * @param file  輸出文件file
         */
        public static void createdPdfByItextHtml(String htmlContent,File file){
            InputStream inputStream = null;
            FileOutputStream outputStream = null;
            PdfWriter writer = null;
            try {
                // 1. 獲取生成pdf的html內容
                inputStream= new ByteArrayInputStream(htmlContent.getBytes("utf-8"));
                outputStream = new FileOutputStream(file);
                Document document = new Document();
                writer = PdfWriter.getInstance(document, outputStream);
                document.open();
                // 2. 添加字體
                XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
                fontImp.register(getFontPath());
                // 3. 設置編碼
                XMLWorkerHelper.getInstance().parseXHtml(writer, document, inputStream, Charset.forName("UTF-8"),fontImp);
                // 4. 關閉,(不關閉則會生成無效pdf)
                document.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }finally {
                try {
                    if(writer!=null){
                        writer.close();
                    }
                    if (outputStream != null) {
                        outputStream.close();
                    }
                    if (inputStream != null) {
                        inputStream.close();
                    }
                }catch(IOException ex){
                    ex.printStackTrace();
                }
            }
        }

    /**
     * 應用場景:
     * 1.在windows下,使用Thread.currentThread()獲取路徑時,出現空對象,致使不能使用
     * 2.在linux下,使用PdfUtils.class獲取路徑爲null,
     * 獲取字體路徑
     * @return
     */
    private static String getFontPath(){
        String path="";
        // 1. 
        ClassLoader classLoader= Thread.currentThread().getContextClassLoader();
        URL url = (classLoader==null)?null:classLoader.getResource("/");
        String threadCurrentPath = (url==null)?"":url.getPath();
        // 2. 若是線程獲取爲null,則使用當前PdfUtils.class加載路徑
        if(StringUtil.isBlank(threadCurrentPath)){
             path = PdfUtils.class.getClass().getResource("/").getPath();
        }
        // 3.拼接字體路徑
        StringBuffer stringBuffer = new StringBuffer(path);
        stringBuffer.append("/fonts/SIMKAI.TTF");
        path = stringBuffer.toString();
        logger.info("getFontPath threadCurrentPath: {}  path: {}",threadCurrentPath,path);
        return path;
    }

上面getFontPath()方法中,對linux以及windows平臺作了字體加載路徑作了兼容處理。

參考資料

  1. http://developers.itextpdf.com/developers-home
  2. http://itextpdf.com/

小結

本文從iText最基本的用法,分別介紹從表格,圖片,HTML,Feemarker來介紹iText,但願可以幫助到你。

 

有任何問題,均可以在公衆號下留言哦,在線提供幫助!

 

掃碼關注,一塊兒進步

我的博客: http://www.andyqian.com

相關文章
相關標籤/搜索