最新要闻
- 当前简讯:浙江女子1600公里追到广州找到被偷的爱猫:苹果AirTag定位器立了大功
- 比亚迪豪华MPV成了!腾势D9上月热销7325台:均价41.5万
- 简讯:俞敏洪最新演讲:不喜欢《狂飙》 企业家只想赚钱就会像高启强后患无穷
- 复旦MOSS团队:取名是致敬《流浪地球2》 参数规模约ChatGPT的1/10
- 世界热推荐:跑着跑着会熄火 日产北美召回超80万辆奇骏:车钥匙背锅
- 【全球新要闻】那舅特大桥建成 又一时速350高铁开铺 南宁至玉林仅50分钟
- 3899元起 惠普战66六代锐龙版上架:锐龙7000系列加持
- 世界热议:3月17日开启Beta测试!《暗黑破坏神4》新预告片透露更多游戏内容
- 焦点短讯!拳头《无畏契约》3月14日起不再支持Win7/8/8.1系统:为了打击外挂!
- 全球消息!苹果何时大降价?iPhone 14 Plus成系列销量最差:用户宁愿买安卓
- 焦点信息:寓言诚不欺我!网友拍下现实版“乌鸦喝水”
- 焦点报道:儿子篮球班倒闭家长花1000万买下 网友:这就是钞能力
- 全球热议:999元卷王小金刚!优派推出VX2758显示器:27英寸2K/170Hz
- 15万就能买特斯拉?特斯拉宣布重大目标:成本降低50%
- 哪吒汽车2月份交付10073台 同比大涨41.5%
- 未成年人沉迷短视频得治 TikTok默认限制每天可刷一小时
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
天天通讯!Java 根据模板导出PDF
- 前言
- 思路一:直接导出pdf
- 使用itext模板导出pdf
- 思路二:先导出word再转成pdf
- 1)导出word
- 2)word转pdf
- 最终方案
docx4jspire.doc.free + freemarker
前言
本文为我搜集的根据模板导出PDF的方法整理而来,所以贴了好多帖子的链接。有的方法仅适合特殊的业务场景,可以根据业务需求选择合适的方法。写的不好请轻喷。
思路一:直接导出pdf
使用itext模板导出pdf
适用范围
业务生成的 pdf 是具有固定格式或者模板的文字及其图片等内容,使用模板,只需要将不一致的地方改成文本域,然后进行文字填充就可以了;如果涉及的业务不能有模块化可以提取出来东西,从开头一步一步去绘画。
(相关资料图)
参考链接
JAVA 使用Itext模板生成pdf,解决图片插入,文本域超出字体缩放,半自动换行java根据模板生成pdf文件并导出
缺点
超出文本域的部分的文字(若不设置自动调整文字大小)则会不显示,无法自动分页。(暂未找到解决方案)
思路二:先导出word再转成pdf
1)导出word
Freemarker
官方参考手册:http://freemarker.foofun.cn/toc.html
Freemarker 将数据填入 .ftl 模板导出 word(.doc/.docx)
参考链接:
SpringBoot整合Freemarker导出word文档表格
freemarker导出Word,文本,可循环表格,合并单元格,可循环图片,目录更新(一)
缺点:
导出的 .doc / .docx 实际上是 xml 文件,用办公软件能正常打开使用。但是转 PDF 的时候发现转不成功。转过之后的 PDF 显示的不是 word 的格式字符,而是像 xml 文件的标签及字符。
Freemarker 结合 .docx 格式的本质将数据填入 .docx 里面的 document.xml文件导出 .docx
参考链接:
freemarker动态生成word并将生成的word转为PDF
优点:
可转换为 pdf
相关错误:
A. Date 格式的数据传输报错!解决方案:
${(initialTime?string("yyyy-MM-dd HH:mm:ss"))!}
附:
a. 循环行及表单行是否显示功能参考链接:
SpringBoot整合Freemarker导出word文档表格
freemarker合并单元格,if、else标签的使用,null、空字符串处理
如:
导出结果如下:
也可向参考链接2一样,在 xml 上定义插入对应的合并代码(如下图)。
docx4j
结合 .docx 格式的本质将数据填入 .docx 里面的 document.xml 文件导出 .docx
docx4j 中模板的使用
docx4j 实现动态表格(模板式)
docx4j 中图片的使用(模板式)
docx4j 实现动态表格(模板式)单元格合并(含多列并列合并)
POI
Poi的导出word和转pdf --已试过,可行
Java文件操作之word转pdf并导出(liunx和windows)
Aspose.word(需要license)(未尝试)
使用Aspose.word (Java) 填充word文档数据(包含图片填充)
2)word转pdf
docx4j 将 .docx 转 pdf
官方下载地址
方法一:使用 docx4j2.8.1在 docx 模板填入数据并且转 pdf
参考链接:docx4j Word文档转换pdf- 解决中文问题和变量替换
方法二:将 docx 转 pdf
参考链接:freemarker动态生成word并将生成的word转为PDF
相关错误:
A.导出的 PDF 乱码(检查 word 文件中的字体是否在字体库中)使用docx4j实现docx转pdf(解决linux环境下中文乱码问题)
B.注意:存在样式 bug!!!暂未找到解决方案。
Spire.Doc 实现 word (.doc / .docx)转 pdf(尝试了免费版,可行)
有付费版和免费版,免费版仅支持三页内的 word 转 pdf
aspose.word 将 word 转 pdf (未尝试)
需要license,破解方法1,破解方法2
最终方案
docx4jspire.doc.free + freemarker
模板准备
将占位变量名写在模板 testTemplate.docx的对应位置上,用
${}
包起来。把复制一份副本,将副本 .docx 的后缀名重命名为 .zip。解压后找到 /word/document.xml,编译器打开文件,代码格式化后,对比例如 ${repliedUserId} 的占位参数是否被拆开(如果拆开需手动修改),修改名字为 testDocument.xml。
将源模板 testTemplate.docx 和 testDocument.xml 放到相应位置。
maven依赖
org.freemarker freemarker 2.3.22 org.docx4j docx4j-JAXB-Internal 8.2.4 org.docx4j docx4j-export-fo 8.2.4 e-iceblue spire.doc.free 5.2.0 com.e-iceblue e-iceblue https://repo.e-iceblue.cn/repository/maven-public/ Controller
@PostMapping("/pdfExport")public ResponseEntity exportPdf(@RequestParam Map
params) { try { // 查找业务数据 TestEntity testEntity = testService.querySheet(params); // 格式转换时的暂存文件名 String fileUuid = UUID.randomUUID().toString().replaceAll("-", ""); String toDocxPath = "E://project//test//ToPDF//word//" + fileUuid + ".docx"; String toPdfPath = "E://project//test//ToPDF//pdf//" + fileUuid + ".pdf"; String toXmlPath = "E://project//test//ToPDF//xml//" + fileUuid + ".xml"; String docxTemplate = "E://project//test//ToPDF//template//testTemplate.docx"; // .xml转.docx(testDocument.xml表示在项目的相对路径下) XmlToDocx.toDocx("testDocument.xml",docxTemplate, toXmlPath, toDocxPath, testEntity); // .docx转.pdf WordToPdf.docxToPdf(toDocxPath, toPdfPath); // 下载pdf并删除本地pdf ResponseEntity response = WordToPdf.downloadPdf("这是PDF的名字啊", toPdfPath); return response; } catch (Exception e) { throw new BusinessException("下载PDF失败!" + e.getMessage()); }} XmlToDocx类
import java.io.*;import java.util.Enumeration;import java.util.Map;import java.util.zip.ZipEntry;import java.util.zip.ZipException;import java.util.zip.ZipFile;import java.util.zip.ZipOutputStream;/** * 其实docx属于zip的一种,这里只需要操作word/document.xml中的数据,其他的数据不用动 * * @author * */public class XmlToDocx { /** * * @param xmlTemplate xml的文件名 * @param docxTemplate docx的路径和文件名(.docx模板) * @param xmlTemp 填充完数据的临时xml * @param toFilePath 目标文件名 * @param object 需要动态传入的数据 */ public static void toDocx(String xmlTemplate, String docxTemplate, String xmlTemp, String toFilePath, Object object) { try { // 1.object是动态传入的数据 // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开// Writer w1 = new OutputStreamWriter(new FileOutputStream(xmlTemp), "gb2312"); Writer w1 = new OutputStreamWriter(new FileOutputStream(xmlTemp), "utf-8"); // 2.把object中的数据动态由freemarker传给xml XmlTplUtil.process(xmlTemplate, object, w1); // 3.把填充完成的xml写入到docx中 XmlToDocx xtd = new XmlToDocx(); File xmlTempFile = new File(xmlTemp); xtd.outDocx(xmlTempFile, docxTemplate, toFilePath); // 删除临时xml文件 xmlTempFile.delete(); }catch (Exception e) { e.printStackTrace(); } } /** * * @param documentFile 动态生成数据的docunment.xml文件 * @param docxTemplate docx的模板 * @param toFilePath 需要导出的文件路径 * @throws ZipException * @throws IOException */ public void outDocx(File documentFile, String docxTemplate, String toFilePath) throws ZipException, IOException { try { File docxFile = new File(docxTemplate); ZipFile zipFile = new ZipFile(docxFile); Enumeration extends ZipEntry> zipEntrys = zipFile.entries(); ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath)); int len = -1; byte[] buffer = new byte[1024]; while (zipEntrys.hasMoreElements()) { ZipEntry next = zipEntrys.nextElement(); InputStream is = zipFile.getInputStream(next); // 把输入流的文件传到输出流中 如果是word/document.xml由我们输入 zipout.putNextEntry(new ZipEntry(next.toString())); if ("word/document.xml".equals(next.toString())) { InputStream in = new FileInputStream(documentFile); while ((len = in.read(buffer)) != -1) { zipout.write(buffer, 0, len); } in.close(); } else { while ((len = is.read(buffer)) != -1) { zipout.write(buffer, 0, len); } is.close(); } } zipout.close(); } catch (Exception e) { e.printStackTrace(); } }}
WordToPdf类
import org.apache.commons.io.IOUtils;import org.docx4j.Docx4J;import org.docx4j.fonts.IdentityPlusMapper;import org.docx4j.fonts.Mapper;import org.docx4j.fonts.PhysicalFonts;import org.docx4j.openpackaging.exceptions.Docx4JException;import org.docx4j.openpackaging.packages.WordprocessingMLPackage;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import java.io.*;import java.net.URLEncoder;public class WordToPdf { /** * (Docx4J).docx转.pdf(当docx的一行全是英文以及标点符号时转换的Pdf那一行会超出范围 https://segmentfault.com/q/1010000043372748) * @param docxPath docx文件路径 * @param pdfPath 输出的pdf文件路径 * @throws Exception */ @Deprecated public static boolean docxToPdf(String docxPath, String pdfPath) throws Exception { FileOutputStream out = null; try { File docxfile = new File(docxPath); WordprocessingMLPackage pkg = Docx4J.load(docxfile); Mapper fontMapper = new IdentityPlusMapper(); fontMapper.put("隶书", PhysicalFonts.get("LiSu")); fontMapper.put("宋体", PhysicalFonts.get("SimSun")); fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei")); fontMapper.put("黑体", PhysicalFonts.get("SimHei")); fontMapper.put("楷体", PhysicalFonts.get("KaiTi")); fontMapper.put("新宋体", PhysicalFonts.get("NSimSun")); fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai")); fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong")); fontMapper.put("仿宋", PhysicalFonts.get("FangSong")); fontMapper.put("幼圆", PhysicalFonts.get("YouYuan")); fontMapper.put("华文宋体", PhysicalFonts.get("STSong")); fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong")); fontMapper.put("等线", PhysicalFonts.get("SimSun")); fontMapper.put("等线 Light", PhysicalFonts.get("SimSun")); fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo")); fontMapper.put("华文隶书", PhysicalFonts.get("STLiti")); fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei")); fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun")); fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti")); fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi")); fontMapper.put("华文细黑", PhysicalFonts.get("STXihei")); fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB")); fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312")); fontMapper.put("新細明體", PhysicalFonts.get("SimSun")); pkg.setFontMapper(fontMapper); out = new FileOutputStream(pdfPath); //docx4j docx转pdf FOSettings foSettings = Docx4J.createFOSettings();// foSettings.setWmlPackage(pkg); foSettings.setOpcPackage(pkg); Docx4J.toFO(foSettings, out, Docx4J.FLAG_EXPORT_PREFER_XSL);// Docx4J.toPDF(pkg, out); // 删除源.docx文件 docxfile.delete(); return true;// } catch (FileNotFoundException e) {// e.printStackTrace();// } catch (Docx4JException e) {// e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); return false; } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * https://www.e-iceblue.cn/spiredocforjavaconversion/java-convert-word-to-pdf.html * (spire.doc.free: * 免费版有篇幅限制。在加载或保存 Word 文档时,要求 Word 文档不超过 500 个段落,25 个表格。 * 同时将 Word 文档转换为 PDF 和 XPS 等格式时,仅支持转换前三页。) * (spire.doc.free)word转pdf * @param wordInPath word输入路径 * @param pdfOutPath Pdf输出路径 * @return */ public static boolean convertWordToPdf(String wordInPath, String pdfOutPath) { try { //实例化Document类的对象 Document doc = new Document(); //加载Word doc.loadFromFile(wordInPath); //保存为PDF格式 doc.saveToFile(pdfOutPath, FileFormat.PDF); return true; } catch (Exception e) {// e.printStackTrace(); return false; } finally { // 删除源word文件 File docxfile = new File(wordInPath); if (docxfile.exists()) { docxfile.delete(); } } }}
-
剑指 Offer 64. 求 1 + 2 + … + n(java解题)
leetcode《图解数据结构》剑指Offer64 求1+2+…+n(java解题)的解题思路和java代码,并附上java中常用...
来源: 天天通讯!Java 根据模板导出PDF
从菜鸟程序员到高级架构师,竟然是因为这个字final
剑指 Offer 64. 求 1 + 2 + … + n(java解题)
当前简讯:浙江女子1600公里追到广州找到被偷的爱猫:苹果AirTag定位器立了大功
比亚迪豪华MPV成了!腾势D9上月热销7325台:均价41.5万
简讯:俞敏洪最新演讲:不喜欢《狂飙》 企业家只想赚钱就会像高启强后患无穷
复旦MOSS团队:取名是致敬《流浪地球2》 参数规模约ChatGPT的1/10
世界热推荐:跑着跑着会熄火 日产北美召回超80万辆奇骏:车钥匙背锅
环球头条:Git介绍下载安装以及基本使用
全球新消息丨解释器模式
每日时讯!promethues【centos7】时间同步
What is Point ?
【全球新要闻】那舅特大桥建成 又一时速350高铁开铺 南宁至玉林仅50分钟
3899元起 惠普战66六代锐龙版上架:锐龙7000系列加持
世界热议:3月17日开启Beta测试!《暗黑破坏神4》新预告片透露更多游戏内容
焦点短讯!拳头《无畏契约》3月14日起不再支持Win7/8/8.1系统:为了打击外挂!
论文阅读笔记(四):AS-MLP AN AXIAL SHIFTED MLP ARCHITECTUREFOR VISION
(数据库系统概论|王珊)第七章数据库设计-第五、六节:物理结构设计和数据库的实施和维护
全球消息!苹果何时大降价?iPhone 14 Plus成系列销量最差:用户宁愿买安卓
焦点信息:寓言诚不欺我!网友拍下现实版“乌鸦喝水”
焦点报道:儿子篮球班倒闭家长花1000万买下 网友:这就是钞能力
全球热议:999元卷王小金刚!优派推出VX2758显示器:27英寸2K/170Hz
15万就能买特斯拉?特斯拉宣布重大目标:成本降低50%
哪吒汽车2月份交付10073台 同比大涨41.5%
未成年人沉迷短视频得治 TikTok默认限制每天可刷一小时
“自己造自己” 特斯拉人形机器人亮相!马斯克承认罕见事实
【世界速看料】腾讯新游《黎明觉醒:生机》开放60帧:骁龙888、iPhone 13以上都能开
每日视点!印度男子展示绝技“乌鸦召唤术” 网友:在古代至少巫师级别
读Java性能权威指南(第2版)笔记06_数据库性能JPA&SpringData
全省严查!正在进行!
每日看点!马斯克大力推荐!特斯拉Cybertruck实车亮相:超级未来感
天天消息!马斯克宏图计划公布:储能240TWh 制造投资10万亿美元
日本死亡人数是新生儿数量两倍有多可怕:850万“幽灵屋”遍布全国
信息:特斯拉下一代电机将不需要任何稀土成分!马斯克挑战全球车企
环球新动态:Spark系列 - (5) Spark Shuffle
热消息:Fireasy3 揭秘 -- 万物伊始(依赖注入与服务发现)
全球信息:英语四级阅读技巧
一加Ace 2V 12+256G起步行业罕见:友商还在搞8+128卡价位的版本
实时:Redmi Note 12 Pro极速版12+256G到手1999元:开机就是MIUI 14
造车新势力2月交付量出炉:理想、蔚来、哪吒破万 零跑压力大
【全球速看料】厦门征求意见!过马路玩手机或将罚款50元 你支持吗?
世界快看:东风概念飞行汽车外观曝光!“鸥翼门”相当炫酷
【当前热闻】2018巴彦淖尔国际马拉松
环球焦点!胡明轩:平时杜导叫我和徐杰一起训练 要求我们承担起更多责任
世界快讯:makefile
基于alpine基础镜像构建jdk镜像以及tomcat镜像及业务构建
Linux极简入门系列(六):其它补充
CSS全局关键字
环球聚焦:委员建议隔周三休成热搜第一 网友吵翻 专家:很难行得通
今日热闻!Model 2明天发?这款15万的特斯拉便宜车:马斯克已经说了17年
环球新消息丨LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)
环球快资讯:MySQL学习笔记-多表查询(上)
当前视讯!量化交易基础 - 011 - 样本外检验
风语筑(603466):上海风语筑文化科技股份有限公司关于股东权益变动比例超过1%的提示性公告
天天观察:希望工程发文感谢《原神》玩家 5天9万多人捐赠240万元
世界聚焦:“刺客”又来了!网友称买到1600元一斤话梅:每颗至少20元
“窄边教科书”上新!戴尔XPS15 9530发布:13代酷睿+RTX 40配8TB SSD
环球微动态丨孟菲斯动物园发大熊猫丫丫新动态 网友:尽快回国!
曝苹果屏下Face ID技术有缺憾:2026年才会趋于完美
C++ STL学习笔记-C++ STL基础
焦点讯息:4-Ribbon负载均衡
信息:可取代eSIM:更完美的iSIM卡来了
二月浏览器大战结果出炉:微软Edge用户数不升反降
头条:《王者荣耀》干将莫邪画中仙皮肤公布:中国古风莫邪绝美
环球微头条丨k8s之list-watch机制、节点调度以及亲和性
全球速讯:记录--虚拟滚动探索与封装
天天百事通!(数据库系统概论|王珊)第七章数据库设计-第四节:逻辑结构设计
焦点热议:Cesium 几何体贴模型 sampleHeight(二十二)
环球滚动:苏富比春拍上海预展即将开展,近150件藏品由谁保驾护航?
全球即时看!蔚来2022年财报公布:全年营收492亿元 同比大涨36%
天天资讯:建议元宵节放假1天:提升人民幸福指数
环球通讯!超19万辆!比亚迪2月新能源销量公布:暴打新势力全家
全球微头条丨2023五一档电影增至五部!哪部对你吸引力更大?
热点!Cesium Transform(二十)
世界速讯:第124篇: 期约Promise
怎么登录新浪微博网页版_如何登陆新浪微博
环球快报:刹车变硬踩不动遭车主集体投诉 铃木召回超7.8万辆汽车
【独家】好利来创始人之子回应开劳斯莱斯摆摊:没想博眼球
天天快播:AI小姐姐比真人还好看? N卡又抓到风口:8GB显存稳定绘图 首选RTX30/40系
春丽今天55岁了!网友:Coser我永远只服成龙大哥
速讯:URLDNS链分析
认识数据标签
每日速递:Python识别图形验证码实战项目
全球播报:记一次CPU占用持续上升问题排查(Nacos动态路由引起)
iOS应用发布ITMS-90704错误解决
荣耀“青海湖技术”揭晓:荣耀Magic5系列全球首发硅碳负极技术
国内专属!新款国产特斯拉Model Y升级悬架:终于不颠了
天天热门:功耗开放470W!影驰名人堂RTX 4080真是生猛
每日热讯!又一游戏成功“入奥”:育碧《舞力全开》入选2023年奥林匹克电子竞技项目
天天头条:女子幼儿园收童子尿煮鸡蛋 吃着香是浙江当地非遗:网友直呼酸爽
今日热闻!中国通才教育:已针对首次公开发售相关指控开展独立调查,将继续停牌
全球百事通!为什么95%的Java程序员人,都是用不好Synchronized?
每日时讯!Python教程:类的派生
你有“ChatGPT综合征”吗:想搞钱,或是失业焦虑?
Python教程:类的继承,什么是继承
加点广告怎么了 爱奇艺新专利可在弹幕中显示广告
环球动态:狂飙8000MHz!朗科Z RGB DDR5-8000 16GB电镀银内存图赏
每日短讯:1:1复刻仿生人手 现实版《西部世界》公司众筹开启
全球头条:5G是高铁 6G就是飞机!工信部:全面推进6G技术研发
焦点热文:公司丢货要求全体员工均摊1万赔款:新员工拒赔反被怀疑偷东西