天天看點

使用poi-tl、poi-tl-ext遇到塊級元素嵌套導緻空白段落問題

作者:北方有雪卻不得見

依賴包版本:

<!--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模闆:

使用poi-tl、poi-tl-ext遇到塊級元素嵌套導緻空白段落問題

word模闆

遇到問題:當html存在塊級元素嵌套,如div嵌套div,則會出現空白段落問題,嵌套幾層,就出現幾個空白段落,這與html在web頁面渲染的效果不一緻。嵌套的html段落如下:

<div  class="xxx">
      <div class="xxx">
          <p>你好!</p>
      </div>
  </div>           

渲染效果:

使用poi-tl、poi-tl-ext遇到塊級元素嵌套導緻空白段落問題

渲染結果多出了兩個空白段落

原因:經過檢視poi-tl-ext源碼(org.ddr.poi.html.HtmlRenderContext.renderElement(Element element)),發現,當遇到【塊級元素】時,會自動生成一個新的段落,雖然也存在空白段落問題的處理,但是為什麼沒有處理到這種情況,暫時還不清楚。

使用poi-tl、poi-tl-ext遇到塊級元素嵌套導緻空白段落問題

HtmlRenderContext提供的防重政策

使用poi-tl、poi-tl-ext遇到塊級元素嵌套導緻空白段落問題

使用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();           

四、效果:

使用poi-tl、poi-tl-ext遇到塊級元素嵌套導緻空白段落問題

調整後的渲染效果

總結:目前暫時不知道這種解決方案,有可能帶來的其它影響,因為目前業務上,以及使用的測試html字元串不是太複雜,是以達不到普适性,參考時,請根據實際=情況自行處理。