疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java PDF生成方案之iText


 

 
前言
 
  这是一篇以前的文章,最近做了优化处理,决定分享在这里,没有时间阅读全文的童鞋,可直接拖到最后一章节或回复『 pdf 』获取代码。进入主题,今天介绍的是电子凭证(pdf)生成的解决方案,会从几个常用的工具来介绍,也会对比一下几者之间的性能。
 
iText是什么?
 
在官网中 http://itextpdf.com/描述:
 
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 products
 
iText,是世界上首选的PDF库,iText是一个软件开发人员工具包,允许用户将PDF功能集成到其他应用程序,流程或者产品中。
其特点有:
 
支持表格
图片,
定制字体
支持合并pdf 等。
准备工作
 
在使用iTex时,我们需要添加Maven依赖,如下:
 
 <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();
        }
    }
效果:
 
 
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>
代码结构为:
 
 
 
效果如下所示:
 
 
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;
    }
}
效果如下:
 
 
其实上面,在windows平台下还是有些问题的,字体路径会加载不到,处理方案,请看下一章节。
 
现成代码
 
如果想拷贝代码直接使用,也可以使用下面代码
 
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平台做了字体加载路径做了兼容处理。
 
小结
 
本文从iText最基本的用法,分别介绍从表格,图片,HTML,Feemarker来介绍iText,希望能够帮助到你。