English 中文(简体)
自动转换 风格
原标题:Automatically convert Style Sheets to inline style

Don必须担心有关系的风格或风格。

我想自动转换类似档案。

<html>
<body>
<style>
body{background:#FFC}
p{background:red}
body, p{font-weight:bold}
</style>
<p>...</p>
</body>
</html>

查阅:

<html>
<body style="background:red;font-weight:bold">
<p style="background:#FFC;font-weight:bold">...</p>
</body>
</html>

如果有一个能够这样做的超文本帽,我会更加感兴趣。

The reason I want to do this is so I can display emails that use global style sheets without their style sheets messing up the rest of my web page. I also would like to send the resulting style to web based rich text editor for reply and original message.

问题回答

这里是关于java的解决办法,我与联合材料图书馆一起:

import java.io.IOException;
import java.util.StringTokenizer;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class AutomaticCssInliner {
    /**
     * Hecho por Grekz, http://grekz.wordpress.com
     */
    public static void main(String[] args) throws IOException {
        final String style = "style";
        final String html = "<html>" + "<body> <style>"
                + "body{background:#FFC} 
 p{background:red}"
                + "body, p{font-weight:bold} </style>"
                + "<p>...</p> </body> </html>";
        // Document doc = Jsoup.connect("http://mypage.com/inlineme.php").get();
        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(
                    "
", "").trim(), delims = "{}";
            StringTokenizer st = new StringTokenizer(styleRules, delims);
            while (st.countTokens() > 1) {
                String selector = st.nextToken(), properties = st.nextToken();
                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();
        }
        System.out.println(doc);// now we have the result html without the
        // styles tags, and the inline css in each
        // element
    }

    private static String concatenateProperties(String oldProp, String newProp) {
        oldProp = oldProp.trim();
        if (!newProp.endsWith(";"))
           newProp += ";";
        return newProp + oldProp; // The existing (old) properties should take precedence.
    }
}

Using jsoup + cssparser:

private static final String STYLE_ATTR = "style";
private static final String CLASS_ATTR = "class";

public String inlineStyles(String html, File cssFile, boolean removeClasses) throws IOException {
    Document document = Jsoup.parse(html);
    CSSOMParser parser = new CSSOMParser(new SACParserCSS3());
    InputSource source = new InputSource(new FileReader(cssFile));
    CSSStyleSheet stylesheet = parser.parseStyleSheet(source, null, null);

    CSSRuleList ruleList = stylesheet.getCssRules();
    Map<Element, Map<String, String>> allElementsStyles = new HashMap<>();
    for (int ruleIndex = 0; ruleIndex < ruleList.getLength(); ruleIndex++) {
        CSSRule item = ruleList.item(ruleIndex);
        if (item instanceof CSSStyleRule) {
            CSSStyleRule styleRule = (CSSStyleRule) item;
            String cssSelector = styleRule.getSelectorText();
            Elements elements = document.select(cssSelector);
            for (Element element : elements) {
                Map<String, String> elementStyles = allElementsStyles.computeIfAbsent(element, k -> new LinkedHashMap<>());
                CSSStyleDeclaration style = styleRule.getStyle();
                for (int propertyIndex = 0; propertyIndex < style.getLength(); propertyIndex++) {
                    String propertyName = style.item(propertyIndex);
                    String propertyValue = style.getPropertyValue(propertyName);
                    elementStyles.put(propertyName, propertyValue);
                }
            }
        }
    }

    for (Map.Entry<Element, Map<String, String>> elementEntry : allElementsStyles.entrySet()) {
        Element element = elementEntry.getKey();
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> styleEntry : elementEntry.getValue().entrySet()) {
            builder.append(styleEntry.getKey()).append(":").append(styleEntry.getValue()).append(";");
        }
        builder.append(element.attr(STYLE_ATTR));
        element.attr(STYLE_ATTR, builder.toString());
        if (removeClasses) {
            element.removeAttr(CLASS_ATTR);
        }
    }

    return document.html();
}

After hours of trying different manual java code solutions and not being satisfied with results (responsive media query handling issues mostly), I stumbled upon https://github.com/mdedetrich/java-premailer-wrapper which works great as a java solution. Note that you might actually be better off running your own "premailer" server. While there is a public api to premailer, I wanted to have my own instance running that I can hit as hard as I want: https://github.com/TrackIF/premailer-server

易于在ec2上运行,仅举几个点:https://docs.aws.amazon.com/elgalbeanstalk/latest/dg/create_deploy_Ruby_sinatra.html

git clone https://github.com/Enalmada/premailer-server
cd premailer-server
eb init  (choose latest ruby)
eb create premailer-server
eb deploy
curl --data "html=<your html>" http://your.eb.url

I haven t tried this but looks like you can use something like CSS parser to get a DOM tree corresponding to your CSS. So you can do something like:

  1. Obtain cssDOM
  2. Obtain htmlDOM (JAXP)
  3. Iterate over each cssDOM element and use xpath to locate and insert the correct style in your htmlDOM.
  4. Convert htmlDOM to string.

我可以发表评论,但我写了一部图象,试图加强已接受的答案,处理 cas式印本的碎块。

It doesn t work perfectly but it is almost there. https://gist.github.com/moodysalem/69e2966834a1f79492a9

您可使用<代码>HtmlUnit和Jsoup。 页: 1 然后,通过<代码>HtmlUnit,获得计算风格。 Jsoup刚刚来到这里,准备了html的产出。

你们可以在此找到一个简单的执行情况:

public final class CssInliner {
   private static final Logger log = Logger.getLogger(CssInliner.class);

   private CssInliner() {
   }

   public static CssInliner make() {
      return new CssInliner();
   }

   /**
    * Main method
    *
    * @param html html to inline
    *
    * @return inlined html
    */
   public String inline(String html) throws IOException {

      try (WebClient webClient = new WebClient()) {

         HtmlPage htmlPage = getHtmlPage(webClient, html);
         Window window = webClient.getCurrentWindow().getScriptableObject();

         for (HtmlElement htmlElement : htmlPage.getHtmlElementDescendants()) {
            applyComputedStyle(window, htmlElement);
         }

         return outputCleanHtml(htmlPage);
      }
   }

   /**
    * Output the HtmlUnit page to a clean html. Remove the old global style tag
    * that we do not need anymore. This in order to simplify of the tests of the
    * output.
    *
    * @param htmlPage
    *
    * @return
    */
   private String outputCleanHtml(HtmlPage htmlPage) {
      Document doc = Jsoup.parse(htmlPage.getDocumentElement().asXml());
      Element globalStyleTag = doc.selectFirst("html style");
      if (globalStyleTag != null) {
         globalStyleTag.remove();
      }
      doc.outputSettings().syntax(Syntax.html);
      return doc.html();
   }

   /**
    * Modify the html elements by adding an style attribute to each element
    *
    * @param window
    * @param htmlElement
    */
   private void applyComputedStyle(Window window, HtmlElement htmlElement) {

      HTMLElement pj = htmlElement.getScriptableObject();
      ComputedCSSStyleDeclaration cssStyleDeclaration = window.getComputedStyle(pj, null);

      Map<String, StyleElement> map = getStringStyleElementMap(cssStyleDeclaration);
      // apply style element to html
      if (!map.isEmpty()) {
         htmlElement.writeStyleToElement(map);
      }
   }

   private Map<String, StyleElement> getStringStyleElementMap(ComputedCSSStyleDeclaration cssStyleDeclaration) {
      Map<String, StyleElement> map = new HashMap<>();
      for (Definition definition : Definition.values()) {
         String style = cssStyleDeclaration.getStyleAttribute(definition, false);

         if (StringUtils.isNotBlank(style)) {
            map.put(definition.getAttributeName(),
                    new StyleElement(definition.getAttributeName(),
                                     style,
                                     "",
                                     SelectorSpecificity.DEFAULT_STYLE_ATTRIBUTE));
         }

      }
      return map;
   }

   private HtmlPage getHtmlPage(WebClient webClient, String html) throws IOException {
      URL url = new URL("http://tinubuinliner/" + Math.random());
      StringWebResponse stringWebResponse = new StringWebResponse(html, url);

      return HTMLParser.parseHtml(stringWebResponse, webClient.getCurrentWindow());
   }
}

为了解决这个问题,你很可能最好使用像《邮报》这样的充满战斗的工具。

他们已经在其年度行动计划中打开了 c锁工具,见:

远比网络更有用。

这里还有开放的Rubyi工具:

Premailer还揭露了APIC和网络形式,见,由作为电子邮件空间其他重要角色之一的运动监测员赞助。

我猜测你可以通过[Jruby]?

http://www.mailchimp.com/labs/inlinecs.php

使用上述联系。 它将节省时间,特别是电子邮件模板。 它是一种免费工具,通过邮购

如果银行/公司允许联系的商务服务公司,例如银行/公司,则电子商务申请往往需要这类东西。 WorldPay.

巨大的挑战与缺乏继承权相比,远远没有转变。 你们必须明确把继承财产归所有后代。 测试至关重要,因为某些浏览器会比其他浏览器带来更多的悲痛。 你们需要增加远比你需要联系的风格单更多的线代码,例如,在连接式的风格上,你们需要的是p{彩色:red }<<>/code>,但你必须明确把彩色放在所有段落上。

From my experience, it s very much a manual process that requires a light touch and a lot of tweaking and cross-browser testing to get right.

我用了头两个答案,并采用了这些答案,以利用CSS教区图书馆的能力:

public String inline(String html, String styles) throws IOException {

    Document document = Jsoup.parse(html);

    CSSRuleList ruleList = getCssRules(styles);

    for (int i = 0; i < ruleList.getLength(); i++) {
        CSSRule rule = ruleList.item(i);
        if (rule instanceof CSSStyleRule) {
            CSSStyleRule styleRule = (CSSStyleRule) rule;
            String selector = styleRule.getSelectorText();

            Elements elements = document.select(selector);
            for (final Element element : elements) {
                applyRuleToElement(element, styleRule);
            }

        }

    }

    removeClasses(document);

    return document.html();
}

private CSSRuleList getCssRules(String styles) throws IOException {
    CSSOMParser parser = new CSSOMParser(new SACParserCSS3());
    CSSStyleSheet styleSheet = parser.parseStyleSheet(new InputSource(new StringReader(styles)), null, null);
    CSSRuleList list = styleSheet.getCssRules();
    return list;
}

private void applyRuleToElement(Element element, CSSStyleRule rule){
    String elementStyleString = element.attr("style");

    CSSStyleDeclarationImpl elementStyleDeclaration = new CSSStyleDeclarationImpl();
    elementStyleDeclaration.setCssText(elementStyleString);

    CSSStyleDeclarationImpl ruleStyleDeclaration = (CSSStyleDeclarationImpl)rule.getStyle();

    for(Property p : ruleStyleDeclaration.getProperties()){
        elementStyleDeclaration.addProperty(p);
    }

    String cssText = elementStyleDeclaration.getCssText();

    element.attr("style", cssText);
}

private void removeClasses(Document document){
    Elements elements = document.getElementsByAttribute("class");
    elements.removeAttr("class");
}

或许可以进一步加以改进,例如使用?





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...