Подтвердить что ты не робот

Автоматическое преобразование таблиц стилей в встроенный стиль

Не нужно беспокоиться о стиле стиля или стиля наведения.

Я хочу автоматически конвертировать файлы вроде этого

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

Я был бы еще более заинтересован, если бы был парсер HTML, который бы это сделал.

Причина, по которой я хочу сделать это, - это отображать электронные письма, в которых используются глобальные таблицы стилей, без их таблиц стилей, испортивших остальную часть моей веб-страницы. Я также хотел бы отправить полученный стиль в текстовый редактор на основе веб-страниц для ответа и оригинального сообщения.

4b9b3361

Ответ 1

Вот решение на java, я сделал это с помощью библиотеки JSoup: http://jsoup.org/download

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} \n 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(
                    "\n", "").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.
    }
}

Ответ 2

Использование 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();
}

Ответ 3

Я не пробовал это, но похоже, что вы можете использовать что-то вроде парсер CSS, чтобы получить дерево DOM, соответствующее вашему CSS. Поэтому вы можете сделать что-то вроде:

  • Получить cssDOM
  • Получить htmlDOM (JAXP)
  • Итерации по каждому элементу cssDOM и использование xpath для поиска и вставки правильного стиля в htmlDOM.
  • Преобразование htmlDOM в строку.

Ответ 4

Я еще не могу прокомментировать, но я написал суть, которая пыталась улучшить принятый ответ для обработки каскадной части каскадных таблиц стилей.

Это не работает отлично, но это почти все. https://gist.github.com/moodysalem/69e2966834a1f79492a9

Ответ 5

После нескольких попыток использования разных ручных Java-кодовых решений и недовольства результатами (в основном, для обработки запросов на обработку мультимедийных запросов) я наткнулся на https://github.com/mdedetrich/java-premailer-wrapper, который отлично работает как Java-решение. Обратите внимание, что на самом деле вам может быть лучше запускать собственный сервер "premailer". Хотя есть публичный api для premailer, я хотел, чтобы мой собственный экземпляр работал, что я могу ударить так сильно, как хочу: https://github.com/TrackIF/premailer-server

Простота запуска на ec2 всего за несколько кликов: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Ruby_sinatra.html

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

Ответ 6

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

Используйте эту ссылку выше. Это сэкономит ваше время и будет сделано специально для шаблонов электронной почты. Это бесплатный инструмент от mailchimp

Ответ 7

Такие вещи часто требуются для приложений электронной коммерции, где банк/что-то не позволяет связать CSS, например. WorldPay.

Большая проблема - это не столько конверсия, сколько отсутствие наследования. Вы должны явно задать унаследованные свойства для всех тегов-потомков. Тестирование имеет жизненно важное значение, поскольку некоторые браузеры вызовут больше горя, чем другие. Вам нужно будет добавить намного больше встроенного кода, чем требуется для связанной таблицы стилей, например, в связанной таблице стилей, в которой вы нуждаетесь, это p { color:red }, но в строке вы должны явно задать цвет во всех абзацах.

По моему опыту, это очень ручной процесс, который требует легкого прикосновения и много настроек и кросс-браузерного тестирования, чтобы получить право.

Ответ 8

Для решения этой проблемы вы, вероятно, лучше всего используете инструмент, усиленный битвой, похожий на инструмент Mailchimp.

Они открыли свой встроенный инструмент css в свой API, см. здесь: http://apidocs.mailchimp.com/api/1.3/inlinecss.func.php

Гораздо полезнее веб-формы.

Здесь также есть инструмент Ruby с открытым исходным кодом: https://github.com/alexdunae/premailer/

Premailer также предоставляет API и веб-форму, см. http://premailer.dialect.ca - спонсируемый Campaign Monitor, который является одним из других крупных игроков в пространство электронной почты.

Я предполагаю, что вы можете интегрировать Premailer в свое приложение Java через [Jruby] [1], хотя у меня нет опыта с этим.