文档生成框架

简介:

序言

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格式,然后从中找出相关的内容,然后修改为模式。

测试用例

插入内存中的图像到Word文档

?
1
2
3
4
5
6
7
8
9
10
11
# @wordDocument ()
 
# @body ()
 
# @image ({ "name" : "111.jpg" , "width" : "249pt" , "height" : "119.25pt" , "data" : "$picData" })
 
#end
 
#end
 
#end

Word结果:

clip_image002

插入文件中的图像到Word文档

?
1
2
3
4
5
6
7
8
9
10
11
# @wordDocument ()
 
# @body ()
 
# @imageFromFile ({ "name" : "111.jpg" , "width" : "249pt" , "height" : "119.25pt" , "file" : "c:\pic.jpg" })
 
#end
 
#end
 
#end

Word结果:

clip_image003

常用功能

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# @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结果:

clip_image005

大纲功能

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# @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结果:

clip_image007

索引

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# @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效果:

clip_image009

段落:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# @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效果:

clip_image011

表格

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# @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效果:

clip_image013

小结

从上面的示例来看,确实可以方便的包含各种元素的Word文档。对于框架中不支持的Word元,也可以进行方便的扩展,扩展过程不需要编写程序,只要编写相应的宏文件即可。

Maven依赖坐标:

?
1
2
3
4
5
< dependency >
   < groupId >org.tinygroup</ groupId >
   < artifactId >docgen</ artifactId >
   < version >0.0.12</ version >
</ dependency >

相关文章
|
5月前
|
JavaScript 前端开发
第七篇-JavascriptDOM 文档对象模型
第七篇-JavascriptDOM 文档对象模型
31 4
|
6月前
|
存储 安全 Linux
构建一个atf必须读点gic的原生文档
构建一个atf必须读点gic的原生文档
88 0
|
机器学习/深度学习 自然语言处理 机器人
开源大模型文档
开源大模型文档
1069 0
|
JavaScript 开发者
elementui是什么,简要概述?
elementui是什么,简要概述?
1042 0
|
编译器 Go
第四十七章 开发自定义标签 - 服务器文档对象模型
第四十七章 开发自定义标签 - 服务器文档对象模型
85 0
|
JSON 缓存 移动开发
前端组件库文档解决方案
本篇主要分享什么内容: 常用的文档/静态站点生成工具有哪些 每个工具有什么特点 工具适应场景 希望可以帮助到你
|
JSON 缓存 前端开发
搭建前端组件库文档最佳姿势:Docz
搭建前端组件库文档最快姿势:Docz
CoreFoundation 框架详细解析
[CoreFoundation框架详细解析(一) —— 基本概览]https://www.jianshu.com(/p/44558550ddc9)CoreFoundation框架详细解析(二) —— 简介CoreFoundation框架详细解析(三) ...
955 0
|
设计模式 API iOS开发
AudioUnit 框架详细解析
1. AudioUnit框架详细解析(一) —— 基本概览2. AudioUnit框架详细解析(二) —— 关于Audio Unit Hosting之概览(一)3.
1172 0
DeviceCheck 框架详细解析
DeviceCheck框架详细解析 (一) —— 基本概览DeviceCheck框架详细解析 (二) —— 访问和修改每个设备的数据
1709 0
下一篇
无影云桌面