欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java根据模板动态生成PDF实例

程序员文章站 2024-02-28 08:20:04
一、需求说明: 根据业务需要,需要在服务器端生成可动态配置的pdf文档,方便数据可视化查看。 二、解决方案: itext+freemarker+jfreechar...

一、需求说明:

根据业务需要,需要在服务器端生成可动态配置的pdf文档,方便数据可视化查看。

二、解决方案:

itext+freemarker+jfreechart生成可动态配置的pdf文档

itext有很强大的pdf处理能力,但是样式和排版不好控制,直接写pdf文档,数据的动态渲染很麻烦。

freemarker能配置动态的html模板,正好解决了样式、动态渲染和排版问题。

jfreechart有这方便的画图api,能画出简单的折线、柱状和饼图,基本能满足需要。

三、实现功能:

1、能动态配置pdf文档内容

2、能动态配置中文字体显示

3、设置自定义的页眉页脚信息

4、能动态生成业务图片

5、完成pdf的分页和图片的嵌入

四、主要代码结构说明:

1、component包:pdf生成的组件 对外提供的是pdfkit工具类和headerfooterbuilder接口,其中pdfkit负责pdf的生成,headerfooterbuilder负责自定义页眉页脚信息。

2、builder包:负责pdf模板之外的额外信息填写,这里主要是页眉页脚的定制。

3、chart包:jfreechart的画图工具包,目前只有一个线形图。

4、test包:测试工具类

5、util包:freemarker等工具类。

五、关键代码说明:

1、模板配置

 <!doctype html public "-//w3c//dtd xhtml 1.0 strict//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
  <meta http-equiv="content-style-type" content="text/css"/>
  <title></title>
  <style type="text/css">
    body {
      font-family: pingfang sc light;
    }
    .center{
      text-align: center;
      width: 100%;
    }
  </style>
</head>
<body>
<!--第一页开始-->
<div class="page" >
  <div class="center"><p>${templatename}</p></div>
  <div><p>itext官网:${itexturl}</p></div>
  <div><p>freemarker官网:${freemarkerurl}</p></div>
  <div><p>jfreechart教程:${jfreecharturl}</p></div>
  <div>列表值:</div>
  <div>
    <#list scores as item>
      <div><p>${item}</p></div>
    </#list>
  </div>
</div>
<!--第一页结束-->
<!---分页标记-->
<span style="page-break-after:always;"></span>
<!--第二页开始-->
<div class="page">
  <div>第二页开始了</div>
  <!--外部链接-->
  <p>百度图标</p>
  <div>
    <img src="${imageurl}" alt="百度图标" width="270" height="129"/>
  </div>
  <!--动态生成的图片-->
  <p>气温变化对比图</p>
  <div>
    <img src="${picurl}" alt="我的图片" width="500" height="270"/>
  </div>
</div>


<!--第二页结束-->
</body>
</html>

2、获取模板内容并填充数据

/**
 * @description 获取模板
 */
public static string getcontent(string filename,object data){

  string templatepath=getpdftemplatepath(filename);//根据pdf名称查找对应的模板名称
  string templatefilename=gettemplatename(templatepath);
  string templatefilepath=gettemplatepath(templatepath);
  if(stringutils.isempty(templatepath)){
    throw new freemarkerexception("templatepath can not be empty!");
  }
  try{
    configuration config = new configuration(configuration.version_2_3_25);//freemarker配置
    config.setdefaultencoding("utf-8");
    config.setdirectoryfortemplateloading(new file(templatefilepath));//注意这里是模板所在文件夹,不是文件
    config.settemplateexceptionhandler(templateexceptionhandler.rethrow_handler);
    config.setlogtemplateexceptions(false);
    template template = config.gettemplate(templatefilename);//根据模板名称 获取对应模板
    stringwriter writer = new stringwriter();
    template.process(data, writer);//模板和数据的匹配
    writer.flush();
    string html = writer.tostring();
    return html;
  }catch (exception ex){
    throw new freemarkerexception("freemarkerutil process fail",ex);
  }
}

3、导出模板到pdf文件

/**
   * @description   导出pdf到文件
   * @param filename 输出pdf文件名
   * @param data   模板所需要的数据
   *
   */
public string exporttofile(string filename,object data){
   string htmldata= freemarkerutil.getcontent(filename, data);//获取freemarker的模板数据
  if(stringutils.isempty(savefilepath)){
    savefilepath=getdefaultsavepath(filename);//设置pdf文件输出路径
  }
  file file=new file(savefilepath);
  if(!file.getparentfile().exists()){
    file.getparentfile().mkdirs();
  }
  fileoutputstream outputstream=null;
  try{
    //设置输出路径
    outputstream=new fileoutputstream(savefilepath);
    //设置文档大小
    document document = new document(pagesize.a4);//itext新建pdf文档
    pdfwriter writer = pdfwriter.getinstance(document, outputstream);//设置文档和输出流的关系

    //设置页眉页脚
    pdfbuilder builder = new pdfbuilder(headerfooterbuilder,data);
    builder.setpresentfontsize(10);
    writer.setpageevent(builder);

    //输出为pdf文件
    converttopdf(writer,document,htmldata);
  }catch(exception ex){
    throw new pdfexception("pdf export to file fail",ex);
  }finally{
    ioutils.closequietly(outputstream);
  }
  return savefilepath;

}

4、测试工具类

 public string createpdf(object data, string filename){
      //pdf保存路径
      try {
        //设置自定义pdf页眉页脚工具类
        pdfheaderfooter headerfooter=new pdfheaderfooter();
        pdfkit kit=new pdfkit();
        kit.setheaderfooterbuilder(headerfooter);
        //设置输出路径
        kit.setsavefilepath("/users/fgm/desktop/pdf/hello.pdf”);//设置出书路径
        string savefilepath=kit.exporttofile(filename,data);
        return savefilepath;
      } catch (exception e) {
        log.error("pdf生成失败{}", exceptionutils.getfullstacktrace(e));
        return null;
      }
    
    }
  
 public static void main(string[] args) {
     reportkit360 kit=new reportkit360();
        templatebo templatebo=new templatebo();//配置模板数据
        templatebo.settemplatename("hello itext! hello freemarker! hello jfreechart!");
        templatebo.setfreemarkerurl("http://www.zheng-hang.com/chm/freemarker2_3_24/ref_directive_if.html");
        templatebo.setitexturl("http://developers.itextpdf.com/examples-itext5");
  
  templatebo.setjfreecharturl("http://www.yiibai.com/jfreechart/jfreechart_referenced_apis.html");
    templatebo.setimageurl("https://ss0.bdstatic.com/5av1bjqh_q23odcf/static/superman/img/logo/bd_logo1_31bdc765.png");
    
    
       list<string> scores=new arraylist<string>();
        scores.add("90");
        scores.add("95");
        scores.add("98");
        templatebo.setscores(scores);
        list<line> linelist=gettemperaturelinelist();
        temperaturelinechart linechart=new temperaturelinechart();
        string picurl=linechart.draw(linelist,0);//自定义的数据画图
        templatebo.setpicurl(picurl);
        string path= kit.createpdf(templatebo,"hello.pdf");
      system.out.println(path);
    
    }

六、生成效果图:

java根据模板动态生成PDF实例

七、项目完整代码

1、github地址:

八、遇到的坑:

1、freemarker配置模板文件样式,在实际pdf生成过程中,可能会出现一些不一致的情形,目前解决方法,就是换种方式调整样式。

2、字体文件放在resource下,在打包时会报错,运行mvn -x compile 会看到详细错误:

这是字体文件是二进制的,而maven项目中配置了资源文件的过滤,不能识别二进制文件导致的,plugins中增加下面这个配置就好了:

<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
  <!--增加的配置,过滤ttf文件的匹配-->
  <plugins>
    <plugin>
      <groupid>org.apache.maven.plugins</groupid>
      <artifactid>maven-resources-plugin</artifactid>
      <version>2.7</version>
      <configuration>
        <encoding>utf-8</encoding>
        <nonfilteredfileextensions>
          <nonfilteredfileextension>ttf</nonfilteredfileextension>
        </nonfilteredfileextensions>
      </configuration>
    </plugin>
  </plugins>
</build>

3、pdf分页配置:

在ftl文件中,增加分页标签: <span style="page-break-after:always;"></span>

九、 完整maven配置:

<!--pdf生成 itext-->
 
 <dependency>
    <groupid>com.itextpdf</groupid>
    <artifactid>itextpdf</artifactid>
    <version>5.4.2</version>
   </dependency>
 <dependency>
  <groupid>com.itextpdf.tool</groupid>
  <artifactid>xmlworker</artifactid>
  <version>5.4.1</version>
</dependency>
<dependency>
  <groupid>com.itextpdf</groupid>
  <artifactid>itext-asian</artifactid>
  <version>5.2.0</version>
</dependency>
<dependency>
  <groupid>org.xhtmlrenderer</groupid>
  <artifactid>flying-saucer-pdf</artifactid>
  <version>9.0.3</version>
</dependency>
<!--freemarker-->
<dependency>
  <groupid>org.freemarker</groupid>
  <artifactid>freemarker</artifactid>
  <version>2.3.26-incubating</version>
</dependency>
<!--jfreechart-->
<dependency>
  <groupid>jfreechart</groupid>
  <artifactid>jfreechart</artifactid>
  <version>1.0.0</version>
</dependency>
<!--log-->
<dependency>
  <groupid>ch.qos.logback</groupid>
  <artifactid>logback-core</artifactid>
  <version>1.0.13</version>
</dependency>
<dependency>
  <groupid>ch.qos.logback</groupid>
  <artifactid>logback-classic</artifactid>
  <version>1.0.13</version>
</dependency>
<dependency>
  <groupid>ch.qos.logback</groupid>
  <artifactid>logback-access</artifactid>
  <version>1.0.13</version>
</dependency>
<dependency>
  <groupid>org.slf4j</groupid>
  <artifactid>slf4j-api</artifactid>
  <version>1.7.5</version>
</dependency>
<dependency>
  <groupid>org.slf4j</groupid>
  <artifactid>log4j-over-slf4j</artifactid>
  <version>1.7.21</version>
</dependency>
<!--util-->
<dependency>
  <groupid>com.google.guava</groupid>
  <artifactid>guava</artifactid>
  <version>20.0</version>
</dependency>
<dependency>
  <groupid>org.projectlombok</groupid>
  <artifactid>lombok</artifactid>
  <version>1.14.8</version>
</dependency>
<dependency>
  <groupid>org.apache.commons</groupid>
  <artifactid>commons-io</artifactid>
  <version>1.3.2</version>
</dependency>
<dependency>
  <groupid>commons-lang</groupid>
  <artifactid>commons-lang</artifactid>
  <version>2.6</version>
</dependency>
<!--servlet-->
<dependency>
  <groupid>javax.servlet</groupid>
  <artifactid>servlet-api</artifactid>
  <version>2.5</version>
</dependency>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。