Skip to content

Commit de0577f

Browse files
Fix issue #235: Preserve base64 encoded images and URLs during CSS parsing and style application (#429)
* Fix issue #235: Preserve base64 encoded images and URLs during CSS parsing and style application Co-Authored-By: [email protected] <[email protected]> * Refactor StyleClassApplier to simplify data URL quote handling Co-Authored-By: [email protected] <[email protected]> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]>
1 parent 95397b4 commit de0577f

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using Xunit;
3+
4+
namespace PreMailer.Net.Tests
5+
{
6+
public class Base64UrlTests
7+
{
8+
[Fact]
9+
public void MoveCssInline_WithUnquotedBase64BackgroundImage_ShouldPreserveBase64Data()
10+
{
11+
string input = "<style>.pt-logo { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA); background-repeat: no-repeat; }</style><span class=\"pt-logo\"></span>";
12+
13+
var result = PreMailer.MoveCssInline(input);
14+
15+
Assert.Contains("background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA)", result.Html);
16+
Assert.Contains("background-repeat: no-repeat", result.Html);
17+
}
18+
19+
[Fact]
20+
public void MoveCssInline_WithQuotedBase64BackgroundImage_ShouldPreserveBase64Data()
21+
{
22+
string input = "<style>.pt-logo { background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA\"); background-repeat: no-repeat; }</style><span class=\"pt-logo\"></span>";
23+
24+
var result = PreMailer.MoveCssInline(input);
25+
26+
Assert.Contains("background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA\")", result.Html);
27+
Assert.Contains("background-repeat: no-repeat", result.Html);
28+
}
29+
30+
[Fact]
31+
public void MoveCssInline_WithSvgXmlBackgroundImage_ShouldPreserveEncodedData()
32+
{
33+
string input = "<style>.my-icon { background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e\"); }</style><span class=\"my-icon\"></span>";
34+
35+
var result = PreMailer.MoveCssInline(input);
36+
37+
Assert.Contains("background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e\")", result.Html);
38+
}
39+
40+
[Fact]
41+
public void MoveCssInline_WithBase64DataWithDoubleEquals_ShouldPreserveEnding()
42+
{
43+
string input = "<style>.pt-logo { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA==); }</style><span class=\"pt-logo\"></span>";
44+
45+
var result = PreMailer.MoveCssInline(input);
46+
47+
Assert.Contains("background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA==)", result.Html);
48+
}
49+
}
50+
}

PreMailer.Net/PreMailer.Net/CssParser.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ private static string CleanUp(string s)
128128
temp = Regex.Replace(temp, "(['\"])([^'\"\\\\]*(?:\\\\.[^'\"\\\\]*)*?)http://([^'\"]*?)\\1", m =>
129129
m.Groups[1].Value + m.Groups[2].Value + "http:" + httpProtocolMarker + m.Groups[3].Value + m.Groups[1].Value);
130130

131-
temp = Regex.Replace(temp, "(data:[^;]+;base64,[^)\"']*?)//([^)\"']*)", m =>
132-
m.Groups[1].Value + dataUrlMarker + m.Groups[2].Value);
131+
temp = Regex.Replace(temp, "(data:[^;]+;base64,)([^)\"']*?)//([^)\"']*)", m =>
132+
m.Groups[1].Value + m.Groups[2].Value + dataUrlMarker + m.Groups[3].Value);
133133

134134
temp = _cssCommentRegex.Replace(temp, "");
135135
temp = _unsupportedAtRuleRegex.Replace(temp, "");
@@ -141,6 +141,9 @@ private static string CleanUp(string s)
141141
temp = temp.Replace(httpProtocolMarker, "//");
142142
temp = temp.Replace(dataUrlMarker, "//");
143143

144+
temp = Regex.Replace(temp, @"url\s*\(\s*(['""]?)data:([^;]+);base64,([^)'""]*)(['""]?)\s*\)", m =>
145+
"url(" + m.Groups[1].Value + "data:" + m.Groups[2].Value + ";base64," + m.Groups[3].Value + m.Groups[4].Value + ")");
146+
144147
return temp;
145148
}
146149

PreMailer.Net/PreMailer.Net/StyleClassApplier.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ private static void SetAttribute(IElement domElement, AttributeToCss attributeTo
4747
value = value.Replace("px", string.Empty);
4848
}
4949

50-
// Quotation marks in CSS is common, but when applied to the style attribute we will use single quotes instead.
51-
if (value.Contains("\""))
50+
if (value.Contains("\"") && !(value.Contains("data:") && value.Contains(";base64,")))
5251
{
5352
value = value.Replace("\"", "'");
5453
}

0 commit comments

Comments
 (0)