package com.vci.starter.word.util; import com.aspose.words.*; import com.vci.starter.web.exception.VciBaseException; import com.vci.starter.word.bo.WordMergeListDataSource; import com.vci.starter.word.bo.WordMergeStartTableDataBO; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.List; import java.util.*; /** * word操作类 * @author weidy * @date 2020/2/19 */ public class WordUtil { /** * 日志 */ private static Logger logger = LoggerFactory.getLogger(WordUtil.class); /** * 提示参数为空 * @param s 相关的参数,两两组合,第一个为参数的对象,第二个为显示的名称 * @throws VciBaseException 如果参数为空的时候会抛出异常 */ public static void alertNotNull(Object... s) throws VciBaseException { if(s!=null && s.length>0){ for(int i = 0 ; i < s.length ; i ++){ Object obj = s[i]; if(obj==null || StringUtils.isBlank(obj.toString())){ String param = ""; try{ i++; param = s[i].toString(); }catch(Exception e){ } throw new VciBaseException("参数[{0}]不能为空",new String[]{param}); } } } } /** * 带逗号的字符串转为list * @param s 字符串 * @return 字符串列表 */ public static List str2List(String s){ if (isNull(s)) { return null; } else { List l = new ArrayList(); Collections.addAll(l,removeComma(s).split(",")); return l; } } /** * 去除最前面的逗号,去除后面的逗号 * @param s 字符串 * @return 去除末尾逗号 */ public static String removeComma(String s){ if(s == null || s.trim().length() == 0) { return s; } else{ if(s.startsWith(",")) { s = s.substring(1, s.length()); } if(s.endsWith(",")) { s = s.substring(0, s.length() - 1); } return s; } } /** * 判断字符串是不是空,不判断trim * @param o 字符串 * @return true表示空 */ public static boolean isNull(String o){ return StringUtils.isEmpty(o); } /** * 往word写入表格数据,这种是使用startTable的方式,不会自动拷贝格式 * @param fileName 文件名,必须是word文件 * @param tableDataBOList 要写入到文件的信息 * @throws FileNotFoundException 文件不存在的时候会抛出异常 * @throws VciBaseException 写入数据出错时会抛出异常 */ public static void setTableDataToWord(String fileName, List tableDataBOList) throws FileNotFoundException, VciBaseException{ alertNotNull(fileName,"word的文件名称"); setTableDataToWord(new File(fileName),tableDataBOList); } /** * 往word写入表格数据,这种是使用startTable的方式,不会自动拷贝格式 * @param file 文件,必须是word文件 * @param tableDataBOList 要写入到文件的信息 * @throws FileNotFoundException 文件不存在的时候会抛出异常 * @throws VciBaseException 写入数据出错时会抛出异常 */ public static void setTableDataToWord(File file, List tableDataBOList) throws FileNotFoundException, VciBaseException{ alertNotNull(file,"word文件"); if(!file.exists()){ throw new FileNotFoundException(file.getName()); } InputStream in = null; Document document = null; try{ in = new FileInputStream(file); document = new Document(in); }catch (Exception e){ IOUtils.closeQuietly(in); //不能在finally里关闭,因为后面还需要用 throw new VciBaseException("初始化word文档对象的时候出现了错误,{0}",new String[]{file.getAbsolutePath()},e); } try { setTableDataToWord(document, tableDataBOList); }catch (VciBaseException e){ throw e; }catch (Throwable e){ throw new VciBaseException("向word里写入tableStart类型的域字段的时候出错,{0}",new String[]{file.getAbsolutePath()},e); }finally { IOUtils.closeQuietly(in); } try{ document.save(file.getAbsolutePath()); }catch (Exception e) { throw new VciBaseException("将word的内容返填到文件中出现了错误,{0}",new String[]{file.getAbsolutePath()},e); } } /** * 把信息写入word中的域字段中,域字段会保留(但是目前打印会依然显示域代码) * @param fileName 文件名 * @param dataMap 要写入的数据 * @throws FileNotFoundException 文件不存在时抛出异常 * @throws VciBaseException 写入出错的时候抛出此异常 */ public static void setFieldDataToWord(String fileName,Map dataMap) throws FileNotFoundException, VciBaseException{ alertNotNull(fileName,"word的文件名称"); setFieldDataToWord(new File(fileName), dataMap,true); } /** * 把信息写入word中的域字段中,域字段会保留(但是目前打印会依然显示域代码) * @param file 文件 * @param dataMap 要写入的数据 * @throws FileNotFoundException 文件不存在时抛出异常 * @throws VciBaseException 写入出错的时候抛出此异常 */ public static void setFieldDataToWord(File file,Map dataMap) throws FileNotFoundException, VciBaseException{ setFieldDataToWord(file, dataMap,true); } /** * 把信息写入word中的域字段中,域字段会保留(但是目前打印会依然显示域代码) * @param file 文件 * @param dataMap 要写入的数据 * @param retainFieldCode true会保留域字段(但是目前打印会依然显示域代码),false不会保留域字段,打印无问题 * @throws FileNotFoundException 文件不存在时抛出异常 * @throws VciBaseException 写入出错的时候抛出此异常 */ public static void setFieldDataToWord(File file,Map dataMap,boolean retainFieldCode ) throws FileNotFoundException, VciBaseException{ alertNotNull(file,"word文件"); if(!file.exists()){ throw new FileNotFoundException(file.getName()); } InputStream in = null; Document document = null; try{ in = new FileInputStream(file); document = new Document(in); }catch (Exception e){ IOUtils.closeQuietly(in); //不能在finally里关闭,因为后面还需要用 throw new VciBaseException("初始化word文档对象的时候出现了错误,{0}",new String[]{file.getAbsolutePath()},e); } try { if(retainFieldCode) { mergeMapForField(document, dataMap); }else{ mergeMapForFieldUnFormate(document, dataMap); } }catch (VciBaseException e){ throw e; }catch (Throwable e){ throw new VciBaseException("向word里的域字段写入信息的时候出错,{0}",new String[]{file.getAbsolutePath()},e); }finally { IOUtils.closeQuietly(in); } try{ document.save(file.getAbsolutePath()); }catch (Exception e) { throw new VciBaseException("将word的内容返填到文件中出现了错误,{0}",new String[]{file.getAbsolutePath()},e); } } /** * 往word写入表格数据,这种是使用startTable的方式,不会自动拷贝格式 * @param document word文档对象 * @param tableDataBOList 要写入到文件的信息 * @throws FileNotFoundException 文件不存在的时候会抛出异常 * @throws VciBaseException 写入数据出错时会抛出异常 */ public static void setTableDataToWord(Document document, List tableDataBOList) throws VciBaseException{ alertNotNull(document,"word文档对象"); if(!CollectionUtils.isEmpty(tableDataBOList)){ try { mergeBeanListUnFormate(document,tableDataBOList); } catch (Exception e) { logger.error("向word里写入tableStart类型的域字段的时候出错",e); throw new VciBaseException("向word里写入tableStart类型的域字段的时候出错,{0}" ,new String[]{e.getLocalizedMessage()},e); } } } /** * 写入表格数据,新的数据行的格式不会自动继承 * @param doc word文档 * @param wordSignMergeTableDataList 要写入的数据 * @throws Exception 写入失败或者表格不存在的时候会抛出异常 */ public static void mergeBeanListUnFormate(Document doc, List wordSignMergeTableDataList) throws Exception { if(!CollectionUtils.isEmpty(wordSignMergeTableDataList)) { for (WordMergeStartTableDataBO tableData : wordSignMergeTableDataList) { boolean needMergeColumn = false; String tableName = tableData.getTableName(); Table thisTable = null; boolean finedField = false; int startRowIndex = -1; Map mergeColumnNameIndexMap = new HashMap(); List mergeColumnList = str2List(tableData.getMergeColumn()); if (StringUtils.isNotBlank(tableData.getMergeColumn()) && tableData.getTableDataList().size()> 1){ needMergeColumn = true; //找到所有的表格中的单元格,看是否有这个表格的tableStart标记 NodeCollection tableNodes = doc.getChildNodes(NodeType.TABLE, true); Iterator tableIt = tableNodes.iterator(); while (tableIt.hasNext()) { Table table = (Table) tableIt.next(); int rowCount = table.getRows().getCount(); if (rowCount > 0) { //说明这个表格有行 RowCollection rowCollection = table.getRows(); for (int i = 0; i < rowCount; i++) { Row row = rowCollection.get(i); CellCollection cellCollection = row.getCells(); if (cellCollection != null) { for (int z = 0; z < cellCollection.getCount(); z++) { Cell cell = cellCollection.get(z); NodeCollection mergeCollection = cell.getChildNodes(NodeType.FIELD_START, true); Iterator fieldNodes = mergeCollection.iterator(); if (mergeCollection.getCount() > 0) { while (fieldNodes.hasNext()) { FieldStart fdStart = (FieldStart) fieldNodes.next(); if (fdStart.getFieldType() == FieldType.FIELD_MERGE_FIELD) { FieldMergeField fdMerge = (FieldMergeField) fdStart.getField(); String fieldName = fdMerge.getFieldName(); if (fieldName.equalsIgnoreCase("TableStart:" + tableName)) { thisTable = table; startRowIndex = i; } if(mergeColumnList.contains(fieldName)){ mergeColumnNameIndexMap.put(fieldName,z); } } } } } } } if(thisTable != null){ finedField = true; break; } } if(finedField){ break; } } } WordMergeListDataSource dataSource = new WordMergeListDataSource(tableData.getTableDataList(),tableName); doc.getMailMerge().executeWithRegions(dataSource); //处理了完数据后,有可能需要将行合并单元格 if (needMergeColumn && thisTable != null && mergeColumnNameIndexMap.size() > 0) { List> thisTableData = dataSource.getDataList(); for (String mergeColumnName : mergeColumnList) { //找所有的数据,记录相同行的数据 if(mergeColumnNameIndexMap.containsKey(mergeColumnName)) { int columnIndex = mergeColumnNameIndexMap.get(mergeColumnName) + 1; Map mergeRowMap = new HashMap(); Object lastRowValue = ""; int lastRowIndex = 0; for (int i = 0; i < thisTableData.size(); i++) { Map thisRowData = thisTableData.get(i); Object thisCellValue = thisRowData.get(mergeColumnName); if (thisCellValue == null) { thisCellValue = ""; } if (i == 0) { lastRowValue = thisCellValue; lastRowIndex = startRowIndex; //等于起始行,1 } else if (i != thisTableData.size() - 1) { if (!thisCellValue.toString().equalsIgnoreCase(lastRowValue.toString())) { //说明和上一行不一样了 mergeRowMap.put(lastRowIndex, i+startRowIndex-1); lastRowValue = thisCellValue; lastRowIndex = startRowIndex + i; } } else { if (!thisCellValue.toString().equalsIgnoreCase(lastRowValue.toString())) { //说明和上一行不一样了 mergeRowMap.put(lastRowIndex, i+startRowIndex-1); lastRowValue = thisCellValue; lastRowIndex = startRowIndex + i; // mergeRowMap.put(startRowIndex +i, startRowIndex +i); }else{ mergeRowMap.put(lastRowIndex, startRowIndex +i); lastRowValue = thisCellValue; lastRowIndex = startRowIndex + i; } } } //找到了所有的相同的内容,肯定会有值 doMerge(thisTable,mergeRowMap,columnIndex); } } } } } } /** * 执行合并单元格 * @param thisTable 表格对象 * @param mergeRowMap 要合并的行,key是其实行,value是结束行,从0开始 * @param columnIndex 列的需要,从0开始 */ private static void doMerge(Table thisTable, Map mergeRowMap, int columnIndex){ if (mergeRowMap.size() > 0) { for (Integer startRowIndex : mergeRowMap.keySet()) { Integer endRowIndex = mergeRowMap.get(startRowIndex); for (int x = startRowIndex; x <= endRowIndex; x++) { //开始的行设置为FIRST,其他的行设置为PREVIOUS if (x == startRowIndex) { Cell cell = thisTable.getRows().get(x).getCells().get(columnIndex - 1); cell.getCellFormat().setVerticalMerge(CellMerge.FIRST); } else { Cell cell = thisTable.getRows().get(x).getCells().get(columnIndex - 1); cell.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS); } } } } } /** * 设置域字段的值,这个域字段修改后域代码还在,不能切换域代码,否则数据丢失(注意,目前发现打印的时候还是显示域代码) * @param doc word文档 * @param dataMap 数据映射 * @throws VciBaseException 写入失败时会抛出异常 */ public static void mergeMapForField(Document doc, Map dataMap) throws VciBaseException{ NodeCollection fieldStartNodes=doc.getChildNodes(NodeType.FIELD_START,true); Iterator fieldNodes=fieldStartNodes.iterator(); while(fieldNodes.hasNext()) { FieldStart fdStart = (FieldStart) fieldNodes.next(); if (fdStart.getFieldType() == FieldType.FIELD_MERGE_FIELD) { FieldMergeField fdMerge = (FieldMergeField)fdStart.getField(); String fieldName = fdMerge.getFieldName(); if (dataMap.containsKey(fieldName)) { try { fdMerge.setResult(dataMap.get(fieldName)); } catch (Exception e) { throw new VciBaseException("写域字段的内容出错,{0}",new String[]{fieldName}); } } } } } /** * 设置域字段的值,这个域字段修改后域代码不会再存在,也就是不能再次使用 * @param doc word文档 * @param dataMap 数据映射 * @throws VciBaseException 写入失败时会抛出异常 */ public static void mergeMapForFieldUnFormate(Document doc, Map dataMap) throws VciBaseException{ String[] fieldName = new String[dataMap.size()]; Object[] values = new Object[dataMap.size()]; int i = 0 ; for(String field:dataMap.keySet()){ fieldName[i] = field; values[i] = dataMap.get(field); i++; } try { doc.getMailMerge().execute(fieldName,values); } catch (Exception e) { throw new VciBaseException("写域字段的内容出错",new String[]{},e); } } }