引言:
工作中需要使用生成pdf记录,选取使用的是itext 生成 pdf方式。分享下实现方式及遇到的问题。
实现效果
这里随便找个html课程表作为示例,添加了几张图片为了展示图片转pdf功能。
代码实现
一:引入jar包
<!--PDF导出POM-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.13</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>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
二:导入ftl文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>课程表</title>
<style>
body{
font-family: Microsoft YaHei;
}
table {
width: 80%;
margin: 0 auto;
border-collapse: collapse;
font-family: Arial, sans-serif;
}
th,
td {
border: 1px solid #999;
padding: 0.5rem;
text-align: center;
}
th {
background-color: #f2f2f2;
font-weight: bold;
}
td {
background-color: #ffffff;
}
h1 {
text-align: center;
font-family: Arial, sans-serif;
font-size: 24px;
margin-bottom: 1rem;
}
.img-style img{
width:28em;
}
.img-style{
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<h1>课程表</h1>
<table>
<thead>
<tr>
<th>时间\\星期</th>
<th>周一</th>
<th>周二</th>
<th>周三</th>
<th>周四</th>
<th>周五</th>
</tr>
</thead>
<tbody>
<tr>
<th>第一节</th>
<td>数学</td>
<td>语文</td>
<td>英语</td>
<td>历史</td>
<td>地理</td>
</tr>
<tr>
<th>第二节</th>
<td>物理</td>
<td>化学</td>
<td>生物</td>
<td>政治</td>
<td>体育</td>
</tr>
<tr>
<th>第三节</th>
<td>美术</td>
<td>音乐</td>
<td>计算机</td>
<td>科学实验</td>
<td>语文</td>
</tr>
<tr>
<th>第四节</th>
<td>英语</td>
<td>数学</td>
<td>地理</td>
<td>历史</td>
<td>政治</td>
</tr>
</tbody>
</table>
<div class='img-style'>
<img src='https://pic1.zhimg.com/v2-d58ce10bf4e01f5086c604a9cfed29f3_r.jpg?source=1940ef5c' />
<img src='https://pic1.zhimg.com/v2-d58ce10bf4e01f5086c604a9cfed29f3_r.jpg?source=1940ef5c' />
<img src='https://pic1.zhimg.com/v2-d58ce10bf4e01f5086c604a9cfed29f3_r.jpg?source=1940ef5c' />
</div>
</body>
</html>
这块使用的是html语法,将文件后缀名改为ftl即可,在需要参数的地方通过ftl语法赋值即可。ftl语法可自行百度。
这里给个示例:
<div class='img-style'>
<#if data??>
<#list data.images as image>
<img src='${image!''}'/>
</#list>
</#if>
</div>
首先#if判断data是否为空,为空不执行,不为空循环给img标签赋值
${image!‘’} 作用也是判断image是否为空,不为空则赋值image,为空则赋默认值单引号
三:java实现ftl转pdf
package org.jeecg.modules.lst.util;
import com.itextpdf.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.util.Locale;
/**
* 生成pdf文件工具类
*
* @author message丶小和尚
* @create 2020/01/10
*/
@Slf4j
public class PdfExportUtil {
/**
* 存放文件模板的地址
*/
/**
* 默认字体资源文件([宋体][simsun.ttc])
*/
//字体文件名称
private final static String DEFAULT_FONT = "yahei.ttf";
//编码格式
private final static String ENCODING = "UTF-8";
//模板文件夹相对路径
public final static String TEMPLATE_PATH = "templates/shopsummary/";
//模板名称
public final static String SHOP_PDF_TEMPLATE_NAME = "shopPDF.ftl";
public final static String TEST_PDF_TEMPLATE_NAME = "test.ftl";
/**
* 创建pdf文件.
* @param uploadPath 文件上传/生成目录
* @param fileName 文件名称
* @param templateCode 模板名称
* @param data 模板所需数据
*/
public static void createPDF(String uploadPath, String fileName, String templateName, Object data) {
try (FileOutputStream out = new FileOutputStream(uploadPath + fileName)) {
File foder = new File(uploadPath);
if (!foder.exists()) {
foder.mkdirs();
}
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));
ITextRenderer renderer = new ITextRenderer();
// 设置 css中 的字体样式(暂时仅支持宋体和黑体)
renderer.getFontResolver().addFont(TEMPLATE_PATH + DEFAULT_FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 设置模板的编码格式
cfg.setEncoding(Locale.CHINA, ENCODING);
// 获取模板文件 template.ftl
Template template = cfg.getTemplate(templateName, ENCODING);
StringWriter writer = new StringWriter();
// 将数据输出到html中
template.process(data, writer);
writer.flush();
String html = writer.toString();
writer.close();
// 把html代码传入渲染器中
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(out);
renderer.finishPDF();
out.flush();
log.info(String.format("create pdf %s succeed", fileName));
} catch (Exception e) {
log.error("PDF导出异常", e);
}
}
}
模板、字体存放路径
我这里是将pdf写入硬盘存储了,如果需要返回,可以使用其他outputStream流接受返回
四:测试生成文件
@Test
public void createPDFTest(){
PdfExportUtil.createPDF(uploadPath, fileName, PdfExportUtil.PDF_TEMPLATE_NAME, data);
System.out.println(new File(uploadPath + fileName).length());
}
输出文件长度,测试文件是否生成成功。
问题详情
一:PDF内容为空
原因:ftl 生成PDF需要设置本地字体,才可以显示中文。
SimSum – 宋体
Microsoft YaHei – 微软雅黑
字体资源没有上传成功,自行百度吧。网上资源很多
java代码中需要将字体资源加载到Itext,之后在ftl中的css样式中添加字体样式即可
示例:
<style>
body{
font-family: Microsoft YaHei;
}
</style>
二:图片不显示
这个问题我没遇到,但是看到网上很多人遇到,我分享下我使用图片的方式。
<!--http-->
<img src='https://pic1.zhimg.com/v2-d58ce10bf4e01f5086c604a9cfed29f3_r.jpg?source=1940ef5c'/>
<!--本地绝对路径-->
<img src='file:///D:\CodeWorkRepository\retail-cms-be\jeecg-module-system\jeecg-system-biz\src\main\resources\uploadFile\1.jpg'/>
上述两种方式都可以正常显示图片
三:本地测试无问题,线上报错找不到模板
原因:线上部署方式是docker 部署jar包方式。
打成jar包后项目本身就是一个文件,不能再用(File)获取文件的方式来读取,只能用流的方式来读取文件内容,本地之所以能运行,是因为IDE中的资源文件在target/classes目录下,是正常的文件系统结构。所以本质都是需要使用流来获取文件。
解决方案:使用input输入流读取文件内容,写入docker容器中。详情可以查看docker部署jar包找不到资源文件-jar包报错找不到资源文件
具体问题暂时想到了这三个,如果大家在使用过程中遇到其他问题,可以给我留言。看到后我会第一时间帮忙解决。
本篇文章到这里就结束了,多谢大家观看。
最后再整句励志语录:
不积跬步无以至千里!!!