利用Freemarker动态生成PDF文档

简介: 利用Freemarker动态生成PDF文档

@[TOC]

1 依赖

  • 用到的依赖
      <dependency>
              <groupId>org.freemarker</groupId>
              <artifactId>freemarker</artifactId>
              <version>2.3.26-incubating</version>
          </dependency>
          <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>
    

    2 写freemarker工具类

  • freemarker读取模板,替换标签值。
    ```java
    import freemarker.cache.FileTemplateLoader;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateException;
    import freemarker.template.TemplateExceptionHandler;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;

/**

  • freemarker工具类
    *
  • @Author:胡云峰
  • @Date:2023/4/14 16:38
    */
    public class FreeMarkerUtil {

    /**

    • 获取模板字符串
      *
    • @param templatesPath 模板路径
    • @param templateName 模板名称
    • @param data 替换数据
    • @return
      */
      public static String getContent(String templatesPath,String templateName,Object data){
      // 获取配置
      StringWriter writer = new StringWriter();
      try {

       Template template = getConfiguration(templatesPath).getTemplate(templateName);
       template.process(data,writer);
      

      } catch (IOException e) {

       e.printStackTrace();
      

      } catch (TemplateException e) {

       e.printStackTrace();
      

      } finally {

       writer.flush();
       return writer.toString();
      

      }
      }

      /**

    • 获取配置
    • @param templateFilePath 模板路径
    • @return
      */
      private static Configuration getConfiguration(String templateFilePath){
      // 设置配置信息
      Configuration config = new Configuration(Configuration.VERSION_2_3_25);
      config.setDefaultEncoding("UTF-8");
      config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
      config.setLogTemplateExceptions(false);
      // 获取模板加载器
      FileTemplateLoader fileTemplateLoader=null;
      try {
       fileTemplateLoader = new FileTemplateLoader(new File(templateFilePath));
      
      } catch (IOException e) {
       e.printStackTrace();
      
      }
      config.setTemplateLoader(fileTemplateLoader);
      return config;
      }

}

# 3 写生成pdf的工具类
* 生成pdf工具
```java
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * @Author:胡云峰
 * @Date:2023/4/15 9:01
 */
public class PdfUtil {

    /**
     * 输出内容到pdf
     *
     * @param outFilePath 输出路径
     * @param content 内容
     * @param fontPath 字体路径
     */
    public static void exportPdfFile(String outFilePath,String content,String fontPath){
        // pdf 文件
        File file = new File(outFilePath);
        // 如果文件所在的父目录不存在,则创建该目录及其所有父目录
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }

        //设置文档大小
        Document document = new Document(PageSize.A4);
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(file);
            // 创建PdfWriter实例
            PdfWriter writer = PdfWriter.getInstance(document, outputStream);

            // 设置页眉页脚
            MyPdfEvent myPdfEvent = new MyPdfEvent();
            myPdfEvent.setFontSize(15);
            writer.setPageEvent(myPdfEvent);

            // 输出为PDF文件
            document.open();
            XMLWorkerHelper.getInstance().parseXHtml(writer,document,
                    new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)),
                    XMLWorkerHelper.class.getResourceAsStream("/default.css"),
                    StandardCharsets.UTF_8,
                    new XMLWorkerFontProvider("E:/siyuan/fonts"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            document.close();
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

4 继承PdfPageEventHelper绑定事件

  • 设置页眉、页脚
    ```java
    import com.itextpdf.text.;
    import com.itextpdf.text.pdf.
    ;

import java.io.IOException;

/**

  • 定义页面属性
    *
  • @Author:胡云峰
  • @Date:2023/4/15 9:48
    */
    public class MyPdfEvent extends PdfPageEventHelper {

    /**

    • 字体文件名称
      */
      private String fontFileName;

      /**

    • 基础字体
      */
      private BaseFont baseFont;

      /**

    • 用于生成中文
      */
      private Font font;

      /**

    • 文档字体大小
      */
      private int fontSize;

      /**

    • 模板
      */
      private PdfTemplate templateFoot;

      /**

    • 页头模板
      */
      private PdfTemplate templateHeader;

      /**

    • 数据实体
      */
      private Object data;

      public MyPdfEvent() {
      }

      /*

    • 关闭每页的时候,写入页眉,页脚等
      /
      @Override
      public void onEndPage(PdfWriter writer, Document document) {
      //1.初始化字体
      initFront();
      //2.写入页眉
      writeHeader(writer,document,this.font);
      // 3.写入前半部分页脚
      try {

       writeFooter(writer,document,data,this.font);
      

      } catch (DocumentException e) {

       e.printStackTrace();
      

      } catch (IOException e) {

       e.printStackTrace();
      

      }
      }

      /**

    • @description 初始化字体
      */
      private void initFront(){
      try {

       if (this.baseFont == null) {
           //添加字体,以支持中文
      

      // String fontPath = "E:/siyuan/fonts/华康少女体W5.ttf";

           String fontPath = MyPdfEvent.class.getClassLoader().getResource("fonts/ping_fang_light.ttf").getPath();
           //创建基础字体
           this.baseFont = BaseFont.createFont(fontPath,BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
       }
       if (this.font == null) {
           // 数据体字体
           this.font = new Font(this.baseFont, fontSize, Font.NORMAL);
       }
      

      } catch (DocumentException e) {

       e.printStackTrace();
      

      } catch (IOException e) {

       e.printStackTrace();
      

      }
      }

      /**

    • @param writer PDF编写类
    • @param document PDF文档对象
    • @param font 字体设置
    • @description PDF页头设置类
      */
      private static void writeHeader(PdfWriter writer, Document document, Font font) {
      PdfContentByte cb = writer.getDirectContent();
      // 页眉横坐标居中
      float x = (document.left() + document.right()) / 2;
      // 页眉纵坐标
      float y = document.top() + 20;

      // 绘制页眉 居中
      ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, new Phrase("我是页眉", font), x, y, 0);

      // 绘制页眉下划线
      cb.setLineWidth(0.5f);
      cb.moveTo(document.left(), y-2);
      cb.lineTo(document.right(), y-2);
      cb.stroke();
      }

      /**

    • @param writer PDF编写类
    • @param document PDF文档对象
    • @param data 业务数据
    • @param font 字体设置
    • @description PDF页脚设置类
      */
      public void writeFooter(PdfWriter writer, Document document, Object data, Font font) throws DocumentException, IOException {
      // if (data == null) {
      // return;
      // }
      int pageS = writer.getPageNumber();
      int currentPage = pageS ;
      if (currentPage <= 0) {

       return;
      

      }

      Phrase footer1 = new Phrase( "2023/04/15", font);
      Phrase footer2 = new Phrase(currentPage + "/", font);

      PdfContentByte cb = writer.getDirectContent();
      ColumnText.showTextAligned(

           cb,
           Element.ALIGN_LEFT,
           footer1,
           (document.left() + 10),
           document.bottom() - 20,
           0);
      

      ColumnText.showTextAligned(

           cb,
           Element.ALIGN_RIGHT,
           footer2,
           (document.right() - 30),
           document.bottom() - 20, 0);
      

      //设置模板位置
      cb.addTemplate(this.templateFoot, document.right() - 30, document.bottom() - 20);
      }

      @Override
      public void onOpenDocument(PdfWriter writer, Document document) {
      this.templateFoot = writer.getDirectContent().createTemplate(50,50);
      }

      /*

    • 关闭文档时,替换模板,完成整个页眉页脚组件
      /
      @Override
      public void onCloseDocument(PdfWriter writer, Document document) {
      templateFoot.beginText();
      templateFoot.setFontAndSize(this.baseFont,this.fontSize);
      int total = writer.getPageNumber() - 1;
      templateFoot.showText(total+ "");
      templateFoot.endText();
      templateFoot.closePath();
      }

      public String getFontFileName() {
      return fontFileName;
      }

      public void setFontFileName(String fontFileName) {
      this.fontFileName = fontFileName;
      }

      public BaseFont getBaseFont() {
      return baseFont;
      }

      public void setBaseFont(BaseFont baseFont) {
      this.baseFont = baseFont;
      }

      public Font getFont() {
      return font;
      }

      public void setFont(Font font) {
      this.font = font;
      }

      public int getFontSize() {
      return fontSize;
      }

      public void setFontSize(int fontSize) {
      this.fontSize = fontSize;
      }

      public PdfTemplate getTemplateFoot() {
      return templateFoot;
      }

      public void setTemplateFoot(PdfTemplate templateFoot) {
      this.templateFoot = templateFoot;
      }

      public PdfTemplate getTemplateHeader() {
      return templateHeader;
      }

      public void setTemplateHeader(PdfTemplate templateHeader) {
      this.templateHeader = templateHeader;
      }

      public Object getData() {
      return data;
      }

      public void setData(Object data) {
      this.data = data;
      }
      }

      # 5 测试
      ## 5.1 准备数据
      * 测试数据
      ```java
      static {
       // 设置个人信息
       person.setName("思源");
       person.setAge(1);
       person.setAvatar("C:\\Users\\Administrator\\Desktop\\siyuan.png");
       person.setSex("1");
       LocalDate localBirthDate = LocalDate.of(2022, 6, 18);
       Date birthDate = Date.from(localBirthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
       person.setBirthDate(birthDate);
       // 添加宠物
       Pet petDog = new Pet("老大","狗");
       Pet petCat = new Pet("老二","猫");
       Pet petDog2 = new Pet("老三","狗");
       Pet petCat2 = new Pet("老四","猫");
       List<Pet> pets = Arrays.asList(petDog, petCat,petDog2,petCat2);
       person.setPetList(pets);
      }
      

      5.2 测试

      • 发送get请求,测试,返回生成pdf路径
        @GetMapping("/genePdf")
        public String generatePdf(){
                 
                 
        String temPath = TestController.class.getClassLoader().getResource("templates").getPath();
        String content = FreeMarkerUtil.getContent(temPath,"思源.ftl", person);
        // 定义输出文件路径
        String outFile = siYuanConfig.getOutPath() + "/" + "siyuan.pdf";
        PdfUtil.exportPdfFile(outFile,content,siYuanConfig.getFontPath());
        return outFile;
        }
        

        5.3 测试结果

      • 生成成功
        在这里插入图片描述
      • pdf内容如下:
        在这里插入图片描述
目录
相关文章
|
8月前
|
Ubuntu Java Linux
在Spring Boot中使用iTextPDF创建动态PDF文档
iTextPDF 是一个用于创建和操作 PDF(Portable Document Format)文档的流行的 Java 库。它提供了一套全面的功能,用于处理 PDF 文件,包括创建新文档、修改现有文档以及提取信息。
490 1
|
24天前
|
存储 运维 安全
《VERICUT 9.X电脑配置要求与安装说明(官方)》—— PDF文档
本文档详细介绍了 CGTech Vericut 9.X 的安装指南,涵盖安装前准备、安装流程、系统要求、故障排除、许可证配置及多种接口配置等内容,为用户提供全面的软件安装与配置指导。
48 2
|
8月前
|
数据挖掘 程序员 数据安全/隐私保护
解锁PDF潜力:9个Python库让你的文档处理更高效
程序员晚枫分享了Python处理PDF的9个第三方库,包括PyPDF2、pdfrw、ReportLab、pikepdf、pdfplumber、pdfminer.six、PyMuPDF、popdf和borb,各具优缺点。选择时需考虑应用场景、功能需求、库的维护状态和开源协议。例如,pdfplumber擅长内容提取,而ReportLab和PyMuPDF适用于创建和修改内容。
914 7
|
2月前
|
API C#
在.NET中使用QuestPDF高效地生成PDF文档
在.NET中使用QuestPDF高效地生成PDF文档
|
5月前
|
C# 开发者 Windows
WPF与PDF文档:解锁创建和编辑PDF文件的新技能——从环境配置到代码实践,手把手教你如何在WPF应用中高效处理PDF,提升文档管理效率
【8月更文挑战第31天】随着数字文档的普及,PDF因跨平台兼容性和高保真度成为重要格式。WPF虽不直接支持PDF处理,但借助第三方库(如iTextSharp)可在WPF应用中实现PDF的创建与编辑。本文通过具体案例和示例代码,详细介绍了如何在WPF中集成PDF库,并展示了从设计用户界面到实现PDF创建与编辑的完整流程。不仅包括创建新文档的基本步骤,还涉及在现有PDF中添加页眉页脚等高级功能。通过这些示例,WPF开发者可以更好地掌握PDF处理技术,提升应用程序的功能性和实用性。
211 0
|
5月前
|
开发框架 前端开发 JavaScript
在Winform分页控件中集成导出PDF文档的功能
在Winform分页控件中集成导出PDF文档的功能
|
5月前
[PDF提取重命名]提取识别文字并对PDF文件批量重命名,提取PDF指定可复制的内容并批量重命名PDF,批量PDF文档指定识别提取区域
本文介绍一款实用工具,能快速从可复制内容的PDF中提取指定区域信息并据此重命名文件。设置提取坐标及导入PDF文档、设定新文件名后启动提取流程,即可高效批量处理。保存坐标设置以便重复使用,适用于需频繁修改大量PDF文件名的场景。
539 0
[PDF提取重命名]提取识别文字并对PDF文件批量重命名,提取PDF指定可复制的内容并批量重命名PDF,批量PDF文档指定识别提取区域
|
6月前
|
JavaScript Java
Java 将Markdown文件转换为Word和PDF文档
【7月更文挑战第5天】Java中使用`Spire.Doc for Java`库可方便地将Markdown转换为Word或PDF。基本步骤包括导入模块,创建`Document`对象,加载Markdown文件,然后保存为目标格式(`.docx`或`.pdf`)。若遇到`Invalid UTF-8 stream`错误,需确保Markdown文件是UTF-8无BOM编码。页面设置可通过`PageSetup`类调整。注意,实际应用会依据具体需求和环境有所调整。
358 6
|
6月前
|
JavaScript 数据库
文本,在线浏览PDF,一个最简单的文档标准样式,文档预览非常简单的样式,文档管理样式设计,标准,好的设计
文本,在线浏览PDF,一个最简单的文档标准样式,文档预览非常简单的样式,文档管理样式设计,标准,好的设计
|
7月前
|
Java 数据安全/隐私保护
Java使用PDFBox开发包实现对PDF文档内容编辑与保存
Java使用PDFBox开发包实现对PDF文档内容编辑与保存
277 7