利用Freemarker动态生成PDF文档

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读取模板,替换标签值。
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工具
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绑定事件

  • 设置页眉、页脚
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 准备数据

  • 测试数据
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内容如下:
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43684214/article/details/130594187