JAVA操作pdf——创建表格
JAVA操作pdf——创建表格
一、前言
在实习的时候遇到了需要将查询到的数据构建成为PDF的情况,于是在网上查找到了相关的Java操作pdf的教程,看到大部分的文章都是使用ITextPdf操作的,于是边借此机会写个笔记记录一下。
准备工作:
此次使用的是maven工具构建环境依赖,依赖如下:
<dependency><groupId>com.itextpdf</groupId><artifactId>kernel</artifactId><version>7.0.3</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>io</artifactId><version>7.0.3</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>layout</artifactId><version>7.0.3</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>font-asian</artifactId><version>7.0.3</version></dependency>
由于我在使用的时候这个依赖的版本已经是7.0.3了,在网上我找到的教程大都是5.0+版本的,所以结合网上的教程和自己的摸索,来探究这个包对pdf的使用方式是怎么样的
二、ItextPdf基本使用
创建一个pdf文档
// 准备写入一个pdf文件
// file对象中的路径是待写入文件的路径
File file = new File("C:/Users/achao/Desktop/hellowpdf.pdf");
// 创建文件输出流
FileOutputStream outputStream = new FileOutputStream(file);
// 创建pdf写入对象
PdfWriter writer = new PdfWriter(outputStream);
// 创建pdf文档对象
PdfDocument pdfDocument = new PdfDocument(writer);
// 创建documen对象(直接与硬盘关联)
Document document = new Document(pdfDocument, PageSize.A4);
// 往document中添加段落
document.add(new Paragraph("hellow pdf"));
// 关闭
document.close();
通过上面的方法,便创建了一个pdf文件在"C:/Users/achao/Desktop/hellowpdf.pdf"路径下,pdf中的内容是一个“hello pdf” 段落。
设置段落格式
// 设置段落的格式
// 对齐方式
paragraph.setTextAlignment(TextAlignment.CENTER); // 居中
paragraph.setTextAlignment(TextAlignment.LEFT); // 靠左
插入表格
File file = new File("C:/Users/achao/Desktop/hellowpdf.pdf");
FileOutputStream outputStream = new FileOutputStream(file);
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(writer);
Document document = new Document(pdfDocument, PageSize.A4);
// 这个数组的大小为表格的列数,里面的数值为列的宽度
float[] columnWidths = new float[]{10.0f, 10.0f, 10.0f, 10.0f};
// 这个布尔值参数的意思是横向拉伸表格,true按比例分配列宽
Table table = new Table(columnWidths, true);
// table和document关联
table.setDocument(document);
// 创建一个单元格
Cell cell = new Cell();
// 单元格添加内容
cell.add("cell1");
// 放进table中
table.addCell(cell);
// 以下的方式可以默认创建cell,但是无法设置更多的样式
table.addCell("string");
table.addCell("string");
table.addCell("string");
table.addCell("string");
table.addCell("string");
table.addCell("string");
table.addCell("string");
// 表格制作完成
table.complete();
document.close();
好了,这就是itext的简单使用,接下来我就要根据工作的具体需求来实现操作表格了。等一下?itex项目中不能用?因为它不是开源的,不能用于商业用途。。。。。
听到上面的话时,心想,那就换一个呗。
三、PdfBox基本使用
反正已经学了itext基础知识,现在学习PdfBox也应该是得心应手的,两者的代码漏记应该是差不多的吧!我略带自信地在网上寻找pdfbox的相关信息。
准备工作
maven依赖:
<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.21</version></dependency>
创建一个pdf文档
// 创建一个pdf对象PDDocument document = new PDDocument();// 向这个pdf对象中添加一个新的页面PDPage page = new PDPage();document.addPage(page);// 获取内容流PDPageContentStream contentStream = new PDPageContentStream(document, page);// 往pdf文件中写入字符串contentStream.showText("hellow pdfbox");// 内容输出流关闭contentStream.close();// 文档保存document.save("C:/Users/achao/Desktop/pdfbox.pdf");// 文档关闭document.close();
写了个hellow pdfbox之后,我开始沾沾自喜,切 ,这么简单,跟那个itext没什么两样嘛,真想不明白那个itext怎么要收费的,只有傻子才会花钱,买它。
后来,咦?啊这,这,这,这表格对象在哪里?我突然一阵懵逼,在网上疯狂搜索信息,就是没有找到它使用表格的类,只找到最最最原始的方法——画线!我一时间腿软了,问问项目组的人上面能不能花钱买个itext,那玩意好啊,真香。结果,贫穷剥削了我们的劳动力,别说了,撸起袖子加油干!
于是乎琢磨了一个下午,按照工作的需求我搞出了下面这些玩意~
绘制表格
public class PdfBox {public static void main(String[] args) throws Exception {List<Map<String, String>> mapList = new ArrayList<>();Map<String, String> map = new LinkedHashMap<>();map.put("sec_code", "NB");map.put("ap_code", "AP");map.put("ARN", "NBSF580002300");map.put("exp_ARN", "NBSF580002300");map.put("no_exp_ARN", "NBSF580002300");Map<String, String> map1 = new LinkedHashMap<>();map1.put("sec_code", "NB");map1.put("ap_code", "AP");map1.put("ARN", "NBSF580002300");map1.put("exp_ARN", "NBSF580002300");map1.put("no_exp_ARN", "NBSF580002300");Map<String, String> map2 = new LinkedHashMap<>();map2.put("sec_code2", "NB");map2.put("ap_code2", "AP");map2.put("ARN2", "NBSF580002300");map2.put("exp_ARN2", "NBSF580002300");map2.put("no_exp_ARN2", "NBSF580002300");Map<String, String> map3 = new LinkedHashMap<>();map3.put("sec_code3", "NB");map3.put("ap_code3", "AP");map3.put("ARN3", "NBSF580002300");map3.put("exp_ARN3", "NBSF580002300");map3.put("no_exp_ARN3", "NBSF580002300");mapList.add(map);mapList.add(map1);mapList.add(map2);mapList.add(map3);pringPdf(mapList);}public static void pringPdf(List<Map<String, String>> mapList) throws Exception {if (mapList == null || mapList.size() < 0) {throw new Exception("mapList is null or mapList`s size is not legal");} else {// 创建一个pdf对象PDDocument document = new PDDocument();// 向这个pdf对象中添加一个新的页面并获取内容流PDPageContentStream contentStream = insertPage(document);// 默认的表格宽度和高度float tableWidth = 500;float tableHeight = 700;// 计算map中的表头数量,确定表格的列数, 添加表头数据tableBodyText(document,contentStream, tableWidth, tableHeight, mapList);// 内容输出流关闭contentStream.close();// 文档保存document.save("C:/Users/achao/Desktop/pdfbox.pdf");// 文档关闭document.close();}}// 绘制表头并填充信息private static void tableBodyText(PDDocument document,PDPageContentStream contentStream, float tableWidth,float tableHeight,List<Map<String, String>> mapList) throws Exception {if (mapList == null || mapList.size() <= 0){throw new Exception("mapList is null or mapList`s size is not legal");}// 绘制表格外部方框drawTable(contentStream, 50, 50, tableWidth, tableHeight);// 表格横数
// int rows = (int) tableHeight / 50;int rows = 3; // test// 计算map中的表头数量,确定表格的列数, 添加表头数据Map<String, String> map = mapList.get(0);Set<String> keys = map.keySet();String[] strings = keys.toArray(new String[keys.size()]);// 按字符串的长度比例分配列宽度float[] wids = distributeWids(map, tableWidth);// 相邻两列的和一半float[] halfWids = new float[wids.length];for (int i = 0; i < wids.length; i++) {if (i==0) {halfWids[i] = wids[i] / 2;}else {halfWids[i] = (wids[i] + wids[i-1]) / 2;}}
// System.out.println(halfWids.length == strings.length);int size = mapList.size();int residue = 0;if (rows < size) {residue = size - rows;size = rows;}for (int i = 0; i < size; i ++){// 列的绘制坐标增量float a = 0;// 插入文字的横坐标增量float b = 0;if (i == 0){// 这个循环添加表头for (int j = 0; j < strings.length; j++) {a += wids[j];b += halfWids[j];// 这里的50是表格边缘的宽度drawLine(contentStream, 50 + a, 50, 50 + a, 50 + tableHeight);// 获取字符串的长度,能使它居中int len = strings[i].length();// 注意这里字体大小12的时候,是len*6/2insertText(contentStream, 50 + b - len * 6 / 2, 50 + tableHeight - 30, strings[j]);}drawLine(contentStream, 50, tableHeight, 50 + tableWidth, tableHeight);}String[] cells = mapList.get(i).values().toArray(new String[mapList.get(i).values().size()]);b = 0;// 插入每行的数据for (int k = 0; k < cells.length; k++) {b += halfWids[k];int len = cells[k].length();insertText(contentStream, 50 + b- len * 6 / 2, tableHeight-i*50-30, cells[k]);}drawLine(contentStream, 50, tableHeight-i*50, 50 + tableWidth, tableHeight-i*50);}if (residue > 0){contentStream.close();residue = mapList.size() - residue;// 对于剩下的数据,新创建page递归填充List<Map<String, String>> newMapList = new ArrayList<>();while (residue < mapList.size()){newMapList.add(mapList.get(residue));residue ++;}PDPageContentStream newcontentStream = insertPage(document);tableBodyText(document, newcontentStream, tableWidth, tableHeight, newMapList);}contentStream.close();}// 绘制表格,返回每条边的长度private static void drawTable(PDPageContentStream contentStream, float x, float y, float tableWidth,float tableHeight) {try {contentStream.setStrokingColor(Color.GRAY);// 左边drawLine(contentStream, x, y, x, y + tableHeight);// 上边drawLine(contentStream, x, y + tableHeight, x + tableWidth, y + tableHeight);// 右边drawLine(contentStream, x + tableWidth, y + tableHeight, x + tableWidth, y);// 下边drawLine(contentStream, x + tableWidth, y, x, y);} catch (IOException e) {e.printStackTrace();}}// 绘制线段private static void drawLine(PDPageContentStream contentStream, float startX, float startY, float endX, float endY) {try {// 这里是确定了画线的两个端点contentStream.moveTo(startX, startY);contentStream.lineTo(endX, endY);contentStream.stroke();} catch (IOException e) {e.printStackTrace();}}// 插入文字private static void insertText(PDPageContentStream contentStream, float x, float y, String text) throws IOException {PDFont font = PDType1Font.HELVETICA_BOLD;contentStream.setFont(font, 12);contentStream.beginText();contentStream.newLineAtOffset(x, y);contentStream.showText(text);contentStream.endText();}// 插入一个新的pageprivate static PDPageContentStream insertPage(PDDocument document) throws IOException {PDPage page = new PDPage();document.addPage(page);return new PDPageContentStream(document, page);}// 按照比例返回每列的宽度的占比private static float[] distributeWids(Map<String, String> map, float tableWidth){Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();List<Float> wids = new ArrayList<>();// 总的字符串长度float sumLen = 0;while (iterator.hasNext()){Map.Entry<String, String> next = iterator.next();if (next.getValue().length() > next.getKey().length()){sumLen += next.getValue().length();wids.add((float) next.getValue().length());}else {sumLen += next.getKey().length();wids.add((float) next.getKey().length());}}float[] widscol = new float[map.size()];for (int i = 0; i < wids.size(); i++) {widscol[i] = tableWidth * wids.get(i) / sumLen;}return widscol;}
}
虽然只有一百行的代码,但是字字透露着我的无奈,我恨不得我直接手绘表格,因为这东西真的是太慢了。效果如下:
我想摊牌了,不想搞下去了,这玩意之后要合并表格岂不是要把我累死。我在想有没有开源的包可以操作doc文档的,然后在转换成pdf。一起负责的同事告诉我说,之前他用过一个模板引擎,感觉还不错。啊这,我果断抛弃pdfbox,追寻这个模板引擎去了。
四、PoiTl模板引擎基本使用
去查看了它的官方文档,好家伙,全中文很是友善,http://deepoove.com/poi-tl/。好像这玩意是个人开发的,这样的话就不用考虑版权的问题了。
粗略开了一下入门的教程,他写的还是很清楚的,我就照搬过来了,等到后面看不懂的时候再复制过来。
大概地操作就是:首先你得有一个本地docx文档,然后这文档内容里面有按照它规范化的标签,在代码中开始编译这个docx文档,等到了相应的对象后,使用一些方法将一些字符串或者行为与文档中的标签关联起来。最后开始渲染输出docx文件。
我一开始心想:这玩意要自己先有模板才能操作?那岂不是我要先自己画好表格然后找它填充数据?感觉有点草率,看到后面才发现它的高级用法——自定义插件(策略)。
表格操作
// 新建一个策略继承指定的接口
public class TablePolicy extends DynamicTableRenderPolicy {@SneakyThrows// 记住这个data参数,之后弄清楚怎么传递的就好了。第一个参数是自动获取的public void render(XWPFTable table, Object data) {// 判空if (data == null || table == null){throw new Exception("data or table is null");}// 拿到传递过来的每行的数据List<Map<String, String>> rows = (List<Map<String, String>>) data;table.removeRow(0);// 获取表头Set<String> keySet = rows.get(0).keySet();String[] heads = keySet.toArray(new String[keySet.size()]);// 先删除标签行table.removeRow(0);// 添加表头行XWPFTableRow headRow = table.insertNewTableRow(0);// 往表头行中添加表头数据for (int i = 0; i < heads.length; i++) {XWPFTableCell cell = headRow.createCell();cell.setText(heads[i]);}List<String> pre = new ArrayList(2);List<Integer> a = new LinkedList<>();// 遍历每一行的数据for (int i = 0; i < rows.size(); i++) {List<String> bef = new ArrayList<>(2);XWPFTableRow row = table.insertNewTableRow(table.getRows().size());RowRenderData rowRenderData = new RowRenderData();// 拿出mapMap<String, String> rowMap = rows.get(i);// 拿出map中的valuesfor (int j = 0; j < heads.length; j++) {// 在该行中添加每个单元格的信息XWPFTableCell cell = row.createCell();cell.setText(rowMap.get(heads[j]));// 记录前三个单元格的信息if (j<2){bef.add(rowMap.get(heads[j]));}}// 判断前面一行与当前行的条件是否一致boolean tag = true;if (bef.size() > 0 && pre.size() > 0){for (int z = 0; z < bef.size(); z++) {if (bef.get(z) != pre.get(z)) {tag = false;break;}}}// 将该行数据添加if (tag && bef.size() > 0 && pre.size() > 0) a.add(table.getRows().size());pre = bef;}// 合并单元格for (int i = 0; i < a.size(); i++) {TableTools.mergeCellsVertically(table, 0, a.get(i)-2-i, a.get(i)-1);TableTools.mergeCellsVertically(table, 1, a.get(i)-2-i, a.get(i)-1);}
// // 遍历data中每行的行数据
// Iterator<Map<String, String>> iterator = rows.iterator();
// while (iterator.hasNext()){// // 创建行
// XWPFTableRow row = table.insertNewTableRow(table.getRows().size());
// // 往行中添加数据
// for (String value : iterator.next().values()){// XWPFTableCell cell = row.createCell();
// cell.setText(value);
// cell.setWidthType(TableWidthType.AUTO);
// }
// }
// // 合并列
// TableTools.mergeCellsVertically(table,1, 1, 2);table.setTableAlignment(TableRowAlign.CENTER);}
}// 数据封装类
public class ReportJob {public static void tablePolicy() throws IOException {// 构造好要传递的数据结构final List<Map<String, String>> data = new ArrayList<Map<String, String>>();data.add(new LinkedHashMap<String, String>(){{put("name", "achao");put("age", "18");put("score", "11");put("school", "xinning");}});data.add(new LinkedHashMap<String, String>(){{put("name", "achao");put("age", "18");put("score", "22");put("school","xinning");}});data.add(new LinkedHashMap<String, String>(){{put("name", "achao");put("age", "18");put("score", "33");put("school","xinning");}});data.add(new LinkedHashMap<String, String>(){{put("name", "dt");put("age", "18");put("score", "44");put("school","xinning");}});Map map = new HashMap(){{put("tablepolicy", data);}};// 策略和标签进行绑定Configure configure = Configure.createDefault();configure.customPolicy("tablepolicy", new TablePolicy());// 编译XWPFTemplate template = XWPFTemplate.compile("C:\\Users\\achao\\Desktop\\payment.docx", configure);template.render(map);FileOutputStream stream = new FileOutputStream("C:\\Users\\achao\\Desktop\\payment_out.docx");// 渲染template.write(stream);stream.flush();// 关闭stream.close();template.close();}
}// 调用上述数据封装类的方法....
先看看效果吧:
在上面的代码中,那个参数的传递是在一个map中的,而且map中的key值要对应这绑定的标签值。
获取到data参数之后,执行了一下操作:
- 根据参数中的字段数量,确定表格的列数
- 根据表格中的数据量,确定行数
- 根据表格中头几个字段属性的情况,合并单元格
至此,对于公司的数据报表的基本功能实现已经完成了,但是后面还是要转换成为pdf呀,这本来觉得很简单的事情再一次搞昏了头脑。。。。
JAVA操作pdf——创建表格相关推荐
- 【Java 代码实例 13】Java操作pdf的工具类itext
目录 一.什么是iText? 二.引入jar 1.项目要使用iText,必须引入jar包 2.输出中文,还要引入下面```itext-asian.jar```包 3.设置pdf文件密码,还要引入下面` ...
- java操作pdf制作电子签章
#java操作pdf制作电子签章 ##电子签章简介 电子签章,与我们所使用的数字证书一样,是用来做为身份验证的一种手段,泛指所有以电子形式存在,依附在电子文件并与其逻辑关联,可用以辨识电子文件签署者身 ...
- java操作PDF文件,可支持分页、合并、图片转PDF等
java操作PDF,有一个很好用的工具--pdfbox.只需要引入依赖,即可使用. <dependency><groupId>org.apache.pdfbox</gro ...
- Java操作PDF之iText超入门
转自:https://www.cnblogs.com/liaojie970/p/7132475.html Java操作PDF之iText超入门 iText是著名的开放项目,是用于生成PDF文档的一个j ...
- java 操作 PDF
近来收到一个需求, 制作 PDF 制作发票. 类似于制作这样的发票 技术选型我选择java 在网上寻找了一些操作PDF的框架决定用iText制作, 因为它比较活跃, 而且后期做签章和插入图片二维码都有 ...
- Java 读取PDF中表格的工具
目录 1.方法1:Spire.PDF 1.1 Maven仓库下载导入 1.2 读取PDF中的表格 1.2.1 代码 1.2.2 表格内容 1.2.3 读取结果 2.方法2:Tabula 2.1 Mav ...
- Java操作PDF大全
PDF预览 /*** pdf文件预览* @param request* @param response* @return* @throws IOException*/ @RequestMapping( ...
- java在模板图片中填写文字,java 操作pdf模板(向指定域添加文本内容和图片)
项目需求涉及到操作pdf模板,根据生成好的模板向里面填充数据 用到的jar包是iText-5.0.6.jar 和iTextAsian.jar 上代码: public static void main( ...
- java中怎么创建表格_Java中的表格怎么利用表格模型进行创建
Java中的表格怎么利用表格模型进行创建 发布时间:2020-12-03 16:44:43 来源:亿速云 阅读:89 作者:Leah 本篇文章给大家分享的是有关Java中的表格怎么利用表格模型进行创建 ...
最新文章
- IOS 后台挂起程序 当程序到后台后,继续完成定位任务
- go package学习——os
- 码农们不得不重视的问题
- SCOM 2012知识分享-26:分布式部署要点总结
- windows 默认使用python3_小白都能上手的Python3编程环境搭建 (Windows 10)
- Android实现点击事件的4种方式
- boost::safe_numerics模块测试对文字的 constexpr 操作
- pual_bot 天气插件编写
- ASP.NET多线程编程(一) 收藏
- C# 定义了 7 种变量类别:静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量
- 第4章 旋转的圆弧(《Python趣味创意编程》教学视频)
- 【毕业答辩】毕业论文答辩温馨提示
- [转载] python中sort,sorted,reverse,reversed的区别
- 命令以及查找帮助方法
- 代购集运系统平台一键上传淘宝商品至韩国coupang经验分享
- 机器学习的环境搭建流程
- 3进制计算机发展,三进制计算机(中国三进制计算机)
- centos(一)安装及 设置基础软件仓库出错 的问题
- RPL(4):RFC6550翻译(4)---RPL的通信流支持RPL实例
- 交易猫源码+后台搭建教程
热门文章
- python用pandas读取excel_浅谈python之利用pandas和openpyxl读取excel数据
- MySQL给查询结果添加序号列的书写格式
- numpy将所有数据变为0和1_《利用python进行数据分析》1.0——Numpy库
- 《语雀 IT 百科》发布了!
- CC1310F128RSMR Sub-1GHz射频微控制器 - MCU 433MHz 868MHz 915MHz ULP Wireless MCU
- 第五章 如何使用java中的线程打印偶数和奇数
- 大数据之当传统产业遭遇互联网
- Java中英文字母的大小写转换ideaeclipse大小写快捷键
- “秘密入职”字节跳动,百度高级经理一审被判赔107万
- 命名需谨慎!科技产品荒谬命名大盘点