1 @Slf4j
2 public class PoiWordUtils {
3
4 /**
5 * 顿号
6 */
7 private static final String DUNHAO = "、";
8
9 /**
10 * 执行
11 *
12 * @param in 模板输入流
13 * @param out 文件输出流
14 * @param params params
15 * @Author LIZHIQIANG
16 * @Date Create in 2020/1/14 9:58
17 */
18 public static void execute(InputStream in, OutputStream out, JSONObject params) {
19 try {
20 XWPFDocument document = new XWPFDocument(in);
21 //需要循环的段落${listRow}开头,暂定最多三个
22 List<XWPFParagraph> targetParagraphs1 = new ArrayList<>();
23 List<XWPFParagraph> targetParagraphs2 = new ArrayList<>();
24 List<XWPFParagraph> targetParagraphs3 = new ArrayList<>();
25 List<IBodyElement> bodyElements = document.getBodyElements();
26 for (IBodyElement element : bodyElements) {
27 //文本段落对象
28 if (element instanceof XWPFParagraph) {
29 XWPFParagraph paragraph = (XWPFParagraph) element;
30 //段落内容
31 String paragraphText = paragraph.getParagraphText();
32
33 //空段落跳过
34 if (paragraphText.isEmpty()) {
35 continue;
36 }
37 //需要循环的段落
38 if (paragraphText.startsWith("${listRow1}")) {
39 targetParagraphs1.add(paragraph);
40 continue;
41 }
42 if (paragraphText.startsWith("${listRow2}")) {
43 targetParagraphs2.add(paragraph);
44 continue;
45 }
46 if (paragraphText.startsWith("${listRow3}")) {
47 targetParagraphs3.add(paragraph);
48 continue;
49 }
50 //处理普通段落
51 processParagraph(paragraph, params);
52 } else if (element instanceof XWPFTable) {
53 XWPFTable table = (XWPFTable) element;
54 List<XWPFTableRow> rows = table.getRows();
55 String tableDataString = params.getString("tableData");
56 JSONObject tableData = JSON.parseObject(tableDataString);
57 System.out.println(JSON.toJSONString(tableData,true));
58 if (tableData == null) {
59 continue;
60 }
61 //需要循环的tableRows
62 List<XWPFTableRow> targetTableRows = new ArrayList<>();
63 List<Integer> targetTableRowsPos = new ArrayList<>();
64 for (int i = 0; i < rows.size(); i++) {
65 XWPFTableRow row = rows.get(i);
66 List<XWPFTableCell> cells = row.getTableCells();
67 String firstParagraphText2 = cells.get(0).getParagraphs().get(0).getParagraphText();
68 String firstParagraphText = cells.get(0).getParagraphs().get(0).getText();
69 if (firstParagraphText.startsWith("${listTables")) {
70 targetTableRows.add(row);
71 targetTableRowsPos.add(i);
72 continue;
73 }
74 for (XWPFTableCell cell : cells) {
75 List<XWPFParagraph> paragraphs = cell.getParagraphs();
76 for (XWPFParagraph paragraph : paragraphs) {
77 processParagraph(paragraph, tableData);
78 }
79 }
80 }
81 processTableRows(table, targetTableRows, targetTableRowsPos, tableData);
82 }
83
84 }
85 //处理循环段落
86 processParagraphs(document, targetParagraphs1, params, "1");
87 processParagraphs(document, targetParagraphs2, params, "2");
88 processParagraphs(document, targetParagraphs3, params, "3");
89 document.write(out);
90
91 } catch (IOException e) {
92 e.printStackTrace();
93 }
94 }
95
96 /**
97 * 处理普通段落
98 *
99 * @param paragraph paragraph
100 * @param params params
101 * @Author LIZHIQIANG
102 * @Date Create in 2020/1/14 9:38
103 */
104 private static void processParagraph(XWPFParagraph paragraph, JSONObject params) {
105
106 List<XWPFRun> runs = paragraph.getRuns();
107 //需要循环替换的单元
108 List<XWPFRun> targetRuns1 = new ArrayList<>();
109 List<XWPFRun> targetRuns2 = new ArrayList<>();
110 List<XWPFRun> targetRuns3 = new ArrayList<>();
111 for (XWPFRun run : runs) {
112 String runKey = run.text();
113 System.out.println(runKey);
114 if (!runKey.startsWith("${") || !runKey.endsWith("}")) {
115 continue;
116 }
117 if ("${#}".equals(runKey)) {
118 run.setText("", 0);
119 continue;
120 }
121 if ("${listTables1}".equals(runKey)) {
122 run.setText("", 0);
123 continue;
124 }
125 if (runKey.startsWith("${listRow")) {
126 run.setText("", 0);
127 continue;
128 }
129 //添加需要循环的单元
130 if (runKey.startsWith("${listRuns1")) {
131 targetRuns1.add(run);
132 continue;
133 }
134 if (runKey.startsWith("${listRuns2")) {
135 targetRuns2.add(run);
136 continue;
137 }
138 if (runKey.startsWith("${listRuns3")) {
139 targetRuns3.add(run);
140 continue;
141 }
142 String key = runKey.replace("${", "").replace("}", "");
143 String value = params.getString(key);
144 if (!StringUtils.isEmpty(value)) {
145 run.setText(value, 0);
146 } else {
147 run.setText("", 0);
148 log.warn("处理普通段落单元失败:{},找不到替换数据", runKey);
149 }
150 }
151 //处理需要循环的单元
152 processRuns(paragraph, targetRuns1, params, "1");
153 processRuns(paragraph, targetRuns2, params, "2");
154 processRuns(paragraph, targetRuns3, params, "3");
155 }
156
157 /**
158 * 处理需要循环的单元
159 *
160 * @param paragraph paragraph
161 * @param targetRuns targetRuns
162 * @param params params
163 * @return void
164 * @Author LIZHIQIANG
165 * @Date Create in 2020/3/1 18:06
166 */
167 private static void processRuns(XWPFParagraph paragraph, List<XWPFRun> targetRuns, JSONObject params, String sorting) {
168
169 if (targetRuns.isEmpty() || params.isEmpty()) {
170 return;
171 }
172 //默认key
173 String defaultKey = "listRuns" + sorting;
174 JSONArray jsonArray = params.getJSONArray(defaultKey);
175 //循环需要替换的数据
176 for (int i = 0; i < jsonArray.size(); i++) {
177 Object data = jsonArray.get(i);
178 //参数 一次循环的参数
179 JSONObject param = JSON.parseObject(data.toString());
180 //循环需要替换的单元
181 for (XWPFRun run : targetRuns) {
182
183 String runKey = run.text();
184 XWPFRun newRun = paragraph.createRun();
185 CTRPr rPr = run.getCTR().getRPr();
186 newRun.getCTR().setRPr(rPr);
187 //处理顿号
188 if (runKey.equals("${" + defaultKey + "DUNHAO}")) {
189 if (i == jsonArray.size() - 1) {
190 newRun.setText("", 0);
191 continue;
192 }
193 newRun.setText(DUNHAO);
194 continue;
195 }
196 String key = runKey.replace("${", "").replace("}", "");
197 String value = param.getString(key);
198 if (!StringUtils.isEmpty(value)) {
199 newRun.setText(value);
200 } else {
201 log.warn("处理循环单元失败:{},找不到对应值", runKey);
202 newRun.setText("", 0);
203 }
204
205 }
206 }
207 for (XWPFRun run : targetRuns) {
208 run.setText("", 0);
209 }
210
211 }
212
213 /**
214 * 处理循环段落(新增段落后删模板段落)
215 *
216 * @param document document
217 * @param targetParagraphs targetParagraphs
218 * @param params params
219 * @Author LIZHIQIANG
220 * @Date Create in 2020/1/14 9:38
221 */
222 private static void processParagraphs(XWPFDocument document, List<XWPFParagraph> targetParagraphs, JSONObject params, String sorting) {
223
224 if (targetParagraphs.isEmpty() || params.isEmpty()) {
225 return;
226 }
227 String listKey = "listRow" + sorting;
228 JSONArray jsonArray = params.getJSONArray(listKey);
229 if (jsonArray == null) {
230 return;
231 }
232
233 //document.insertNewParagraph(cursor) 插到这个光标前面 所以反转
234 Collections.reverse(targetParagraphs);
235 Collections.reverse(jsonArray);
236 //需要追加的此段落后 最后一个
237 XWPFParagraph lastParagraph = document.getParagraphArray(document.getPosOfParagraph(targetParagraphs.get(0)));
238 //循环需要替换的数据
239 // for (int i = 0; i < jsonArray.size(); i++) {
240 // Object data = jsonArray.get(i);
241 // //参数 一次循环的参数
242 // JSONObject param = JSON.parseObject(data.toString());
243 // System.out.println(JSON.toJSONString(param,true));
244 // for (XWPFParagraph paragraph : targetParagraphs) {
245 // System.out.println("处理段落:"+paragraph.getText());
246 // //根据光标插入段落
247 // //需要追加的此段落光标后 最后一个
248 // XmlCursor cursor = lastParagraph.getCTP().newCursor();
249 // XWPFParagraph newPar = document.insertNewParagraph(cursor);
250 //
251 // lastParagraph = newPar;
252 // //重置光标 最后插入的光标是这个
253 // //获取段落对象的样式的ppr标签
254 // CTPPr pPr = paragraph.getCTP().getPPr();
255 // newPar.getCTP().setPPr(pPr);
256 // List<XWPFRun> runs = paragraph.getRuns();
257 //
258 // //需要循环替换的单元
259 // List<XWPFRun> targetRuns1 = new ArrayList<>();
260 // List<XWPFRun> targetRuns2 = new ArrayList<>();
261 // List<XWPFRun> targetRuns3 = new ArrayList<>();
262 // for (XWPFRun run : runs) {
263 // String runKey = run.text();
264 // CTRPr rPr = run.getCTR().getRPr();
265 // XWPFRun newRun = newPar.createRun();
266 // newRun.getCTR().setRPr(rPr);
267 // if (!runKey.startsWith("${") || !runKey.endsWith("}")) {
268 // newRun.setText(runKey);
269 // continue;
270 // }
271 // //添加需要循环的单元
272 // if (runKey.startsWith("${listRuns1")) {
273 // targetRuns1.add(run);
274 // continue;
275 // }
276 // if (runKey.startsWith("${listRuns2")) {
277 // targetRuns2.add(run);
278 // continue;
279 // }
280 // if (runKey.startsWith("${listRuns3")) {
281 // targetRuns3.add(run);
282 // continue;
283 // }
284 // if (("${" + listKey + "}").equals(runKey)) {
285 // newRun.setText("");
286 // continue;
287 // }
288 // if ("${#}".equals(runKey)) {
289 // run.setText("", 0);
290 // continue;
291 // }
292 // String key = runKey.replace("${", "").replace("}", "");
293 // String value = param.getString(key);
294 // if (!StringUtils.isEmpty(value)) {
295 // newRun.setText(value);
296 //// System.out.println("处理单元:"+runKey+" > "+value);
297 // continue;
298 // } else {
299 // log.warn("处理循环模板段落单元失败:{},找不到对应值", runKey);
300 // }
301 // newRun.setText(runKey);
302 // }
303 // //===处理需要循环的单元
304 // processRuns(newPar, targetRuns1, param, "1");
305 // processRuns(newPar, targetRuns2, param, "2");
306 // processRuns(newPar, targetRuns3, param, "3");
307 //
308 // }
309 // }
310 //删除模板集合段落
311 XmlCursor lastCursor = lastParagraph.getCTP().newCursor();
312 for (Object data : jsonArray) {
313 JSONObject param = JSON.parseObject(data.toString());
314 for (XWPFParagraph paragraph : targetParagraphs) {
315 //根据光标插入段落
316 XWPFParagraph newPar = document.insertNewParagraph(lastCursor);
317 //重置光标 最后插入的光标是这个
318 lastCursor = newPar.getCTP().newCursor();
319 //复制段落
320 copyParagraph(paragraph, newPar);
321 //处理普通段落
322 processParagraph(newPar, param);
323 }
324 }
325 for (XWPFParagraph paragraph : targetParagraphs) {
326 document.removeBodyElement(document.getPosOfParagraph(paragraph));
327 }
328 }
329
330 /**
331 * 处理循环的表格行
332 *
333 * @param table table
334 * @param targetTableRows targetTableRows
335 * @param targetTableRowsPos targetTableRowsPos
336 * @param params params
337 * @return void
338 * @Author LIZHIQIANG
339 * @Date Create in 2020/3/5 20:56
340 */
341 private static void processTableRows(XWPFTable table, List<XWPFTableRow> targetTableRows, List<Integer> targetTableRowsPos, JSONObject params) {
342 if (targetTableRows.isEmpty() || params.isEmpty()) {
343 return;
344 }
345 //默认key
346 String defaultKey = "listTables1";
347 JSONArray jsonArray = params.getJSONArray(defaultKey);
348 if (jsonArray == null) {
349 return;
350 }
351
352 for (int i = 0; i < targetTableRows.size(); i++) {
353 XWPFTableRow row = targetTableRows.get(i);
354 //循环需要替换的数据
355 for (Object data : jsonArray) {
356 //参数 一次循环的参数
357 JSONObject param = JSON.parseObject(data.toString());
358 XWPFTableRow newRow = table.createRow();
359 copyTableRow(row, newRow);
360 List<XWPFTableCell> newRowCells = newRow.getTableCells();
361 for (XWPFTableCell newRowCell : newRowCells) {
362 List<XWPFParagraph> paragraphs = newRowCell.getParagraphs();
363 for (XWPFParagraph paragraph : paragraphs) {
364 processParagraph(paragraph, param);
365 }
366 }
367 }
368 table.removeRow(targetTableRowsPos.get(i));
369 }
370 }
371
372
373 /**
374 * 复制单元
375 *
376 * @param target target
377 * @param source source
378 */
379 private static void copyRun(XWPFRun source, XWPFRun target) {
380 // 设置run属性
381 target.getCTR().setRPr(source.getCTR().getRPr());
382 // 设置文本
383 target.setText(source.text());
384 }
385
386 /**
387 * 复制段落
388 *
389 * @param source 源段落
390 * @param target 目标段落
391 */
392 private static void copyParagraph(XWPFParagraph source, XWPFParagraph target) {
393 // 设置run属性
394 target.getCTP().setPPr(source.getCTP().getPPr());
395 //清除新段落的单元
396 List<XWPFRun> targetRuns = target.getRuns();
397 if (targetRuns != null && targetRuns.size() > 0) {
398 for (int pos = 0; pos < targetRuns.size(); pos++) {
399 target.removeRun(pos);
400 }
401 }
402 //复制源段落的单元到目标段落
403 for (XWPFRun sourceRun : source.getRuns()) {
404 XWPFRun targetRun = target.createRun();
405 copyRun(sourceRun, targetRun);
406 }
407 }
408
409 /**
410 * 复制表格列
411 *
412 * @param source 源
413 * @param target 目标
414 */
415 private static void copyTableCell(XWPFTableCell source, XWPFTableCell target) {
416 // 设置TableCell属性
417 target.getCTTc().setTcPr(source.getCTTc().getTcPr());
418
419 List<XWPFParagraph> targetParagraphs = target.getParagraphs();
420 if (targetParagraphs != null && targetParagraphs.size() > 0) {
421 for (int pos = 0; pos < targetParagraphs.size(); pos++) {
422 target.removeParagraph(pos);
423 }
424 }
425 List<XWPFParagraph> sourceParagraphs = source.getParagraphs();
426 for (XWPFParagraph sourceParagraph : sourceParagraphs) {
427 XWPFParagraph targetParagraph = target.addParagraph();
428 copyParagraph(sourceParagraph, targetParagraph);
429 }
430 }
431
432 /**
433 * 复制表格行
434 *
435 * @param source 源
436 * @param target 目标
437 */
438 private static void copyTableRow(XWPFTableRow source, XWPFTableRow target) {
439 // 设置TableRow属性
440 target.getCtRow().setTrPr(source.getCtRow().getTrPr());
441
442 List<XWPFTableCell> sourceCells = source.getTableCells();
443 List<XWPFTableCell> targetCells = target.getTableCells();
444 //复制列及其属性和内容
445 for (int pos = 0; pos < sourceCells.size(); pos++) {
446 XWPFTableCell sourceCell = sourceCells.get(pos);
447 XWPFTableCell targetCell = targetCells.get(pos);
448 copyTableCell(sourceCell, targetCell);
449 }
450 }
451
452 }