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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
package com.product.print.util;
 
import cn.hutool.core.io.IoUtil;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.render.RenderContext;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.TableTools;
import com.product.common.lang.StringUtils;
import com.product.core.exception.BaseException;
import com.product.print.config.CmnCode;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalAlignRun;
 
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
/**
 * @Author cheng
 * @Date 2023/6/27 15:50
 * @Desc
 */
public class DynamicTableRenderPolicy extends com.deepoove.poi.policy.DynamicTableRenderPolicy {
 
    private String replaceKey;
 
    private final String indexKey = "~index~";
 
    public DynamicTableRenderPolicy(String replaceKey) {
        this.replaceKey = replaceKey;
 
    }
 
    private XWPFRun run;
 
    @Override
    public void doRender(RenderContext<Object> context) throws Exception {
        RunTemplate runTemplate = (RunTemplate) context.getEleTemplate();
        XWPFRun run = runTemplate.getRun();
        try {
            if (!TableTools.isInsideTable(run)) {
                throw new IllegalStateException(
                        "The template tag " + runTemplate.getSource() + " must be inside a table");
            }
            XWPFTableCell cell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
            XWPFTable table = cell.getTableRow().getTable();
            render(table, context.getData());
        } catch (Exception e) {
            throw new RenderException("Dynamic render table error:" + e.getMessage(), e);
        }
    }
 
    @Override
    public void render(XWPFTable xwpfTable, Object data) throws Exception {
        if (xwpfTable == null) {
            return;
        }
        List<Map<String, Object>> subTableData = (List<Map<String, Object>>) data;
        List<XWPFTableRow> rows = xwpfTable.getRows();
        //读取rows中的内容
        String tableExpression = "{{" + this.replaceKey + "}}";
        //获取表格起始行和结束行
        int startRowIndex = -1;
        rows:
        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            for (int j = 0; j < row.getTableCells().size(); j++) {
                XWPFTableCell cell = row.getTableCells().get(j);
                String text = cell.getText();
                if (tableExpression.equals(text)) {
                    startRowIndex = i;
                    break rows;
                }
 
            }
        }
        if (startRowIndex == -1) {
            throw new BaseException(CmnCode.NOT_FIND_START_FLAG);
        }
 
        //在表格中查找子表字段以{{$开头}}以}}结尾的内容
        String regex = "\\{\\{\\$[a-zA-Z0-9_]+\\}\\}";
        Pattern pattern = Pattern.compile(regex);
        //字段所在行
        XWPFTableRow fieldRow = null;
        for (int i = startRowIndex; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            for (int j = 0; j < row.getTableCells().size(); j++) {
                XWPFTableCell cell = row.getTableCells().get(j);
                String text = cell.getText();
                Matcher matcher = pattern.matcher(text);
                if (matcher.find()) {
                    fieldRow = row;
                    i = rows.size();
                    break;
                }
            }
        }
        if (fieldRow == null) {
            throw new BaseException(CmnCode.NOT_FIND_CHILD_TABLE_FIELD);
        }
 
        //读取最后行每个单元格的值,调用getReplaceKey方法获取表达式中的值
        String[] fieldNames = new String[fieldRow.getTableCells().size()];
        for (int i = 0; i < fieldRow.getTableCells().size(); i++) {
            XWPFTableCell cell = fieldRow.getTableCells().get(i);
            String text = cell.getText();
            String replaceKey = getReplaceKey(text);
            //获取表达式中的值
            if (StringUtils.isEmpty(replaceKey)) {
                //设置单元格为空值
                replaceKey = "";
            }
            fieldNames[i] = replaceKey;
        }
        //获取fieldRow所在的下标
        int fieldRowIndex = xwpfTable.getRows().indexOf(fieldRow);
 
        for (int i = 0; i < subTableData.size(); i++) {
            Map<String, Object> map = subTableData.get(i);
            //创建一行在fieldRowIndex下面
            XWPFTableRow row = xwpfTable.insertNewTableRow(fieldRowIndex + 1 + i);
            copyTableRow(row, fieldRow);
            //设置row的属性与fieldRow一致
            row.setHeight(fieldRow.getHeight());
            //遍历字段每个字段创建一个单元格
            for (int j = 0; j < fieldNames.length; j++) {
                //当前单元格
                XWPFTableCell cell;
                //判断row中第j个单元格是否存在
                if (row.getTableCells().size() > j) {
                    cell = row.getCell(j);
                } else {
                    cell = row.createCell();
                }
                //设置单元格的值从map中取出
                //判断是否是序号列
                if (indexKey.equals(fieldNames[j])) {
                    cell.setText(String.valueOf(i + 1));
                    continue;
                }
                Object value = map.get(fieldNames[j]);
                if (value == null) {
                    value = "";
                }
                String text = cell.getText();
                //删除单元格中的旧内容
                if (!StringUtils.isEmpty(text)) {
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    if (paragraphs.size() > 1) {
                        cell.removeParagraph(1);
                    }
                    List<XWPFRun> runs = paragraphs.get(0).getRuns();
                    //清空文字
                    for (int k = 0; k < runs.size(); k++) {
                        runs.get(k).setText("", 0);
                    }
                }
                cell.setText(value.toString());
                if ("lx_flow_opinion".equals(this.replaceKey) && "opinion".equals(fieldNames[j]) && !StringUtils.isEmpty(map.get("sign_attachment_uuid"))) {
                    //意见框 插入签名图片到单元格右下角位置
                    XWPFParagraph xwpfParagraph = cell.addParagraph();
                    xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
                    XWPFRun run = xwpfParagraph.createRun();
                    String signAttachmentBase64 = map.get("sign_attachment_uuid").toString();
                    //将base64转换字节流
                    byte[] bytes = Base64.getDecoder().decode(signAttachmentBase64.split(",")[1]);
                    //将字节流转换为输入流
                    InputStream inputStream = new ByteArrayInputStream(bytes);
                    try {
                        //换行插入图片
                        run.addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, "sign.png", Units.toEMU(50), Units.toEMU(20));
                        inputStream.close();
                    } catch (InvalidFormatException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
 
            }
        }
        //删除起始行
        xwpfTable.removeRow(startRowIndex);
        //删除fieldRow
        xwpfTable.removeRow(fieldRowIndex - 1);
 
//        //读取完毕后删除最后一行
//        xwpfTable.removeRow(rows.size() - 1);
        //获取所有的行判断单元格是否有值没有就删除该行
//        List<XWPFTableRow> tableRows = xwpfTable.getRows();
//        List<XWPFTableRow> deleteRows = new ArrayList<>();
//        for (int i = 0; i < tableRows.size(); i++) {
//            XWPFTableRow row = tableRows.get(i);
//            boolean flag = false;
//            for (int j = 0; j < row.getTableCells().size(); j++) {
//                XWPFTableCell cell = row.getTableCells().get(j);
//                if (StringUtils.isNotEmpty(cell.getText())) {
//                    flag = true;
//                    break;
//                }
//            }
//            if (!flag) {
//                deleteRows.add(row);
//            }
//        }
//        //遍历要删除的行获取所在的下标进行删除
//        for (int i = 0; i < deleteRows.size(); i++) {
//            XWPFTableRow row = deleteRows.get(i);
//            int index = xwpfTable.getRows().indexOf(row);
//            xwpfTable.removeRow(index);
//        }
//        //遍历数据集合,每个map对应一行数据
//        for (int i = 0; i < subTableData.size(); i++) {
//            Map<String, Object> map = subTableData.get(i);
//            //创建一行
//            XWPFTableRow row = xwpfTable.createRow();
//            //遍历字段每个字段创建一个单元格
//            for (int j = 0; j < fieldNames.length; j++) {
//                //当前单元格
//                XWPFTableCell cell;
//                //判断row中第j个单元格是否存在
//                if (row.getTableCells().size() > j) {
//                    cell = row.getTableCells().get(j);
//                } else {
//                    cell = row.createCell();
//                }
//                //设置单元格的值从map中取出
//                //判断是否是序号列
//                if (indexKey.equals(fieldNames[j])) {
//                    cell.setText(String.valueOf(i + 1));
//                    continue;
//                }
//                Object value = map.get(fieldNames[j]);
//                if (value == null) {
//                    value = "";
//                }
//                //设置单元格的值
//                cell.setText(value.toString());
//            }
//        }
 
    }
 
    /**
     * 解析表达式中的值获取 以{{$开头,以}}结尾的字符串取出$后面的值截止到}}
     */
    private String getReplaceKey(String text) {
        //使用正则解析表达式中的值获取 以{{$开头,以}}结尾的字符串取出$后面的值截止到}}
        Pattern pattern = Pattern.compile("\\{\\{\\$(.*?)\\}\\}");
        Matcher matcher = pattern.matcher(text);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }
 
    /**
     * 复制行,从source到target
     *
     * @param target
     * @param source
     */
    public void copyTableRow(XWPFTableRow target, XWPFTableRow source) {
        // 复制样式
        if (source.getCtRow() != null) {
            target.getCtRow().setTrPr(source.getCtRow().getTrPr());
        }
        // 复制单元格
        for (int i = 0; i < source.getTableCells().size(); i++) {
            XWPFTableCell cell1 = target.getCell(i);
            XWPFTableCell cell2 = source.getCell(i);
            if (cell1 == null) {
                cell1 = target.addNewTableCell();
            }
            copyTableCell(cell1, cell2);
        }
 
    }
 
    /**
     * 复制单元格,从source到target
     *
     * @param target
     * @param source
     */
    public void copyTableCell(XWPFTableCell target, XWPFTableCell source) {
        // 列属性
        if (source.getCTTc() != null) {
            target.getCTTc().setTcPr(source.getCTTc().getTcPr());
        }
        // 删除段落
        for (int pos = 0; pos < target.getParagraphs().size(); pos++) {
            target.removeParagraph(pos);
        }
        // 添加段落
        for (XWPFParagraph sp : source.getParagraphs()) {
            XWPFParagraph targetP = target.addParagraph();
            copyParagraph(targetP, sp);
        }
    }
 
    /**
     * 复制图片到target
     *
     * @param target
     * @param picture
     * @throws IOException
     * @throws InvalidFormatException
     */
    public void copyPicture(XWPFRun target, XWPFPicture picture) throws IOException, InvalidFormatException {
 
        String filename = picture.getPictureData().getFileName();
        InputStream pictureData = new ByteArrayInputStream(picture
                .getPictureData().getData());
        int pictureType = picture.getPictureData().getPictureType();
        int width = (int) picture.getCTPicture().getSpPr().getXfrm().getExt()
                .getCx();
 
        int height = (int) picture.getCTPicture().getSpPr().getXfrm().getExt()
                .getCy();
 
        // target.addBreak();
        target.addPicture(pictureData, pictureType, filename, width, height);
        // target.addBreak(BreakType.PAGE);
    }
 
    /**
     * 复制段落,从source到target
     *
     * @param target
     * @param source
     */
    public void copyParagraph(XWPFParagraph target, XWPFParagraph source) {
 
        // 设置段落样式
        target.getCTP().setPPr(source.getCTP().getPPr());
 
        // 移除所有的run
        for (int pos = target.getRuns().size() - 1; pos >= 0; pos--) {
            target.removeRun(pos);
        }
 
        // copy 新的run
        for (XWPFRun s : source.getRuns()) {
            XWPFRun targetrun = target.createRun();
            copyRun(targetrun, s);
        }
 
 
    }
 
    /**
     * 复制RUN,从source到target
     *
     * @param target
     * @param source
     */
    public void copyRun(XWPFRun target, XWPFRun source) {
        // 设置run属性
        target.getCTR().setRPr(source.getCTR().getRPr());
        // 设置文本
        target.setText(source.text());
        // 处理图片
        List<XWPFPicture> pictures = source.getEmbeddedPictures();
 
        for (XWPFPicture picture : pictures) {
            try {
                copyPicture(target, picture);
            } catch (InvalidFormatException e) {
                logger.error("copyRun", e);
            } catch (IOException e) {
                logger.error("copyRun", e);
            }
        }
    }
 
}