2011-04-20 2 views
0

Nous créons dynamiquement des PDF en utilisant itext dans notre application. Le contenu du PDF est inséré par l'utilisateur dans l'application Web à l'aide d'un écran dans lequel il dispose d'un éditeur de texte enrichi.Supprimer les styles HTML et CSS à partir de PDF créés en utilisant itext

Vous trouverez ci-dessous les étapes spécifiques.

  1. L'utilisateur accède à une page de contenu PDF.
  2. La page d'ajout comporte un éditeur de texte enrichi dans lequel il peut entrer le contenu PDF.
  3. Parfois, l'utilisateur peut copier/coller le contenu du document Word existant et entrer dans le RTE.
  4. Une fois qu'il soumet le contenu, PDF est créé.

Le RTE est utilisé parce que nous avons d'autres pages où nous devons montrer le contenu en gras, italique, etc.

Mais, nous ne voulons pas ce genre de choses RTE dans le PDF généré.

Nous avons utilisé une partie utilitaire java pour enlever la substance RTE du contenu avant de générer le PDF.

Cela fonctionne normalement, mais lorsque le contenu est copié à partir du document Word, HTML et les styles appliqués par le css document ne sont pas supprimés par l'utilitaire java que nous utilisons.

Comment puis-je générer le PDF sans HTML ou CSS il?

Voici le code

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

Et la méthode removeHTML est comme ci-dessous

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

Et ci-dessous est le contenu supplémentaire étant représenté en PDF quand je copier/coller à partir du document Word.

<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" 

Aidez-nous!

Merci.

Répondre

2

Notre application est similaire, nous avons un éditeur de texte enrichi (TinyMCE), et notre production est PDF généré par iText PDF. Nous voulons que le code HTML soit le plus clair possible et, idéalement, n'utiliser que les balises HTML prises en charge par HTMLWorker d'iText. TinyMCE peut faire cela, mais il y a encore des situations où un utilisateur final peut soumettre du HTML qui est vraiment bousillé, et qui peut éventuellement casser la capacité d'iText à générer un PDF.

Nous utilisons une combinaison de jSoup et de jTidy + CSSParser pour filtrer les styles CSS indésirables entrés dans les attributs "style" HTML. Le HTML entré dans TinyMCE est nettoyé en utilisant ce service qui nettoie toute pâte du balisage de mot (si l'utilisateur n'a pas utilisé le bouton Coller depuis Word dans TinyMCE) et nous donne un HTML qui se traduit bien pour iTextPDFs HTMLWorker. J'ai également trouvé des problèmes avec les largeurs de table dans l'analyseur HTMLWorker d'iText (5.0.6) si la largeur de la table est dans l'attribut style, HTMLWorker l'ignore et définit la largeur de la table à 0, donc c'est une logique pour corriger cela . Nous utilisons les libs suivantes: a

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 

Ci-dessous un code d'un service Groovy nous avons construit pour nettoyer le code HTML et ne garder que les balises et les attributs de style pris en charge par iText + résout le problème de la table. Il y a quelques hypothèses faites dans le code qui sont spécifiques à notre application. Cela fonctionne très bien pour nous en ce moment.

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

Si vous voulez juste le contenu du texte du document HTML, utilisez une API XML tel que SAX ou DOM pour émettre uniquement les nœuds texte du document. C'est trivial avec l'API DocumentTraversal si vous connaissez votre chemin DOM. Si je devais courir mon IDE, je coller un échantillon ...

En outre, la méthode est inefficace removeHtml montré. Utilisez Pattern.compile et cachez-le dans une variable statique et utilisez l'API Matcher pour faire les remplacements dans un StringBuffer (ou peut-être StringBuilder, si c'est ce qu'il utilise). De cette façon, vous ne créez pas un tas de chaînes intermédiaires et les jetez.

+0

Hi. Merci pour la réponse. Je ne récupère pas le contenu textuel du document HTML mais de la base de données. Lorsque l'utilisateur soumet le contenu de l'environnement d'exécution, il entre d'abord dans la base de données, puis il est extrait de la base de données et utilisé pour la génération de PDF. – ashishjmeshram

Questions connexes