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——创建表格相关推荐

  1. 【Java 代码实例 13】Java操作pdf的工具类itext

    目录 一.什么是iText? 二.引入jar 1.项目要使用iText,必须引入jar包 2.输出中文,还要引入下面```itext-asian.jar```包 3.设置pdf文件密码,还要引入下面` ...

  2. java操作pdf制作电子签章

    #java操作pdf制作电子签章 ##电子签章简介 电子签章,与我们所使用的数字证书一样,是用来做为身份验证的一种手段,泛指所有以电子形式存在,依附在电子文件并与其逻辑关联,可用以辨识电子文件签署者身 ...

  3. java操作PDF文件,可支持分页、合并、图片转PDF等

    java操作PDF,有一个很好用的工具--pdfbox.只需要引入依赖,即可使用. <dependency><groupId>org.apache.pdfbox</gro ...

  4. Java操作PDF之iText超入门

    转自:https://www.cnblogs.com/liaojie970/p/7132475.html Java操作PDF之iText超入门 iText是著名的开放项目,是用于生成PDF文档的一个j ...

  5. java 操作 PDF

    近来收到一个需求, 制作 PDF 制作发票. 类似于制作这样的发票 技术选型我选择java 在网上寻找了一些操作PDF的框架决定用iText制作, 因为它比较活跃, 而且后期做签章和插入图片二维码都有 ...

  6. 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 ...

  7. Java操作PDF大全

    PDF预览 /*** pdf文件预览* @param request* @param response* @return* @throws IOException*/ @RequestMapping( ...

  8. java在模板图片中填写文字,java 操作pdf模板(向指定域添加文本内容和图片)

    项目需求涉及到操作pdf模板,根据生成好的模板向里面填充数据 用到的jar包是iText-5.0.6.jar 和iTextAsian.jar 上代码: public static void main( ...

  9. java中怎么创建表格_Java中的表格怎么利用表格模型进行创建

    Java中的表格怎么利用表格模型进行创建 发布时间:2020-12-03 16:44:43 来源:亿速云 阅读:89 作者:Leah 本篇文章给大家分享的是有关Java中的表格怎么利用表格模型进行创建 ...

最新文章

  1. IOS 后台挂起程序 当程序到后台后,继续完成定位任务
  2. go package学习——os
  3. 码农们不得不重视的问题
  4. SCOM 2012知识分享-26:分布式部署要点总结
  5. windows 默认使用python3_小白都能上手的Python3编程环境搭建 (Windows 10)
  6. Android实现点击事件的4种方式
  7. boost::safe_numerics模块测试对文字的 constexpr 操作
  8. pual_bot 天气插件编写
  9. ASP.NET多线程编程(一) 收藏
  10. C# 定义了 7 种变量类别:静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量
  11. 第4章 旋转的圆弧(《Python趣味创意编程》教学视频)
  12. 【毕业答辩】毕业论文答辩温馨提示
  13. [转载] python中sort,sorted,reverse,reversed的区别
  14. 命令以及查找帮助方法
  15. 代购集运系统平台一键上传淘宝商品至韩国coupang经验分享
  16. 机器学习的环境搭建流程
  17. 3进制计算机发展,三进制计算机(中国三进制计算机)
  18. centos(一)安装及 设置基础软件仓库出错 的问题
  19. RPL(4):RFC6550翻译(4)---RPL的通信流支持RPL实例
  20. 交易猫源码+后台搭建教程

热门文章

  1. python用pandas读取excel_浅谈python之利用pandas和openpyxl读取excel数据
  2. MySQL给查询结果添加序号列的书写格式
  3. numpy将所有数据变为0和1_《利用python进行数据分析》1.0——Numpy库
  4. 《语雀 IT 百科》发布了!
  5. CC1310F128RSMR Sub-1GHz射频微控制器 - MCU 433MHz 868MHz 915MHz ULP Wireless MCU
  6. 第五章 如何使用java中的线程打印偶数和奇数
  7. 大数据之当传统产业遭遇互联网
  8. Java中英文字母的大小写转换ideaeclipse大小写快捷键
  9. “秘密入职”字节跳动,百度高级经理一审被判赔107万
  10. 命名需谨慎!科技产品荒谬命名大盘点