2011-04-20 93 views
0

我們在應用程序中使用itext動態創建PDF。用戶通過使用富文本編輯器的屏幕將PDF內容插入到Web應用程序中。從使用itext創建的PDF中刪除HTML和CSS樣式

以下是具體的步驟。

  1. 用戶轉到添加PDF內容頁面。
  2. 添加頁面有一個富文本編輯器,可以在其中輸入PDF內容。
  3. 有時用戶可以複製/粘貼現有​​word文檔中的內容並輸入到RTE中。
  4. 一旦他提交了內容,就會創建PDF。

的RTE的使用,因爲我們有一些其他的網頁,我們需要表現出與粗體,斜體等

內容,但是,我們不希望產生的PDF這RTE的東西。

在生成PDF之前,我們使用了一些Java實用程序從內容中刪除了RTE內容。

這可以正常工作,但是當從word文檔複製內容時,文檔應用的html和css樣式不會被我們使用的java實用程序刪除。

如何生成沒有任何HTML或CSS的PDF?

下面是代碼

Paragraph paragraph = new Paragraph(Util.removeHTML(content), font); 

而且removeHTML方法如下

public static String removeHTML(String htmlString) { 
    if (htmlString == null) 
     return ""; 
    htmlString.replace("\"", "'"); 
    htmlString = htmlString.replaceAll("\\<.*?>", ""); 
    htmlString = htmlString.replaceAll("&nbsp;", ""); 
    return htmlString; 
} 

並且在下面示於當我複製/從字文檔粘貼PDF中的附加內容。

<w:LsdException Locked="false" Priority="10" SemiHidden="false 
UnhideWhenUsed="false" QFormat="true" Name="Title" /> 
<w:LsdException Locked="false" Priority="11" SemiHidden="false" 
UnhideWhenUsed="false" QFormat="true" Name="Subtitle" /> 
<w:LsdException Locked="false" Priority="22" SemiHidden="false" 

請幫忙!

謝謝。

回答

2

我們的應用程序類似,我們有一個富文本編輯器(TinyMCE),我們的輸出是通過iText PDF生成PDF。我們希望HTML儘可能乾淨,理想情況下只使用iText HTMLWorker支持的HTML標籤。 TinyMCE可以做到這一點,但仍然有一些情況,最終用戶可能會提交HTML,這真的搞砸了,這可能會破壞iText生成PDF的能力。

我們使用jSoup和jTidy + CSSParser的組合過濾掉HTML中「style」屬性中輸入的不需要的CSS樣式。輸入到TinyMCE中的HTML會使用此服務進行清理,清理所有來自文字標記的粘貼(如果用戶沒有使用TinyMCE中的從Word粘貼按鈕),並且爲我們提供了可以翻譯iTextPDF HTMLWorker的HTML。

如果表格寬度在style屬性中,HTMLWorker會忽略它並將表格寬度設置爲0,我還發現iText的HTMLWorker解析器(5.0.6)中的表格寬度問題,並將表格寬度設置爲0,所以這是一些邏輯來解決以下問題。我們用下面的庫:一個

com.itextpdf:itextpdf:5.0.6     // used to generate PDFs 
org.jsoup:jsoup:1.5.2      // used for cleaning HTML, primary cleaner 
net.sf.jtidy:jtidy:r938      // used for cleaning HTML, secondary cleaner 
net.sourceforge.cssparser:cssparser:0.9.5 // used to parse out unwanted HTML "style" attribute values 

下面是我們建立擦洗HTML只保留通過iText的+所支持的標記和樣式屬性Groovy的一些服務代碼修復表的問題。代碼中有一些特定於我們的應用程序的假設。目前這對我們非常有用。

import com.steadystate.css.parser.CSSOMParser 
import org.htmlcleaner.CleanerProperties 
import org.htmlcleaner.HtmlCleaner; 
import org.htmlcleaner.PrettyHtmlSerializer 
import org.htmlcleaner.SimpleHtmlSerializer 
import org.htmlcleaner.TagNode 
import org.jsoup.Jsoup 
import org.jsoup.nodes.Document 
import org.jsoup.safety.Cleaner 
import org.jsoup.safety.Whitelist 
import org.jsoup.select.Elements 
import org.w3c.css.sac.InputSource 
import org.w3c.dom.css.CSSRule 
import org.w3c.dom.css.CSSRuleList 
import org.w3c.dom.css.CSSStyleDeclaration 
import org.w3c.dom.css.CSSStyleSheet 
import org.w3c.tidy.Tidy 

class HtmlCleanerService { 

    static transactional = true 

    def cleanHTML(def html) { 

     // clean with JSoup which should filter out most unwanted things and 
     // ensure good html syntax 
     html = soupClean(html); 

     // run through JTidy to remove repeated nested tags, clean anything JSoup left out 
     html = tidyClean(html); 

     return html; 
    } 

    def tidyClean(def html) { 
     Tidy tidy = new Tidy() 
     tidy.setAsciiChars(true) 
     tidy.setDropEmptyParas(true) 
     tidy.setDropProprietaryAttributes(true) 
     tidy.setPrintBodyOnly(true) 

     tidy.setEncloseText(true) 
     tidy.setJoinStyles(true) 
     tidy.setLogicalEmphasis(true) 
     tidy.setQuoteMarks(true) 
     tidy.setHideComments(true) 
     tidy.setWraplen(120) 

     // (makeClean || dropFontTags) = replaces presentational markup by style rules 
     tidy.setMakeClean(true)  // remove presentational clutter. 
     tidy.setDropFontTags(true) 

     // word2000 = drop style & class attributes and empty p, span elements 
     // draconian cleaning for Word2000 
     tidy.setWord2000(true)  
     tidy.setMakeBare(true)  // remove Microsoft cruft. 
     tidy.setRepeatedAttributes(org.w3c.tidy.Configuration.KEEP_FIRST) // keep first or last duplicate attribute 

     // TODO ? tidy.setForceOutput(true) 

     def reader = new StringReader(html); 
     def writer = new StringWriter(); 

     // hide output from stderr 
     tidy.setShowWarnings(false) 
     tidy.setErrout(new PrintWriter(new StringWriter())) 

     tidy.parse(reader, writer); // run tidy, providing an input and output stream 
     return writer.toString() 
    } 

    def soupClean(def html) { 

     // clean the html 
     Document dirty = Jsoup.parseBodyFragment(html); 
     Cleaner cleaner = new Cleaner(createWhitelist()); 
     Document clean = cleaner.clean(dirty); 

     // now hunt down all style attributes and ensure we only have those that render with iTextPDF 
     Elements styledNodes = clean.select("[style]"); // a with href 
     styledNodes.each { element -> 
      def style = element.attr("style"); 
      def tag = element.tagName().toLowerCase() 
      def newstyle = "" 
      CSSOMParser parser = new CSSOMParser(); 
      InputSource is = new InputSource(new StringReader(style)) 
      CSSStyleDeclaration styledeclaration = parser.parseStyleDeclaration(is) 
      boolean hasProps = false 
      for (int i=0; i < styledeclaration.getLength(); i++) { 
       def propname = styledeclaration.item(i) 
       def propval = styledeclaration.getPropertyValue(propname) 
       propval = propval ? propval.trim() : "" 

       if (["padding-left", "text-decoration", "text-align", "font-weight", "font-style"].contains(propname)) { 
        newstyle = newstyle + propname + ": " + propval + ";" 
        hasProps = true 
       } 

       // standardize table widths, itextPDF won't render tables if there is only width in the 
       // style attribute. Here we ensure the width is in its own attribute, and change the value so 
       // it is in percentage and no larger than 100% to avoid end users from creating really goofy 
       // tables that they can't edit properly becuase they have made the width too large. 
       // 
       // width of the display area in the editor is about 740px, so let's ensure everything 
       // is relative to that 
       // 
       // TODO could get into trouble with nested tables and widths within as we assume 
       // one table (e.g. could have nested tables both with widths of 500) 
       if (tag.equals("table") && propname.equals("width")) { 
        if (propval.endsWith("%")) { 
         // ensure it is <= 100% 
         propval = propval.replaceAll(~"[^0-9]", "") 
         propval = Math.min(100, propval.toInteger()) 
        } 
        else { 
         // else we have measurement in px or assumed px, clean up and 
         // get integer value, then calculate a percentage 
         propval = propval.replaceAll(~"[^0-9]", "") 
         propval = Math.min(100, (int) (propval.toInteger()/740)*100) 
        } 
        element.attr("width", propval + "%") 
       } 
      } 
      if (hasProps) { 
       element.attr("style", newstyle) 
      } else { 
       element.removeAttr("style") 
      } 

     } 

     return clean.body().html(); 
    } 

    /** 
    * Returns a JSoup whitelist suitable for sane HTML output and iTextPDF 
    */ 
    def createWhitelist() { 
     Whitelist wl = new Whitelist(); 

     // iText supported tags 
     wl.addTags(
      "br", "div", "p", "pre", "span", "blockquote", "q", "hr", 
      "h1", "h2", "h3", "h4", "h5", "h6", 
      "u", "strike", "s", "strong", "sub", "sup", "em", "i", "b", 
      "ul", "ol", "li", "ol", 
      "table", "tbody", "td", "tfoot", "th", "thead", "tr", 
      ); 

     // iText attributes recognized which we care about 
     // padding-left (div/p/span indentation) 
     // text-align (for table right/left align) 
     // text-decoration (for span/div/p underline, strikethrough) 
     // font-weight (for span/div/p bolder etc) 
     // font-style (for span/div/p italic etc) 
     // width (for tables) 
     // colspan/rowspan (for tables) 

     ["span", "div", "p", "table", "ul", "ol", "pre", "td", "th"].each { tag -> 
      ["style", "padding-left", "text-decoration", "text-align", "font-weight", "font-style"].each { attr -> 
       wl.addAttributes(tag, attr) 
      } 
     } 

     ["td", "th"].each { tag -> 
      ["colspan", "rowspan", "width"].each { attr -> 
       wl.addAttributes(tag, attr) 
      } 
     } 
     wl.addAttributes("table", "width", "style", "cellpadding") 

     // img support 
     // wl.addAttributes("img", "align", "alt", "height", "src", "title", "width") 


     return wl 
    } 
} 
0

如果您只是想要HTML文檔的文本內容,那麼請使用XML API(如SAX或DOM)僅發佈文檔中的文本節點。如果您知道如何使用DOM,那麼對於DocumentTraversal API來說這是微不足道的。如果我有我的IDE運行,我會粘貼一個樣本...

此外,顯示的removeHtml方法效率低下。使用Pattern.compile並將其緩存在一個靜態變量中,並使用Matcher API來替換StringBuffer(或者StringBuilder,如果它使用的話)。這樣你就不會創建一堆中間字符串並把它扔掉。

+0

嗨。謝謝回覆。我不是從HTML文檔中獲取文本內容,而是從數據庫中獲取文本內容。當用戶從RTE提交內容時,它首先進入數據庫,然後從數據庫中檢索並用於生成PDF。 – ashishjmeshram 2011-04-20 05:17:22