diff --git a/itext.tests/itext.html2pdf.tests/itext/html2pdf/css/CssStylesResolvingTest.cs b/itext.tests/itext.html2pdf.tests/itext/html2pdf/css/CssStylesResolvingTest.cs
index 68f25d613..5efbf762c 100644
--- a/itext.tests/itext.html2pdf.tests/itext/html2pdf/css/CssStylesResolvingTest.cs
+++ b/itext.tests/itext.html2pdf.tests/itext/html2pdf/css/CssStylesResolvingTest.cs
@@ -191,6 +191,22 @@ public virtual void HtmlStylesConvertingTest11() {
, "font-family: times");
}
+ [NUnit.Framework.TestCase("cssVariablesTest01.html", "30px", "30px")]
+ [NUnit.Framework.TestCase("cssVariablesTest02.html", "50px", "30px")]
+ [NUnit.Framework.TestCase("cssVariablesTest03.html", "35px", "35px")]
+ public virtual void CssVariablesTest1(string fileName, string expectedMargin, string expectedVarValue) {
+ Test(fileName, "html body div",
+ "display: block",
+ $"--test-var: {expectedVarValue}",
+ $"margin-top: {expectedMargin}",
+ $"margin-right: {expectedMargin}",
+ $"margin-bottom: {expectedMargin}",
+ $"margin-left: {expectedMargin}",
+ "font-family: times",
+ "font-size: 12pt"
+ );
+ }
+
private void ResolveStylesForTree(INode node, ICssResolver cssResolver, CssContext context) {
if (node is IElementNode) {
IElementNode element = (IElementNode)node;
diff --git a/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest01.html b/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest01.html
new file mode 100644
index 000000000..dbbdc57f2
--- /dev/null
+++ b/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest01.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ This is a div text with margin 30px
+
+
+
diff --git a/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest02.html b/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest02.html
new file mode 100644
index 000000000..fa65bd76a
--- /dev/null
+++ b/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest02.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ This is a div text with margin 50px
+
+
+
diff --git a/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest03.html b/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest03.html
new file mode 100644
index 000000000..5c36fc646
--- /dev/null
+++ b/itext.tests/itext.html2pdf.tests/resources/itext/html2pdf/css/CssElementStylesResolvingTest/cssVariablesTest03.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ This is a div text with margin 35px
+
+
+
diff --git a/itext/itext.html2pdf/itext/html2pdf/css/resolve/DefaultCssResolver.cs b/itext/itext.html2pdf/itext/html2pdf/css/resolve/DefaultCssResolver.cs
index b392ca755..d130e6410 100644
--- a/itext/itext.html2pdf/itext/html2pdf/css/resolve/DefaultCssResolver.cs
+++ b/itext/itext.html2pdf/itext/html2pdf/css/resolve/DefaultCssResolver.cs
@@ -23,6 +23,7 @@ You should have received a copy of the GNU Affero General Public License
using System;
using System.Collections.Generic;
using System.IO;
+using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using iText.Commons;
using iText.Html2pdf.Attach;
@@ -51,6 +52,8 @@ namespace iText.Html2pdf.Css.Resolve {
/// interface.
///
public class DefaultCssResolver : ICssResolver {
+ private static readonly Regex CssVarDecl = new Regex(@"^\s*var\(\s*(?--.*)\)\s*$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
/// The CSS style sheet.
private CssStyleSheet cssStyleSheet;
@@ -199,6 +202,10 @@ private IDictionary ResolveStyles(INode element, CssContext cont
keys.Add(entry.Key);
}
}
+
+ // Resolve CSS variables
+ ResolveCssVariables(elementStyles);
+
foreach (String key in keys) {
elementStyles.Put(key, CssDefaults.GetDefaultValue(key));
}
@@ -208,6 +215,57 @@ private IDictionary ResolveStyles(INode element, CssContext cont
return elementStyles;
}
+ private static bool IsCssVarDecl(string decl, out string innerDecl) {
+ if (decl == null) {
+ innerDecl = null;
+ return false;
+ }
+
+ var cssVarDecl = CssVarDecl.Match(decl);
+ if (cssVarDecl.Success) {
+ innerDecl = cssVarDecl.Groups["decl"].Value;
+ return true;
+ }
+
+ innerDecl = null;
+ return false;
+ }
+
+ private static void ResolveCssVariables(IDictionary elementStyles) {
+ var varOverrides = new Dictionary();
+
+ foreach (KeyValuePair entry in elementStyles) {
+ if (IsCssVarDecl(entry.Value, out var decl)) {
+ var value = ResolveVariable(decl);
+ varOverrides.Add(entry.Key, value);
+ }
+ continue;
+
+ string ResolveVariable(string substring) {
+ var hasDefault = substring.IndexOf(',');
+ var varName = hasDefault == -1 ? substring : substring.Substring(0, hasDefault);
+ var dfltVal = hasDefault == -1 ? null : substring.Substring(hasDefault + 1).Trim();
+ if (elementStyles.TryGetValue(varName, out var variable)) {
+ return variable;
+ }
+
+ if (dfltVal is null) {
+ return null;
+ }
+
+ if (IsCssVarDecl(dfltVal, out var innerDecl)) {
+ return ResolveVariable(innerDecl);
+ }
+
+ return dfltVal;
+ }
+ }
+
+ foreach (var varOverride in varOverrides) {
+ elementStyles.Put(varOverride.Key, varOverride.Value);
+ }
+ }
+
private IDictionary ResolveElementsStyles(INode element) {
IList ruleSets = new List();
ruleSets.Add(new CssRuleSet(null, UserAgentCss.GetStyles(element)));