本文简介
本文涉及pdf操作,如下:
- pdf模板制作
- 基于pdf模板生成,并支持下载
- 自定义中文字体
- 完全基于代码生成,并保存到指定目录
- 合并pdf,并保存到指定目录
- 合并pdf,并支持下载
基于pdf模板生成:适用于固定格式的pdf模板,基于内容进行填空,例如:合同信息生成、固定格式表格等等
完全基于代码生成:适用于不固定的pdf,例如:动态表格、动态添加某块内容、不确定的内容大小等不确定的场景
pdf文件简介
pdf是可移植文档格式,是一种电子文件格式,具有许多其他电子文档格式无法相比的优点。pdf文件格式可以将文字、字型、格式、颜色及独立于设备和分辨率的图形图像等封装在一个文件中。该格式文件还可以包含超文本链接、声音和动态影像等电子信息,支持特长文件,集成度和安全可靠性都较高。在系统开发中通常用来生成比较正式的报告或者合同类的电子文档。
代码实现pdf操作
首先需要引入我们的依赖,这里通过maven管理依赖
在pom.xml文件添加以下依赖
<!--pdf操作--> <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf --> <dependency> <groupid>com.itextpdf</groupid> <artifactid>itextpdf</artifactid> <version>5.5.6</version> </dependency> <!-- 这个主要用来设置样式什么的 --> <dependency> <groupid>com.itextpdf</groupid> <artifactid>itext-asian</artifactid> <version>5.2.0</version> </dependency>
基于pdf模板生成,并下载
pdf模板制作
首先在word或者其他软件里面制作模板,挑选你熟悉的软件即可,前提是可生成pdf。
将word文件转为pdf文件。
使用adobe acrobat软件操作pdf,这里用的是这个软件,只要能实现这个功能,其他的软件也可~
选择表单编辑哈,我们要在对应的坑上添加表单占位
在表单上添加文本域即可,所有的格式都用文本域即可,这里只是占坑。
对应的域名称要与程序的名称对应,方便后面数据填充,不然后面需要手动处理赋值。
创建个简单的模板吧,要注意填充的空间要充足,不然会出现数据展示不全呦~
效果如下:
好了,到这里模板就生成好了,我们保存一下,然后放在我们的/resources/templates
目录下
pdf生成代码编写
在util
包下创建pdfutil.java
工具类,代码如下:
package com.maple.demo.util; import com.itextpdf.text.document; import com.itextpdf.text.documentexception; import com.itextpdf.text.image; import com.itextpdf.text.rectangle; import com.itextpdf.text.pdf.*; import javax.servlet.servletoutputstream; import java.io.bytearrayoutputstream; import java.io.ioexception; import java.util.map; import java.util.objects; /** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >https://www.xiaoxiaofeng.com</a> */ public class pdfutil { private pdfutil() { } /** * 利用模板生成pdf * * @param data 写入的数据 * @param photomap 图片信息 * @param out 自定义保存pdf的文件流 * @param templatepath pdf模板路径 */ public static void filltemplate(map<string, object> data, map<string, string> photomap, servletoutputstream out, string templatepath) { pdfreader reader; bytearrayoutputstream bos; pdfstamper stamper; try { // 读取pdf模板 reader = new pdfreader(templatepath); bos = new bytearrayoutputstream(); stamper = new pdfstamper(reader, bos); acrofields acrofields = stamper.getacrofields(); // 赋值 for (string name : acrofields.getfields().keyset()) { string value = data.get(name) != null ? data.get(name).tostring() : null; acrofields.setfield(name, value); } // 图片赋值 for (map.entry<string, string> entry : photomap.entryset()) { if (objects.isnull(entry.getkey())) { continue; } string key = entry.getkey(); string url = entry.getvalue(); // 根据地址读取需要放入pdf中的图片 image image = image.getinstance(url); // 设置图片在哪一页 pdfcontentbyte overcontent = stamper.getovercontent(acrofields.getfieldpositions(key).get(0).page); // 获取模板中图片域的大小 rectangle signrect = acrofields.getfieldpositions(key).get(0).position; float x = signrect.getleft(); float y = signrect.getbottom(); // 图片等比缩放 image.scaleabsolute(signrect.getwidth(), signrect.getheight()); // 图片位置 image.setabsoluteposition(x, y); // 在该页加入图片 overcontent.addimage(image); } // 如果为false那么生成的pdf文件还能编辑,一定要设为true stamper.setformflattening(true); stamper.close(); document doc = new document(); pdfcopy copy = new pdfcopy(doc, out); doc.open(); pdfimportedpage importpage = copy.getimportedpage(new pdfreader(bos.tobytearray()), 1); copy.addpage(importpage); doc.close(); bos.close(); } catch (ioexception | documentexception e) { e.printstacktrace(); } } }
在controller
包下创建testpdfcontroller.java
类,并i代码如下:
package com.maple.demo.controller; import com.maple.demo.util.pdfutil; import io.swagger.annotations.api; import io.swagger.annotations.apioperation; import lombok.extern.slf4j.slf4j; import org.springframework.web.bind.annotation.getmapping; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.restcontroller; import javax.servlet.servletoutputstream; import javax.servlet.http.httpservletresponse; import java.io.ioexception; import java.util.hashmap; import java.util.map; /** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >https://www.xiaoxiaofeng.com</a> */ @slf4j @restcontroller @requestmapping("/example") @api(tags = "实例演示-pdf操作接口") public class testpdfcontroller { @apioperation(value = "根据pdf模板导出pdf") @getmapping("/exportpdf") public void exportpdf(httpservletresponse response) { map<string, object> datamap = new hashmap<>(16); datamap.put("nickname", "笑小枫"); datamap.put("age", 18); datamap.put("sex", "男"); datamap.put("csdnurl", "https://zhangfz.blog.csdn.net/"); datamap.put("siteurl", "https://www.xiaoxiaofeng.com/"); datamap.put("desc", "大家好,我是笑小枫。"); map<string, string> photomap = new hashmap<>(16); photomap.put("logo", "https://profile.csdnimg.cn/c/9/4/2_qq_34988304"); // 设置response参数,可以打开下载页面 response.reset(); response.setcharacterencoding("utf-8"); // 定义输出类型 response.setcontenttype("application/pdf;charset=utf-8"); // 设置名称 response.setheader("content-disposition", "attachment; filename=" + "xiaoxiaofeng.pdf"); try (servletoutputstream out = response.getoutputstream()) { // 模板路径记 pdfutil.filltemplate(datamap, photomap, out, "src/main/resources/templates/xiaoxiaofeng.pdf"); } catch (ioexception e) { e.printstacktrace(); } } }
重启项目,在浏览器访问:http://localhost:6666/example/exportpdf
导出的文件效果如下:
完全基于代码生成,并保存
完全基于代码生成pdf文件,这个就比较定制化了,这里只讲常见的操作,大家可以用作参考:
自定义字体
pdf生成的时候,有没有遇到过丢失中文字体的问题呢,唉~解决一下
先下载字体,这里用黑体演示哈,下载地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345
下载完会得到一个simhei.ttf
文件,对我们就是要它
先在util
包下创建一个字体工具类吧,代码如下:
注意:代码中相关的绝对路径要替换成自己的
package com.maple.demo.util; import com.itextpdf.text.*; import com.itextpdf.text.pdf.basefont; import com.itextpdf.text.pdf.pdfptable; import java.io.ioexception; import java.util.list; /** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >https://www.xiaoxiaofeng.com</a> */ public class pdffontutil { private pdffontutil() { } /** * 基础配置,可以放相对路径,这里演示绝对路径,因为字体文件过大,这里不传到项目里面了,需要的自己下载 * 下载地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345 */ public static final string font = "d:\\font/simhei.ttf"; /** * 基础样式 */ public static final font title_font = fontfactory.getfont(font, basefont.identity_h, 20, font.bold); public static final font node_font = fontfactory.getfont(font, basefont.identity_h, 15, font.bold); public static final font block_font = fontfactory.getfont(font, basefont.identity_h, 13, font.bold, basecolor.black); public static final font info_font = fontfactory.getfont(font, basefont.identity_h, 12, font.normal, basecolor.black); public static final font content_font = fontfactory.getfont(font, basefont.identity_h, basefont.not_embedded); /** * 段落样式获取 */ public static paragraph getparagraph(string content, font font, integer alignment) { paragraph paragraph = new paragraph(content, font); if (alignment != null && alignment >= 0) { paragraph.setalignment(alignment); } return paragraph; } /** * 图片样式 */ public static image getimage(string imgpath, float width, float height) throws ioexception, badelementexception { image image = image.getinstance(imgpath); image.setalignment(image.middle); if (width > 0 && height > 0) { image.scaleabsolute(width, height); } return image; } /** * 表格生成 */ public static pdfptable getpdftable(int numcolumns, float totalwidth) { // 表格处理 pdfptable table = new pdfptable(numcolumns); // 设置表格宽度比例为%100 table.setwidthpercentage(100); // 设置宽度:宽度平均 table.settotalwidth(totalwidth); // 锁住宽度 table.setlockedwidth(true); // 设置表格上面空白宽度 table.setspacingbefore(10f); // 设置表格下面空白宽度 table.setspacingafter(10f); // 设置表格默认为无边框 table.getdefaultcell().setborder(0); table.setpaddingtop(50); table.setsplitlate(false); return table; } /** * 表格内容带样式 */ public static void addtablecell(pdfptable datatable, font font, list<string> celllist) { for (string content : celllist) { datatable.addcell(getparagraph(content, font, -1)); } } }
pdf生成工具类
继续在util
包下的pdfutil.java
工具类里添加,因为本文涉及到的操作比较多,这里只贴相关代码,最后统一贴一个完成的文件
注意:代码中相关的绝对路径要替换成自己的
/** * 创建pdf,并保存到指定位置 * * @param filepath 保存路径 */ public static void createpdfpage(string filepath) { // fileoutputstream 需要关闭,释放资源 try (fileoutputstream outputstream = new fileoutputstream(filepath)) { // 创建文档 document document = new document(); pdfwriter writer = pdfwriter.getinstance(document, outputstream); document.open(); // 报告标题 document.add(pdffontutil.getparagraph("笑小枫的网站介绍", title_font, 1)); document.add(pdffontutil.getparagraph("\n网站名称:笑小枫(www.xiaoxiaofeng.com)", info_font, -1)); document.add(pdffontutil.getparagraph("\n生成时间:2022-07-02\n\n", info_font, -1)); // 报告内容 // 段落标题 + 报表图 document.add(pdffontutil.getparagraph("文章数据统计", node_font, -1)); document.add(pdffontutil.getparagraph("\n· 网站首页图\n\n", block_font, -1)); // 设置图片宽高 float documentwidth = document.getpagesize().getwidth() - document.leftmargin() - document.rightmargin(); float documentheight = documentwidth / 580 * 320; document.add(pdffontutil.getimage("d:\\xiaoxiaofeng.jpg", documentwidth - 80, documentheight - 80)); // 数据表格 document.add(pdffontutil.getparagraph("\n· 数据详情\n\n", block_font, -1)); // 生成6列的表格 pdfptable datatable = pdffontutil.getpdftable(6, 500); // 设置表格 list<string> tableheadlist = tablehead(); list<list<string>> tabledatalist = gettabledata(); pdffontutil.addtablecell(datatable, content_font, tableheadlist); for (list<string> tabledata : tabledatalist) { pdffontutil.addtablecell(datatable, content_font, tabledata); } document.add(datatable); document.add(pdffontutil.getparagraph("\n· 报表描述\n\n", block_font, -1)); document.add(pdffontutil.getparagraph("数据报告可以监控每天的推广情况," + "可以针对不同的数据表现进行分析,以提升推广效果。", content_font, -1)); document.newpage(); document.close(); writer.close(); } catch (exception e) { e.printstacktrace(); } } /** * 模拟数据 */ private static list<string> tablehead() { list<string> tableheadlist = new arraylist<>(); tableheadlist.add("省份"); tableheadlist.add("城市"); tableheadlist.add("数量"); tableheadlist.add("百分比1"); tableheadlist.add("百分比2"); tableheadlist.add("百分比3"); return tableheadlist; } /** * 模拟数据 */ private static list<list<string>> gettabledata() { list<list<string>> tabledatalist = new arraylist<>(); for (int i = 0; i < 3; i++) { list<string> tabledata = new arraylist<>(); tabledata.add("浙江" + i); tabledata.add("杭州" + i); tabledata.add("276" + i); tabledata.add("33.3%"); tabledata.add("34.3%"); tabledata.add("35.3%"); tabledatalist.add(tabledata); } return tabledatalist; }
在controller
包下的testpdfcontroller.java
类中添加代码,因为本文涉及到的操作比较多,这里只贴相关代码,最后统一贴一个完成的文件,相关代码如下:
注意:代码中相关的绝对路径要替换成自己的
@apioperation(value = "测试纯代码生成pdf到指定目录") @getmapping("/createpdflocal") public void create() { pdfutil.createpdfpage("d:\\xxf.pdf"); }
重启项目,在浏览器访问:http://localhost:6666/example/createpdflocal
可以在d:\\test
目录下看到xxf.pdf
文件
我们打开看一下效果:
合并pdf,并保存
继续在util
包下的pdfutil.java
工具类里添加,因为本文涉及到的操作比较多,这里只贴相关代码,最后统一贴一个完成的文件。
/** * 合并pdf文件 * * @param files 要合并文件数组(绝对路径如{ "d:\\test\\1.pdf", "d:\\test\\2.pdf" , "d:\\test\\3.pdf"}) * @param newfile 合并后存放的目录d:\\test\\xxf-merge.pdf * @return boolean 生成功返回true, 否則返回false */ public static boolean mergepdffiles(string[] files, string newfile) { boolean retvalue = false; document document; try (fileoutputstream fileoutputstream = new fileoutputstream(newfile)) { document = new document(new pdfreader(files[0]).getpagesize(1)); pdfcopy copy = new pdfcopy(document, fileoutputstream); document.open(); for (string file : files) { pdfreader reader = new pdfreader(file); int n = reader.getnumberofpages(); for (int j = 1; j <= n; j++) { document.newpage(); pdfimportedpage page = copy.getimportedpage(reader, j); copy.addpage(page); } } retvalue = true; document.close(); } catch (exception e) { e.printstacktrace(); } return retvalue; }
在controller
包下的testpdfcontroller.java
类中添加代码,因为本文涉及到的操作比较多,这里只贴相关代码,最后统一贴一个完成的文件,相关代码如下:
注意:代码中相关的绝对路径要替换成自己的
@apioperation(value = "测试合并pdf到指定目录") @getmapping("/mergepdf") public boolean mergepdf() { string[] files = {"d:\\test\\1.pdf", "d:\\test\\2.pdf"}; string newfile = "d:\\test\\xxf-merge.pdf"; return pdfutil.mergepdffiles(files, newfile); }
我们首先要准备两个文件d:\\test\\1.pdf,d:\\test\\2.pdf
,这里可以指定文件,也可以是生成的pdf文件。
如果是处理生成的文件,这里说下思想:程序创建一个临时目录,注意要唯一命名,然后将生成pdf文件保存到这个目录,然后从这个目录下拿到pdf进行处理,最后处理完成,保存到对应的目录下,删除这个临时目录和下面的文件。这里不做演示。《合并pdf,并下载》里面略有涉及,但是处理的单个文件,稍微改造即可
重启项目,在浏览器访问:http://localhost:6666/example/mergepdf
可以在d:\\test
目录下看到xxf-merge
文件
打开看一下效果:
合并pdf,并下载
继续在util
包下的pdfutil.java
工具类里添加,这里只贴将文件转为输出流,并删除文件的相关代码,合并相关代码见《合并pdf,并保存》的util类。
/** * 读取pdf,读取后删除pdf,适用于生成后需要导出pdf,创建临时文件 */ public static void readdeletepdf(string filename, servletoutputstream outputstream) { file file = new file(filename); if (!file.exists()) { system.out.println(filename + "文件不存在"); } try (inputstream in = new fileinputstream(filename)) { ioutils.copy(in, outputstream); } catch (exception e) { e.printstacktrace(); } finally { try { files.delete(file.topath()); } catch (ioexception e) { e.printstacktrace(); } } }
在controller
包下的testpdfcontroller.java
类中添加代码,因为本文涉及到的操作比较多,这里只贴相关代码,最后统一贴一个完成的文件,相关代码如下:
注意:代码中相关的绝对路径要替换成自己的
@apioperation(value = "测试合并pdf后并导出") @getmapping("/exportmergepdf") public void createpdf(httpservletresponse response) { // 设置response参数,可以打开下载页面 response.reset(); response.setcharacterencoding("utf-8"); // 定义输出类型 response.setcontenttype("application/pdf;charset=utf-8"); // 设置名称 response.setheader("content-disposition", "attachment; filename=" + "xiaoxiaofeng.pdf"); try (servletoutputstream out = response.getoutputstream()) { string[] files = {"d:\\test\\1.pdf", "d:\\test\\2.pdf"}; // 生成为临时文件,转换为流后,再删除该文件 string newfile = "src\\main\\resources\\templates\\" + uuid.randomuuid() + ".pdf"; boolean isok = pdfutil.mergepdffiles(files, newfile); if (isok) { pdfutil.readdeletepdf(newfile, out); } } catch (ioexception e) { e.printstacktrace(); } }
重启项目,在浏览器访问:http://localhost:6666/example/exportmergepdf
会提示我们下载。
完整代码
pdfutil.java
package com.maple.demo.util; import com.itextpdf.text.document; import com.itextpdf.text.documentexception; import com.itextpdf.text.image; import com.itextpdf.text.rectangle; import com.itextpdf.text.pdf.*; import org.apache.commons.compress.utils.ioutils; import javax.servlet.servletoutputstream; import java.io.*; import java.nio.file.files; import java.util.arraylist; import java.util.list; import java.util.map; import java.util.objects; import static com.maple.demo.util.pdffontutil.*; /** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >https://www.xiaoxiaofeng.com</a> */ public class pdfutil { private pdfutil() { } /** * 利用模板生成pdf * * @param data 写入的数据 * @param photomap 图片信息 * @param out 自定义保存pdf的文件流 * @param templatepath pdf模板路径 */ public static void filltemplate(map<string, object> data, map<string, string> photomap, servletoutputstream out, string templatepath) { pdfreader reader; bytearrayoutputstream bos; pdfstamper stamper; try { // 读取pdf模板 reader = new pdfreader(templatepath); bos = new bytearrayoutputstream(); stamper = new pdfstamper(reader, bos); acrofields acrofields = stamper.getacrofields(); // 赋值 for (string name : acrofields.getfields().keyset()) { string value = data.get(name) != null ? data.get(name).tostring() : null; acrofields.setfield(name, value); } // 图片赋值 for (map.entry<string, string> entry : photomap.entryset()) { if (objects.isnull(entry.getkey())) { continue; } string key = entry.getkey(); string url = entry.getvalue(); // 根据地址读取需要放入pdf中的图片 image image = image.getinstance(url); // 设置图片在哪一页 pdfcontentbyte overcontent = stamper.getovercontent(acrofields.getfieldpositions(key).get(0).page); // 获取模板中图片域的大小 rectangle signrect = acrofields.getfieldpositions(key).get(0).position; float x = signrect.getleft(); float y = signrect.getbottom(); // 图片等比缩放 image.scaleabsolute(signrect.getwidth(), signrect.getheight()); // 图片位置 image.setabsoluteposition(x, y); // 在该页加入图片 overcontent.addimage(image); } // 如果为false那么生成的pdf文件还能编辑,一定要设为true stamper.setformflattening(true); stamper.close(); document doc = new document(); pdfcopy copy = new pdfcopy(doc, out); doc.open(); pdfimportedpage importpage = copy.getimportedpage(new pdfreader(bos.tobytearray()), 1); copy.addpage(importpage); doc.close(); bos.close(); } catch (ioexception | documentexception e) { e.printstacktrace(); } } /** * 创建pdf,并保存到指定位置 * * @param filepath 保存路径 */ public static void createpdfpage(string filepath) { // fileoutputstream 需要关闭,释放资源 try (fileoutputstream outputstream = new fileoutputstream(filepath)) { // 创建文档 document document = new document(); pdfwriter writer = pdfwriter.getinstance(document, outputstream); document.open(); // 报告标题 document.add(pdffontutil.getparagraph("笑小枫的网站介绍", title_font, 1)); document.add(pdffontutil.getparagraph("\n网站名称:笑小枫(www.xiaoxiaofeng.com)", info_font, -1)); document.add(pdffontutil.getparagraph("\n生成时间:2022-07-02\n\n", info_font, -1)); // 报告内容 // 段落标题 + 报表图 document.add(pdffontutil.getparagraph("文章数据统计", node_font, -1)); document.add(pdffontutil.getparagraph("\n· 网站首页图\n\n", block_font, -1)); // 设置图片宽高 float documentwidth = document.getpagesize().getwidth() - document.leftmargin() - document.rightmargin(); float documentheight = documentwidth / 580 * 320; document.add(pdffontutil.getimage("d:\\xiaoxiaofeng.jpg", documentwidth - 80, documentheight - 80)); // 数据表格 document.add(pdffontutil.getparagraph("\n· 数据详情\n\n", block_font, -1)); // 生成6列的表格 pdfptable datatable = pdffontutil.getpdftable(6, 500); // 设置表格 list<string> tableheadlist = tablehead(); list<list<string>> tabledatalist = gettabledata(); pdffontutil.addtablecell(datatable, content_font, tableheadlist); for (list<string> tabledata : tabledatalist) { pdffontutil.addtablecell(datatable, content_font, tabledata); } document.add(datatable); document.add(pdffontutil.getparagraph("\n· 报表描述\n\n", block_font, -1)); document.add(pdffontutil.getparagraph("数据报告可以监控每天的推广情况," + "可以针对不同的数据表现进行分析,以提升推广效果。", content_font, -1)); document.newpage(); document.close(); writer.close(); } catch (exception e) { e.printstacktrace(); } } /** * 模拟数据 */ private static list<string> tablehead() { list<string> tableheadlist = new arraylist<>(); tableheadlist.add("省份"); tableheadlist.add("城市"); tableheadlist.add("数量"); tableheadlist.add("百分比1"); tableheadlist.add("百分比2"); tableheadlist.add("百分比3"); return tableheadlist; } /** * 模拟数据 */ private static list<list<string>> gettabledata() { list<list<string>> tabledatalist = new arraylist<>(); for (int i = 0; i < 3; i++) { list<string> tabledata = new arraylist<>(); tabledata.add("浙江" + i); tabledata.add("杭州" + i); tabledata.add("276" + i); tabledata.add("33.3%"); tabledata.add("34.3%"); tabledata.add("35.3%"); tabledatalist.add(tabledata); } return tabledatalist; } /** * 合并pdf文件 * * @param files 要合并文件数组(绝对路径如{ "d:\\test\\1.pdf", "d:\\test\\2.pdf" , "d:\\test\\3.pdf"}) * @param newfile 合并后存放的目录d:\\test\\xxf-merge.pdf * @return boolean 生成功返回true, 否則返回false */ public static boolean mergepdffiles(string[] files, string newfile) { boolean retvalue = false; document document; try (fileoutputstream fileoutputstream = new fileoutputstream(newfile)) { document = new document(new pdfreader(files[0]).getpagesize(1)); pdfcopy copy = new pdfcopy(document, fileoutputstream); document.open(); for (string file : files) { pdfreader reader = new pdfreader(file); int n = reader.getnumberofpages(); for (int j = 1; j <= n; j++) { document.newpage(); pdfimportedpage page = copy.getimportedpage(reader, j); copy.addpage(page); } } retvalue = true; document.close(); } catch (exception e) { e.printstacktrace(); } return retvalue; } /** * 读取pdf,读取后删除pdf,适用于生成后需要导出pdf,创建临时文件 */ public static void readdeletepdf(string filename, servletoutputstream outputstream) { file file = new file(filename); if (!file.exists()) { system.out.println(filename + "文件不存在"); } try (inputstream in = new fileinputstream(filename)) { ioutils.copy(in, outputstream); } catch (exception e) { e.printstacktrace(); } finally { try { files.delete(file.topath()); } catch (ioexception e) { e.printstacktrace(); } } } }
pdffontutil.java
package com.maple.demo.util; import com.itextpdf.text.*; import com.itextpdf.text.pdf.basefont; import com.itextpdf.text.pdf.pdfptable; import java.io.ioexception; import java.util.list; /** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >https://www.xiaoxiaofeng.com</a> */ public class pdffontutil { private pdffontutil() { } /** * 基础配置,可以放相对路径,这里演示绝对路径,因为字体文件过大,这里不传到项目里面了,需要的自己下载 * 下载地址:https://www.zitijia.com/downloadpage?itemid=281258939050380345 */ public static final string font = "d:\\font/simhei.ttf"; /** * 基础样式 */ public static final font title_font = fontfactory.getfont(font, basefont.identity_h, 20, font.bold); public static final font node_font = fontfactory.getfont(font, basefont.identity_h, 15, font.bold); public static final font block_font = fontfactory.getfont(font, basefont.identity_h, 13, font.bold, basecolor.black); public static final font info_font = fontfactory.getfont(font, basefont.identity_h, 12, font.normal, basecolor.black); public static final font content_font = fontfactory.getfont(font, basefont.identity_h, basefont.not_embedded); /** * 段落样式获取 */ public static paragraph getparagraph(string content, font font, integer alignment) { paragraph paragraph = new paragraph(content, font); if (alignment != null && alignment >= 0) { paragraph.setalignment(alignment); } return paragraph; } /** * 图片样式 */ public static image getimage(string imgpath, float width, float height) throws ioexception, badelementexception { image image = image.getinstance(imgpath); image.setalignment(image.middle); if (width > 0 && height > 0) { image.scaleabsolute(width, height); } return image; } /** * 表格生成 */ public static pdfptable getpdftable(int numcolumns, float totalwidth) { // 表格处理 pdfptable table = new pdfptable(numcolumns); // 设置表格宽度比例为%100 table.setwidthpercentage(100); // 设置宽度:宽度平均 table.settotalwidth(totalwidth); // 锁住宽度 table.setlockedwidth(true); // 设置表格上面空白宽度 table.setspacingbefore(10f); // 设置表格下面空白宽度 table.setspacingafter(10f); // 设置表格默认为无边框 table.getdefaultcell().setborder(0); table.setpaddingtop(50); table.setsplitlate(false); return table; } /** * 表格内容带样式 */ public static void addtablecell(pdfptable datatable, font font, list<string> celllist) { for (string content : celllist) { datatable.addcell(getparagraph(content, font, -1)); } } }
testpdfcontroller.java
package com.maple.demo.controller; import com.maple.demo.util.pdfutil; import io.swagger.annotations.api; import io.swagger.annotations.apioperation; import lombok.extern.slf4j.slf4j; import org.springframework.web.bind.annotation.getmapping; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.restcontroller; import javax.servlet.servletoutputstream; import javax.servlet.http.httpservletresponse; import java.io.ioexception; import java.util.hashmap; import java.util.map; import java.util.uuid; /** * @author 笑小枫 * @date 2022/8/15 * @see <a href="https://www.xiaoxiaofeng.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >https://www.xiaoxiaofeng.com</a> */ @slf4j @restcontroller @requestmapping("/example") @api(tags = "实例演示-pdf操作接口") public class testpdfcontroller { @apioperation(value = "根据pdf模板导出pdf") @getmapping("/exportpdf") public void exportpdf(httpservletresponse response) { map<string, object> datamap = new hashmap<>(16); datamap.put("nickname", "笑小枫"); datamap.put("age", 18); datamap.put("sex", "男"); datamap.put("csdnurl", "https://zhangfz.blog.csdn.net/"); datamap.put("siteurl", "https://www.xiaoxiaofeng.com/"); datamap.put("desc", "大家好,我是笑小枫。"); map<string, string> photomap = new hashmap<>(16); photomap.put("logo", "https://profile.csdnimg.cn/c/9/4/2_qq_34988304"); // 设置response参数,可以打开下载页面 response.reset(); response.setcharacterencoding("utf-8"); // 定义输出类型 response.setcontenttype("application/pdf;charset=utf-8"); // 设置名称 response.setheader("content-disposition", "attachment; filename=" + "xiaoxiaofeng.pdf"); try { servletoutputstream out = response.getoutputstream(); // 模板路径记 pdfutil.filltemplate(datamap, photomap, out, "src/main/resources/templates/xiaoxiaofeng.pdf"); } catch (ioexception e) { e.printstacktrace(); } } @apioperation(value = "测试纯代码生成pdf到指定目录") @getmapping("/createpdflocal") public void create() { pdfutil.createpdfpage("d:\\test\\xxf.pdf"); } @apioperation(value = "测试合并pdf到指定目录") @getmapping("/mergepdf") public boolean mergepdf() { string[] files = {"d:\\test\\1.pdf", "d:\\test\\2.pdf"}; string newfile = "d:\\test\\xxf-merge.pdf"; return pdfutil.mergepdffiles(files, newfile); } @apioperation(value = "测试合并pdf后并导出") @getmapping("/exportmergepdf") public void createpdf(httpservletresponse response) { // 设置response参数,可以打开下载页面 response.reset(); response.setcharacterencoding("utf-8"); // 定义输出类型 response.setcontenttype("application/pdf;charset=utf-8"); // 设置名称 response.setheader("content-disposition", "attachment; filename=" + "xiaoxiaofeng.pdf"); try (servletoutputstream out = response.getoutputstream()) { string[] files = {"d:\\test\\1.pdf", "d:\\test\\2.pdf"}; // 生成为临时文件,转换为流后,再删除该文件 string newfile = "src\\main\\resources\\templates\\" + uuid.randomuuid() + ".pdf"; boolean isok = pdfutil.mergepdffiles(files, newfile); if (isok) { pdfutil.readdeletepdf(newfile, out); } } catch (ioexception e) { e.printstacktrace(); } } }
以上就是springboot生成和操作pdf的代码详解的详细内容,更多关于springboot生成和操作pdf的资料请关注代码网其它相关文章!
发表评论