参考easypoi
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.afterturn</groupId>
<artifactId>easypoi</artifactId>
<version>4.4.0</version>
</parent>
<artifactId>easypoi-base</artifactId>
<dependencies>
<!-- poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<!-- Excel -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
HTML可以先用freemarker生成!!!
test1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style type = "text/css">
body{
font-family:SimHei;
margin: 25mm 25mm 25mm 25mm;
width: 542mm;
}
</style>
</head>
<body>
<table sheetName="test1" name="testName" border="1">
<tr>
<td style="height:110px;width:20px;">ma</td>
<td></td>
</tr>
<tr>
<th>Month</th>
<th>Savi11ngs</th>
</tr>
<tr>
<td>January</td>
<td>1111111$100</td>
</tr>
<tr>
<td name="img">
<img src="data:png;base64,${file64Str}" width="600px" />
</td>
<td>
</td>
</tr>
</table>
</body>
</html>
mytester
package com.study.easypoi.test;
import com.study.easypoi.excel.ExcelXorHtmlUtil;
import com.study.easypoi.excel.entity.enmus.ExcelType;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.junit.Test;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Scanner;
@RestController
public class mytester {
//获取批注对象
// XSSFClientAnchor的参数说明:
// 参数 说明
// dx1 第1个单元格中x轴的偏移量
// dy1 第1个单元格中y轴的偏移量
// dx2 第2个单元格中x轴的偏移量
// dy2 第2个单元格中y轴的偏移量
// col1 第1个单元格的列号
// row1 第1个单元格的行号
// col2 第2个单元格的列号
// row2 第2个单元格的行号
// 获取图片需要放置的位置
/*
* XSSFClientAnchor 参数
* @param dx1 左上侧所在单元格里面左上角X坐标
* @param dy1 左上侧所在单元格里面左上角Y坐标.
* @param dx2 右下侧所在单元格里面左上角X坐标.
* @param dy2 右下侧所在单元格里面左上角Y坐标.
* @param col1 图片左侧所在col
* @param row1 图片上侧所在row
* @param col2 图片由侧所在col
* @param row2 图片下侧所在row
* */
//(int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2)
public void insertPic(Workbook wb, XSSFSheet sheet) {
BufferedImage bufferImg = null;
try {
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
bufferImg = ImageIO.read(new File("D:\\gitee\\easypoi-my-simple\\Freemarker-Excel.png"));
ImageIO.write(bufferImg, "png", byteArrayOut);
XSSFDrawing patriarch = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = new XSSFClientAnchor(
0, 0,
250, 250,
(short) 0, 0,
(short) 1, 1);
// 插入图片
patriarch.createPicture(anchor, wb.addPicture(byteArrayOut.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG));
System.out.println("----Excle文件已生成------");
} catch (Exception e) {
e.printStackTrace();
}
}
public void insertPic(Workbook wb, HSSFSheet sheet) {
BufferedImage bufferImg = null;
try {
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
bufferImg = ImageIO.read(new File("D:\\gitee\\easypoi-my-simple\\Freemarker-Excel.png"));
ImageIO.write(bufferImg, "png", byteArrayOut);
HSSFPatriarch patriarch= sheet.createDrawingPatriarch();
HSSFClientAnchor anchor = new HSSFClientAnchor(
0, 0,
250, 250,
(short) 0, 0,
(short) 1, 1);
// 插入图片
patriarch.createPicture(anchor, wb.addPicture(byteArrayOut.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG));
System.out.println("----Excle文件已生成------");
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void htmlToExcelByStr() throws Exception {
String path = mytester.class.getClassLoader().getResource("test1.html").getPath();
StringBuilder html = new StringBuilder();
Scanner s = new Scanner(getClass().getClassLoader().getResourceAsStream("test1.html"), "utf-8");
while (s.hasNext()) {
html.append(s.nextLine());
}
s.close();
Workbook workbook = ExcelXorHtmlUtil.htmlToExcel(html.toString(), ExcelType.XSSF);
File savefile = new File("D:\\gitee\\easypoi-my-simple");
if (!savefile.exists()) {
savefile.mkdirs();
}
FileOutputStream fos = new FileOutputStream("D:\\gitee\\easypoi-my-simple\\htmlToExcelByStr.xlsx");
Sheet sheet = workbook.getSheetAt(0);
insertPic(workbook, (XSSFSheet) sheet);
workbook.write(fos);
fos.close();
workbook = ExcelXorHtmlUtil.htmlToExcel(html.toString(), ExcelType.HSSF);
Sheet sheetH = workbook.getSheetAt(0);
insertPic(workbook, (HSSFSheet) sheetH);
fos = new FileOutputStream("D:\\gitee\\easypoi-my-simple\\htmlToExcelByStr.xls");
workbook.write(fos);
fos.close();
}
}
PoiCssUtils
package com.study.easypoi.util;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.util.HSSFColor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PoiCssUtils {
private static final Logger log = LoggerFactory.getLogger(PoiCssUtils.class);
/**
* matches #rgb
*/
private static final String COLOR_PATTERN_VALUE_SHORT = "^(#(?:[a-f]|\\d){3})$";
private static final Pattern COLOR_PATTERN_VALUE_SHORT_PATTERN = Pattern.compile("([a-f]|\\d)");
/**
* matches #rrggbb
*/
private static final String COLOR_PATTERN_VALUE_LONG = "^(#(?:[a-f]|\\d{2}){3})$";
/**
* matches #rgb(r, g, b)
**/
private static final String COLOR_PATTERN_RGB = "^(rgb\\s*\\(\\s*(.+)\\s*,\\s*(.+)\\s*,\\s*(.+)\\s*\\))$";
private static final Pattern INT_AND_PER_PATTERN = Pattern.compile("^(\\d*\\.?\\d+)\\s*(%)?$");
private static final Pattern INT_PATTERN = Pattern.compile("^(\\d+)(?:\\w+|%)?$");
public static String processColor(String color) {
log.info("Process Color [{}].", color);
String colorRtn = null;
if (StringUtils.isNotBlank(color)) {
HSSFColor poiColor = null;
// #rgb -> #rrggbb
if (color.matches(COLOR_PATTERN_VALUE_SHORT)) {
log.debug("Short Hex Color [{}] Found.", color);
StringBuffer sbColor = new StringBuffer();
Matcher m = COLOR_PATTERN_VALUE_SHORT_PATTERN.matcher(color);
while (m.find()) {
m.appendReplacement(sbColor, "$1$1");
}
colorRtn = sbColor.toString();
log.debug("Translate Short Hex Color [{}] To [{}].", color, colorRtn);
}
// #rrggbb
else if (color.matches(COLOR_PATTERN_VALUE_LONG)) {
colorRtn = color;
log.debug("Hex Color [{}] Found, Return.", color);
}
// rgb(r, g, b)
else if (color.matches(COLOR_PATTERN_RGB)) {
Matcher m = Pattern.compile(COLOR_PATTERN_RGB).matcher(color);
if (m.matches()) {
log.debug("RGB Color [{}] Found.", color);
colorRtn = convertColor(calcColorValue(m.group(2)), calcColorValue(m.group(3)),
calcColorValue(m.group(4)));
log.debug("Translate RGB Color [{}] To Hex [{}].", color, colorRtn);
}
}
// color name, red, green, ...
else if ((poiColor = getHssfColor(color)) != null) {
log.debug("Color Name [{}] Found.", color);
short[] t = poiColor.getTriplet();
colorRtn = convertColor(t[0], t[1], t[2]);
log.debug("Translate Color Name [{}] To Hex [{}].", color, colorRtn);
}
}
return colorRtn;
}
private static String convertColor(int r, int g, int b) {
return String.format("#%02x%02x%02x", r, g, b);
}
private static HSSFColor getHssfColor(String color) {
String tmpColor = color.replace("_", "").toUpperCase();
switch (tmpColor) {
case "BLACK":
return HSSFColor.HSSFColorPredefined.BLACK.getColor();
case "BROWN":
return HSSFColor.HSSFColorPredefined.BROWN.getColor();
case "OLIVEGREEN":
return HSSFColor.HSSFColorPredefined.OLIVE_GREEN.getColor();
case "DARKGREEN":
return HSSFColor.HSSFColorPredefined.DARK_GREEN.getColor();
case "DARKTEAL":
return HSSFColor.HSSFColorPredefined.DARK_TEAL.getColor();
case "DARKBLUE":
return HSSFColor.HSSFColorPredefined.DARK_BLUE.getColor();
case "INDIGO":
return HSSFColor.HSSFColorPredefined.INDIGO.getColor();
case "GREY80PERCENT":
return HSSFColor.HSSFColorPredefined.GREY_80_PERCENT.getColor();
case "ORANGE":
return HSSFColor.HSSFColorPredefined.ORANGE.getColor();
case "DARKYELLOW":
return HSSFColor.HSSFColorPredefined.DARK_YELLOW.getColor();
case "GREEN":
return HSSFColor.HSSFColorPredefined.GREEN.getColor();
case "TEAL":
return HSSFColor.HSSFColorPredefined.TEAL.getColor();
case "BLUE":
return HSSFColor.HSSFColorPredefined.BLUE.getColor();
case "BLUEGREY":
return HSSFColor.HSSFColorPredefined.BLUE_GREY.getColor();
case "GREY50PERCENT":
return HSSFColor.HSSFColorPredefined.GREY_50_PERCENT.getColor();
case "RED":
return HSSFColor.HSSFColorPredefined.RED.getColor();
case "LIGHTORANGE":
return HSSFColor.HSSFColorPredefined.LIGHT_ORANGE.getColor();
case "LIME":
return HSSFColor.HSSFColorPredefined.LIME.getColor();
case "SEAGREEN":
return HSSFColor.HSSFColorPredefined.SEA_GREEN.getColor();
case "AQUA":
return HSSFColor.HSSFColorPredefined.AQUA.getColor();
case "LIGHTBLUE":
return HSSFColor.HSSFColorPredefined.LIGHT_BLUE.getColor();
case "VIOLET":
return HSSFColor.HSSFColorPredefined.VIOLET.getColor();
case "GREY40PERCENT":
return HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getColor();
case "GOLD":
return HSSFColor.HSSFColorPredefined.GOLD.getColor();
case "YELLOW":
return HSSFColor.HSSFColorPredefined.YELLOW.getColor();
case "BRIGHTGREEN":
return HSSFColor.HSSFColorPredefined.BRIGHT_GREEN.getColor();
case "TURQUOISE":
return HSSFColor.HSSFColorPredefined.TURQUOISE.getColor();
case "DARKRED":
return HSSFColor.HSSFColorPredefined.DARK_RED.getColor();
case "SKYBLUE":
return HSSFColor.HSSFColorPredefined.SKY_BLUE.getColor();
case "PLUM":
return HSSFColor.HSSFColorPredefined.PLUM.getColor();
case "GREY25PERCENT":
return HSSFColor.HSSFColorPredefined.GREY_25_PERCENT.getColor();
case "ROSE":
return HSSFColor.HSSFColorPredefined.ROSE.getColor();
case "LIGHTYELLOW":
return HSSFColor.HSSFColorPredefined.LIGHT_YELLOW.getColor();
case "LIGHTGREEN":
return HSSFColor.HSSFColorPredefined.LIGHT_GREEN.getColor();
case "LIGHTTURQUOISE":
return HSSFColor.HSSFColorPredefined.LIGHT_TURQUOISE.getColor();
case "PALEBLUE":
return HSSFColor.HSSFColorPredefined.PALE_BLUE.getColor();
case "LAVENDER":
return HSSFColor.HSSFColorPredefined.LAVENDER.getColor();
case "WHITE":
return HSSFColor.HSSFColorPredefined.WHITE.getColor();
case "CORNFLOWERBLUE":
return HSSFColor.HSSFColorPredefined.CORNFLOWER_BLUE.getColor();
case "LEMONCHIFFON":
return HSSFColor.HSSFColorPredefined.LEMON_CHIFFON.getColor();
case "MAROON":
return HSSFColor.HSSFColorPredefined.MAROON.getColor();
case "ORCHID":
return HSSFColor.HSSFColorPredefined.ORANGE.getColor();
case "CORAL":
return HSSFColor.HSSFColorPredefined.CORAL.getColor();
case "ROYALBLUE":
return HSSFColor.HSSFColorPredefined.ROYAL_BLUE.getColor();
case "LIGHTCORNFLOWERBLUE":
return HSSFColor.HSSFColorPredefined.LIGHT_CORNFLOWER_BLUE.getColor();
case "TAN":
return HSSFColor.HSSFColorPredefined.TAN.getColor();
}
return null;
}
public static int calcColorValue(String color) {
int rtn = 0;
// matches 64 or 64%
Matcher m = INT_AND_PER_PATTERN.matcher(color);
if (m.matches()) {
// % not found
if (m.group(2) == null) {
rtn = Math.round(Float.parseFloat(m.group(1))) % 256;
} else {
rtn = Math.round(Float.parseFloat(m.group(1)) * 255 / 100) % 256;
}
}
return rtn;
}
/**
* check number string
*
* @param strValue string
* @return true if string is number
*/
public static boolean isNum(String strValue) {
return StringUtils.isNotBlank(strValue) && strValue.matches("^\\d+(\\w+|%)?$");
}
/**
* get int value of string
*
* @param strValue string value
* @return int value
*/
public static int getInt(String strValue) {
int value = 0;
if (StringUtils.isNotBlank(strValue)) {
Matcher m = INT_PATTERN.matcher(strValue);
if (m.find()) {
value = Integer.parseInt(m.group(1));
}
}
return value;
}
}
PoiMergeCellUtil
package com.study.easypoi.util;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 纵向合并单元格工具类
*
*/
public class PoiMergeCellUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(PoiMergeCellUtil.class);
private PoiMergeCellUtil() {
}
public static void addMergedRegion(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
try {
sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));
} catch (Exception e) {
LOGGER.debug("发生了一次合并单元格错误,{},{},{},{}", new Integer[]{
firstRow, lastRow, firstCol, lastCol
});
// 忽略掉合并的错误,不打印异常
LOGGER.debug(e.getMessage(), e);
}
}
}
ExcelXorHtmlUtil
package com.study.easypoi.excel;
import com.study.easypoi.excel.entity.enmus.ExcelType;
import com.study.easypoi.excel.html.HtmlToExcelService;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExcelXorHtmlUtil {
private ExcelXorHtmlUtil() {
}
/**
* Html 读取Excel
* @param html
* @param type
* @return
*/
public static Workbook htmlToExcel(String html, ExcelType type) {
Workbook workbook = null;
if (ExcelType.HSSF.equals(type)) {
workbook = new HSSFWorkbook();
} else {
workbook = new XSSFWorkbook();
}
new HtmlToExcelService().createSheet(html, workbook);
return workbook;
}
}
HtmlToExcelService
package com.study.easypoi.excel.html;
import com.study.easypoi.excel.export.styler.ExcelExportStylerDefaultImpl;
import com.study.easypoi.excel.html.css.CssParseService;
import com.study.easypoi.excel.html.css.ICssConvertToExcel;
import com.study.easypoi.excel.html.entity.ExcelCssConstant;
import com.study.easypoi.excel.html.entity.HtmlCssConstant;
import com.study.easypoi.excel.html.entity.style.CellStyleEntity;
import com.study.easypoi.util.PoiMergeCellUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.RuntimeErrorException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* 读取Table数据生成Excel
*/
public class HtmlToExcelService {
private final Logger LOGGER = LoggerFactory.getLogger(HtmlToExcelService.class);
private Sheet sheet;
private int maxRow = 0;
private Map<String, CellStyle> cellStyles = new HashMap<String, CellStyle>();
private Map<String, Object> cellsOccupied = new HashMap<String, Object>();
private CellStyle defaultCellStyle;
private CssParseService cssParse = new CssParseService();
//样式
private static final List<ICssConvertToExcel> STYLE_APPLIERS = new LinkedList<ICssConvertToExcel>();
//Cell 高宽
private static final List<ICssConvertToExcel> SHEET_APPLIERS = new LinkedList<ICssConvertToExcel>();
public Workbook createSheet(String html, Workbook workbook) {
Elements els = Jsoup.parseBodyFragment(html).select("table");
Map<String, Sheet> sheets = new HashMap<String, Sheet>();
Map<String, Integer> maxRowMap = new HashMap<String, Integer>();
for (Element table : els) {
String sheetName = table.attr(ExcelCssConstant.SHEET_NAME);
if (StringUtils.isBlank(sheetName)) {
LOGGER.error("table必须存在name属性!");
throw new RuntimeErrorException(null, "table必须存在name属性");
}
if (sheets.containsKey(sheetName)) {
maxRow = maxRowMap.get(sheetName);
//cellStyles = csStyleMap.get(sheetName);
//cellsOccupied = cellsOccupiedMap.get(sheetName);
sheet = sheets.get(sheetName);
} else {
maxRow = 0;
cellStyles.clear();
cellsOccupied.clear();
sheet = workbook.createSheet(sheetName);
}
//生成一个默认样式
defaultCellStyle = new ExcelExportStylerDefaultImpl(workbook).stringNoneStyle(workbook, false);
processTable(table);
maxRowMap.put(sheetName, maxRow);
sheets.put(sheetName, sheet);
}
return workbook;
}
private void processTable(Element table) {
int rowIndex = 0;
if (maxRow > 0) {
// blank row
maxRow += 2;
rowIndex = maxRow;
}
LOGGER.debug("Interate Table Rows.");
String freezeCol = null;
int freezeColIndex = -1;
for (Element row : table.select("tr")) {
LOGGER.debug("Parse Table Row [{}]. Row Index [{}].", row, rowIndex);
String freezeRow = row.attr(ExcelCssConstant.FREEZE_ROW);
if ("true".equals(freezeRow)) {
sheet.createFreezePane(0, rowIndex + 1, 0, rowIndex + 1);
}
int colIndex = 0;
LOGGER.debug("Interate Cols.");
for (Element td : row.select("td, th")) {
freezeCol = td.attr(ExcelCssConstant.FREEZE_COL);
if ("true".equals(freezeCol)) {
if (colIndex > freezeColIndex) {
freezeColIndex = colIndex;
}
}
// skip occupied cell
while (cellsOccupied.get(rowIndex + "_" + colIndex) != null) {
LOGGER.debug("Cell [{}][{}] Has Been Occupied, Skip.", rowIndex, colIndex);
++colIndex;
}
LOGGER.debug("Parse Col [{}], Col Index [{}].", td, colIndex);
int rowSpan = 0;
String strRowSpan = td.attr("rowspan");
if (StringUtils.isNotBlank(strRowSpan) && StringUtils.isNumeric(strRowSpan)) {
LOGGER.debug("Found Row Span [{}].", strRowSpan);
rowSpan = Integer.parseInt(strRowSpan);
}
int colSpan = 0;
String strColSpan = td.attr("colspan");
if (StringUtils.isNotBlank(strColSpan) && StringUtils.isNumeric(strColSpan)) {
LOGGER.debug("Found Col Span [{}].", strColSpan);
colSpan = Integer.parseInt(strColSpan);
}
// col span & row span
if (colSpan > 1 && rowSpan > 1) {
spanRowAndCol(td, rowIndex, colIndex, rowSpan, colSpan);
colIndex += colSpan;
}
// col span only
else if (colSpan > 1) {
spanCol(td, rowIndex, colIndex, colSpan);
colIndex += colSpan;
}
// row span only
else if (rowSpan > 1) {
spanRow(td, rowIndex, colIndex, rowSpan);
++colIndex;
}
// no span
else {
createCell(td, getOrCreateRow(rowIndex), colIndex).setCellValue(td.text());
++colIndex;
}
}
++rowIndex;
}
if (freezeColIndex != -1) {
sheet.createFreezePane(freezeColIndex + 1, 0, freezeColIndex + 1, 0);
}
}
private void spanRowAndCol(Element td, int rowIndex, int colIndex, int rowSpan, int colSpan) {
LOGGER.debug("Span Row And Col, From Row [{}], Span [{}].", rowIndex, rowSpan);
LOGGER.debug("From Col [{}], Span [{}].", colIndex, colSpan);
mergeRegion(rowIndex, rowIndex + rowSpan - 1, colIndex, colIndex + colSpan - 1);
for (int i = 0; i < rowSpan; ++i) {
Row row = getOrCreateRow(rowIndex + i);
for (int j = 0; j < colSpan; ++j) {
createCell(td, row, colIndex + j);
cellsOccupied.put((rowIndex + i) + "_" + (colIndex + j), true);
}
}
getOrCreateRow(rowIndex).getCell(colIndex).setCellValue(td.text());
}
private void spanCol(Element td, int rowIndex, int colIndex, int colSpan) {
LOGGER.debug("Span Col, From Col [{}], Span [{}].", colIndex, colSpan);
mergeRegion(rowIndex, rowIndex, colIndex, colIndex + colSpan - 1);
Row row = getOrCreateRow(rowIndex);
for (int i = 0; i < colSpan; ++i) {
createCell(td, row, colIndex + i);
}
row.getCell(colIndex).setCellValue(td.text());
}
private void spanRow(Element td, int rowIndex, int colIndex, int rowSpan) {
LOGGER.debug("Span Row , From Row [{}], Span [{}].", rowIndex, rowSpan);
mergeRegion(rowIndex, rowIndex + rowSpan - 1, colIndex, colIndex);
for (int i = 0; i < rowSpan; ++i) {
Row row = getOrCreateRow(rowIndex + i);
createCell(td, row, colIndex);
cellsOccupied.put((rowIndex + i) + "_" + colIndex, true);
}
getOrCreateRow(rowIndex).getCell(colIndex).setCellValue(td.text());
}
private Cell createCell(Element td, Row row, int colIndex) {
Cell cell = row.getCell(colIndex);
if (cell == null) {
LOGGER.debug("Create Cell [{}][{}].", row.getRowNum(), colIndex);
cell = row.createCell(colIndex);
}
return applyStyle(td, cell);
}
private Row getOrCreateRow(int rowIndex) {
Row row = sheet.getRow(rowIndex);
if (row == null) {
LOGGER.debug("Create New Row [{}].", rowIndex);
row = sheet.createRow(rowIndex);
if (rowIndex > maxRow) {
maxRow = rowIndex;
}
}
return row;
}
private Cell applyStyle(Element td, Cell cell) {
String style = td.attr(HtmlCssConstant.STYLE);
CellStyle cellStyle = null;
if (StringUtils.isNotBlank(style)) {
CellStyleEntity styleEntity = cssParse.parseStyle(style.trim());
cellStyle = cellStyles.get(styleEntity.toString());
if (cellStyle == null) {
LOGGER.debug("No Cell Style Found In Cache, Parse New Style.");
cellStyle = cell.getRow().getSheet().getWorkbook().createCellStyle();
cellStyle.cloneStyleFrom(defaultCellStyle);
for (ICssConvertToExcel cssConvert : STYLE_APPLIERS) {
cssConvert.convertToExcel(cell, cellStyle, styleEntity);
}
cellStyles.put(styleEntity.toString(), cellStyle);
}
for (ICssConvertToExcel cssConvert : SHEET_APPLIERS) {
cssConvert.convertToExcel(cell, cellStyle, styleEntity);
}
if (cellStyles.size() >= 4000) {
LOGGER.info(
"Custom Cell Style Exceeds 4000, Could Not Create New Style, Use Default Style.");
cellStyle = defaultCellStyle;
}
} else {
LOGGER.debug("Style is null ,Use Default Cell Style.");
cellStyle = defaultCellStyle;
}
cell.setCellStyle(cellStyle);
return cell;
}
private void mergeRegion(int firstRow, int lastRow, int firstCol, int lastCol) {
LOGGER.debug("Merge Region, From Row [{}], To [{}].", firstRow, lastRow);
LOGGER.debug("From Col [{}], To [{}].", firstCol, lastCol);
PoiMergeCellUtil.addMergedRegion(sheet, firstRow, lastRow, firstCol, lastCol);
}
}
HtmlCssConstant
package com.study.easypoi.excel.html.entity;
public interface HtmlCssConstant {
// constants
public final String PATTERN_LENGTH = "\\d*\\.?\\d+\\s*(?:em|ex|cm|mm|q|in|pt|pc|px)?";
public final String STYLE = "style";
public final String FONT = "font";
public final String FONT_STYLE = "font-style";
public final String FONT_FAMILY = "font-family";
public final String FONT_WEIGHT = "font-weight";
public final String FONT_SIZE = "font-size";
public final String COLOR = "color";
public final String BACKGROUND = "background";
public final String BACKGROUND_COLOR = "background-color";
public final String ITALIC = "italic";
public final String BOLD = "bold";
// direction
public final String TOP = "top";
public final String RIGHT = "right";
public final String BOTTOM = "bottom";
public final String LEFT = "left";
public final String BORDER = "border";
public final String WIDTH = "width";
public final String HEIGHT = "height";
public final String TEXT_ALIGN = "text-align";
public final String VETICAL_ALIGN = "vertical-align";
public final String TEXT_DECORATION = "text-decoration";
public final String NONE = "none";
public final String HIDDEN = "hidden";
public final String SOLID = "solid";
public final String DOUBLE = "double";
public final String DOTTED = "dotted";
public final String DASHED = "dashed";
}
ExcelCssConstant
package com.study.easypoi.excel.html.entity;
/**
* Excel 自定义处理的自定义Html标签
*/
public interface ExcelCssConstant {
public final String SHEET_NAME = "sheetName";
public final String FREEZE_ROW = "freezeRow";
public final String FREEZE_COL = "freezeCol";
}
CssStyleFontEnity
package com.study.easypoi.excel.html.entity.style;
import lombok.Data;
/**
* 字体样式
*/
@Data
public class CssStyleFontEnity {
/**
* italic 浏览器会显示一个斜体的字体样式。
*/
private String style;
/**
* bold 定义粗体字符
*/
private String weight;
/**
* 仅支持**px获取不带px的数字大小
*/
private int size;
private String family;
private String decoration;
private String color;
}
package com.study.easypoi.excel.html.entity.style;
import lombok.Data;
/**
* Cell 具有的样式
*/
@Data
public class CellStyleEntity {
/**
* 宽
*/
private String width;
/**
* 高
*/
private String height;
/**
* 边框
*/
private CellStyleBorderEntity border;
/**
* 背景
*/
private String background;
/**
* 水平位置
*/
private String align;
/**
* 垂直位置
*/
private String vetical;
/**
* 字体设置
*/
private CssStyleFontEnity font;
}
package com.study.easypoi.excel.html.entity.style;
import lombok.Data;
/**
* 边框样式
*/
@Data
public class CellStyleBorderEntity {
private String borderLeftColor;
private String borderRightColor;
private String borderTopColor;
private String borderBottomColor;
private String borderLeftStyle;
private String borderRightStyle;
private String borderTopStyle;
private String borderBottomStyle;
private String borderLeftWidth;
private String borderRightWidth;
private String borderTopWidth;
private String borderBottomWidth;
}
package com.study.easypoi.excel.html.css;
import com.study.easypoi.excel.html.entity.style.CellStyleEntity;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
public interface ICssConvertToExcel {
void convertToExcel(Cell cell, CellStyle cellStyle, CellStyleEntity styleEntity);
}
package com.study.easypoi.excel.html.css;
import com.study.easypoi.excel.html.entity.style.CellStyleBorderEntity;
import com.study.easypoi.excel.html.entity.style.CellStyleEntity;
import com.study.easypoi.excel.html.entity.style.CssStyleFontEnity;
import com.study.easypoi.util.PoiCssUtils;
import com.study.easypoi.excel.html.entity.HtmlCssConstant;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 把Css样式解析成对应的Model
*/
public class CssParseService {
private static final Logger log = LoggerFactory.getLogger(CssParseService.class);
private static final Pattern STYLE_PATTERN = Pattern.compile("(?:^|\\s+)(italic|oblique)(?:\\s+|$)");
private static final Pattern WEIGHT_PATTERN = Pattern.compile("(?:^|\\s+)(bold(?:er)?|[7-9]00)(?:\\s+|$)");
private final static Set<String> BORDER_STYLES = new HashSet<String>() {
{
// Specifies no border
add(HtmlCssConstant.NONE);
// The same as "none", except in border conflict resolution for table elements
add(HtmlCssConstant.HIDDEN);
// Specifies a dotted border
add(HtmlCssConstant.DOTTED);
// Specifies a dashed border
add(HtmlCssConstant.DASHED);
// Specifies a solid border
add(HtmlCssConstant.SOLID);
// Specifies a double border
add(HtmlCssConstant.DOUBLE);
}
};
public CellStyleEntity parseStyle(String style) {
Map<String, String> mapStyle = new HashMap<String, String>();
for (String s : style.split("\\s*;\\s*")) {
if (StringUtils.isNotBlank(s)) {
String[] ss = s.split("\\s*\\:\\s*");
if (ss.length == 2 && StringUtils.isNotBlank(ss[0])
&& StringUtils.isNotBlank(ss[1])) {
String attrName = ss[0].toLowerCase();
String attrValue = ss[1];
// do not change font name
if (!HtmlCssConstant.FONT.equals(attrName) && !HtmlCssConstant.FONT_FAMILY.equals(attrName)) {
attrValue = attrValue.toLowerCase();
}
mapStyle.put(attrName, attrValue);
}
}
}
parseFontAttr(mapStyle);
parseBackground(mapStyle);
parseBorder(mapStyle);
return mapToCellStyleEntity(mapStyle);
}
private void parseFontAttr(Map<String, String> mapRtn) {
log.debug("Parse Font Style.");
// color
String color = PoiCssUtils.processColor(mapRtn.get(HtmlCssConstant.COLOR));
if (StringUtils.isNotBlank(color)) {
log.debug("Text Color [{}] Found.", color);
mapRtn.put(HtmlCssConstant.COLOR, color);
}
// font
String font = mapRtn.get(HtmlCssConstant.FONT);
if (StringUtils.isNotBlank(font)
&& !ArrayUtils.contains(new String[] { "small-caps", "caption", "icon", "menu",
"message-box", "small-caption", "status-bar" },
font)) {
log.debug("Parse Font Attr [{}].", font);
String[] ignoreStyles = new String[] { "normal",
// font weight normal
"[1-3]00" };
StringBuffer sbFont = new StringBuffer(
font.replaceAll("^|\\s*" + StringUtils.join(ignoreStyles, "|") + "\\s+|$", " "));
log.debug("Font Attr [{}] After Process Ingore.", sbFont);
// style
Matcher m = STYLE_PATTERN.matcher(sbFont.toString());
if (m.find()) {
sbFont.setLength(0);
if (log.isDebugEnabled()) {
log.debug("Font Style [{}] Found.", m.group(1));
}
mapRtn.put(HtmlCssConstant.FONT_STYLE, HtmlCssConstant.ITALIC);
m.appendReplacement(sbFont, " ");
m.appendTail(sbFont);
}
// weight
m = WEIGHT_PATTERN.matcher(sbFont.toString());
if (m.find()) {
sbFont.setLength(0);
if (log.isDebugEnabled()) {
log.debug("Font Weight [{}](bold) Found.", m.group(1));
}
mapRtn.put(HtmlCssConstant.FONT_WEIGHT, HtmlCssConstant.BOLD);
m.appendReplacement(sbFont, " ");
m.appendTail(sbFont);
}
// size xx-small | x-small | small | medium | large | x-large | xx-large | 18px [/2]
m = Pattern.compile(
// before blank or start
new StringBuilder("(?:^|\\s+)")
// font size
.append("(xx-small|x-small|small|medium|large|x-large|xx-large|").append("(?:")
.append(HtmlCssConstant.PATTERN_LENGTH).append("))")
// line height
.append("(?:\\s*\\/\\s*(").append(HtmlCssConstant.PATTERN_LENGTH).append("))?")
// after blank or end
.append("(?:\\s+|$)").toString())
.matcher(sbFont.toString());
if (m.find()) {
sbFont.setLength(0);
log.debug("Font Size[/line-height] [{}] Found.", m.group());
String fontSize = m.group(1);
if (StringUtils.isNotBlank(fontSize)) {
fontSize = StringUtils.deleteWhitespace(fontSize);
log.debug("Font Size [{}].", fontSize);
if (fontSize.matches(HtmlCssConstant.PATTERN_LENGTH)) {
mapRtn.put(HtmlCssConstant.FONT_SIZE, fontSize);
} else {
log.info("Font Size [{}] Not Supported, Ignore.", fontSize);
}
}
String lineHeight = m.group(2);
if (StringUtils.isNotBlank(lineHeight)) {
log.info("Line Height [{}] Not Supported, Ignore.", lineHeight);
}
m.appendReplacement(sbFont, " ");
m.appendTail(sbFont);
}
// font family
if (sbFont.length() > 0) {
log.debug("Font Families [{}].", sbFont);
// trim & remove '"
String fontFamily = sbFont.toString().split("\\s*,\\s*")[0].trim()
.replaceAll("'|\"", "");
log.debug("Use First Font Family [{}].", fontFamily);
mapRtn.put(HtmlCssConstant.FONT_FAMILY, fontFamily);
}
}
font = mapRtn.get(HtmlCssConstant.FONT_STYLE);
if (ArrayUtils.contains(new String[] { HtmlCssConstant.ITALIC, "oblique" }, font)) {
log.debug("Font Italic [{}] Found.", font);
mapRtn.put(HtmlCssConstant.FONT_STYLE, HtmlCssConstant.ITALIC);
}
font = mapRtn.get(HtmlCssConstant.FONT_WEIGHT);
if (StringUtils.isNotBlank(font) && Pattern.matches("^bold(?:er)?|[7-9]00$", font)) {
log.debug("Font Weight [{}](bold) Found.", font);
mapRtn.put(HtmlCssConstant.FONT_WEIGHT, HtmlCssConstant.BOLD);
}
font = mapRtn.get(HtmlCssConstant.FONT_SIZE);
if (!PoiCssUtils.isNum(font)) {
log.debug("Font Size [{}] Error.", font);
mapRtn.remove(HtmlCssConstant.FONT_SIZE);
}
}
private String parseBackground(Map<String, String> style) {
String bg = style.get(HtmlCssConstant.BACKGROUND);
String bgColor = null;
if (StringUtils.isNotBlank(bg)) {
for (String bgAttr : bg.split("(?<=\\)|\\w|%)\\s+(?=\\w)")) {
if ((bgColor = PoiCssUtils.processColor(bgAttr)) != null) {
style.put(HtmlCssConstant.BACKGROUND_COLOR, bgColor);
break;
}
}
}
bg = style.get(HtmlCssConstant.BACKGROUND_COLOR);
if (StringUtils.isNotBlank(bg) && (bgColor = PoiCssUtils.processColor(bg)) != null) {
style.put(HtmlCssConstant.BACKGROUND_COLOR, bgColor);
}
if (bgColor != null) {
bgColor = style.get(HtmlCssConstant.BACKGROUND_COLOR);
if ("#ffffff".equals(bgColor)) {
style.remove(HtmlCssConstant.BACKGROUND_COLOR);
}
}
return null;
}
public void parseBorder(Map<String, String> style) {
for (String pos : new String[] { null, HtmlCssConstant.TOP, HtmlCssConstant.RIGHT, HtmlCssConstant.BOTTOM, HtmlCssConstant.LEFT }) {
// border[-attr]
if (pos == null) {
setBorderAttr(style, pos, style.get(HtmlCssConstant.BORDER));
setBorderAttr(style, pos, style.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.COLOR));
setBorderAttr(style, pos, style.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.WIDTH));
setBorderAttr(style, pos, style.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.STYLE));
}
// border-pos[-attr]
else {
setBorderAttr(style, pos, style.get(HtmlCssConstant.BORDER + "-" + pos));
for (String attr : new String[] { HtmlCssConstant.COLOR, HtmlCssConstant.WIDTH, HtmlCssConstant.STYLE }) {
String attrName = HtmlCssConstant.BORDER + "-" + pos + "-" + attr;
String attrValue = style.get(attrName);
if (StringUtils.isNotBlank(attrValue)) {
style.put(attrName, attrValue);
}
}
}
}
}
private CellStyleEntity mapToCellStyleEntity(Map<String, String> mapStyle) {
CellStyleEntity entity = new CellStyleEntity();
entity.setAlign(mapStyle.get(HtmlCssConstant.TEXT_ALIGN));
entity.setVetical(mapStyle.get(HtmlCssConstant.VETICAL_ALIGN));
entity.setBackground(parseBackground(mapStyle));
entity.setHeight(mapStyle.get(HtmlCssConstant.HEIGHT));
entity.setWidth(mapStyle.get(HtmlCssConstant.WIDTH));
entity.setFont(getCssStyleFontEnity(mapStyle));
entity.setBackground(mapStyle.get(HtmlCssConstant.BACKGROUND_COLOR));
entity.setBorder(getCssStyleBorderEntity(mapStyle));
return entity;
}
private void setBorderAttr(Map<String, String> mapBorder, String pos, String value) {
if (StringUtils.isNotBlank(value)) {
String borderColor = null;
for (String borderAttr : value.split("\\s+")) {
if ((borderColor = PoiCssUtils.processColor(borderAttr)) != null) {
setBorderAttr(mapBorder, pos, HtmlCssConstant.COLOR, borderColor);
} else if (PoiCssUtils.isNum(borderAttr)) {
setBorderAttr(mapBorder, pos, HtmlCssConstant.WIDTH, borderAttr);
} else if (BORDER_STYLES.contains(borderAttr)) {
setBorderAttr(mapBorder, pos, HtmlCssConstant.STYLE, borderAttr);
} else {
log.info("Border Attr [{}] Is Not Suppoted.", borderAttr);
}
}
}
}
private void setBorderAttr(Map<String, String> mapBorder, String pos, String attr,
String value) {
if (StringUtils.isNotBlank(pos)) {
mapBorder.put(HtmlCssConstant.BORDER + "-" + pos + "-" + attr, value);
} else {
for (String name : new String[] { HtmlCssConstant.TOP, HtmlCssConstant.RIGHT, HtmlCssConstant.BOTTOM, HtmlCssConstant.LEFT }) {
mapBorder.put(HtmlCssConstant.BORDER + "-" + name + "-" + attr, value);
}
}
}
private CssStyleFontEnity getCssStyleFontEnity(Map<String, String> style) {
CssStyleFontEnity font = new CssStyleFontEnity();
font.setStyle(style.get(HtmlCssConstant.FONT_STYLE));
int fontSize = PoiCssUtils.getInt(style.get(HtmlCssConstant.FONT_SIZE));
if (fontSize > 0) {
font.setSize(fontSize);
}
font.setWeight(style.get(HtmlCssConstant.FONT_WEIGHT));
font.setFamily(style.get(HtmlCssConstant.FONT_FAMILY));
font.setDecoration(style.get(HtmlCssConstant.TEXT_DECORATION));
font.setColor(style.get(HtmlCssConstant.COLOR));
return font;
}
private CellStyleBorderEntity getCssStyleBorderEntity(Map<String, String> mapStyle) {
CellStyleBorderEntity border = new CellStyleBorderEntity();
border.setBorderTopColor(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.TOP + "-" + HtmlCssConstant.COLOR));
border.setBorderBottomColor(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.BOTTOM + "-" + HtmlCssConstant.COLOR));
border.setBorderLeftColor(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.LEFT + "-" + HtmlCssConstant.COLOR));
border.setBorderRightColor(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.RIGHT + "-" + HtmlCssConstant.COLOR));
border.setBorderTopWidth(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.TOP + "-" + HtmlCssConstant.WIDTH));
border.setBorderBottomWidth(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.BOTTOM + "-" + HtmlCssConstant.WIDTH));
border.setBorderLeftWidth(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.LEFT + "-" + HtmlCssConstant.WIDTH));
border.setBorderRightWidth(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.RIGHT + "-" + HtmlCssConstant.WIDTH));
border.setBorderTopStyle(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.TOP + "-" + HtmlCssConstant.STYLE));
border.setBorderBottomStyle(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.BOTTOM + "-" + HtmlCssConstant.STYLE));
border.setBorderLeftStyle(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.LEFT + "-" + HtmlCssConstant.STYLE));
border.setBorderRightStyle(mapStyle.get(HtmlCssConstant.BORDER + "-" + HtmlCssConstant.RIGHT + "-" + HtmlCssConstant.STYLE));
return border;
}
}
package com.study.easypoi.excel.export.styler;
public interface IExcelExportStyler {
}
package com.study.easypoi.excel.export.styler;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
public class ExcelExportStylerDefaultImpl extends AbstractExcelExportStyler implements IExcelExportStyler {
public ExcelExportStylerDefaultImpl(Workbook workbook) {
super.createStyles(workbook);
}
@Override
public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setDataFormat(STRING_FORMAT);
if (isWarp) {
style.setWrapText(true);
}
return style;
}
}
package com.study.easypoi.excel.export.styler;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Workbook;
/**
* 抽象接口提供两个公共方法
*/
public class AbstractExcelExportStyler implements IExcelExportStyler {
//单行
protected CellStyle stringNoneStyle;
protected CellStyle stringNoneWrapStyle;
//间隔行
protected CellStyle stringSeptailStyle;
protected CellStyle stringSeptailWrapStyle;
protected Workbook workbook;
protected static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
protected void createStyles(Workbook workbook) {
this.stringNoneStyle = stringNoneStyle(workbook, false);
this.stringNoneWrapStyle = stringNoneStyle(workbook, true);
this.stringSeptailStyle = stringSeptailStyle(workbook, false);
this.stringSeptailWrapStyle = stringSeptailStyle(workbook, true);
this.workbook = workbook;
}
public CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {
return null;
}
public CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {
return null;
}
}
package com.study.easypoi.excel.entity.enmus;
/**
* Excel Type
*/
public enum ExcelType {
HSSF , XSSF;
}
导入图片时,xy轴偏移量没生效?主要原因竟然是偏移量的数值设置得不。。。够。。。大。。。
XSSFClientAnchor anchor = new XSSFClientAnchor( 200000, 200000, 100, 100, (short) 1, 2, (short) 5, 7);
参考 https://blog.csdn.net/u011279583/article/details/105092785/