Word文档生成在许多项目中都是需要的,目前来看有两种方案,一种是通过Apache POI工具包、iText来生成,另外一种是利用Office Open Xml规范来进行生成。各种方案各有优缺点,这里不对其进行详细比较,已经有许多人进行过深入的比较,详细请咨询谷哥和度娘。这里采用的方案是利Office Open Xml的方式进行文档生成。
对于采用POI,iText来说,要生成复杂的Doc文件,其实并不容易。而采用Xml结构生成,去把一个文档生成xml文件,看看里面的内容,估计也会是满头是汗。
Tiny构建者,认为,生成Doc,Xls,PDF等文档类型是管理系统绕不过去的坑,那既然没有办法绕过,那就是说无论如何都要过了?
随便百度Google一下,发现这种方案其实许多人已经做过了,比如在下面的连接中,作者已经有了良好的实践:http://developer.51cto.com/art/201106/270815.htm
本文的解决方案,与之存在类似之处--都是采用模板语言结合xml来生成xml格式Word文件,但是实现模式还是不一样的,本文提供的方案更具有通用性、易用性,易用到像做网站一样生成Word文档。
为此,Tiny框架对于生成文本格式文件的内容进行了抽象,只要是生成文本格式的文件,都可以通过Tiny框架的文档生成框架进行扩展,使得开发人员可以方便快捷的生成文本类型的文档。
由于Office文档已经有了xml格式的存储方式,由于pdf可以通过xml生成,因此,最常用的office文档和pdf文档都可以通过本框架快速生成,当然,有一些基础性工作还是要自己做的(偷偷的透露一下:常用的模板框架中都会包含的)。
由于Office Open Xml实际上是只要生成Xml文件,就可以用Word打开。因此问题就转变成生成Xml文本文件,而生成Xml文本文件,比较好的方案是模板语言,这个时候可以采用Velocity、Freemaker或其它模板语言都是可以的,本文采用Velocity模板语言来进行生成。
当然,Tiny构建者又把这个问题进行了泛化,因为不仅仅会生成word,还会生成pdf、xls等文件,甚至是某种语言的源文件。因此我们认为所有的文本类型文件都可以利用宏文件+模板文件的方式进行生,宏文件中定义了已经写好的宏,用来生成某种类型的文本内容,而宏文件,则是最终用来调用的文件。宏文件是精通某种文档格式的人员编写的,而宏文件则是普通的程序员所写。
而框架部分的代码,已经写好,因此要进行生成的文档类型的扩展只要编写相应的宏文件即可。
宏文件的编写有两种方式,一种是根据Office Open Xml规范进行编写,另外一种简单的办法就是利用Word文档先写一点内容,然后另存为Xml格式,然后从中找出相关的内容,然后修改为模式。
#@wordDocument() #@body() #@image({"name":"111.jpg","width":"249pt","height":"119.25pt","data":"$picData"}) #end #end #end
Word结果:
#@wordDocument() #@body() #@imageFromFile({"name":"111.jpg","width":"249pt","height":"119.25pt","file":"c:\pic.jpg"}) #end #end #end
Word结果:
#@wordDocument() #@body() #@bookmark("我的书签") #set($content="这里是书签") #h($content, {"font":{"name":"方正姚体","size":"48","color":"FF0000","bold":"","highlight":"cyan"}} ) #end #set($url=" http://my.oschina.net/tinyframework") #set($text="点击这里可以跳转到悠然首页") #@link($url) #h($text, {"font":{"name":"方正姚体","size":"48","color":"FF0000","bold":"","highlight":"green"}} ) #end #end #end
Word结果:
大纲功能
#@wordDocument() #@body() #@outline({"grade":"1","bookmarkNO":"_Toc372801234","name":"第 1 章 对象入门","font":{"name":"方正姚体","size":"48","color":"FF0000","bold":"","highlight":"cyan"}}) ## 定义了一级大纲,名称为:第 1 章 对象入门,该大纲的ID: _Toc372801234 #@outline({"grade":"2","bookmarkNO":"_Toc2ny301234","name":"1.1 抽象的进步"}) #@outline({"grade":"3","bookmarkNO":"_Toc37ahy2234","name":"1.1.1 注释文档"}) #end #end #@outline({"grade":"2","bookmarkNO":"_Toc3728mi734","name":"1.2 对象的接口"}) #@outline({"grade":"3","bookmarkNO":"_Toc37280me84","name":"1.2.1 集合与继承器"}) #end #end #@outline({"grade":"2","bookmarkNO":"_Toc3plk23234","name":"1.3 方案的重复使用"}) #end #end #@outline({"grade":"1","bookmarkNO":"_Toc37281nju4","name":"第2 章一切都是对象"}) #end #end #end
Word结果:
索引
#@wordDocument() #@body() #cataHeader("目录",{"font":"宋体","color":"00B050","size":"30","bold":"on"}) #catalogue({"begin":"","grade":"10","bookmarkNo":"_Toc374566201","pageNo":"1","content":"1. 对象入门"}) #catalogue({"grade":"20","bookmarkNo":"_Toc374566202","pageNo":"1","content":"1.1 抽象的进步"}) #catalogue({"grade":"30","bookmarkNo":"_Toc374566203","pageNo":"1","content":"1.1.1 注释文档"}) #catalogue({"grade":"20","bookmarkNo":"_Toc374566204","pageNo":"1","content":"1.2 对象的接口"}) #catalogue({"grade":"30","bookmarkNo":"_Toc374566205","pageNo":"1","content":"1.2.1 集合与继承器"}) #catalogue({"grade":"20","bookmarkNo":"_Toc374566206","pageNo":"1","content":"1.3 方案的重复使用"}) #cataEnd() #@outline({"grade":"1","bookmarkNO":"_Toc374566201","name":"对象入门","multilevel":{"level":"0","ilfo":"41","char":"1"}})#end #@outline({"grade":"2","bookmarkNO":"_Toc374566202","name":"抽象的进步","multilevel":{"level":"1","ilfo":"41","char":"1.1"}})#end #@outline({"grade":"3","bookmarkNO":"_Toc374566203","name":"注释文档","multilevel":{"level":"2","ilfo":"41","char":"1.1.1"}})#end #@outline({"grade":"2","bookmarkNO":"_Toc374566204","name":"对象的接口","multilevel":{"level":"1","ilfo":"41","char":"1.2"}})#end #@outline({"grade":"3","bookmarkNO":"_Toc374566205","name":"集合与继承器","multilevel":{"level":"2","ilfo":"41","char":"1.2.1"}})#end #@outline({"grade":"2","bookmarkNO":"_Toc374566206","name":"方案的重复使用","multilevel":{"level":"1","ilfo":"41","char":"1.3"}})#end #end #end
Word效果:
段落:
#@wordDocument() #@body() #p("普通段落测试") #p("左对齐",{"align":"left"}) #p("居中",{"align":"center"}) #p("右对齐",{"align":"right"}) #p("两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐两端对齐") #p("分散对齐",{"align":"distribute"}) #p("方正姚体",{"font":{"name":"方正姚体"}}) #p("48号字体",{"font":{"size":"48"}}) #p("红色字体",{"font":{"color":"FF0000"}}) #p("加粗",{"font":{"bold":""}}) #p("斜体",{"font":{"incline":""}}) #p("删除线",{"font":{"strike":""}}) #p("双下划线",{"font":{"u":"double"}}) #p("黄色高亮",{"font":{"highlight":"yellow"}}) #p("首字缩进",{"indent":{"firstLine":"300"}}) #end #end
Word效果:
表格
#@wordDocument() #@body() #p("表格测试1,两行两列,单元格高度和宽度自动",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table() #@tableRow() #@tableCell() #p("单元格11单元格11单元格11单元格11单元格11") #end #@tableCell() #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22单元格22") #end #end #end #p("表格测试2,两行两列,高度自动",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table() #@tableRow() #@tableCell({"span":{"width":"4000"}}) ##单元格宽度 4000 #p("单元格11") #end #@tableCell({"span":{"width":"3000"}}) ##单元格宽度 3000 #p("单元格12") #end #end #@tableRow() #@tableCell({"span":{"width":"4000"}}) ##单元格宽度 4000 #p("单元格21") #end #@tableCell({"span":{"width":"3000"}}) ##单元格宽度 3000 #p("单元格22") #end #end #end #p("表格测试3,两行两列,宽度自动",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table() #@tableRow() #@tableCell() #p("单元格11") #end #@tableCell() #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22") #end #end #end #p("表格测试4,边框类型为单线,颜色为绿色,宽度为30",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table({"borders":{"wval":"single","color":"92D050","size":"30"}}) #@tableRow() #@tableCell() #p("单元格11") #end #@tableCell() #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22") #end #end #end #p("表格测试5,边框类型为双划线,颜色为紫色,宽度为20",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table({"borders":{"wval":"double","color":"7030A0","size":"20"}}) #@tableRow() #@tableCell() #p("单元格11") #end #@tableCell() #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22") #end #end #end #p("表格测试6,",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table({"shading":{"wval":"solid","color":"92D050","fill":"auto"}}) #@tableRow() #@tableCell({"shading":{"wval":"solid","color":"FFFF00","fill":"auto"}, "span":{"width":"6000","type":"dxa"}}) #p("单元格11") #end #@tableCell() #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22") #end #end #end #p("表格测试7,",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table({"shading":{"wval":"solid","color":"92D050","fill":"auto"}}) #@tableRow() #@tableCell() #p("单元格11") #end #@tableCell({"borders":{"wval":"thin-thick-medium-gap","width":"120","color":"00B050"}, "shading":{"wval":"solid","color":"FF0000","fill":"FF0000"}}) #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22") #end #end #end #p("表格测试8,",{"font":{"size":"24","color":"FF0000","bold":""}}) #@table({"shading":{"wval":"solid","color":"92D050","fill":"auto"}}) #@tableRow() #@tableCell({"borders":{"wval":"dash-dot-stroked","width":"120","color":"E36C0A"}, "shading":{"wval":"solid","color":"FFFF00","fill":"auto"}}) #p("单元格11") #end #@tableCell() #p("单元格12") #end #end #@tableRow() #@tableCell() #p("单元格21") #end #@tableCell() #p("单元格22") #end #end #end #end #end
Word效果:
从上面的示例来看,确实可以方便的包含各种元素的Word文档。对于框架中不支持的Word元,也可以进行方便的扩展,扩展过程不需要编写程序,只要编写相应的宏文件即可。
Maven依赖坐标:
<dependency> <groupId>org.tinygroup</groupId> <artifactId>docgen</artifactId> <version>0.0.12</version> </dependency>
最新版本源码在线查看地址:
https://git.oschina.net/tinyframework/tiny
https://git.oschina.net/tinyframework/tiny/tree/master/framework/org.tinygroup.docgen