Poi的Maven依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.poi.xwpf.converter.pdf</artifactId>
<version>2.0.2</version>
<exclusions>
<exclusion>
<artifactId>2.1.7</artifactId>
<groupId>fr.opensagres.xdocreport.itext</groupId>
</exclusion>
</exclusions>
</dependency>
Word模板
${}
占位符用于替换固定文本值- 检验结果表格中追加结果数据
- [PLACEHOLDER] 用于添加空行,使样本送检日期那个表格始终在第一页最底部
- 附录表格用于添加附录说明,其中第一行前两个单元格在内容添加完成之后需要合并,而且所有内容添加完成之后需要将第一列值相同且连续的单元格合并
实现步骤
封装待替换的数据
有两类的数据,一个是固定文本值的,还有一个是表格数据
固定文本数据
可以使用Map进行封装
Map<String, String> map= new HashMap<>();
map.put("checkedName","张三");
map.put("telNum","13517899871");
...
表格数据
使用两个List封装结果集和附录集
// 阳性结果集
List<Map<String, Object>> positiveResultList = new ArrayList<>();
// 附录集
List<Map<String, Object>> appendixList = new ArrayList<>();
- 结果集
// 单个阳性结果
Map<String, Object> positiveResult = new LinkedHashMap<>();
// 中文名
String chineseName = pathogenList.get(i);
positiveResult.put("chineseName", chineseName);
// 拉丁文名
positiveResult.put("latinName", PathogenEnum.getPathogenLatinName(chineseName));
// 分类
positiveResult.put("virusType", PathogenEnum.getPathogenVirusType(chineseName));
// 判定结果
positiveResult.put("analyzeResult", "阳性");
positiveResultList.add(positiveResult);
- 附录集
// 单个附录
Map<String, Object> appendix = new LinkedHashMap<>();
// 病原体类型
appendix.put("virusType", PathogenEnum.getPathogenVirusType(chineseName));
// 中文名
appendix.put("chineseName", chineseName);
// 参考文献
appendix.put("references", PathogenEnum.getPathogenReferences(chineseName));
// 病原体注释说明
appendix.put("comments", PathogenEnum.getPathogenComments(chineseName));
appendixList.add(appendix);
读取模板文件
// 模板文件
File file = new File(modelPath);
InputStream inputStream = new FileInputStream(file);
操作模板
XWPFDocument document = new XWPFDocument(inputStream);
// 替换表格
Iterator<XWPFTable> itTable = document.getTablesIterator();//获得Word的表格
// 表格index
int tableIndex = 0;
// 遍历表格
while (itTable.hasNext()) {
XWPFTable table = itTable.next();
// 在第二个表格中添加结果数据
if (tableIndex == 1) {
for (Map<String, Object> rowMap : positiveResultList) {
// 创建新行
XWPFTableRow row = table.createRow();
// 添加单元格
for (int i = 0; i < rowMap.keySet().size(); i++) {
XWPFTableCell cell = row.getCell(i);
if (cell == null) {
cell = row.createCell();
}
// 设置文字垂直居中
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
// 获取或创建段落和运行来设置文本和属性
XWPFParagraph paragraph;
if (cell.getParagraphs() == null || cell.getParagraphs().isEmpty()) {
paragraph = cell.addParagraph();
} else {
paragraph = cell.getParagraphs().get(0);
}
// 设置文字垂直居中
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
// 设置单元格文本
String cellText = rowMap.get(new ArrayList<>(rowMap.keySet()).get(i)).toString();
run.setText(cellText);
// 中文文本长度超过10则缩小填充
if (i == 0) {
if (cellText.length() > 10) {
// 缩小字体 -- 9磅
run.setFontSize(9);
}
// 拉丁文名,则设置斜体
}if (i == 1) {
run.setItalic(true);
}
}
}
// 在第四个表格中添加附录
} else if (tableIndex == 3) {
for (Map<String, Object> rowMap : appendixList) {
// 创建新行
XWPFTableRow row = table.createRow();
// 添加单元格
for (int i = 0; i < rowMap.keySet().size(); i++) {
XWPFTableCell cell = row.getCell(i);
if (cell == null) {
cell = row.createCell();
}
// 设置文字垂直居中
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
// 获取或创建段落和运行来设置文本和属性
XWPFParagraph paragraph;
if (cell.getParagraphs() == null || cell.getParagraphs().isEmpty()) {
paragraph = cell.addParagraph();
} else {
paragraph = cell.getParagraphs().get(0);
}
// 设置文字垂直居中
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
// 设置单元格文本
String cellText = rowMap.get(new ArrayList<>(rowMap.keySet()).get(i)).toString();
run.setText(cellText);
}
}
// 合并表头前两列
XWPFTableRow firstRow = table.getRow(0);
// 获取单元格
List<XWPFTableCell> cells = firstRow.getTableCells();
// 如果有至少两个单元格,合并它们
if (cells.size() >= 2) {
cells.get(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
cells.get(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
}
/*
合并第一列值相同且连续的单元格
*/
// 上一个单元格的值
String prevCellValue = null;
// 合并开始位置
int mergeStart = -1;
for (int i = 0; i < table.getRows().size(); i++) {
XWPFTableRow row = table.getRow(i);
XWPFTableCell cell = row.getCell(0);
String cellValue = cell.getText();
if (prevCellValue == null) {
prevCellValue = cellValue;
mergeStart = i;
} else if (prevCellValue.equals(cellValue)) {
// 如果当前单元格值与上一个单元格值相同,则将此单元格设置为合并状态
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
} else {
// 如果当前单元格与上一个单元格的值不同,检查是否需要开始新的合并
if (mergeStart < i - 1) {
table.getRow(mergeStart).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
}
prevCellValue = cellValue;
mergeStart = i;
}
}
// 如果最后一个单元格是合并的一部分
if (mergeStart < table.getRows().size() - 1) {
table.getRow(mergeStart).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
}
// 其他表格替换占位符的文字
} else {
// 获得表格总行数
int count = table.getNumberOfRows();
//遍历表格的每一行
for (int i = 0; i < count; i++) {
// 获得表格的行
XWPFTableRow row = table.getRow(i);
// 在行元素中,获得表格的单元格
List<XWPFTableCell> cells = row.getTableCells();
// 遍历单元格
for (XWPFTableCell cell : cells) {
// 所有需要填充的元素
for (Map.Entry<String, String> e : map.entrySet()) {
// 单元格原占位符: 姓名:${checkedName}
String text = cell.getText();
// 匹配${}
Matcher m = Pattern.compile("\\$\\{(.*?)}").matcher(text);
if (m.find()) {
// 找到需要替换变量" checkedName
String sub=text.substring(text.indexOf("${"),text.indexOf("}")+1);
// 如果单元格中的变量和‘键’相等,就用‘键’所对应的‘值’代替。
if (sub.equals(e.getKey())) {
CTTc cttc = cell.getCTTc();
CTTcPr ctPr = cttc.addNewTcPr();
ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
String replace = text.replace(sub, e.getValue());
cell.removeParagraph(0);
cell.setText(replace);
}
}
}
}
}
}
tableIndex++;
}
// 替换段落
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (int i = 0; i < paragraphs.size(); i++) {
XWPFParagraph paragraph = paragraphs.get(i);
// 最大支持添加20行空白行
if (positiveResultList.size() < 20) {
// 空白行标识
if (paragraph.getText().contains("[PLACEHOLDER]")) {
// 在PLACEHOLDER前插入20 - positiveResultList.size())个空段落
for (int j = 0; j < (20 - positiveResultList.size()); j++) {
XmlCursor cursor = paragraph.getCTP().newCursor();
document.insertNewParagraph(cursor);
}
// 删除原有的[PLACEHOLDER]段落
document.removeBodyElement(document.getPosOfParagraph(paragraph));
break;
}
}
}
输出文件
File f = new File(docxPath + saveFileNameword);
createDir(f.getParentFile());
fileOutputStream = new FileOutputStream(f);
document.write(fileOutputStream);
转换为PDF
/**
* @param inputFile 目标文件地址
* @param pdfpathdir 输出文件夹
* @return
*/
public static boolean word2Pdf(String inputFile, String pdfpathdir){
logger.debug("word2Pdf inputFile={}, pdfpathdir={}",inputFile,pdfpathdir);
long start = System.currentTimeMillis();
String command = null;
boolean flag =true;
String osName = System.getProperty("os.name");
if (osName.contains("Windows")) {
command = "cmd.exe /c start soffice --headless --invisible --convert-to pdf:writer_pdf_Export " + inputFile + " --outdir " + pdfpathdir;
}else {
command = "libreoffice --headless --invisible --convert-to pdf:writer_pdf_Export " + inputFile + " --outdir " + pdfpathdir ;
}
logger.debug("word2Pdf command={}",command);
flag = executeLibreOfficeCommand(command);
long end = System.currentTimeMillis();
logger.debug("word2Pdf 消耗时长:{} ms", end - start);
return flag;
}
private synchronized static boolean executeLibreOfficeCommand(String command) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
logger.error("executeLibreOfficeCommand , Thread ,e ={}",e);
}
Process process=null;
Runtime runtime = Runtime.getRuntime();
try {
process=runtime.exec(command);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(process.getInputStream())));
String line=null;
while ((line = bufferedReader.readLine())!=null){
logger.debug("子进程输出:"+line);
}
bufferedReader.close();
} catch (Exception e) {
logger.error("executeLibreOfficeCommand ,e ={}",e);
}finally {
try {
process.getInputStream().close();
process.getInputStream().close();
process.getErrorStream().close();
//方法将导致当前的线程等待,如果必要的话,直到由该Process对象表示的进程已经终止。此方法将立即返回,如果子进程已经终止。如果子进程尚未终止,则调用线程将被阻塞,直到子进程退出。
process.waitFor();
runtime.freeMemory();
process.destroy();
} catch (Exception e) {
logger.error("executeLibreOfficeCommand , finally ,e ={}",e);
}
}
return false;
}
效果
评论区