iText+freemarker 生成PDF 使用ftl模板

news/2024/11/17 3:51:37/

iText+freemarker 生成PDF

    • 生成样式图片
    • 模板地址
    • maven
    • PDF生成工具类
    • PDF生成辅助类
    • base64编码工具类
    • service方法调用
    • 总结

生成样式图片

根据模板生成

模板地址

在这里插入图片描述

maven

		<!--PDF  start--><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.1.5</version></dependency><dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-itext5</artifactId><version>9.1.5</version></dependency><!--PDF  end--><!--freemarker--><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.26-incubating</version></dependency><!--条形码--><dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.3.3</version></dependency>

PDF生成工具类

import com.lowagie.text.DocumentException;
import freemarker.core.ParseException;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;
import org.xhtmlrenderer.pdf.ITextRenderer;import java.io.IOException;
import java.io.OutputStream;/*** @Description PDF生成工具类* @author GGBind* @date 2021/12/28/10:03*/
public class PdfUtils {/*** 生成PDF到输出流中(ServletOutputStream用于下载PDF)** @param ftlPath       ftl模板文件的路径(不含文件名)* @param ftlName       ftl模板文件的名称(不含路径)* @param imageDiskPath 如果PDF中要求图片,那么需要传入图片所在位置的磁盘路径* @param data          输入到FTL中的数据* @param out      HttpServletResponse* @return* @throws TemplateNotFoundException* @throws MalformedTemplateNameException* @throws ParseException* @throws IOException* @throws TemplateException* @throws DocumentException*/public static OutputStream generateToServletOutputStream(String ftlPath, String ftlName, String imageDiskPath, Object data, OutputStream out) throws IOException, TemplateException, DocumentException {PdfHelper pdfHelper = new PdfHelper();String html =  pdfHelper.getPdfContent(ftlPath, ftlName, data);ITextRenderer render = null;render = pdfHelper.getRender();render.setDocumentFromString(html);if (null != imageDiskPath && !"".equals(imageDiskPath)) {//html中如果有图片,图片的路径则使用这里设置的路径的相对路径,这个是作为根路径render.getSharedContext().setBaseURL("file:/" + imageDiskPath);}render.layout();render.createPDF(out);render.finishPDF();return out;}
}

PDF生成辅助类

package com.saicmotor.maxus.yuejin.partorder.common.util.pdfUtil;import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import lombok.extern.slf4j.Slf4j;
import org.xhtmlrenderer.pdf.ITextRenderer;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Locale;/*** PDF生成辅助类* @author GGBind* @date 2022/2/09*/
@Slf4j
@SuppressWarnings("deprecation")
public class PdfHelper {public ITextRenderer getRender() throws DocumentException, IOException {ITextRenderer render = new ITextRenderer();//添加字体,以支持中文render.getFontResolver().addFont("/static/fonts/NSimSun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);return render;}/*** 获取要写入PDF的内容* @param ftlPath* @param ftlName* @param o* @return* @throws IOException* @throws TemplateException*/public String getPdfContent(String ftlPath, String ftlName, Object o) throws IOException, TemplateException {return useTemplate(ftlPath, ftlName, o);}/*** 使用freemarker得到html内容* @param ftlPath* @param ftlName* @param o* @return* @throws IOException* @throws TemplateException*/public String useTemplate(String ftlPath, String ftlName, Object o) throws IOException, TemplateException {String html = null;Template tpl = getFreemarkerConfig(ftlPath).getTemplate(ftlName);tpl.setEncoding("UTF-8");StringWriter writer = new StringWriter();tpl.process(o, writer);writer.flush();html = writer.toString();return html;}/*** 获取Freemarker配置** @param templatePath* @return* @throws IOException*/private Configuration getFreemarkerConfig(String templatePath) throws IOException {Version version = new Version("2.3.22");Configuration config = new Configuration(version);config.setClassLoaderForTemplateLoading(Thread.currentThread().getContextClassLoader(), "/templates");config.setEncoding(Locale.CHINA, "utf-8");return config;}/*** 获取类路径** @return*/public String getPath() {return PdfHelper.class.getResource("/").getPath().substring(1);}/*** 获取前端response的输出流* 并设置response 编码格式* @return 输出流out*/public ServletOutputStream setResponse(HttpServletResponse response, String Name) throws Exception{response.setContentType("application/pdf");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition", "attachment;filename="+ new String(Name.getBytes("utf-8"), "ISO8859-1") + ".pdf");ServletOutputStream out = response.getOutputStream();return out;}}

base64编码工具类

import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Encoder;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;/*** @Description* @Author GGBond* @Date 2022/2/14*/
@Slf4j
public class BarcodeUtil {/*** 给前端产生一个条形码** @param number 编码* @param width  宽度* @param height 高度*/public static String getCode(String number, Integer width, Integer height) {// 生成条形码BufferedImage image = getBarCode(number, width, height);// 使用流的方式ByteArrayOutputStream out = new ByteArrayOutputStream();try {ImageIO.write(image, "png", out);} catch (Exception e) {log.info("generate code error! error message:{}", "出现问题!");e.printStackTrace();}// 将流转成数组byte[] bytes = out.toByteArray();BASE64Encoder encoder = new BASE64Encoder();// 把生成的编码返回去return  encoder.encodeBuffer(bytes).trim();}/*** 产生条形码的方法** @param number 编码* @param width  宽度* @param height 高度*/public static BufferedImage getBarCode(String number, Integer width, Integer height) {try {BitMatrix bitMatrix = new MultiFormatWriter().encode(number, BarcodeFormat.CODE_128, width, height);return MatrixToImageWriter.toBufferedImage(bitMatrix);} catch (Exception e) {e.printStackTrace();}return null;}/*** 调用测试*/public static void main(String[] args) {String code = getCode("1120220202", 50, 50);System.out.println(code);}
}

service方法调用

PdfHelper pdfHelper = new PdfHelper();List<StockCheckDetailPdf> lists = baseMapper.getStockCheckDetailPdf(dto);ServletOutputStream out = pdfHelper.setResponse(response, "盘点单据");String path = pdfHelper.getPath();Map<String,Object> map = new HashMap<>(16);map.put("list",lists);String code = BarcodeUtil.getCode(dto.getInventoryNumber(), 80, 20);map.put("picture", code);map.put("builder",builder.toString());map.put("inWarehouse",documentCounting.getInWarehouse());map.put("printDate",String.valueOf(DateUtil.getCurrentDate()));//这里我们需要将生成的base64图片编码传到工具类里,进行单独设置,静态图片也是如此PdfUtils.generateToServletOutputStream(path, "/documentCountingPDF.ftl", code, map, out);out.flush();
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>PDF</title><style>/*设置编码格式*/body {font-family: NSimSun}/*设置纸张大小*/@page {size: a4}@media print {div.header-right {display: block;position: running(header-right);}/*设置每页结束的是表格,还是图片,还是其他的*//*img{page-break-inside:avoid;}*/table {page-break-inside: avoid;}}* {margin: 0;padding: 0;box-sizing: border-box;}section {display: block;margin: 20px 10px;}.preface p {line-height: 30px;}section > table {table-layout: fixed;width: 100%;margin: 20px 0px;text-align:center;word-wrap:break-word;border-collapse: collapse;border-style: solid;border-width: 1px 0px 0px 1px;}section table td {padding: 5px 0px;border-style: solid;border-width: 0px 1px 1px 0px;margin: 0;padding: 0;}.center {width: 5.8%;}.part {width: 159.1px;word-break: keep-all;white-space: nowrap;}</style>
</head>
<body>
<section><#--该list 是由service层的map对应,数据使用占位符去匹配${}--><#list list as item><#if item_index % 14 == 0 || item_index == 0><table cellSpacing="0" cellpadding="0" border="1" width="650px" align="center" ><colgroup><col class="center"></col><col class="part"></col><col class="part"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col class="center"></col><col style="width: 70px;word-break: keep-all;white-space: nowrap"></col></colgroup></#if><#if item_index == 0><tr style="width: 100%;"><td class="center" colspan="2"><img src="data:image/png;base64,${picture}"/></td><td class="center" colspan="7"><h4>实物盘点记录表</h4></td><td class="center" colspan="7"><h1>${builder}</h1></td></tr><tr style="width: 100%;"><td class="center" colspan="4">填报单位:</td><td class="center" colspan="2">存货类别:</td><td class="center" colspan="9">仓库名称(代码): ${inWarehouse}</td><td style="word-break: keep-all;white-space: nowrap;">${printDate}</td></tr></#if><#if item_index % 14 == 0 || item_index == 0><tr style="width: 100%;"><td class="center" rowspan="2">序号</td><td class="part" rowspan="2">件号</td><td class="part" rowspan="2">名称</td><td class="center" rowspan="2">规格</td><td class="center" rowspan="2">项目类</td><td class="center" rowspan="2">计量单位</td><td class="center" rowspan="2">外借量</td><td class="center" rowspan="2">库位</td><td class="center" rowspan="2">合计</td><td class="center" rowspan="2">货位</td><td class="center" colspan="3">实盘数量</td><td class="center" rowspan="2">盘点人</td><td class="center" rowspan="2">监盘人</td><td class="center" rowspan="2">备注</td></tr><tr style="width: 100%;"><td class="center">合计</td><td class="center">企业库存</td><td class="center">供应商库存</td></tr></#if><tr style="width: 100%;"><td class="center">${item_index +1}</td><td class="part">${item.accessoryItemNo}</td><td class="part"><#if item.partsChineseName?length < 8 >${item.partsChineseName}<#else>${item.partsChineseName?substring(0,7)}</#if></td><td class="center">&nbsp;&nbsp;</td><td class="center">&nbsp;&nbsp;</td><td class="center">${item.unit}</td><td class="center">${item.lendingVolume?default(0)}</td><td class="center"><#if item.atWarehouse?exists>${item.atWarehouse!}</#if></td><td class="center">&nbsp;&nbsp;</td><td class="center">&nbsp;&nbsp;</td><td class="center">${item.theoreticalInventory}</td><td class="center">${(item.theoreticalInventory?default(0)) - (item.lendingVolume?default(0))}</td><td class="center">&nbsp;&nbsp;</td><td class="center">&nbsp;&nbsp;</td><td class="center">&nbsp;&nbsp;</td><td class="center"><#if item.remark?exists>${item.remark!}</#if></td></tr><#--用来判断每页展示多少条数据,最后需要加上 “table” 标签--><#if (item_index+1) % 14 == 0 || (item_index+1) == list?size></table></#if></#list>
</section>
</body>
</html>

总结

需要注意的是:当前此方法不能控制表格中一行数据变长,自动向下延申后,每页的格式问题;

例如: 当前页14条数据占满本页后,如果表格中字段较长,14行表格会向下延申到第二页,甚至第三页,才会进行下一个表格的展示;目前本方案解决办法是将一页展示的数量变小了,给预留出足够的空白页让其展示;目前还在寻找方法中
因备注字数过长,分页控制就失效了
在这里插入图片描述

目前为止如果表格不够复杂,是够用的,其他的问题暂时还没碰到;


http://www.ppmy.cn/news/701131.html

相关文章

Delphi处理高速文件上传下载的代码及思路

Delphi处理高速文件上传下载的代码及思路 上传和下载是一对方向不同的概念&#xff0c;下面对应的客户端和服务器代码&#xff1a;掉个头&#xff0c;它就是下载&#xff1b;再掉个头&#xff0c;它就是上传。 一、思路 1、将大文件&#xff1a;分段&#xff08;即常说的“断点…

昂**诚供应链管理系统任意文件上传漏洞复现 CNVD-2023-26756

目录 1.漏洞概述 2.影响版本 3.漏洞等级 4.漏洞复现 5.Nuclei自动化扫描POC 5.修复建议

511遇见易语言资源表的导入和导出

易语言资源表添加声音资源&#xff0c;图片资源&#xff0c;图片组资源&#xff0c;和向资源表中导入可执行文件&#xff0c;将资源表中的资源导出&#xff0c;使用资源表中的资源&#xff0c;通过随机播放音乐&#xff0c;随机播放图片&#xff0c;已经把exe文件写出并执行做了…

百度OCR识别表格文字,并自动下载到本地(准确率很高)

一、输入文件及申请的Token import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.List; import java.util.Map; import j…

免费调用快递鸟物流跟踪轨迹订阅接口技术文档

物流跟踪由轨迹订阅接口和轨迹推送接口组成&#xff0c;对接时需要对接以下二个接口 1.轨迹订阅接口 1.1 功能说明 快递鸟物流轨迹订阅接口用于向快递鸟订阅物流轨迹信息。将订单内容通过订阅接口订阅到快递鸟&#xff0c;客户可自动获取运单的轨迹节点信息。 免费试用接口…

神策(Android)- 在曝光采集基础上学习项目架构

开篇的时候我就在想这篇blog到底有没有意义&#xff1f;因为本身使用的就是神策提供的功能&#xff0c;同时神策也提供了很完善的文档&#xff0c;而唯一要我们做的也仅仅是将它正确的集成到项目内&#xff0c;并且随着版本升级&#xff0c;文档肯定也会有一定变更… 不过&…

Flask boostrap实现图片视频上传下载展示

Flask boostrap实现图片视频上传下载展示 1、展示效果2、前端代码3、后端代码 1、展示效果 项目目录结构 2、前端代码 html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title>&l…

在Blender和Zbrush中创建激光指示器,新手硬表面建模码住!

大家好&#xff0c;今天云渲染小编给大家带来的分享是硬表面建模&#xff0c;CG艺术家Lyubov使用Blender和Zbrush创建激光指示器的幕后花絮。 介绍 我叫 Lyubov&#xff0c;来自俄罗斯圣彼得堡&#xff0c;是一名 3D 建模的初学者。虽然学习还不到一年&#xff0c;但是我对它…