前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[实用][更新中]Java Apache POI 打印Word文档工具(含文本替换,动态表格功能)

[实用][更新中]Java Apache POI 打印Word文档工具(含文本替换,动态表格功能)

作者头像
RRT冻羊
发布2022-11-03 14:10:59
3.1K0
发布2022-11-03 14:10:59
举报
文章被收录于专栏:冻羊技术思考冻羊技术思考

[实用]【更新中】Java Apache POI 打印Word文档工具(含文本替换,动态表格功能)

基于Apache POI对Word进行操作

你好!这是由一个刚毕业的学生,由于项目所需,需要通过Java后台的方式打印Word文档,因此在对大量能操作word的Java API中,选择了Apache POI。以下将简单分享一下这个在学习和开发这个基于POI的word文档打印工具时,一些心得:

  • Apache POI在操作word上非常费劲,在选型的过程中还遇到过很多,如Freemarker,freemarker本人没有研究,但是大概知道是基于word保存为xml后,然后用占位符替换的方式,对xml中整段整段的内容进行文本替换,最终输出word文档,就能得到word文档。
  • Freemarker的缺点(只是看别人总结的,自己没有求证) 1.freemark在进行文本替换的时候,很难保持原有的样式 2.在好不容易编辑好word模板后,转成xml的时候,还需要打开xml对里面的内容进行核对,听说会由于word文档一些字符串处理不好,倒是xml中 标签的缺失or错误,需要手动处理。如果word文档少还好,但是如果文档内容多,那就很麻烦(up主的项目所需打印的word文档就很多内容)
  • Apache POI能很好的保持原来的样式,在理解底层接口原理后,还是挺好操作的,但是对于使用者来说,你们就不需要理解底层原理,因为我已经高度封装好了。

接下来,我将会对Apache POI进行讲解。以及我这套工具的一些底层原理,目的是为了和各位大牛交流,以及有人有定制需求的话,可以基于我这个工具进行改写,来适应不同的项目。如果你不想学原理,则只需要跳过本段内容,到最后一小节,我会用最黑盒的方式,来快速教大家上手使用我这套工具

(由于本人技术有限,而且公文写作能力一般,因此有口误的地方请大家指出,并且欢迎大家提出更好的解决建议) (本工具现在是V1.0版本,代码方面也还没进行过多优化,性能暂时还OK,但还有很大优化空间 )


一、基于Apache POI封装的word文档工具V1.0介绍

已实现的功能

  1. 文本替换
  2. 静态表格的文本替换
  3. 动态表格(行的变化)
  4. 动态表格(整个表格动态增减)
  5. 动态表格(整个表格动态增减,与上面不同的是,这个表格会附带表格标题以及跟随文本
  6. 图片插入

后期可能扩展的方向

  1. 富文本

本工具与网上其他POI打印工具类对比 特点

  1. 文本替换可以灵活的在word文档的任意位置,并且不会受到左右其他文字的影响(网上绝大部分,只是简单封装POI,实际上他们的文本替换需要占据一整行,这是极度不灵活的)
  2. 文本替换功能,在编辑模板的时候,可以设置它的样式。文本替换的时候,会根据你给定的样式替换文本。
  3. 表格内支持样式自定义,很多百度其他封装工具,都不支持样式自定义
  4. 动态表格比较灵活,支持一整块的扩展。
  5. 插入图片支持自定义大小

简单例子 (1)word模板

在这里插入图片描述
在这里插入图片描述

(2)通过apache poi打印后

在这里插入图片描述
在这里插入图片描述

二、Apache POI 知识

apache poi官方文档:http://poi.apache.org/

1. jar包(maven的,这个不多做解释了)

代码语言:javascript
复制
<properties>
   <java.poi.version>4.0.0</java.poi.version>
</properties>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>${java.poi.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>${java.poi.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>${java.poi.version}</version>
</dependency>

2. poi的类

  • XWPFDocument:一个word文档对应一个document
  • XWPFHeaderFooterPolicy:文档的页眉页脚(可以设置每一页的页眉页脚不同,也可以统一一个默认的页眉页脚作为全局,一般来说,后者用的比较多,因此我的工具里也是后者)
  • XWPFTable:一个表格对应一个XWPFTable对象
  • XWPFTableRow:一个表格的每一行对应一个XWPFTableRow
  • XWPFTableCell:table中的每一个单元格对应一个XWPFTableCell(Cell特别特殊,他的里面相当于一个XWPFDocument,也就是说,一个单元格里面,可以进行插入文字,图片,表格等操作,类似于document
  • XWPFParagraph:一段文本对应一个XWPFParagraph(注意,是一段文本,后面会解释合为一段文本)
  • XWPFRun:一处具有相同样式的文本(一个XWPFParagraph里面包含多个XWPFRun)

他们之间的关系结构如图所示(为了更方便大家的理解,用一个图表示)

结构如下
结构如下
在这里插入图片描述
在这里插入图片描述

因此:

  • 一个Document包含多段Paragraph和多个Table。
  • 一个Paragraph包含多个Run(一个Paragraph也可能只有一个Run,需要参考这一段文字中是否有样式不同的文字)
  • 一个Run就一个text(一段文字中相同样式的一段文字)
  • 一个Table包含多个TableRow(也就是一个表格有多少行,就有多少个TableRow。注意:一个Table没有行,这个table还是存在,只不过不会显示,如果要让一个table完全消息,必须调用document的removeBodyElement(int index))
  • 一个TableRow包含多个TableCell(也就是一行中有很多个单元格)
  • 一个TableCell,就相当于一个小的document。(一般不会对单元格进行特殊的操作,都是一段文字,因此tableCell里面的Paragraph起显示文字的作用)
  • 附加:document里面维持一个bodyElement的数组,一个Paragraph或者一个Table对象就对应一个IBodyElement。由于document将段落和表格分开了两个List保存,因此我们无法知道,一个表格在两段文字中的位置或者一段文字在两个表格的位置。因此这一个bodyElement[]就起了能对word文档每个元素进行定位的功能。举了例子: 文档结构:段落1,表格1,段落2 。他的段落列表:段落1,段落2 。他的表格列表:表格1我们无法通过两个数组,知道表格1在两段文字的哪里,因此借助document的bodyElement[]可以得到这样的关系,段落1,表格1,段落2在这里可以清晰的知道,表格1位于两段文字中间。而bodyElement也是非常重要的,能让你定位文档任何一个位置,操作文档内容

3.常用的方法:

  1. 获取XWPFDocument的段落列表
在这里插入图片描述
在这里插入图片描述
  1. 获取XWPFDocument的表格列表
在这里插入图片描述
在这里插入图片描述
  1. 读取整一个段落的所有文字内容
代码语言:javascript
复制
String text = paragraphs.get(0).getText();

4.设置段落的样式

代码语言:javascript
复制
//每一个XWPFParagraph可以设置对齐方式,边框,加粗等等,自己看里面的方法即可
String text = paragraphs.get(0).setXXX();

5.获取段落的Run,并修改这段Run的文字

代码语言:javascript
复制
//获取段落的所有Run
List<XWPFRun> runs = paragraph.getRuns();
//删除某一个Run
paragraph.removeRun(下标);
//增加一个没有文字的Run
paragraph.createRun();
//修改文字(覆盖原文本,追加的话则不使用第二个参数)
runs.get(0).setText("第二个参数表示从哪个下标开始修改字符串", 0);
//一个Run里面又有很多样式可以选择,如加粗,斜体等等
runs.get(0).setXXX();
//如果需要将一整个段落都替换成一个新的文本
while (paragraph.getRuns().isEmpty()){
	paragraph.removeRun(0);
}
paragraph.createRun().setText("新文本");
//上面的代码会导致,原来的Run样式都没了,新的Run使用默认的样式。如果想保留原来Run的样式,可以删除的时候不要删掉全部Run,如
while (paragraph.getRuns().size() > 1){
  paragraph.removeRun(1);
}
paragraph.getRuns().get(0).setText("新文本", 0);

由于要工作,暂没时间写完(后续更新)

三、工具使用教程(不需要了解基础知识,直接快速使用)

1. 占位符的约定规则

  • 段落文本替换:@${t_*}@
  • 静态表格(文本替换): ${at_static_*}
    • 静态文档里面需要文本替换的地方,使用@${t_*}@
  • 动态表格(行动态): ${at_row_*}
  • 动态表格(整个表格增减): ${at_max01_*}
  • 动态表格(整个表格增减,附带标题和跟随文本): ${at_max02_*}

其中:

  • 不同表格类型的命名定义,需要放在每个表格的第一行第一列,任何表格除非不需要替换内容,否则都需要在原表格的上方增加一行,并在第一行第一列设置表格名(打印时,第一行会被去掉)
  • 普通文本:@${t_*}@ 是替换文本的内容,这几个字符都必须使用相同的样式,并且他的样式决定了打印后文本替换的样式。两边的@字符需要设置独立的样式,并且必须独占一个XWPFRun(也就是@与的相邻的字符,样式不一样,我的做法是给@加粗并且变为指数)
  • 静态表格(文本替换): ${at_static_*} 。说明表格的行数列数固定,只是需要填充不同的文字内容。
  • 动态表格(行动态): ${at_row_*} ,表格的列是固定的,行数不固定。根据给定的List数组决定有多少行。
  • 动态表格(整个表格增减): ${at_max01_*} 。表格的行,列是固定的。
  • 动态表格(整个表格增减,但会携带标题和随后文本): ${at_max02_*} 。表格行列固定,但是不同的是,表格上方和下方会跟随一段文字
  • 在了解Apache POI后,是可以自己自定义各种各样的规则,上面的规则仅是针对我遇到的项目所需,大部分情况下,是已经够用了。可能会有人需要,动态增减整个表格,并且每个表格里面的行不固定,这些都是可以定制的。

2. word模板编辑

建议使用WPS编辑word模板,因为目前Apache Poi对office不太友好,在我约束的规则下,我发现一个 占位符无法对应一个XWPFRun,在处理上非常不方便

( * 表示通配符,可以是任意字符) (1) 文本替换,使用@${t_*}@的方式(其中两边的@,需要独占一种样式)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PS : 两边的@是必不可少的,并且需要使用一种与周围字符样式不同的样式。我的做法通常是,加粗+变为指数。在进行打印的过程中, ${xxx}的内容会被你指定的文本替换掉,两边的@也会被删掉。

(2) 静态表格(文本替换) 表格上方多增加一行,在第一行第一列中指定静态表格 ${at_static_*} 表格内需要进行文本替换的地方,与普通文本替换的规则一样

在这里插入图片描述
在这里插入图片描述

(3) 动态表格(行动态)

  • 表格上方增加一行,指定动态表格(行动态) ${at_row_*}-
  • 表格一定要有3行,第一行指定动态表格,第二行是表格头的标题,第三行则是允许你设置每一个单元格内容的样式,在后续动态生成的每一行,都与这一行对应单元格的样式一致
在这里插入图片描述
在这里插入图片描述

(4) 动态表格(整个表格动态)

  • 表格的行列固定,表格最上方新增一行指定动态表格规则 ${at_max01_*}
  • 目前只允许整个表格行列固定的形式动态增减表格,若有定制需求,可以在简单研究POI原理后,对工具代码进行定制。
在这里插入图片描述
在这里插入图片描述

(5) 动态表格(携带标题和跟随文本)

  • 表格的行列固定,表格上方新增一行指定规则 ${at_max02_*}
  • 请注意看,最外层有一层虚线,它是一个 1行1列的Table,边框使用虚线,在打印时,虚线是不会被显示的(实际上这个不是虚线,是边框设置为none后的效果,它和真正的虚线边框是不同的)
  • 之所以要设计用一个一行一列的单元格包住整个 动态表格。是因为,POI的原理是 段落和表格 分开处理的,为了让整个表格更加方便的复制,因此用了一个 单元格包住整个内容进行动态增减。
  • PS:标题文字紧挨着表格紧挨着跟随文本 。表格样式,单元格样式以及文本样式都可以自定义。如果不需要标题跟随文本,在Java可以设空串。(如有定制需求,可以询问up或者自行研究源代码)
在这里插入图片描述
在这里插入图片描述

3. Java准备数据和导出word

(1)封装好的工具简单介绍

在这里插入图片描述
在这里插入图片描述
  • PoiWordUtil 封装好的打印word工具,里面只有一个公共方法。
  • PoiWordKeyMatchRule 这里设置了4种输出规则即对应上方的文本替换,静态表格,动态表格等。使用的是通配符匹配算法对 ${xxx} 进行规则的匹配。
  • IPoiWordTable接口:所有Table表格的接口,里面简单的定义了 行,列,以及每个单元格内容的二维数组。
  • PoiWordAutoTable实现类:这个对应动态表格(整个表格动态) at_max01_*
  • PWATwithHeaderBottom实现类:这个对应动态表格(携带标题和跟随文本) at_max02_*

(2) Java对应word模板DEMO的示例

代码语言:javascript
复制
  //word模板的路径
  String inputUrl = "F:\\poidemo\\TESTPOI.docx";
  //输出的位置(可以不存在文件)
  String outputUrl = "F:\\poidemo\\OUTPUT.docx";
  
  //文本替换Map
  Map<String, String> textMap = new HashMap<>();
  //动态表格Map
  Map<String, List<IPoiWordTable>> tableMap = new HashMap<>();
  //表格为空时,文本替换的Map(这个的用法是,如果某一个表格是不需要显示的,则把他规则的名字放进key里面,value如果设为null,则该表格不显示,如果是文本内容,则这个表格的位置,会被一段文字替换)
  Map<String, String> noneTableMap = new HashMap<>();

//准备数据
//文本替换 and 静态表格文本替换 都是放在textMap里
textMap.put("t_author", "走在刀剑上的羊");
textMap.put("t_email", "448241091@qq.com");
textMap.put("t_year", "2018");
textMap.put("t_month", "11");
textMap.put("t_day", "30");
textMap.put("t_poi_cool", "[我不会影响左右两边文字]";

//动态表格都放入tableMap中
//动态表格(行)
PoiWordAutoTable rowTable = new PoiWordAutoTable(2, 3);	//指定2行3列的动态行table
rowTable.setCell(0, 0, "row1col1");
rowTable.setCell(0, 1, "row1col2");
rowTable.setCell(0, 2, "row1col3");
rowTable.setCell(1, 0, "row2col1");
rowTable.setCell(1, 1, "row2col2");
rowTable.setCell(1, 2, "row2col3");

tableMap.put("at_row_autoRow", Arrays.asList(rowTable));
//如果不需要显示这个表格。表格会隐藏,并在相应位置出现一段文字提示
//noneTableMap.put("at_row_autoRow", "暂无数据");

//动态表格01,使用PoiWordAutoTable,行列根据原表格固定
PoiWordAutoTable data1 = new PoiWordAutoTable(2,2);
data1.setCell(0, 0, "企业名称");
data1.setCell(0, 1, "xxx");
data1.setCell(1, 0, "注册号");
data1.setCell(1, 1, "XXX123");

PoiWordAutoTable data2 = new PoiWordAutoTable(2,2);
data2.setCell(0, 0, "企业名称");
data2.setCell(0, 1, "xxx");
data2.setCell(1, 0, "注册号");
data2.setCell(1, 1, "---x2---");
tableMap.put("at_max01_auto", Arrays.asList(data1, data2));

//动态表格02,使用PWATwithHeaderBottom
PWATwithHeaderBottom pwat1 = new PWATwithHeaderBottom(3,2);
//如果标题 或 跟随文本不需要显示内容,则用"" 或 null代替
pwat1.setTitle("1.实际控制人:xxx(身份证号:441900XXXXXXX)查询日期:1995年11月23日");
//pwat1.setTitle(null);
pwat1.setBottom("底部跟随文本");
pwat1.setCell(0, 1, "信用卡");
pwat1.setCell(1, 0, "账户数");
pwat1.setCell(1, 1, "2个");
pwat1.setCell(2, 0, "未结清/未注销账户数");
pwat1.setCell(2, 1, "2个");

PWATwithHeaderBottom pwat2 = new PWATwithHeaderBottom(3,2);
pwat2.setTitle("2.实际控制人:xxx(身份证号:xxx)查询日期:2018年11月22日");
pwat2.setBottom("底部跟随文本");
pwat2.setCell(0, 1, "信用卡");
pwat2.setCell(1, 0, "账户数");
pwat2.setCell(1, 1, "255个");
pwat2.setCell(2, 0, "未结清/未注销账户数");
pwat2.setCell(2, 1, "255个");
tableMap.put("at_max02_auto", Arrays.asList(pwat1, pwat2));

//最后使用工具
PoiWordUtil.changWord(inputUrl, outputUrl, textMap, tableMap, noneTableMap);

四、GIT-HUB 地址

再次强调:建议使用WPS编辑word模板,因为我发现如果用office编辑模板,一个占位符无法对应一个XWPFRun,如果各位发现office也能正常编辑 占位符,请留言

https://github.com/YellowWinterSun/poiWordUtil

最近更新:

  • 2018年12月26日:更新了 图片替换功能
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [实用]【更新中】Java Apache POI 打印Word文档工具(含文本替换,动态表格功能)
  • 基于Apache POI对Word进行操作
    • 一、基于Apache POI封装的word文档工具V1.0介绍
      • 二、Apache POI 知识
        • apache poi官方文档:http://poi.apache.org/
        • 1. jar包(maven的,这个不多做解释了)
        • 2. poi的类
        • 3.常用的方法:
      • 三、工具使用教程(不需要了解基础知识,直接快速使用)
        • 1. 占位符的约定规则
        • 2. word模板编辑
        • 3. Java准备数据和导出word
      • 四、GIT-HUB 地址
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档