From 0121f3e1c50ce3602bca56dbe21efeb9e9840b39 Mon Sep 17 00:00:00 2001
From: Jimmy Pun <11005408@life.hkbu.edu.hk>
Date: Sat, 8 Jul 2023 23:52:48 +0800
Subject: [PATCH 1/2] Use SkiaSharp and SixLabors.ImageSharp to support
 SvgQRCode in .NET6

---
 QRCoder/QRCoder.csproj    | 127 ++++++++------
 QRCoder/SvgQRCode_NET6.cs | 357 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 429 insertions(+), 55 deletions(-)
 create mode 100644 QRCoder/SvgQRCode_NET6.cs

diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj
index d67a7989..ae536617 100644
--- a/QRCoder/QRCoder.csproj
+++ b/QRCoder/QRCoder.csproj
@@ -1,63 +1,80 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
-  <PropertyGroup>
-    <TargetFrameworks>net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows</TargetFrameworks>
-    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
-    <DefineConstants Condition="'$(TargetFramework)' == 'net5.0-windows'">$(DefineConstants);NET5_0_WINDOWS</DefineConstants>
-	  <DefineConstants Condition="'$(TargetFramework)' == 'net6.0-windows'">$(DefineConstants);NET6_0_WINDOWS</DefineConstants>
-    <CheckEolTargetFramework>false</CheckEolTargetFramework>
-    <DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
-  </PropertyGroup>
-
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
-    <NoWarn />
-  </PropertyGroup>
-
-  <PropertyGroup>
-    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-    <PackageId>QRCoder</PackageId>
-    <Version>1.4.3</Version>
-    <Authors>Raffael Herrmann</Authors>
-    <PackageOwners>Raffael Herrmann</PackageOwners>
-    <AssemblyName>QRCoder</AssemblyName>
-    <PackageLicenseExpression>MIT</PackageLicenseExpression>
-    <PackageProjectUrl>https://github.com/codebude/QRCoder/</PackageProjectUrl>
-    <PackageIcon>nuget-icon.png</PackageIcon>
-    <PackageReadmeFile>nuget-readme.md</PackageReadmeFile>
-    <PackageTags>c# csharp qr qrcoder qrcode qr-generator qr-code-generator</PackageTags>
-    <RepositoryUrl>https://github.com/codebude/QRCoder.git</RepositoryUrl>
-    <RepositoryType>git</RepositoryType>
-    <Description>QRCoder is a simple library, written in C#.NET, which enables you to create QR codes.</Description>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <None Include="Assets\nuget-icon.png" Pack="true" PackagePath="\" />
-    <None Include="Assets\nuget-readme.md" Pack="true" PackagePath="\" />
-  </ItemGroup>
-
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net35' or '$(TargetFramework)' == 'net40' ">
-    <Reference Include="PresentationCore" />
-    <Reference Include="PresentationFramework" />
-    <Reference Include="WindowsBase" />
-  </ItemGroup>
-
-  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' ">
-    <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
-  </ItemGroup>
-
-  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net5.0-windows' ">
-    <PackageReference Include="System.Drawing.Common" Version="5.0.3" />
-  </ItemGroup>
+	<PropertyGroup>
+		<TargetFrameworks>net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows</TargetFrameworks>
+		<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
+		<DefineConstants Condition="'$(TargetFramework)' == 'net5.0-windows'">$(DefineConstants);NET5_0_WINDOWS</DefineConstants>
+		<DefineConstants Condition="'$(TargetFramework)' == 'net6.0-windows'">$(DefineConstants);NET6_0_WINDOWS</DefineConstants>
+		<CheckEolTargetFramework>false</CheckEolTargetFramework>
+		<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
+	</PropertyGroup>
+
+	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+		<NoWarn />
+	</PropertyGroup>
+
+	<PropertyGroup>
+		<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+		<PackageId>QRCoder</PackageId>
+		<Version>1.4.3</Version>
+		<Authors>Raffael Herrmann</Authors>
+		<PackageOwners>Raffael Herrmann</PackageOwners>
+		<AssemblyName>QRCoder</AssemblyName>
+		<PackageLicenseExpression>MIT</PackageLicenseExpression>
+		<PackageProjectUrl>https://github.com/codebude/QRCoder/</PackageProjectUrl>
+		<PackageIcon>nuget-icon.png</PackageIcon>
+		<PackageReadmeFile>nuget-readme.md</PackageReadmeFile>
+		<PackageTags>c# csharp qr qrcoder qrcode qr-generator qr-code-generator</PackageTags>
+		<RepositoryUrl>https://github.com/codebude/QRCoder.git</RepositoryUrl>
+		<RepositoryType>git</RepositoryType>
+		<Description>QRCoder is a simple library, written in C#.NET, which enables you to create QR codes.</Description>
+	</PropertyGroup>
+
+	<ItemGroup>
+		<None Include="Assets\nuget-icon.png" Pack="true" PackagePath="\" />
+		<None Include="Assets\nuget-readme.md" Pack="true" PackagePath="\" />
+	</ItemGroup>
+
+	<ItemGroup Condition=" '$(TargetFramework)' == 'net35' or '$(TargetFramework)' == 'net40' ">
+		<Reference Include="PresentationCore" />
+		<Reference Include="PresentationFramework" />
+		<Reference Include="WindowsBase" />
+	</ItemGroup>
+
+	<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' ">
+		<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
+	</ItemGroup>
+
+	<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net5.0-windows' ">
+		<PackageReference Include="System.Drawing.Common" Version="5.0.3" />
+	</ItemGroup>
 	<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0-windows' ">
 		<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
+		<PackageReference Include="SkiaSharp">
+			<Version>2.88.3</Version>
+		</PackageReference>
+		<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
 	</ItemGroup>
+	<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
+		<PackageReference Include="SkiaSharp">
+			<Version>2.88.3</Version>
+		</PackageReference>
+		<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
+	</ItemGroup>
+	<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
+		<PackageReference Include="SkiaSharp">
+			<Version>2.88.3</Version>
+		</PackageReference>
+		<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
+	</ItemGroup>
+
 
-  <PropertyGroup>
-    <FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
-    <AutomaticallyUseReferenceAssemblyPackages Condition=" '$(TargetFramework)' == 'net35' ">false</AutomaticallyUseReferenceAssemblyPackages>
-    <SignAssembly>true</SignAssembly>
-    <AssemblyOriginatorKeyFile>QRCoderStrongName.snk</AssemblyOriginatorKeyFile>
-    <DelaySign>false</DelaySign>
-  </PropertyGroup>
+	<PropertyGroup>
+		<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
+		<AutomaticallyUseReferenceAssemblyPackages Condition=" '$(TargetFramework)' == 'net35' ">false</AutomaticallyUseReferenceAssemblyPackages>
+		<SignAssembly>true</SignAssembly>
+		<AssemblyOriginatorKeyFile>QRCoderStrongName.snk</AssemblyOriginatorKeyFile>
+		<DelaySign>false</DelaySign>
+	</PropertyGroup>
 
 </Project>
diff --git a/QRCoder/SvgQRCode_NET6.cs b/QRCoder/SvgQRCode_NET6.cs
new file mode 100644
index 00000000..bfde11e4
--- /dev/null
+++ b/QRCoder/SvgQRCode_NET6.cs
@@ -0,0 +1,357 @@
+#if NET6_0
+using QRCoder.Extensions;
+using SixLabors.ImageSharp;
+using SkiaSharp;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using static QRCoder.QRCodeGenerator;
+using static QRCoder.SvgQRCode_NET6;
+
+namespace QRCoder
+{
+    /// <summary>
+    /// There class is basically the same as SvgQrCode but removed all System.Drawing
+    /// </summary>
+    public class SvgQRCode_NET6 : AbstractQRCode, IDisposable
+    {
+        /// <summary>
+        /// Constructor without params to be used in COM Objects connections
+        /// </summary>
+        public SvgQRCode_NET6() { }
+        public SvgQRCode_NET6(QRCodeData data) : base(data) { }
+        public string GetGraphic(int pixelsPerModule)
+        {
+            var viewBox = new SKSize(pixelsPerModule * this.QrCodeData.ModuleMatrix.Count, pixelsPerModule * this.QrCodeData.ModuleMatrix.Count);
+            return this.GetGraphic(viewBox, Color.Black.ToHex(), Color.White.ToHex());
+        }
+
+        /// <summary>
+        /// Returns a QR code as SVG string with custom colors, optional quietzone and logo
+        /// </summary>
+        /// <param name="pixelsPerModule">The pixel size each b/w module is drawn</param>
+        /// <param name="darkColor">Color of the dark modules</param>
+        /// <param name="lightColor">Color of the light modules</param>
+        /// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
+        /// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
+        /// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
+        /// <returns>SVG as string</returns>
+        public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        {
+            var offset = drawQuietZones ? 0 : 4;
+            var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule);
+            var viewBox = new SKSize(edgeSize, edgeSize);
+            return this.GetGraphic(viewBox, darkColor.ToHex(), lightColor.ToHex(), drawQuietZones, sizingMode, logo);
+        }
+
+        /// <summary>
+        /// Returns a QR code as SVG string with custom colors (in HEX syntax), optional quietzone and logo
+        /// </summary>
+        /// <param name="pixelsPerModule">The pixel size each b/w module is drawn</param>
+        /// <param name="darkColorHex">The color of the dark/black modules in hex (e.g. #000000) representation</param>
+        /// <param name="lightColorHex">The color of the light/white modules in hex (e.g. #ffffff) representation</param>
+        /// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
+        /// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
+        /// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
+        /// <returns>SVG as string</returns>
+        public string GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        {
+            var offset = drawQuietZones ? 0 : 4;
+            var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule);
+            var viewBox = new SKSize(edgeSize, edgeSize);
+            return this.GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo);
+        }
+
+        /// <summary>
+        /// Returns a QR code as SVG string with custom colors (in HEX syntax), optional quietzone and logo
+        /// </summary>
+        /// <param name="viewBox">The viewbox of the QR code graphic</param>
+        /// <param name="darkColorHex">The color of the dark/black modules in hex (e.g. #000000) representation</param>
+        /// <param name="lightColorHex">The color of the light/white modules in hex (e.g. #ffffff) representation</param>
+        /// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
+        /// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
+        /// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
+        /// <returns>SVG as string</returns>
+        public string GetGraphic(SKSize viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        {
+            int offset = drawQuietZones ? 0 : 4;
+            int drawableModulesCount = this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2);
+            double pixelsPerModule = Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount;
+            double qrSize = drawableModulesCount * pixelsPerModule;
+            string svgSizeAttributes = (sizingMode == SizingMode.WidthHeightAttribute) ? $@"width=""{viewBox.Width}"" height=""{viewBox.Height}""" : $@"viewBox=""0 0 {viewBox.Width} {viewBox.Height}""";
+            ImageAttributes? logoAttr = null;
+            if (logo != null)
+                logoAttr = GetLogoAttributes(logo, viewBox);
+
+            // Merge horizontal rectangles
+            int[,] matrix = new int[drawableModulesCount, drawableModulesCount];
+            for (int yi = 0; yi < drawableModulesCount; yi += 1)
+            {
+                BitArray bitArray = this.QrCodeData.ModuleMatrix[yi + offset];
+
+                int x0 = -1;
+                int xL = 0;
+                for (int xi = 0; xi < drawableModulesCount; xi += 1)
+                {
+                    matrix[yi, xi] = 0;
+                    if (bitArray[xi + offset] && (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo((xi + offset) * pixelsPerModule, (yi + offset) * pixelsPerModule, logoAttr, pixelsPerModule)))
+                    {
+                        if (x0 == -1)
+                        {
+                            x0 = xi;
+                        }
+                        xL += 1;
+                    }
+                    else
+                    {
+                        if (xL > 0)
+                        {
+                            matrix[yi, x0] = xL;
+                            x0 = -1;
+                            xL = 0;
+                        }
+                    }
+                }
+
+                if (xL > 0)
+                {
+                    matrix[yi, x0] = xL;
+                }
+            }
+
+            StringBuilder svgFile = new StringBuilder($@"<svg version=""1.1"" baseProfile=""full"" shape-rendering=""crispEdges"" {svgSizeAttributes} xmlns=""http://www.w3.org/2000/svg"" xmlns:xlink=""http://www.w3.org/1999/xlink"">");
+            svgFile.AppendLine($@"<rect x=""0"" y=""0"" width=""{CleanSvgVal(qrSize)}"" height=""{CleanSvgVal(qrSize)}"" fill=""{lightColorHex}"" />");
+            for (int yi = 0; yi < drawableModulesCount; yi += 1)
+            {
+                double y = yi * pixelsPerModule;
+                for (int xi = 0; xi < drawableModulesCount; xi += 1)
+                {
+                    int xL = matrix[yi, xi];
+                    if (xL > 0)
+                    {
+                        // Merge vertical rectangles
+                        int yL = 1;
+                        for (int y2 = yi + 1; y2 < drawableModulesCount; y2 += 1)
+                        {
+                            if (matrix[y2, xi] == xL)
+                            {
+                                matrix[y2, xi] = 0;
+                                yL += 1;
+                            }
+                            else
+                            {
+                                break;
+                            }
+                        }
+
+                        // Output SVG rectangles
+                        double x = xi * pixelsPerModule;
+                        if (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(x, y, logoAttr, pixelsPerModule))
+                            svgFile.AppendLine($@"<rect x=""{CleanSvgVal(x)}"" y=""{CleanSvgVal(y)}"" width=""{CleanSvgVal(xL * pixelsPerModule)}"" height=""{CleanSvgVal(yL * pixelsPerModule)}"" fill=""{darkColorHex}"" />");
+                    }
+                }
+            }
+
+            //Render logo, if set
+            if (logo != null)
+            {
+                if (!logo.IsEmbedded())
+                {
+                    svgFile.AppendLine($@"<svg width=""100%"" height=""100%"" version=""1.1"" xmlns = ""http://www.w3.org/2000/svg"">");
+                    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()}"" />");
+                    svgFile.AppendLine(@"</svg>");
+                }
+                else
+                {
+                    var rawLogo = (string)logo.GetRawLogo();
+                    var svg = System.Xml.Linq.XDocument.Parse(rawLogo);
+                    svg.Root.SetAttributeValue("x", CleanSvgVal(logoAttr.Value.X));
+                    svg.Root.SetAttributeValue("y", CleanSvgVal(logoAttr.Value.Y));
+                    svg.Root.SetAttributeValue("width", CleanSvgVal(logoAttr.Value.Width));
+                    svg.Root.SetAttributeValue("height", CleanSvgVal(logoAttr.Value.Height));
+                    svg.Root.SetAttributeValue("shape-rendering", "geometricPrecision");
+                    svgFile.AppendLine(svg.ToString(System.Xml.Linq.SaveOptions.DisableFormatting).Replace("svg:", ""));
+                }
+            }
+
+            svgFile.Append(@"</svg>");
+            return svgFile.ToString();
+        }
+
+        private bool IsBlockedByLogo(double x, double y, ImageAttributes? attr, double pixelPerModule)
+        {
+            return x + pixelPerModule >= attr.Value.X && x <= attr.Value.X + attr.Value.Width && y + pixelPerModule >= attr.Value.Y && y <= attr.Value.Y + attr.Value.Height;
+        }
+
+        private ImageAttributes GetLogoAttributes(SvgLogo_NET6 logo, SKSize viewBox)
+        {
+            var imgWidth = logo.GetIconSizePercent() / 100d * viewBox.Width;
+            var imgHeight = logo.GetIconSizePercent() / 100d * viewBox.Height;
+            var imgPosX = viewBox.Width / 2d - imgWidth / 2d;
+            var imgPosY = viewBox.Height / 2d - imgHeight / 2d;
+            return new ImageAttributes()
+            {
+                Width = imgWidth,
+                Height = imgHeight,
+                X = imgPosX,
+                Y = imgPosY
+            };
+        }
+
+        private struct ImageAttributes
+        {
+            public double Width;
+            public double Height;
+            public double X;
+            public double Y;
+        }
+
+        private string CleanSvgVal(double input)
+        {
+            //Clean double values for international use/formats
+            //We use explicitly "G15" to avoid differences between .NET full and Core platforms
+            //https://stackoverflow.com/questions/64898117/tostring-has-a-different-behavior-between-net-462-and-net-core-3-1
+            return input.ToString("G15", System.Globalization.CultureInfo.InvariantCulture);
+        }
+
+        /// <summary>
+        /// Mode of sizing attribution on svg root node
+        /// </summary>
+        public enum SizingMode
+        {
+            WidthHeightAttribute,
+            ViewBoxAttribute
+        }
+
+        /// <summary>
+        /// Represents a logo graphic that can be rendered on a SvgQRCode_NET6
+        /// </summary>
+        public class SvgLogo_NET6
+        {
+            private string _logoData;
+            private MediaType _mediaType;
+            private int _iconSizePercent;
+            private bool _fillLogoBackground;
+            private object _logoRaw;
+            private bool _isEmbedded;
+
+
+            /// <summary>
+            /// Create a logo object to be used in SvgQRCode_NET6 renderer
+            /// </summary>
+            /// <param name="iconRasterized">Logo to be rendered as Bitmap/rasterized graphic</param>
+            /// <param name="iconSizePercent">Degree of percentage coverage of the QR code by the logo</param>
+            /// <param name="fillLogoBackground">If true, the background behind the logo will be cleaned</param>
+            public SvgLogo_NET6(Image iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true)
+            {
+                _iconSizePercent = iconSizePercent;
+                using (var ms = new System.IO.MemoryStream())
+                {
+                    iconRasterized.SaveAsPng(ms);
+                    _logoData = Convert.ToBase64String(ms.GetBuffer(), Base64FormattingOptions.None);
+                }
+
+                _mediaType = MediaType.PNG;
+                _fillLogoBackground = fillLogoBackground;
+                _logoRaw = iconRasterized;
+                _isEmbedded = false;
+            }
+
+            /// <summary>
+            /// Create a logo object to be used in SvgQRCode_NET6 renderer
+            /// </summary>
+            /// <param name="iconVectorized">Logo to be rendered as SVG/vectorized graphic/string</param>
+            /// <param name="iconSizePercent">Degree of percentage coverage of the QR code by the logo</param>
+            /// <param name="fillLogoBackground">If true, the background behind the logo will be cleaned</param>
+            /// <param name="iconEmbedded">If true, the logo will embedded as native svg instead of embedding it as image-tag</param>
+            public SvgLogo_NET6(string iconVectorized, int iconSizePercent = 15, bool fillLogoBackground = true, bool iconEmbedded = true)
+            {
+                _iconSizePercent = iconSizePercent;
+                _logoData = Convert.ToBase64String(Encoding.UTF8.GetBytes(iconVectorized), Base64FormattingOptions.None);
+                _mediaType = MediaType.SVG;
+                _fillLogoBackground = fillLogoBackground;
+                _logoRaw = iconVectorized;
+                _isEmbedded = iconEmbedded;
+            }
+
+            /// <summary>
+            /// Returns the raw logo's data
+            /// </summary>
+            /// <returns></returns>
+            public object GetRawLogo()
+            {
+                return _logoRaw;
+            }
+
+            /// <summary>
+            /// Defines, if the logo shall be natively embedded.
+            /// true=native svg embedding, false=embedding via image-tag
+            /// </summary>
+            /// <returns></returns>
+            public bool IsEmbedded()
+            {
+                return _isEmbedded;
+            }
+
+            /// <summary>
+            /// Returns the media type of the logo
+            /// </summary>
+            /// <returns></returns>
+            public MediaType GetMediaType()
+            {
+                return _mediaType;
+            }
+
+            /// <summary>
+            /// Returns the logo as data-uri
+            /// </summary>
+            /// <returns></returns>
+            public string GetDataUri()
+            {
+                return $"data:{_mediaType.GetStringValue()};base64,{_logoData}";
+            }
+
+            /// <summary>
+            /// Returns how much of the QR code should be covered by the logo (in percent)
+            /// </summary>
+            /// <returns></returns>
+            public int GetIconSizePercent()
+            {
+                return _iconSizePercent;
+            }
+
+            /// <summary>
+            /// Returns if the background of the logo should be cleaned (no QR modules will be rendered behind the logo)
+            /// </summary>
+            /// <returns></returns>
+            public bool FillLogoBackground()
+            {
+                return _fillLogoBackground;
+            }
+
+            /// <summary>
+            /// Media types for SvgLogo_NET6s
+            /// </summary>
+            public enum MediaType : int
+            {
+                [StringValue("image/png")]
+                PNG = 0,
+                [StringValue("image/svg+xml")]
+                SVG = 1
+            }
+        }
+    }
+    public static class SvgQRCode_NET6Helper
+    {
+        public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        {
+            using (var qrGenerator = new QRCodeGenerator())
+            using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
+            using (var qrCode = new SvgQRCode_NET6(qrCodeData))
+                return qrCode.GetGraphic(pixelsPerModule, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo);
+        }
+    }
+}
+#endif
\ No newline at end of file

From a73ab498a18bf9bbedd4667a1459c41d38ea220d Mon Sep 17 00:00:00 2001
From: Jimmy Pun <11005408@life.hkbu.edu.hk>
Date: Tue, 11 Jul 2023 09:18:48 +0800
Subject: [PATCH 2/2] Remove ImageSharp and use SkiaSharp only because of
 license issue

---
 QRCoder/QRCoder.csproj    | 12 +++---------
 QRCoder/SvgQRCode_NET6.cs | 39 +++++++++++++++++++++++++++++++++------
 2 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj
index ae536617..445e4aa8 100644
--- a/QRCoder/QRCoder.csproj
+++ b/QRCoder/QRCoder.csproj
@@ -53,22 +53,16 @@
 		<PackageReference Include="SkiaSharp">
 			<Version>2.88.3</Version>
 		</PackageReference>
-		<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
 	</ItemGroup>
-	<ItemGroup Condition="'$(TargetFramework)' == 'net5.0-windows'">
+	<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
 		<PackageReference Include="SkiaSharp">
 			<Version>2.88.3</Version>
 		</PackageReference>
-		<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
-	</ItemGroup>
-	<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
-		<PackageReference Include="SkiaSharp">
-			<Version>2.88.3</Version>
-		</PackageReference>
-		<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
 	</ItemGroup>
 
 
+
+
 	<PropertyGroup>
 		<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
 		<AutomaticallyUseReferenceAssemblyPackages Condition=" '$(TargetFramework)' == 'net35' ">false</AutomaticallyUseReferenceAssemblyPackages>
diff --git a/QRCoder/SvgQRCode_NET6.cs b/QRCoder/SvgQRCode_NET6.cs
index bfde11e4..a8a55325 100644
--- a/QRCoder/SvgQRCode_NET6.cs
+++ b/QRCoder/SvgQRCode_NET6.cs
@@ -1,6 +1,5 @@
 #if NET6_0
 using QRCoder.Extensions;
-using SixLabors.ImageSharp;
 using SkiaSharp;
 using System;
 using System.Collections;
@@ -25,7 +24,7 @@ public SvgQRCode_NET6(QRCodeData data) : base(data) { }
         public string GetGraphic(int pixelsPerModule)
         {
             var viewBox = new SKSize(pixelsPerModule * this.QrCodeData.ModuleMatrix.Count, pixelsPerModule * this.QrCodeData.ModuleMatrix.Count);
-            return this.GetGraphic(viewBox, Color.Black.ToHex(), Color.White.ToHex());
+            return this.GetGraphic(viewBox, SKColors.Black, SKColors.White);
         }
 
         /// <summary>
@@ -38,12 +37,12 @@ public string GetGraphic(int pixelsPerModule)
         /// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
         /// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
         /// <returns>SVG as string</returns>
-        public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        public string GetGraphic(int pixelsPerModule, SKColor darkColor, SKColor lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
         {
             var offset = drawQuietZones ? 0 : 4;
             var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule);
             var viewBox = new SKSize(edgeSize, edgeSize);
-            return this.GetGraphic(viewBox, darkColor.ToHex(), lightColor.ToHex(), drawQuietZones, sizingMode, logo);
+            return this.GetGraphic(viewBox, darkColor.ToString(), lightColor.ToString(), drawQuietZones, sizingMode, logo);
         }
 
         /// <summary>
@@ -180,6 +179,34 @@ public string GetGraphic(SKSize viewBox, string darkColorHex, string lightColorH
             return svgFile.ToString();
         }
 
+        /// <summary>
+        /// Returns a QR code as SVG string with optional quietzone and logo
+        /// </summary>
+        /// <param name="viewBox">The viewbox of the QR code graphic</param>
+        /// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
+        /// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
+        /// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
+        /// <returns>SVG as string</returns>
+        public string GetGraphic(SKSize viewBox, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        {
+            return this.GetGraphic(viewBox, SKColors.Black, SKColors.White, drawQuietZones, sizingMode, logo);
+        }
+
+        /// <summary>
+        /// Returns a QR code as SVG string with custom colors and optional quietzone and logo
+        /// </summary>
+        /// <param name="viewBox">The viewbox of the QR code graphic</param>
+        /// <param name="darkColor">Color of the dark modules</param>
+        /// <param name="lightColor">Color of the light modules</param>
+        /// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
+        /// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
+        /// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
+        /// <returns>SVG as string</returns>
+        public string GetGraphic(SKSize viewBox, SKColor darkColor, SKColor lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo_NET6 logo = null)
+        {
+            return this.GetGraphic(viewBox, darkColor.ToString(), lightColor.ToString(), drawQuietZones, sizingMode, logo);
+        }
+
         private bool IsBlockedByLogo(double x, double y, ImageAttributes? attr, double pixelPerModule)
         {
             return x + pixelPerModule >= attr.Value.X && x <= attr.Value.X + attr.Value.Width && y + pixelPerModule >= attr.Value.Y && y <= attr.Value.Y + attr.Value.Height;
@@ -244,12 +271,12 @@ public class SvgLogo_NET6
             /// <param name="iconRasterized">Logo to be rendered as Bitmap/rasterized graphic</param>
             /// <param name="iconSizePercent">Degree of percentage coverage of the QR code by the logo</param>
             /// <param name="fillLogoBackground">If true, the background behind the logo will be cleaned</param>
-            public SvgLogo_NET6(Image iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true)
+            public SvgLogo_NET6(SKImage iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true)
             {
                 _iconSizePercent = iconSizePercent;
                 using (var ms = new System.IO.MemoryStream())
                 {
-                    iconRasterized.SaveAsPng(ms);
+                    iconRasterized.Encode(SKEncodedImageFormat.Png, 100).SaveTo(ms);
                     _logoData = Convert.ToBase64String(ms.GetBuffer(), Base64FormattingOptions.None);
                 }