Skip to content

Commit 9749532

Browse files
committed
Improved SVG embedding
1 parent 8395725 commit 9749532

File tree

2 files changed

+29
-17
lines changed

2 files changed

+29
-17
lines changed

QRCoder/SvgQRCode.cs

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -179,33 +179,31 @@ public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex
179179
// Output SVG rectangles
180180
double x = xi * pixelsPerModule;
181181
if (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(x, y, logoAttr, pixelsPerModule))
182-
svgFile.AppendLine($@"<rect x=""{CleanSvgVal(x)}"" y=""{CleanSvgVal(y)}"" width=""{CleanSvgVal(xL * pixelsPerModule)}"" height=""{CleanSvgVal(yL * pixelsPerModule)}"" fill=""{darkColorHex}"" />");
183-
182+
svgFile.AppendLine($@"<rect x=""{CleanSvgVal(x)}"" y=""{CleanSvgVal(y)}"" width=""{CleanSvgVal(xL * pixelsPerModule)}"" height=""{CleanSvgVal(yL * pixelsPerModule)}"" fill=""{darkColorHex}"" />");
184183
}
185184
}
186185
}
187186

188187
//Render logo, if set
189188
if (logo != null)
190-
{
191-
192-
if (logo.GetMediaType() == SvgLogo.MediaType.PNG)
189+
{
190+
if (logo.GetMediaType() == SvgLogo.MediaType.PNG || logo.IsEmbedded())
193191
{
194192
svgFile.AppendLine($@"<svg width=""100%"" height=""100%"" version=""1.1"" xmlns = ""http://www.w3.org/2000/svg"">");
195193
svgFile.AppendLine($@"<image x=""{CleanSvgVal(logoAttr.Value.X)}"" y=""{CleanSvgVal(logoAttr.Value.Y)}"" width=""{CleanSvgVal(logoAttr.Value.Width)}"" height=""{CleanSvgVal(logoAttr.Value.Height)}"" xlink:href=""{logo.GetDataUri()}"" />");
194+
svgFile.AppendLine(@"</svg>");
196195
}
197196
else if (logo.GetMediaType() == SvgLogo.MediaType.SVG)
198197
{
199-
svgFile.AppendLine($@"<svg x=""{CleanSvgVal(logoAttr.Value.X)}"" y=""{CleanSvgVal(logoAttr.Value.Y)}"" width=""{CleanSvgVal(logoAttr.Value.Width)}"" height=""{CleanSvgVal(logoAttr.Value.Height)}"" version=""1.1"" xmlns = ""http://www.w3.org/2000/svg"">");
200-
var rawLogo = (string)logo.GetRawLogo();
201-
//Remove some attributes from logo, because it would lead to wrong sizing inside our svg wrapper
202-
new List<string>() { "width", "height", "x", "y" }.ForEach(attr =>
203-
{
204-
rawLogo = Regex.Replace(rawLogo, $@"(?!=<svg[^>]*?) +{attr}=(""[^""]+""|'[^']+')(?=[^>]*>)", "");
205-
});
206-
svgFile.Append(rawLogo);
198+
var rawLogo = (string)logo.GetRawLogo();
199+
var svg = System.Xml.Linq.XDocument.Parse(rawLogo);
200+
svg.Root.SetAttributeValue("x", CleanSvgVal(logoAttr.Value.X));
201+
svg.Root.SetAttributeValue("y", CleanSvgVal(logoAttr.Value.Y));
202+
svg.Root.SetAttributeValue("width", CleanSvgVal(logoAttr.Value.Width));
203+
svg.Root.SetAttributeValue("height", CleanSvgVal(logoAttr.Value.Height));
204+
svg.Root.SetAttributeValue("shape-rendering", "geometricPrecision");
205+
svgFile.AppendLine(svg.ToString(System.Xml.Linq.SaveOptions.DisableFormatting).Replace("svg:", ""));
207206
}
208-
svgFile.AppendLine(@"</svg>");
209207
}
210208

211209
svgFile.Append(@"</svg>");
@@ -267,8 +265,9 @@ public class SvgLogo
267265
private int _iconSizePercent;
268266
private bool _fillLogoBackground;
269267
private object _logoRaw;
268+
private bool _isEmbedded;
269+
270270

271-
272271
/// <summary>
273272
/// Create a logo object to be used in SvgQRCode renderer
274273
/// </summary>
@@ -289,6 +288,7 @@ public SvgLogo(Bitmap iconRasterized, int iconSizePercent = 15, bool fillLogoBac
289288
_mediaType = MediaType.PNG;
290289
_fillLogoBackground = fillLogoBackground;
291290
_logoRaw = iconRasterized;
291+
_isEmbedded = false;
292292
}
293293

294294
/// <summary>
@@ -297,13 +297,15 @@ public SvgLogo(Bitmap iconRasterized, int iconSizePercent = 15, bool fillLogoBac
297297
/// <param name="iconVectorized">Logo to be rendered as SVG/vectorized graphic/string</param>
298298
/// <param name="iconSizePercent">Degree of percentage coverage of the QR code by the logo</param>
299299
/// <param name="fillLogoBackground">If true, the background behind the logo will be cleaned</param>
300-
public SvgLogo(string iconVectorized, int iconSizePercent = 15, bool fillLogoBackground = true)
300+
/// <param name="iconEmbedded">If true, the logo will be expressed as <image>-tag instead of embedding the svg</param>
301+
public SvgLogo(string iconVectorized, int iconSizePercent = 15, bool fillLogoBackground = true, bool iconEmbedded = false)
301302
{
302303
_iconSizePercent = iconSizePercent;
303304
_logoData = Convert.ToBase64String(Encoding.UTF8.GetBytes(iconVectorized), Base64FormattingOptions.None);
304305
_mediaType = MediaType.SVG;
305306
_fillLogoBackground = fillLogoBackground;
306307
_logoRaw = iconVectorized;
308+
_isEmbedded = iconEmbedded;
307309
}
308310

309311
/// <summary>
@@ -315,6 +317,16 @@ public object GetRawLogo()
315317
return _logoRaw;
316318
}
317319

320+
/// <summary>
321+
/// Defines, if the logo shall be natively embedded.
322+
/// true=native svg embedding, false=embedding via image-tag
323+
/// </summary>
324+
/// <returns></returns>
325+
public bool IsEmbedded()
326+
{
327+
return _isEmbedded;
328+
}
329+
318330
/// <summary>
319331
/// Returns the media type of the logo
320332
/// </summary>

QRCoderTests/SvgQRCodeRendererTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public void can_render_svg_qrcode_with_svg_logo()
113113
var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj);
114114

115115
var result = HelperFunctions.StringToHash(svg);
116-
result.ShouldBe("71f461136fdbe2ab85902d23ad2d7eb8");
116+
result.ShouldBe("592271ef77406c0074a3005f78130906");
117117
}
118118

119119
[Fact]

0 commit comments

Comments
 (0)