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