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<String> str2List(String s){
|
if (isNull(s)) {
|
return null;
|
} else {
|
List<String> l = new ArrayList<String>();
|
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<WordMergeStartTableDataBO> 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<WordMergeStartTableDataBO> 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<String,String> 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<String,String> 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<String,String> 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<WordMergeStartTableDataBO> 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<WordMergeStartTableDataBO> 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<String,Integer> mergeColumnNameIndexMap = new HashMap<String, Integer>();
|
List<String> 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<Map<String, Object>> thisTableData = dataSource.getDataList();
|
for (String mergeColumnName : mergeColumnList) {
|
//找所有的数据,记录相同行的数据
|
if(mergeColumnNameIndexMap.containsKey(mergeColumnName)) {
|
int columnIndex = mergeColumnNameIndexMap.get(mergeColumnName) + 1;
|
Map<Integer, Integer> mergeRowMap = new HashMap<Integer, Integer>();
|
Object lastRowValue = "";
|
int lastRowIndex = 0;
|
for (int i = 0; i < thisTableData.size(); i++) {
|
Map<String, Object> 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<Integer, Integer> 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<String, String> 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<String, String> 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);
|
}
|
}
|
|
|
|
}
|