All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.rahulsom.nexusmonitor.CssInliner.groovy Maven / Gradle / Ivy

The newest version!
package com.github.rahulsom.nexusmonitor

import groovy.transform.CompileStatic
import org.apache.commons.codec.binary.Base64
import org.apache.commons.io.IOUtils
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements

import java.net.URL
import java.net.URLConnection
import java.text.MessageFormat
import java.util.StringTokenizer

/**
 * 

Inlines CSS to avoid the dreaded GMail Grimace.

* *

The magic keyword is data-inline="true".

* *
    *
  • * When used in conjunction with the style tag, it inlines the stylesheet defined there into all * html elements matching the rules. *
  • *
  • * When used with the img tag, it base64 encodes the image it found to make it visible in the * email. *
  • *
* * @author Rahul Somasunderam */ @CompileStatic class CssInliner { public static final String CSS_STYLE = "cssstyle" public static final String STYLE_ATTR = "style" public static final String STYLE_TAG = "style" public static final String IMG_TAG = "img" public static final String IMG_SRC_ATTR = "src" private static String concatenateProperties(String oldProp, String newProp) { if (!oldProp.endsWith(";")) oldProp += ";" return oldProp.trim() + " " + newProp.trim() + ";" } /** * Generates a stylesheet from an html document * * @param doc the html document * @return a string representing the stylesheet. */ private static String fetchStyles(Document doc) { Elements els = doc.select(STYLE_TAG) StringBuilder styles = new StringBuilder() for (Element e : els) { if (e.attr("data-inline").equals("true")) { styles.append(e.data()) e.remove() } } return styles.toString() } /** * Takes an input string representing an html document and processes it with the Css Inliner. * @param input the html document * @return the processed html document */ static String process(String input) { Document doc = Jsoup.parse(input) extractStyles(doc) applyStyles(doc) inlineImages(doc) String output = doc.toString() return output } /** * Inlines images marked with data-inline="true" * * @param doc the html document */ private static void inlineImages(Document doc) { Elements allImages = doc.getElementsByTag(IMG_TAG) for (Element img : allImages) { if (img.attr("data-inline").equals("true")) { String src = img.attr(IMG_SRC_ATTR) try { URL url = new URL(src) URLConnection urlConnection = url.openConnection() urlConnection.connect() String contentType = urlConnection.getContentType() urlConnection.getContent() byte[] srcContent = IOUtils.toByteArray(url.openStream()) String base64 = new Base64().encodeToString(srcContent) img.attr(IMG_SRC_ATTR, MessageFormat.format("data:{0};base64,{1}", contentType, base64)) } catch (Exception e) { e.printStackTrace() } } } } /** * Transfers styles from the cssstyle attribute to the style attribute. * * @param doc the html document */ private static void applyStyles(Document doc) { Elements allStyledElements = doc.getElementsByAttribute(CSS_STYLE) for (Element e : allStyledElements) { String newStyle = e.attr(CSS_STYLE) String oldStyle = e.attr(STYLE_ATTR) e.attr(STYLE_ATTR, (newStyle + "; " + oldStyle).replace(";;", ";")) e.removeAttr(CSS_STYLE) } } /** * Extracts styles from the stylesheet and applies them to a cssstyle attribute. This is because the * styles need to be applied sequentially, but before the style defined for the element inline. * * @param doc the html document */ private static void extractStyles(Document doc) { String stylesheet = fetchStyles(doc) String trimmedStylesheet = stylesheet.replaceAll("\n", "").replaceAll("/\\*.*?\\*/", "").replaceAll(" +", " ") String styleRules = trimmedStylesheet.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(CSS_STYLE) selElem.attr(CSS_STYLE, oldProperties.length() > 0 ? concatenateProperties( oldProperties, properties) : properties) } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy