依賴包版本:
<!--poi-tl word支援-->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.4</version>
</dependency>
<!--poi-tl html轉word插件支援-->
<dependency>
<groupId>io.github.draco1023</groupId>
<artifactId>poi-tl-ext</artifactId>
<version>0.4.1</version>
</dependency>
普通情況下使用方式:
// 定義一個html渲染政策
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
Configure configure = Configure.builder()
.bind("html", htmlRenderPolicy)
.build();
word模闆:
word模闆
遇到問題:當html存在塊級元素嵌套,如div嵌套div,則會出現空白段落問題,嵌套幾層,就出現幾個空白段落,這與html在web頁面渲染的效果不一緻。嵌套的html段落如下:
<div class="xxx">
<div class="xxx">
<p>你好!</p>
</div>
</div>
渲染效果:
渲染結果多出了兩個空白段落
原因:經過檢視poi-tl-ext源碼(org.ddr.poi.html.HtmlRenderContext.renderElement(Element element)),發現,當遇到【塊級元素】時,會自動生成一個新的段落,雖然也存在空白段落問題的處理,但是為什麼沒有處理到這種情況,暫時還不清楚。
HtmlRenderContext提供的防重政策
使用markDedupe标記段落以防止塊狀元素嵌套産生多餘的空段落
初步解決方案:利用org.ddr.poi.html.HtmlRenderContext.renderElement提供的markDedupe()和unmarkDedupe(),然後自定義div(或者其它标簽)标簽的渲染邏輯。
改進後的方案:
一、新增塊級元素(div示例)的渲染政策:
import org.apache.commons.lang3.StringUtils;
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlRenderContext;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import java.util.Objects;
/**
* @description: div渲染
* @author: xx
* @create: 2022-11-16 17:01:56
**/
public class BlockRenderer implements ElementRenderer {
private static final String[] TAGS = {"div", "p"};
/**
* 開始渲染
*
* @param element HTML元素
* @param context 渲染上下文
* @return 是否繼續渲染子元素
*/
@Override
public boolean renderStart(Element element, HtmlRenderContext context) {
// 如果目前塊級元素,存在内容
String ownText = element.ownText();
if (StringUtils.isNotBlank(ownText)) {
return true;
}
Node firstNode = element.childNode(0);
// 如果沒有子元素,則傳回
if (Objects.isNull(firstNode)) {
return true;
}
// 判斷第一個子元素是否是塊級元素
if (firstNode instanceof Element) {
Element firstChildElement = ((Element) firstNode);
if (firstChildElement.isBlock()) {
// 如果是,則目前塊級元素不生成新的段落,否則會形成空白行
context.markDedupe(context.getClosestParagraph());
}
}
return true;
}
/**
* 元素渲染結束需要執行的邏輯
*
* @param element HTML元素
* @param context 渲染上下文
*/
@Override
public void renderEnd(Element element, HtmlRenderContext context) {
context.unmarkDedupe();
}
/**
* 支援的标簽
*/
@Override
public String[] supportedTags() {
return TAGS;
}
/**
* 是否渲染為塊級元素,如果是,則建立新段落
*
*/
@Override
public boolean renderAsBlock() {
return true;
}
}
二、配置html的渲染政策:
import org.ddr.poi.html.ElementRenderer;
import org.ddr.poi.html.HtmlRenderConfig;
import org.ddr.poi.html.HtmlRenderPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @description: html渲染政策
* @author:
* @create: 2022-11-17 09:13:37
**/
public class HtmlRenderPolicyConfig {
/**
* 擷取html渲染政策
*
* @return
*/
public static HtmlRenderPolicy getHtmlRenderPolicy() {
HtmlRenderConfig htmlRenderConfig = new HtmlRenderConfig();
List<ElementRenderer> customRenderers = htmlRenderConfig.getCustomRenderers();
if (Objects.isNull(customRenderers)) {
customRenderers = new ArrayList<>(4);
}
// 裝載自定義的标簽渲染器政策
customRenderers.add(new BlockRenderer());
htmlRenderConfig.setCustomRenderers(customRenderers);
HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy(htmlRenderConfig);
return htmlRenderPolicy;
}
}
三、使用方式不變:
Configure configure = Configure.builder()
.bind("html", HtmlRenderPolicyConfig.getHtmlRenderPolicy())
.build();
四、效果:
調整後的渲染效果
總結:目前暫時不知道這種解決方案,有可能帶來的其它影響,因為目前業務上,以及使用的測試html字元串不是太複雜,是以達不到普适性,參考時,請根據實際=情況自行處理。