BrightSide Workbench Full Report + Source Code
MailInline.java
Go to the documentation of this file.
1 /*
2  * TurrĂ³ i Cutiller Foundation. License notice.
3  * Copyright (C) 2022 Lluis TurrĂ³ Cutiller <http://www.turro.org/>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 package org.turro.mail.message;
20 
21 import com.steadystate.css.parser.CSSOMParser;
22 import java.io.File;
23 import java.io.FilenameFilter;
24 import java.io.IOException;
25 import java.io.StringReader;
26 import java.util.HashMap;
27 import java.util.LinkedHashMap;
28 import java.util.Map;
29 import java.util.StringTokenizer;
30 import org.turro.string.Strings;
31 import org.jsoup.Jsoup;
32 import org.jsoup.nodes.Document;
33 import org.jsoup.nodes.Element;
34 import org.jsoup.select.Elements;
35 import org.turro.elephant.context.ElephantContext;
36 import org.turro.elephant.impl.util.FileUtil;
37 import org.turro.html.HTMLEntities;
38 import org.turro.log.WebLoggers;
39 import org.w3c.css.sac.InputSource;
40 import org.w3c.dom.css.CSSRule;
41 import org.w3c.dom.css.CSSRuleList;
42 import org.w3c.dom.css.CSSStyleDeclaration;
43 import org.w3c.dom.css.CSSStyleRule;
44 import org.w3c.dom.css.CSSStyleSheet;
45 
52 public class MailInline {
53 
54  public static final String MAIL_CSS = "/WEB-INF/elephant/templates-mail/css";
55 
56  public String inline(String html) {
57  if(Strings.isBlank(html)) {
58  return html;
59  }
60 // try {
61 // html = inlineStyles(html);
62 // } catch (IOException ex) {
63 // Logger.getLogger(MailFormat.class.getName()).log(Level.SEVERE, ElephantContext.logMsg(null), ex);
64 // }
65  File rootCSS = new File(ElephantContext.getRealPath(MAIL_CSS));
66  if (rootCSS.exists()) {
67  for (File css : rootCSS.listFiles(new FilenameFilter() {
68  @Override
69  public boolean accept(File dir, String name) {
70  return name.endsWith(".css");
71  }
72  })) {
73  try {
74  html = inlineCSS(css, html);
75  } catch (IOException ex) {
76  WebLoggers.info(this).exception(ex).log();
77  }
78  }
79  }
80  return HTMLEntities.escape(html);
81  }
82 
83  private String inlineCSS(File css, String html) throws IOException {
84  CSSOMParser parser = new CSSOMParser();
85  CSSStyleSheet stylesheet = parser.parseStyleSheet(new InputSource(new StringReader(FileUtil.getContent(css))), null, null);
86 
87  final Document doc = Jsoup.parse(html);
88  final CSSRuleList rules = stylesheet.getCssRules();
89  final Map<Element, Map<String, String>> elementStyles = new HashMap<>();
90 
91  for (int i = 0; i < rules.getLength(); i++) {
92  final CSSRule rule = rules.item(i);
93  if (rule instanceof CSSStyleRule) {
94  final CSSStyleRule styleRule = (CSSStyleRule) rule;
95  final String selector = styleRule.getSelectorText();
96 
97  if (!selector.contains(":")) {
98  final Elements selectedElements = doc.select(selector);
99  for (final Element selected : selectedElements) {
100  if (!elementStyles.containsKey(selected)) {
101  elementStyles.put(selected, new LinkedHashMap<String, String>());
102  }
103 
104  final CSSStyleDeclaration styleDeclaration = styleRule.getStyle();
105 
106  for (int j = 0; j < styleDeclaration.getLength(); j++) {
107  final String propertyName = styleDeclaration.item(j);
108  final String propertyValue = styleDeclaration.getPropertyValue(propertyName);
109  final Map<String, String> elementStyle = elementStyles.get(selected);
110  elementStyle.put(propertyName, convert(propertyValue));
111  }
112 
113  }
114  }
115  }
116  }
117 
118  for (final Map.Entry<Element, Map<String, String>> elementEntry : elementStyles.entrySet()) {
119  final Element element = elementEntry.getKey();
120  final StringBuilder builder = new StringBuilder();
121  for (final Map.Entry<String, String> styleEntry : elementEntry.getValue().entrySet()) {
122  builder.append(styleEntry.getKey()).append(":").append(styleEntry.getValue()).append(";");
123  }
124  builder.append(element.attr("style"));
125  element.attr("style", builder.toString());
126  //element.removeAttr("class");
127  }
128 
129  return doc.html();
130  }
131 
132  public String inlineStyles(String html) throws IOException {
133  final Document doc = Jsoup.parse(html);
134  String style = "style";
135  Elements els = doc.select(style);
136  for (Element e : els) {
137  String styleRules = e.getAllElements().get(0).data().replaceAll("\n", "").trim(), delims
138  = "{}";
139  StringTokenizer st = new StringTokenizer(styleRules, delims);
140  while (st.countTokens() > 1) {
141  String selector = st.nextToken(), properties = st.nextToken();
142  if (selector.indexOf(":") > 0) {
143  selector = selector.substring(0, selector.indexOf(":"));
144  }
145  if (Strings.isBlank(selector)) {
146  continue;
147  }
148  Elements selectedElements = doc.select(selector);
149  for (Element selElem : selectedElements) {
150  String oldProperties = selElem.attr(style);
151  selElem.attr(
152  style,
153  oldProperties.length() > 0 ? concatenateProperties(oldProperties,
154  properties) : properties);
155  }
156  }
157  e.remove();
158  }
159 
160  return doc.html();
161  }
162 
163  private static String concatenateProperties(String oldProp, String newProp) {
164  oldProp = oldProp.trim();
165  if (!newProp.endsWith(";")) {
166  newProp += ";";
167  }
168  return newProp + oldProp;
169  }
170 
171  private String convert(String propertyValue) {
172  if(propertyValue.startsWith("rgb")) {
173  String colors[] = propertyValue.substring(4, propertyValue.length() - 1).split("\\s*,\\s*");
174  propertyValue = String.format("#%02x%02x%02x",
175  Integer.parseInt(colors[0]), Integer.parseInt(colors[1]), Integer.parseInt(colors[2]));
176  }
177  return propertyValue; //.replaceAll("\\&quot\\;", "'");
178  }
179 
180 }
static String getContent(File file)
Definition: FileUtil.java:245
static String escape(String html)
WebLoggers exception(Throwable throwable)
Definition: WebLoggers.java:29
static WebLoggers info(Object entity)
Definition: WebLoggers.java:43
String inlineStyles(String html)
static final String MAIL_CSS
Definition: MailInline.java:54