完美转换: 复杂Word表格转HTML
转换效果图:
图片因责任问题无法展示
原图:
图片因责任问题无法展示
代码:
业务模型
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
/**
* 表名:WORD_DATA
* 表注释:WORD数据存放表;、只存放一个word的一张表的数据信息,按照行列位置存放
* 用于生成html代码
*/
@ApiModel("WORD数据存放表;、只存放一个word的一张表的数据信息,按照行列位置存放用于生成html代码")
@Table(name = "`RES_WORD_DATA`")
public class WordData implements Serializable {
@Id
@Column(name = "`REC_TABLE_ID`")
@ApiModelProperty("")
private Long recTableId;
/**
* Word文件id
*/
@Column(name = "`WORD_ID`")
@ApiModelProperty("Word文件id")
private Long wordId;
/**
* 只有得分列才有序号值;
*/
@Column(name = "`ORDER_NUM`")
@ApiModelProperty("只有得分列才有序号值;")
private Integer orderNum;
/**
* 行位置
*/
@Column(name = "`ROW`")
@ApiModelProperty("行位置")
private Integer row;
/**
* 列位置:真实的列位置
*/
@Column(name = "`COL`")
@ApiModelProperty("列位置:真实的列位置")
private Integer col;
/**
* 该单元格的宽度:从word文件里面取得
*/
@Column(name = "`CELL_WEIGHT`")
@ApiModelProperty("该单元格的宽度:从word文件里面取得")
private Integer cellWeight;
/**
* 该单元格是否是合并单元格
1:表示合并;0表示未合并
*/
@Column(name = "`V_MERGE`")
@ApiModelProperty("该单元格是否是合并单元格 1:表示合并;0表示未合并")
private Integer vMerge;
/**
* 该单元格在列方向上合并的单元格数量
*/
@Column(name = "`COL_SPAN`")
@ApiModelProperty("该单元格在列方向上合并的单元格数量")
private Integer colSpan;
/**
* 在横向方向上合并了多少个单元格
*/
@Column(name = "`ROW_SPAN`")
@ApiModelProperty("在横向方向上合并了多少个单元格")
private Integer rowSpan;
/**
* 该单元格在此行的宽度占总宽度的比列
*/
@Column(name = "`CELL_WEIGHT_RATE`")
@ApiModelProperty("该单元格在此行的宽度占总宽度的比列")
private Float cellWeightRate;
/**
* 只有评分、得分列才有值:打分单元格:1、还是总分单元格:2、还是其他:3
*/
@Column(name = "`CELL_TYPE`")
@ApiModelProperty("只有评分、得分列才有值:打分单元格:1、还是总分单元格:2、还是其他:3 ")
private Integer cellType;
/**
* 只有评分、得分列才有值:该单元格总计求和,应该求和那些单元格;
*/
@Column(name = "`SUM_RANGE`")
@ApiModelProperty("只有评分、得分列才有值:该单元格总计求和,应该求和那些单元格;")
private String sumRange;
@Column(name = "`CELL_ATTRIBUTE`")
@ApiModelProperty("")
private String cellAttribute;
/**
* 只能存放字符串,二进制有待开发
*/
@Column(name = "`VALUE`")
@ApiModelProperty("只能存放字符串,二进制有待开发")
private String value;
public static final String REC_TABLE_ID = "recTableId";
public static final String DB_REC_TABLE_ID = "REC_TABLE_ID";
public static final String WORD_ID = "wordId";
public static final String DB_WORD_ID = "WORD_ID";
public static final String ORDER_NUM = "orderNum";
public static final String DB_ORDER_NUM = "ORDER_NUM";
public static final String ROW = "row";
public static final String DB_ROW = "ROW";
public static final String COL = "col";
public static final String DB_COL = "COL";
public static final String CELL_WEIGHT = "cellWeight";
public static final String DB_CELL_WEIGHT = "CELL_WEIGHT";
public static final String V_MERGE = "vMerge";
public static final String DB_V_MERGE = "V_MERGE";
public static final String COL_SPAN = "colSpan";
public static final String DB_COL_SPAN = "COL_SPAN";
public static final String ROW_SPAN = "rowSpan";
public static final String DB_ROW_SPAN = "ROW_SPAN";
public static final String CELL_WEIGHT_RATE = "cellWeightRate";
public static final String DB_CELL_WEIGHT_RATE = "CELL_WEIGHT_RATE";
public static final String CELL_TYPE = "cellType";
public static final String DB_CELL_TYPE = "CELL_TYPE";
public static final String SUM_RANGE = "sumRange";
public static final String DB_SUM_RANGE = "SUM_RANGE";
public static final String CELL_ATTRIBUTE = "cellAttribute";
public static final String DB_CELL_ATTRIBUTE = "CELL_ATTRIBUTE";
public static final String VALUE = "value";
public static final String DB_VALUE = "VALUE";
private static final long serialVersionUID = 1L;
/**
* @return REC_TABLE_ID
*/
public Long getRecTableId() {
return recTableId;
}
/**
* @param recTableId
*/
public void setRecTableId(Long recTableId) {
this.recTableId = recTableId;
}
/**
* 获取Word文件id
*
* @return WORD_ID - Word文件id
*/
public Long getWordId() {
return wordId;
}
/**
* 设置Word文件id
*
* @param wordId Word文件id
*/
public void setWordId(Long wordId) {
this.wordId = wordId;
}
/**
* 获取只有得分列才有序号值;
*
* @return ORDER_NUM - 只有得分列才有序号值;
*/
public Integer getOrderNum() {
return orderNum;
}
/**
* 设置只有得分列才有序号值;
*
* @param orderNum 只有得分列才有序号值;
*/
public void setOrderNum(Integer orderNum) {
this.orderNum = orderNum;
}
/**
* 获取行位置
*
* @return ROW - 行位置
*/
public Integer getRow() {
return row;
}
/**
* 设置行位置
*
* @param row 行位置
*/
public void setRow(Integer row) {
this.row = row;
}
/**
* 获取列位置:真实的列位置
*
* @return COL - 列位置:真实的列位置
*/
public Integer getCol() {
return col;
}
/**
* 设置列位置:真实的列位置
*
* @param col 列位置:真实的列位置
*/
public void setCol(Integer col) {
this.col = col;
}
/**
* 获取该单元格的宽度:从word文件里面取得
*
* @return CELL_WEIGHT - 该单元格的宽度:从word文件里面取得
*/
public Integer getCellWeight() {
return cellWeight;
}
/**
* 设置该单元格的宽度:从word文件里面取得
*
* @param cellWeight 该单元格的宽度:从word文件里面取得
*/
public void setCellWeight(Integer cellWeight) {
this.cellWeight = cellWeight;
}
/**
* 获取该单元格是否是合并单元格
1:表示合并;0表示未合并
*
* @return V_MERGE - 该单元格是否是合并单元格
1:表示合并;0表示未合并
*/
public Integer getvMerge() {
return vMerge;
}
/**
* 设置该单元格是否是合并单元格
1:表示合并;0表示未合并
*
* @param vMerge 该单元格是否是合并单元格
1:表示合并;0表示未合并
*/
public void setvMerge(Integer vMerge) {
this.vMerge = vMerge;
}
/**
* 获取该单元格在列方向上合并的单元格数量
*
* @return COL_SPAN - 该单元格在列方向上合并的单元格数量
*/
public Integer getColSpan() {
return colSpan;
}
/**
* 设置该单元格在列方向上合并的单元格数量
*
* @param colSpan 该单元格在列方向上合并的单元格数量
*/
public void setColSpan(Integer colSpan) {
this.colSpan = colSpan;
}
/**
* 获取在横向方向上合并了多少个单元格
*
* @return ROW_SPAN - 在横向方向上合并了多少个单元格
*/
public Integer getRowSpan() {
return rowSpan;
}
/**
* 设置在横向方向上合并了多少个单元格
*
* @param rowSpan 在横向方向上合并了多少个单元格
*/
public void setRowSpan(Integer rowSpan) {
this.rowSpan = rowSpan;
}
/**
* 获取该单元格在此行的宽度占总宽度的比列
*
* @return CELL_WEIGHT_RATE - 该单元格在此行的宽度占总宽度的比列
*/
public Float getCellWeightRate() {
return cellWeightRate;
}
/**
* 设置该单元格在此行的宽度占总宽度的比列
*
* @param cellWeightRate 该单元格在此行的宽度占总宽度的比列
*/
public void setCellWeightRate(Float cellWeightRate) {
this.cellWeightRate = cellWeightRate;
}
/**
* 获取只有评分、得分列才有值:打分单元格:1、还是总分单元格:2、还是其他:3
*
* @return CELL_TYPE - 只有评分、得分列才有值:打分单元格:1、还是总分单元格:2、还是其他:3
*/
public Integer getCellType() {
return cellType;
}
/**
* 设置只有评分、得分列才有值:打分单元格:1、还是总分单元格:2、还是其他:3
*
* @param cellType 只有评分、得分列才有值:打分单元格:1、还是总分单元格:2、还是其他:3
*/
public void setCellType(Integer cellType) {
this.cellType = cellType;
}
/**
* 获取只有评分、得分列才有值:该单元格总计求和,应该求和那些单元格;
*
* @return SUM_RANGE - 只有评分、得分列才有值:该单元格总计求和,应该求和那些单元格;
*/
public String getSumRange() {
return sumRange;
}
/**
* 设置只有评分、得分列才有值:该单元格总计求和,应该求和那些单元格;
*
* @param sumRange 只有评分、得分列才有值:该单元格总计求和,应该求和那些单元格;
*/
public void setSumRange(String sumRange) {
this.sumRange = sumRange == null ? null : sumRange.trim();
}
/**
* @return CELL_ATTRIBUTE
*/
public String getCellAttribute() {
return cellAttribute;
}
/**
* @param cellAttribute
*/
public void setCellAttribute(String cellAttribute) {
this.cellAttribute = cellAttribute == null ? null : cellAttribute.trim();
}
/**
* 获取只能存放字符串,二进制有待开发
*
* @return VALUE - 只能存放字符串,二进制有待开发
*/
public String getValue() {
return value;
}
/**
* 设置只能存放字符串,二进制有待开发
*
* @param value 只能存放字符串,二进制有待开发
*/
public void setValue(String value) {
this.value = value == null ? null : value.trim();
}
}
数据传输模型
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "Word单元格属性值传输对象")
public class Word2XmlDto {
/**
* 行位置
*/
@ApiModelProperty("行位置")
private Integer row;
/**
* 列位置
*/
@ApiModelProperty("列位置")
private Integer col;
/**
* 该单元格的值:只能是字符串
*/
@ApiModelProperty("该单元格的值:只能是字符串")
private String value;
/**
* 根据单元格宽度
* word 文件单元格宽度,用作计算html宽度
*/
@ApiModelProperty("单元格宽度")
private Integer w;
/**
* 该单元格是否合并
*/
@ApiModelProperty("该单元格是否合并")
private Boolean vMerge;
/**
* 合并了多少个列单元格 在y轴方向
*/
@ApiModelProperty("合并了多少个列单元格 在y轴方向")
private Integer colSpan;
/**
* 在x轴方向上
* 合并了多少个行单元格
*/
@ApiModelProperty("合并了多少个行单元格")
private Integer rowSpan;
/**
* 单元宽度占比,
*/
@ApiModelProperty("单元宽度占比,")
private Float rowRate;
}
转换工具类
import com.entity.Word2XmlDto;
import com.table.model.WordData;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.springframework.context.annotation.Description;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
/**
* word转html代码正式版本:只能转换为表格
*
* @Author :yeguojin
* @Date :Created in 2020/11/6 15:04
* @Description:word转html代码正式版本:只能转换为表格
* @Modified By:
*/
@Description("word转html代码正式版本:只转换表格")
public class WordToHtmlCode {
/**
* @param input word文件输入流
* @return List<ArrayList < Word2XmlDto>> 返回的list完全依照word表格的顺序存放
* Word2XmlDto 是每个单元格的数据;ArrayList < Word2XmlDto> :是整行的数据
* @throws IOException
*/
public static List<ArrayList<Word2XmlDto>> Word2007ToWordDataLocal(InputStream input) throws IOException {
XWPFDocument document = new XWPFDocument(input);
//获取所有表格
List<XWPFTable> tables = document.getTables();
List<ArrayList<Word2XmlDto>> lists = new ArrayList<>();
//表格循环
for (XWPFTable table : tables) {
//行循环 i:表示行
for (int i = 0; i < table.getRows().size(); i++) {
ArrayList<Word2XmlDto> word2XmlDtos = new ArrayList<>();
XWPFTableRow row = table.getRow(i);
List<XWPFTableCell> tableCells = row.getTableCells();
//该行合并单元格的数量
Integer cellSpan = 0;
//单元格循环 j:表示列
for (int j = 0; j < tableCells.size(); j++) {
Word2XmlDto word2XmlDto = new Word2XmlDto();
//文本内容
String text = tableCells.get(j).getText();
//
CTTcPr tcPr = tableCells.get(j).getCTTc().getTcPr();
CTVMerge vMerge = tcPr.getVMerge();
if (vMerge != null) {
word2XmlDto.setVMerge(Boolean.TRUE);
} else {
word2XmlDto.setVMerge(Boolean.FALSE);
}
//同一行合并表格数量
Integer span = tcPr.getGridSpan() != null ? tcPr.getGridSpan().getVal().intValue() : null;
word2XmlDto.setW(Integer.valueOf(tcPr.getTcW().getW().intValue()));
word2XmlDto.setValue(text);
word2XmlDto.setRowSpan(span);
//列数
word2XmlDto.setCol(j + cellSpan);
//行数
word2XmlDto.setRow(i);
if (span != null && span > 1) {
cellSpan += (span - 1);
}
word2XmlDtos.add(word2XmlDto);
}
lists.add(word2XmlDtos);
}
}
//得到合并行单元格数量--》同一列合并表格数量
//处理单元格占比
for (int i = 0; i < lists.size(); i++) {
ArrayList<Word2XmlDto> word2XmlDtos = lists.get(i);
for (int j = 0; j < word2XmlDtos.size(); j++) {
Integer col = word2XmlDtos.get(j).getCol();
String value = word2XmlDtos.get(j).getValue().replaceAll(" ", "");
//如果合并单元格属性为true,并且该单元格的值为不为空串时计算合并单元格数量,并赋值
//计算该列合并单元格的数量
if (word2XmlDtos.get(j).getVMerge() && !"".equals(value)) {
word2XmlDtos.get(j).setColSpan(getColSpan(lists, i + 1, col, lists.size()));
}
//设置该数据的宽度占比
word2XmlDtos.get(j).setRowRate(getRowRate(col, word2XmlDtos));
}
}
return lists;
}
/**
* 获取该行 ,某列数据在整行中占多少宽度
*
* @param col
* @param word2XmlDtos
* @return Float 宽度的数值 如 占90.21% ,则返回90.21
*/
public static Float getRowRate(Integer col, List<Word2XmlDto> word2XmlDtos) {
Float sumWeigth = 0f;
Float colWeigth = 0f;
for (Word2XmlDto word2XmlDto : word2XmlDtos) {
sumWeigth += word2XmlDto.getW().floatValue();
if (col.equals(word2XmlDto.getCol())) {
colWeigth = word2XmlDto.getW().floatValue();
}
}
if (sumWeigth.equals(0f)) {
return 0f;
}
DecimalFormat df = new DecimalFormat("0.0000");
df.setRoundingMode(RoundingMode.HALF_UP);
Float rate = colWeigth / sumWeigth;
return Float.parseFloat(df.format(rate)) * 100;
}
/**
* 获取某个数据,在整个word文件中占多少个列数;即是:该单元格在y轴方向上的合并单元格数量
*
* @param lists 含有整个word文件中表格数据的 list
* @param i 从哪一行开始计算合并数量,能取到该行
* @param col 实际列数:注意实际二字
* @param maxRow 最大行数
* @return
*/
public static Integer getColSpan(List<ArrayList<Word2XmlDto>> lists, Integer i, Integer col, Integer maxRow) {
//合并单元格数量,至少有一个合并
Integer colSpan = 1;
for (int m = i; m < maxRow; m++) {
ArrayList<Word2XmlDto> word2XmlDtoArrayList = lists.get(m);
int flag = 0;
for (int j = 0; j < word2XmlDtoArrayList.size(); j++) {
Word2XmlDto word2XmlDto = word2XmlDtoArrayList.get(j);
if (col.equals(word2XmlDto.getCol()) && word2XmlDto.getVMerge() && "".equals(word2XmlDto.getValue().replaceAll(" ", ""))) {
colSpan += 1;
} else if (col.equals(word2XmlDto.getCol()) && !"".equals(word2XmlDto.getValue().replaceAll(" ", ""))) {
flag = 1;
break;
}
}
if (flag == 1) {
break;
}
}
if (0 == colSpan) {
return null;
}
return colSpan;
}
/**
* 将处理完全的word单元格数据转换为html代码
*
* @param lists 存有表格数据的list
* @return 返回html代码的字符串
*/
public static String wordDataLocalToHtmlCode(List<ArrayList<Word2XmlDto>> lists) {
String tableBegin = "<div style=\"width:100%;\">" +
"<table style=\"width:100%;border-collapse:collapse;\">" +
"<tbody>";
String tableEnd = "</tbody>" +
"</table>" +
"</div>";
String trBegin = "<tr>";
String trEnd = "</tr>";
String tdBegin = "<td style=\"";
String tdBeginWegitn =
"border-top:0.5px solid #000000;" +
"border-bottom:0.5px solid #000000;" +
"border-left:0.5px solid #000000;" +
"border-right:0.5px solid #000000;\"";
String tdBegin_1 = ">" + "<span style=>";
String tdColSpan = "colspan=";
String tdRowSpan = "rowspan=";
String tdEnd = "</span> </td>";
String htmlCode = tableBegin;
//所有行
for (ArrayList<Word2XmlDto> list : lists) {
htmlCode += trBegin;
//一行
for (Word2XmlDto word2XmlDto : list) {
String value = word2XmlDto.getValue();
//如果该数据合并属性为true,并且该行是空串,那么表示该数据是被合并的;在竖方向被合并,该数据不做展示
if (word2XmlDto.getVMerge() && "".equals(value.replaceAll(" ", ""))) {
continue;
}
htmlCode += tdBegin + "width:" + word2XmlDto.getRowRate() + "%; " + tdBeginWegitn;
if (word2XmlDto.getRowSpan() != null) {
htmlCode += " " + tdColSpan + "\"" + word2XmlDto.getRowSpan() + "\"";
}
if (word2XmlDto.getColSpan() != null) {
htmlCode += " " + tdRowSpan + "\"" + word2XmlDto.getColSpan() + "\"";
}
htmlCode += tdBegin_1 + word2XmlDto.getValue() + tdEnd;
}
htmlCode += trEnd;
}
htmlCode += tableEnd;
System.out.println(htmlCode);
return htmlCode;
}
/**
* 将将数据库返回的数据转换为html代码,并且只是视图文件
*
* @param lists 存有表格数据的list
* @return 返回html代码的字符串
*/
public static String wordDataToHtmlCodeOnlyView(List<ArrayList<WordData>> lists) {
String tableBegin = "<div style=\"width:100%;\">" +
"<table style=\"width:100%;border-collapse:collapse;\">" +
"<tbody>";
String tableEnd = "</tbody>" +
"</table>" +
"</div>";
String trBegin = "<tr>";
String trEnd = "</tr>";
String tdBegin = "<td style=\"";
String tdBeginWegitn =
"border-top:0.5px solid #000000;" +
"border-bottom:0.5px solid #000000;" +
"border-left:0.5px solid #000000;" +
"border-right:0.5px solid #000000;\"";
String tdBegin_1 = ">" + "<span style=>";
String tdColSpan = "colspan=";
String tdRowSpan = "rowspan=";
String tdEnd = "</span> </td>";
String htmlCode = tableBegin;
//所有行
for (ArrayList<WordData> list : lists) {
htmlCode += trBegin;
//一行
for (WordData word2XmlDto : list) {
String value = word2XmlDto.getValue();
//如果该数据合并属性为true,并且该行是空串,那么表示该数据是被合并的;在竖方向被合并,该数据不做展示
if (word2XmlDto.getvMerge() == 1 && "".equals(value.replaceAll(" ", ""))) {
continue;
}
htmlCode += tdBegin + "width:" + word2XmlDto.getCellWeightRate() + "%; " + tdBeginWegitn;
if (word2XmlDto.getRowSpan() != null) {
htmlCode += " " + tdColSpan + "\"" + word2XmlDto.getRowSpan() + "\"";
}
if (word2XmlDto.getColSpan() != null) {
htmlCode += " " + tdRowSpan + "\"" + word2XmlDto.getColSpan() + "\"";
}
htmlCode += tdBegin_1 + word2XmlDto.getValue() + tdEnd;
}
htmlCode += trEnd;
}
htmlCode += tableEnd;
// System.out.println(htmlCode);
return htmlCode;
}
/**
* 根据某个单元格中文字,找到第一个行列位置
*/
public static Word2XmlDto getWord2XmlDtoByKey(List<ArrayList<Word2XmlDto>> lists, String key) {
for (ArrayList<Word2XmlDto> list : lists) {
for (Word2XmlDto word2XmlDto : list) {
if (key.equals(word2XmlDto.getValue())) {
return word2XmlDto;
}
}
}
return null;
}
/**
* 根据列数,起始行数,找到该列所有数据
* 左闭右闭
*/
public static List<Word2XmlDto> getColsWord2XmlDtoByCol(List<ArrayList<Word2XmlDto>> lists, Integer col, Integer begin, Integer end) {
List<Word2XmlDto> word2XmlDtos = new ArrayList<>();
for (int i = begin; i <= end; i++) {
ArrayList<Word2XmlDto> word2XmlDtos1 = lists.get(i);
for (Word2XmlDto word2XmlDto : word2XmlDtos1) {
if (col.equals(word2XmlDto.getCol())) {
word2XmlDtos.add(word2XmlDto);
}
}
}
return word2XmlDtos;
}
/**
* 根据行列位置得到该位置的Word2XmlDto
*/
public static Word2XmlDto getWord2XmlDtoByRowAndCol(List<ArrayList<Word2XmlDto>> lists, Integer row, Integer col) {
for (int i = 0; i < lists.size(); i++) {
for (int j = 0; j < lists.get(i).size(); j++) {
if (row.equals(i) && col.equals(j)) {
return lists.get(i).get(j);
}
}
}
return null;
}
/**
* 根据行,真实列位置得到该位置的Word2XmlDto
*/
public static Word2XmlDto getWord2XmlDtoByRowAndRealCol(List<ArrayList<Word2XmlDto>> lists, Integer row, Integer col) {
for (int i = 0; i < lists.size(); i++) {
for (int j = 0; j < lists.get(i).size(); j++) {
if (row.equals(i) && col.equals(lists.get(i).get(j).getCol())) {
return lists.get(i).get(j);
}
}
}
return null;
}
/**
* 根据起始行,和关键字模糊匹配,返回该单元格
* 左闭
*/
public static Word2XmlDto getWord2XmlDtoByLikeKey(List<ArrayList<Word2XmlDto>> lists, String key, Integer row) {
if (row > lists.size()) {
return null;
}
for (int i = row; i < lists.size(); i++) {
for (int j = 0; j < lists.get(i).size(); j++) {
if (lists.get(i).get(j).getValue().indexOf(key) != -1) {
return lists.get(i).get(j);
}
}
}
return null;
}
/**
* 返回盖表格所有单元格数量
*/
public static Integer getWord2XmlDtoSize(List<ArrayList<Word2XmlDto>> lists) {
Integer size = 0;
if (CollectionUtils.isEmpty(lists)) {
return size;
}
for (int i = 0; i < lists.size(); i++) {
for (int j = 0; j < lists.get(i).size(); j++) {
size++;
}
}
return size;
}
}