9

I am looking for a java library which can inline an external CSS file with a HTML document based on its ID/class attributes.

I've found jStyleParser but I am not sure if this is the right library for me. I seem to fail to understand if it can do the job of inlining the CSS on the elements from the HTML. The documentation and examples is not what I expected.

Is there anyone who can answer that question or does there exist another library for this?

Thanks

fancyPants
  • 50,732
  • 33
  • 89
  • 96
janhartmann
  • 14,713
  • 15
  • 82
  • 138

2 Answers2

12

You may try CSSBox. Just take a look at the ComputeStyles demo contained in the package (see the doc/examples/README file in the distribution package for information about running the demo). It computes all the styles and creates a new HTML document (represented by a DOM) with the corresponding inline style definitions.

The source is in src/org/fit/cssbox/demo/ComputeStyles.java and it's pretty short. Actually, it uses jStyleParser for doing the main job, CSSBox just provides a nicer interface for this.

        //Open the network connection 
        DocumentSource docSource = new DefaultDocumentSource(args[0]);

        //Parse the input document
        DOMSource parser = new DefaultDOMSource(docSource);
        Document doc = parser.parse();

        //Create the CSS analyzer
        DOMAnalyzer da = new DOMAnalyzer(doc, docSource.getURL());
        da.attributesToStyles(); //convert the HTML presentation attributes to inline styles
        da.addStyleSheet(null, CSSNorm.stdStyleSheet(), DOMAnalyzer.Origin.AGENT); //use the standard style sheet
        da.addStyleSheet(null, CSSNorm.userStyleSheet(), DOMAnalyzer.Origin.AGENT); //use the additional style sheet
        da.getStyleSheets(); //load the author style sheets

        //Compute the styles
        System.err.println("Computing style...");
        da.stylesToDomInherited();

        //Save the output
        PrintStream os = new PrintStream(new FileOutputStream(args[1]));
        Output out = new NormalOutput(doc);
        out.dumpTo(os);
        os.close();

        docSource.close();
Chloe
  • 25,162
  • 40
  • 190
  • 357
radkovo
  • 868
  • 6
  • 10
  • 2
    Ack! It doesn't work in AppEngine! Curse you Google! – Chloe Jan 08 '14 at 22:15
  • @radkovo How would this example be modified in case the source is a String input = "..." (instead of a URL)? Nice work btw! – athspk Sep 16 '14 at 19:34
  • @athspk You would have to write your own DocumentSource implementation that creates an input stream from a string instead of an URL. This should be quite simple, just take a look at the original DefaultDocumentSource implementation. [Here](http://stackoverflow.com/questions/247161/how-do-i-turn-a-string-into-a-stream-in-java) you may find how to create an input stream from a string. – radkovo Sep 17 '14 at 12:05
  • Yes, in the end this is how i did it. Thanks again! – athspk Sep 17 '14 at 17:54
  • Can you create a example to create a DocumentSource from an html? I didnt find a way until now. I dont hava a file to read neither a file to output. I would like read a string and output a string... – GarouDan Mar 03 '15 at 13:44
  • At time of writing, CSSBox is not OSGI compliant. JSoup is an alternative, see: http://stackoverflow.com/questions/4521557/automatically-convert-style-sheets-to-inline-style – Skurpi Apr 28 '15 at 08:02
  • Note the LGPL license for those looking to embed. – Matthew Rathbone Aug 25 '15 at 16:08
7

I've been very happy with JSoup (v1.5.2). I have a method like this:

 public static String inlineCss(String html) {
    final String style = "style";
    Document doc = Jsoup.parse(html);
    Elements els = doc.select(style);// to get all the style elements
    for (Element e : els) {
      String styleRules = e.getAllElements().get(0).data().replaceAll("\n", "").trim();
      String delims = "{}";
      StringTokenizer st = new StringTokenizer(styleRules, delims);
      while (st.countTokens() > 1) {
        String selector = st.nextToken(), properties = st.nextToken();
        if (!selector.contains(":")) { // skip a:hover rules, etc.
          Elements selectedElements = doc.select(selector);
          for (Element selElem : selectedElements) {
            String oldProperties = selElem.attr(style);
            selElem.attr(style,
                oldProperties.length() > 0 ? concatenateProperties(
                    oldProperties, properties) : properties);
          }
        }
      }
      e.remove();
    }
    return doc.toString();
  }

  private static String concatenateProperties(String oldProp, @NotNull String newProp) {
    oldProp = oldProp.trim();
    if (!oldProp.endsWith(";"))
      oldProp += ";";
    return oldProp + newProp.replaceAll("\\s{2,}", " ");
  }
Nic Cottrell
  • 9,401
  • 7
  • 53
  • 76