【Apache POI - Excel导出】html代码导出到单元格,支持简单样式渲染
需求:将渲染后的html代码,导出到单元格中。
依赖
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.8.2</version>
</dependency>
代码实现
public class ExcelUtil {
/**
* 设置单元格富文本值
*/
public static void setCellRichTextString(Workbook wb, Cell cell, Object obj, boolean isHead){
if(obj == null){
cell.setCellValue(StringPool.EMPTY);
return;
}
String htmlCode = (String) obj;
Document document = Jsoup.parse(htmlCode);
//获取html文本内容
StringBuilder htmlCodeStrSb = new StringBuilder();
//前端特殊字符组件每个<p>标签为一行
Elements allP = document.select("p");
if(allP.size() == 0){
htmlCodeStrSb.append(obj);
}else {
setTextByElements(allP, htmlCodeStrSb);
}
//对于表格头不需要进行字体样式渲染
if(isHead){
cell.setCellValue(htmlCodeStrSb.toString());
return;
}
//html转为RichTextString
RichTextString richTextString = ExcelUtil.convertToRichTextString(wb, document.body(), htmlCodeStrSb.toString());
cell.setCellValue(richTextString);
}
public static void setTextByElements(Elements elements, StringBuilder htmlCodeStrSb){
if(!elements.isEmpty()) {
for (Element element : elements) {
for (Node childNode : element.childNodes()) {
setTextByNode(childNode, htmlCodeStrSb);
}
htmlCodeStrSb.append("\n");
}
htmlCodeStrSb.deleteCharAt(htmlCodeStrSb.length() - 1);
}
}
public static void setTextByNode(Node node, StringBuilder htmlCodeStrSb){
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
if (!text.isEmpty()) {
htmlCodeStrSb.append(text);
}
} else if (node instanceof Element) {
for (Node childNode : node.childNodes()) {
setTextByNode(childNode, htmlCodeStrSb);
}
}
}
public static RichTextString convertToRichTextString(Workbook workbook, Element element, String htmlCode) {
RichTextString richTextString = new HSSFRichTextString(htmlCode);
int startIndex = 0;
for (Node node : element.childNodes()) {
Font font = workbook.createFont();
startIndex = applyFontRecursive(workbook, richTextString, node, startIndex, font);
}
return richTextString;
}
public static int applyFontRecursive(Workbook workbook, RichTextString richTextString,
Node node, int startIndex, Font parentFont) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
if (!text.isEmpty()) {
startIndex = startIndex + text.length();
}
} else if (node instanceof Element) {
Element childElement = (Element) node;
String tagName = childElement.tagName();
Font font = copyFont(workbook, parentFont);
switch (tagName) {
case "strong": //加粗
font.setBold(true);
break;
case "em": //斜体
font.setItalic(true);
break;
case "sup": //上标
font.setTypeOffset(Font.SS_SUPER);
break;
case "sub": //下标
font.setTypeOffset(Font.SS_SUB);
break;
case "span":
String style = childElement.attr("style");
if(StringUtils.isNotBlank(style)) {
if (style.contains("text-decoration: line-through;")) { //水平删除斜线
font.setStrikeout(true);
}
if (style.contains("text-decoration: underline;")) { //下划线
font.setUnderline(Font.U_SINGLE);
}
}
default:
break;
}
richTextString.applyFont(startIndex, startIndex + ((Element) node).text().length(), font);
for (Node childNode : childElement.childNodes()) {
startIndex = applyFontRecursive(workbook, richTextString, childNode, startIndex, font);
}
}
return startIndex;
}
/**
* 拷贝字体
*/
public static Font copyFont(Workbook workbook, Font font){
Font copyFont = workbook.createFont();
copyFont.setBold(font.getBold());
copyFont.setItalic(font.getItalic());
copyFont.setStrikeout(font.getStrikeout());
copyFont.setUnderline(font.getUnderline());
copyFont.setTypeOffset(font.getTypeOffset());
return copyFont;
}
}
注意事项
如果html代码中含有<img>
标签,则需要删除或者替代。目前并不支持导出文字+图片的形式。
/**
*
* @param htmlCode html代码
* @param replaceStr 替换的字符串
*/
private String replaceImg(String htmlCode, String replaceStr){
Document doc = Jsoup.parse(htmlCode);
Elements imgElements = doc.select("img");
if(!imgElements.isEmpty()) {
for (Element img : imgElements) {
img.replaceWith(new TextNode(replaceStr));
}
}
return doc.body().html();
}