依赖包版本:
<!--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模板:
遇到问题:当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)),发现,当遇到【块级元素】时,会自动生成一个新的段落,虽然也存在空白段落问题的处理,但是为什么没有处理到这种情况,暂时还不清楚。
初步解决方案:利用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字符串不是太复杂,所以达不到普适性,参考时,请根据实际=情况自行处理。