diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java
index 1d3fa5c29..24207db3e 100644
--- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java
+++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/context/StyleReference.java
@@ -36,6 +36,7 @@
import com.openhtmltopdf.css.extend.lib.DOMTreeResolver;
import com.openhtmltopdf.css.newmatch.CascadedStyle;
import com.openhtmltopdf.css.newmatch.PageInfo;
+import com.openhtmltopdf.css.newmatch.Selector;
import com.openhtmltopdf.css.parser.CSSPrimitiveValue;
import com.openhtmltopdf.css.sheet.PropertyDeclaration;
import com.openhtmltopdf.css.sheet.Stylesheet;
@@ -196,7 +197,16 @@ public CascadedStyle getCascadedStyle(Element e, boolean restyle) {
if (e == null) return CascadedStyle.emptyCascadedStyle;
return _matcher.getCascadedStyle(e, restyle);
}
-
+
+ /**
+ * Given an element, returns all selectors and their rulesets
+ * for its descendants. Useful for getting the styles that should be
+ * applied to SVG, etc.
+ */
+ public String getCSSForAllDescendants(Element e) {
+ return _matcher.getCSSForAllDescendants(e);
+ }
+
public PageInfo getPageStyle(String pageName, String pseudoPage) {
return _matcher.getPageCascadedStyle(pageName, pseudoPage);
}
@@ -268,14 +278,7 @@ private List getStylesheets() {
return infos;
}
-
- @Deprecated
- public void removeStyle(Element e) {
- if (_matcher != null) {
- _matcher.removeStyle(e);
- }
- }
-
+
public List getFontFaceRules() {
return _matcher.getFontFaceRules();
}
diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/SVGProperty.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/SVGProperty.java
new file mode 100644
index 000000000..f39e42249
--- /dev/null
+++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/SVGProperty.java
@@ -0,0 +1,48 @@
+package com.openhtmltopdf.css.constants;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * This is a partial list of common SVG properties that are not present in
+ * the HTML renderer of this project. This list is here so we can suppress
+ * warnings for these properties.
+ *
+ * List from:
+ * https://css-tricks.com/svg-properties-and-css/
+ */
+public enum SVGProperty {
+ CLIP,
+ CLIP_PATH,
+ CLIP_RULE,
+ MASK,
+ FILTER,
+ STOP_COLOR,
+ STOP_OPACITY,
+ FILL,
+ FILL_RULE,
+ FILL_OPACITY,
+ MARKER,
+ MARKER_START,
+ MARKER_MID,
+ MARKER_END,
+ STROKE,
+ STROKE_DASHARRAY,
+ STROKE_DASHOFFSET,
+ STROKE_LINECAP,
+ STROKE_LINEJOIN,
+ STROKE_MITERLIMIT,
+ STROKE_OPACITY,
+ STROKE_WIDTH;
+
+ private static final Set _set =
+ Arrays.stream(values())
+ .map(v -> v.name().toLowerCase(Locale.US).replace('_', '-'))
+ .collect(Collectors.toSet());
+
+ public static Set properties() {
+ return _set;
+ }
+}
diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Condition.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Condition.java
index 47e3205a6..ab06fcd30 100644
--- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Condition.java
+++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Condition.java
@@ -37,6 +37,7 @@
abstract class Condition {
abstract boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes);
+ abstract void toCSS(StringBuilder sb);
/**
* the CSS condition [attribute]
@@ -217,6 +218,17 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
return compare(val, _value);
}
+
+ protected void toCSS(StringBuilder sb, String type) {
+ sb.append('[');
+ sb.append(_name);
+ sb.append(type);
+ sb.append('=');
+ sb.append('\"');
+ sb.append(_value);
+ sb.append('\"');
+ sb.append(']');
+ }
}
private static class AttributeExistsCondition extends AttributeCompareCondition {
@@ -241,6 +253,13 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
protected boolean compare(String attrValue, String conditionValue) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append('[');
+ sb.append(_name);
+ sb.append(']');
+ }
}
private static class AttributeEqualsCondition extends AttributeCompareCondition {
@@ -252,6 +271,11 @@ private static class AttributeEqualsCondition extends AttributeCompareCondition
protected boolean compare(String attrValue, String conditionValue) {
return attrValue.equals(conditionValue);
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ toCSS(sb, "");
+ }
}
private static class AttributePrefixCondition extends AttributeCompareCondition {
@@ -263,6 +287,11 @@ private static class AttributePrefixCondition extends AttributeCompareCondition
protected boolean compare(String attrValue, String conditionValue) {
return attrValue.startsWith(conditionValue);
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ toCSS(sb, "^");
+ }
}
private static class AttributeSuffixCondition extends AttributeCompareCondition {
@@ -274,6 +303,11 @@ private static class AttributeSuffixCondition extends AttributeCompareCondition
protected boolean compare(String attrValue, String conditionValue) {
return attrValue.endsWith(conditionValue);
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ toCSS(sb, "$");
+ }
}
private static class AttributeSubstringCondition extends AttributeCompareCondition {
@@ -285,8 +319,13 @@ private static class AttributeSubstringCondition extends AttributeCompareConditi
protected boolean compare(String attrValue, String conditionValue) {
return attrValue.indexOf(conditionValue) > -1;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ toCSS(sb, "*");
+ }
}
-
+
private static class AttributeMatchesListCondition extends AttributeCompareCondition {
AttributeMatchesListCondition(String namespaceURI, String name, String value) {
super(namespaceURI, name, value);
@@ -303,6 +342,11 @@ protected boolean compare(String attrValue, String conditionValue) {
}
return matched;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ toCSS(sb, "~");
+ }
}
private static class AttributeMatchesFirstPartCondition extends AttributeCompareCondition {
@@ -318,6 +362,11 @@ protected boolean compare(String attrValue, String conditionValue) {
}
return false;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ toCSS(sb, "|");
+ }
}
private static class ClassCondition extends Condition {
@@ -343,6 +392,12 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
// in an XML DOM, space normalization in attributes is supposed to have happened already.
return (" " + c + " ").indexOf(_paddedClassName) != -1;
}
+
+ @Override
+ public void toCSS(StringBuilder sb) {
+ sb.append('.');
+ sb.append(_paddedClassName.substring(1, _paddedClassName.length() - 1));
+ }
}
private static class IDCondition extends Condition {
@@ -363,6 +418,12 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
}
return true;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append('#');
+ sb.append(_id);
+ }
}
private static class LangCondition extends Condition {
@@ -390,6 +451,13 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
}
return false;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":lang(");
+ sb.append(_lang);
+ sb.append(')');
+ }
}
private static class FirstChildCondition extends Condition {
@@ -401,6 +469,11 @@ private static class FirstChildCondition extends Condition {
boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
return treeRes.isFirstChildElement(e);
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":first-child");
+ }
}
private static class LastChildCondition extends Condition {
@@ -412,6 +485,11 @@ private static class LastChildCondition extends Condition {
boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
return treeRes.isLastChildElement(e);
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":last-child");
+ }
}
private static class NthChildCondition extends Condition {
@@ -420,10 +498,12 @@ private static class NthChildCondition extends Condition {
private final int a;
private final int b;
+ private final String input;
- NthChildCondition(int a, int b) {
+ NthChildCondition(int a, int b, String input) {
this.a = a;
this.b = b;
+ this.input = input;
}
@Override
@@ -442,16 +522,23 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
}
}
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":nth-child(");
+ sb.append(input);
+ sb.append(')');
+ }
+
static NthChildCondition fromString(String number) {
number = number.trim().toLowerCase();
if ("even".equals(number)) {
- return new NthChildCondition(2, 0);
+ return new NthChildCondition(2, 0, number);
} else if ("odd".equals(number)) {
- return new NthChildCondition(2, 1);
+ return new NthChildCondition(2, 1, number);
} else {
try {
- return new NthChildCondition(0, Integer.parseInt(number));
+ return new NthChildCondition(0, Integer.parseInt(number), number);
} catch (NumberFormatException e) {
Matcher m = pattern.matcher(number);
@@ -467,7 +554,7 @@ static NthChildCondition fromString(String number) {
b *= -1;
}
- return new NthChildCondition(a, b);
+ return new NthChildCondition(a, b, number);
}
}
}
@@ -484,6 +571,11 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
int position = treeRes.getPositionOfElement(e);
return position >= 0 && position % 2 == 0;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":nth-child(even)");
+ }
}
private static class OddChildCondition extends Condition {
@@ -496,6 +588,11 @@ boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
int position = treeRes.getPositionOfElement(e);
return position >= 0 && position % 2 == 1;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":nth-child(odd)");
+ }
}
private static class LinkCondition extends Condition {
@@ -507,6 +604,11 @@ private static class LinkCondition extends Condition {
boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
return attRes.isLink(e);
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ sb.append(":link");
+ }
}
/**
@@ -521,6 +623,11 @@ private static class UnsupportedCondition extends Condition {
boolean matches(Object e, AttributeResolver attRes, TreeResolver treeRes) {
return false;
}
+
+ @Override
+ void toCSS(StringBuilder sb) {
+ // Nothing we can do...
+ }
}
private static String[] split(String s, char ch) {
diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java
index 0038be132..1674470d1 100644
--- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java
+++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/newmatch/Matcher.java
@@ -20,16 +20,19 @@
*/
package com.openhtmltopdf.css.newmatch;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Deque;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
-
import com.openhtmltopdf.css.constants.MarginBoxName;
import com.openhtmltopdf.css.extend.AttributeResolver;
import com.openhtmltopdf.css.extend.StylesheetFactory;
@@ -43,36 +46,29 @@
* @author Torbjoern Gannholm
*/
public class Matcher {
+ private final Mapper docMapper;
+ private final AttributeResolver _attRes;
+ private final TreeResolver _treeRes;
+ private final StylesheetFactory _styleFactory;
- private Mapper docMapper;
- private com.openhtmltopdf.css.extend.AttributeResolver _attRes;
- private com.openhtmltopdf.css.extend.TreeResolver _treeRes;
- private com.openhtmltopdf.css.extend.StylesheetFactory _styleFactory;
+ private final Map