目录
Why haiwei-poi-word
haiwei-poi-word 基于poi,自己编码实现比较负责,且需要兼容各种版本,可以做但很繁杂。word不同于excel行列分明,并且跟实际客户端的排版设置有关系,单纯代码实现工作量大,可扩展性差,并且效果不确定。
同类比较:Poi-tl,也是个不错的组件,功能强大。有poi-tl为啥要封装Haiwei-poi呢?poi-tl就像是成品柜,Haiwei-poi-word更像是针对Saas化的定制。
侧重点不同:
poi-tl: 数据和模板没有完全分离,是用代码操作word的比较好的封装。
haiwei-poi-word则完全分离,所有样式全部由模板word决定。
基本思想
文档 = 模板 + 模型(Model)
模板: 提供所有布局和样式
Model:只提供模板中需要填充的数据
模板只在逻辑上预留位置,并设定展示的参数
组件会根据数据类型 + 展示参数 来进行展示
问题1: 如果位置是方框,参数是图片,数据是字符串 >> 字符串
问题2: 位置是文本,参数是图片,数据是字符串 >> 字符串
问题3:位置是文本,参数是图片,数据是图片 >> 根据参数展示图片
问题4:位置是文本,参数没有,数据是图片 >> 展示原图
如果模板和模型不匹配时展示原则:牺牲样式,尽可能展示数据
- 好处:把问题展示出来,有助于问题的暴露和及早进行模板或者数据调整。
模型决定数据类型
- 字符串,图片
开发方法论
TDD 测试驱动设计
这么复杂的系统,靠前期的设计很难设计完整,所以就按照case走。
术语
标签:占位符,place_holder label sign 都指的是 ##{customer.name}
版本要求
POI 4.1.2+
JDK 1.8+
Office Word 2007(咱不支持03)
快速入门
Start
<dependency>
<groupId>com.haiwei</groupId>
<artifactId>haiwei-poi-word</artifactId>
<version>1.0.1</version>
</dependency>
模板文件:
示例代码:
// 加载模板文件
final XWPFDocument document = HaiweiDocumentGenerator.build(new File("template.docx"));
// 数据构建
List<Item1> items1 = new ArrayList<>();
items1.add(new Item1("键盘11","电子类11","2020/8/11",new HaiweiImages()));
items1.add(new Item1("键盘12","电子类12","2020/8/12",new HaiweiImages()));
items1.get(0).getImages().addFiles("img1.jpg","img2.jpg");
items1.get(1).getImages().addFiles("img2.jpg","img2.jpg");
List<Item1> items2 = new ArrayList<>();
items2.add(new Item1("键盘21","电子类21","2018/8/21",new HaiweiImages()));
items2.add(new Item1("键盘22","电子类22","2018/8/22",new HaiweiImages()));
items2.get(0).getImages().addFiles("img1.jpg","img2.jpg");
items2.get(1).getImages().addFiles("img2.jpg","img2.jpg");
Order1 order1 = new Order1("梅长苏1","N0001",items1);
Order1 order2 = new Order1("梅长苏2","N0002",items2);
List<Order1> orders = new ArrayList<>();
orders.add(order1);
orders.add(order2);
// 添加bean
HaiweiBeanCacheManager.setBean(orders,"orders");
// 根据路径设置 单个变量
HaiweiBeanCacheManager.setString("文档编号0011","文档编号00011");
// 打印数据树
HaiweiBeanCacheManager.print();
// 设置回调函数,解决解析过程中的数据获取场景
HaiweiBeanCacheManager.setCallBackCache(new HaiweiBeanCallback() {
@Override
public Object getBean(String path) {
return null;
}
});
//解析文档
HaiweiXWPFDocumentUtil.parse(document);
// 生成文件
final File file = new File("结果文件.docx");
HaiweiDocumentGenerator.toFile(document,file);
模板编写
组件能力
数据类型
文本(字符串,数字等可用字符串展示的),图片,图标
标签位置:
文本,文本框,表格
文档位置:
正文,页眉,页脚,备注
表格:
并联(不限制长度),嵌套(不限制深度),并联嵌套
风格:
背景,水印
说明:已支持的为黑体展示。
文本替换
标签格式:##{ 标签名称.子对象.Name1:key=value&key1=value1 }
注意:
- 中间不可有空格和换行(上面只是为了展示用,要不然就给替换了)
- 标签名称字符:中文,英文大小写,数字
- 风格符:点. 冒号: 等于= 与&
- 子对象可以多级,逻辑上没有层数限制
最简单文本匹配
##{aa}
标签前后格式不同
按照最前面字符格式展示
##{aa}
文本中有标签
@$&*1我 AAA#######{aa}}}}}BBB@$&*1我
标签中不可有空格
有空格不会替换
##{a a}
文本框
文本框处理只是位置不同,处理方式一致。
1###{aa}}} |
表格支持
Loop单表
订单编号:##{order.no}
订单名称:##{order.name}
行项1 | 产品名称1 | 产品金额1 |
##={begin:order.items_item}##={order:time_降序} | ||
##{ item.name} | ##{ item.type} | ##{ item.time1} |
##={end} |
Loop单表多行
订单编号:##{order.no}
订单名称:##{order.name}
行项1 | 产品名称1 | 产品金额1 |
##={begin:order.items_item}##={order:time_降序} | ||
##{ item.name} | ##{ item.type} | ##{ item.time1} |
##{ item.time} | ||
##={end} |
Loop并联
订单编号:##={编号}
订单名称:##={名称}
订单类型:##={业务类型}
订单时间:##={创建时间}
行项1 | 产品名称 | 产品金额 |
##={begin:Items_item}##={order: item.时间_升序/降序} | ||
##{item.名称} | ##{item.业务类型} | ##={item.创建时间} |
##={end} | ||
行项1 | 产品名称 | 产品金额 |
##={begin:Items_item}##={order: item.时间_升序/降序} | ||
##={ item.名称} | ##={ item.业务类型} | ##={ item.创建时间} |
##={end} |
##={begin:orders_order} | ||
订单编号: | ##={ order.no} | |
订单名称: | ##={ order.name} | |
产品名称 | 产品类型 | 时间 |
##={begin:order.items_item} | ||
##={item.name } | ##={item.type} | ##={item.time} |
##={end} | ||
##={end} |
Loop并联多行
订单编号:##={order.no}
订单名称:##={order.name}
行项1 | 产品名称1 | 产品金额1 |
##={begin:order.items_item}##={order:time_降序} | ||
##={ item.name} | ##={ item.type} | ##={ item.time1} |
##={ item.time} | ||
##={end} | ||
行项2 | 产品名称2 | 产品金额2 |
##={begin:order.items_item}##={order:item.时间_降序} | ||
##={ item.name} | ##={ item.type} | ##={ item.time} |
##={ item.time1} | ||
##={end} |
Loop嵌套
##={begin:orderList_order}##={order:order.account_升序} | ||
订单编号: | ##={ order.no} | |
订单名称: | ##={ order.name} | |
订单详细: | ||
行项1 | 产品名称 | 产品金额 |
##={begin:order.Items_item}##={order:item.time_升序} | ||
##={item.名称} | ##={item.业务类型} | ##={item.创建时间} |
##={end} | ||
##={end} |
图片打印
- 表格:固定表格宽度,设置图片参数,缺省按照原图展示
- 文字中,插入表格,文字替换为图片
- 样例:
- ##{image_placeholder1:width=2.6&height=3.5}图片固定宽度,单位厘米
- ##{ image_placeholder1:size=auto} 前提条件:表格中,固定宽度,否则该参数失效。如根据图片数量,设置自动调整图片大小,布局为:2*2 3*3
- ##{image_placeholder1}原图输出
- 设置优先级:
- 明确指定宽和高 优先级最高
- 表格中,固定宽度,备注参数:size=auto,根据图片数量跳转图片宽度,高度按照宽度等比缩放
- 上述都没有就按照原图宽度输出
- 单位:point默认,px像素,cm厘米
- 打印图片数量limit
- 默认1
- 设置后才按照设置的数量打印,不足则按照实际输出,超过则按照limit设置数量
- Limit取值:all,即为全部打印
-
自动跳转尺寸适应内容别选了。选择后格式会通过内容多少来自动调整,可能不是用户想要的。所以需要判断字段内容的长度。
- 需要考虑单元格边距,默认2mm,固定宽度的时候考虑进去
代码架构
- HaiweiBeanCacheManager 数据提供者
- 提前进行线程缓存
- 在解析word需要数据的时候通过回调获取
- HaiweiXWPFDocumentUtil 文档解析类
- 获取文档的页眉,页脚,正文分别对段落和表格进行处理
- 表格处理:解析模板生成Haiweitable模型,将模型和Bean结合,删除模板行
- HaiweiDocumentGenerator 文档对象生成和输出为docs文件工具类
Q&A
Word版本支持03吗
暂不支持
图片打印宽度单位和关系
English Metric Units (EMUs) 英制公制单位(EMU),有时称为A单位
转换类:org.apache.poi.util.Units.toEMU
PIXEL像素 = 9525 * 1EMU
CENTIMETER厘米 = 360000 EMUs = 28.34 * Point点
Inch 英寸 = 2.54厘米
1英尺 = 12英寸
1POINT点 = 12700 * EMU = 13.3 * PIXEL像素
Point 点数 打印和印刷单位
DXA 是1/20 * 点
Dxa/20/28.34 = 厘米
扩展能力
- 封装占位符和循环开始结束符,用户可自定义规则***** 提取出来
- SpringEL表达式***
- 异常处理 *
- 拦截器*
- 插件*
- 文档输出*****
- BeanCache缓存实现