Html轉Doc的一種方案


  公司的合同相關部分提出需求要把html轉doc文本,合同模塊是現成的,基於現有合同模塊初步考慮了一下可以采取以下三種方案

  1.合同模塊本身有合同的html展示,可以通過前端,基於html的Doc來直接生成doc文檔。

  2.公司的合同html文本是基於一套自己設計的機制,把合同條款存在數據庫,以組合節點做拼接成html來做html文本動態生成的,一種可行方案是把這套機制作為數據來源,基於doc相關的開放接口包來從代碼生成doc文檔

  3.把現在有的html文本用第三方包轉化成doc,補充上缺失的信息

 

  公司當前采取的是第1種方案,用jQuery的wordExport生成的doc。這種doc實際的存儲方式還是用html語法,如果只是需要doc文檔展示用這種方案是沒問題的,但是如果要解析doc內容,貌似是沒法直接解析的。這也是目前需要進行轉換的原因。

  如果要生成內容准確和豐富的doc,用第二種方案來做會比較好。第二種方案的缺點在於實現復雜,需要找一個第三方生成doc的包熟悉接口才能展開,而且當前合同數據存儲時,內容中已經內嵌了html文本,用這種方案的話,需要再做內容過濾或者是從數據源的角度,額外規定一個適合於內容填充符合doc生成規則的數據來源。

  第三種方案的話,也是需要找第三方包來支持的。缺點在於不確定第三方包轉換限制和精確程度如何。做了幾個簡單和有代表性的樣例測試,基於docx4j來做html轉doc是可行的。不過缺點在於docx4j對於源html的語法要求很嚴格,很多在html里面支持的可選語法在docx4j轉換時就不支持。

  調試過程中統計到的語法錯誤至少有:

  * 屬性值缺失引號,比如item_id=123這種

  * input標簽缺少結束符,比如<input ... > ,並沒有對於的/或</input>

       * 錯誤的屬性,比如  甲方,這種

  數據庫里面的合同條目有8400+條,一條一條調試太過於費時,所以考慮追加一步html格式化以及過濾的步驟,選取的是google的owasp-java-html-sanitizer。初步測試是可行的。

  

相關依賴包為:

<!-- docx4j 的pom依賴 -->
<!-- https://mvnrepository.com/artifact/org.docx4j/docx4j -->
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j</artifactId>
    <version>6.1.1</version>
</dependency>

<!-- owasp-java-html-sanitizer的pom依賴 -->
<!-- https://mvnrepository.com/artifact/com.googlecode.owasp-java-html-sanitizer/owasp-java-html-sanitizer -->
<dependency>
    <groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
    <artifactId>owasp-java-html-sanitizer</artifactId>
    <version>20181114.1</version>
</dependency>

 

規則過濾的關鍵代碼

/**
     * 
     * Description: 添加過濾規則<br> 
     *  
     * @author XXX <br>
     * @taskId <br>
     * @param configs
     * @return <br>
     */
    private PolicyFactory loadFilterConfigs(Map<String, List<String>> configs) {
        HtmlPolicyBuilder builder = new HtmlPolicyBuilder();
        Set<String> labelConfigs = configs.keySet();
        for (String label : labelConfigs) {
            builder.allowElements(label);//設置支持的標簽
            List<String> attributes = configs.get(label);// 如果沒屬性要求返回空數組
            log.debug("load label:" + label + " and attributes are :" + attributes);
            if (CollectionUtils.isEmpty(attributes)) {
                continue;
            }
            for (String attribute : attributes) {// 配置屬性
                if (StringUtil.isEmpty(attribute)) {
                    continue;
                }
                builder.allowAttributes(attribute).onElements(label);
            }

        }
        return builder.toFactory();
    }

這里補充說明一下,owasp-java-html-sanitizer支持對於單個標簽定制過濾規則:

    builder.allowElements(new ElementPolicy() {
        @Override
        public String apply(String elementName, List<String> attrs) {// 配置屬性值過濾
            System.out
                    .println("filter :" + elementName + " attrs:" + attrs );
            if (attrs != null) {
                int itemIdIndex = attrs.indexOf("item_id");
                if (itemIdIndex < 0) {// 不包含item_id
                    return elementName;
                }
                String item_id = attrs.get(itemIdIndex + 1);
                if (attrs.contains(item_id)) {// 需要過濾
                    return null;
                }
                return elementName;

            }
            return elementName;
        }
    }, label);

我們合同生成doc有一個需求是把沒有選定的checkbox不生成到doc中,我上面代碼的意圖是,想先收集哪些塊是沒有選定的,然后通過上面的規律規則把沒有選定的塊過濾掉。但是實際測試結果是,這種規則過濾只對當前標簽單層有效,對於嵌套的子標簽並不會繼承。比如:

<div item_id='123'>
       <input type='checkbox' item_id='123' /> 
       <p>test</p>
<div>

這種通過上面這種思路簡單配置時,<p>標簽的內容並不會受到item_id的影響而過濾。這與實際的需求不符合。

當前公司基於節點的模式,做節點解析來剔除節點是很簡單的,所以上面方案測試不成功就沒有繼續往下深入,對於內容篩選的需求就直接基於節點來處理,owasp-java-html-sanitizer只專注於做html格式化就好了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM