From 752454161d54e9fca22f7cb8bbf34461550e45bc Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 13 Apr 2024 14:52:43 -0400 Subject: [PATCH 1/7] Support Base64QRCode on Linux --- QRCoder/Base64QRCode.cs | 104 +++++++++++++++++----- QRCoder/QRCoder.csproj | 2 +- QRCoderTests/Base64QRCodeRendererTests.cs | 87 ++++++++++++++++++ QRCoderTests/Helpers/HelperFunctions.cs | 4 +- QRCoderTests/QRCoderTests.csproj | 1 + 5 files changed, 172 insertions(+), 26 deletions(-) create mode 100644 QRCoderTests/Base64QRCodeRendererTests.cs diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index 539af073..711785c5 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -1,33 +1,25 @@ -#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS +#if !NETSTANDARD1_3 using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; +using System.Runtime.InteropServices; using static QRCoder.Base64QRCode; using static QRCoder.QRCodeGenerator; namespace QRCoder { -#if NET6_0_WINDOWS - [System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif public class Base64QRCode : AbstractQRCode, IDisposable { - private QRCode qr; - /// /// Constructor without params to be used in COM Objects connections /// - public Base64QRCode() { - qr = new QRCode(); - } - - public Base64QRCode(QRCodeData data) : base(data) { - qr = new QRCode(data); + public Base64QRCode() + { } - public override void SetQRCodeData(QRCodeData data) { - this.qr.SetQRCodeData(data); + public Base64QRCode(QRCodeData data) : base(data) + { } public string GetGraphic(int pixelsPerModule) @@ -43,16 +35,73 @@ public string GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string li public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, ImageType imgType = ImageType.Png) { - var base64 = string.Empty; - using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones)) + if (imgType == ImageType.Png) { - base64 = BitmapToBase64(bmp, imgType); + var pngCoder = new PngByteQRCode(QrCodeData); + + byte[] pngData; + if (darkColor == Color.Black && lightColor == Color.White) + { + pngData = pngCoder.GetGraphic(pixelsPerModule, drawQuietZones); + } + else + { + byte[] darkColorBytes; + byte[] lightColorBytes; + if (darkColor.A != 255 || lightColor.A != 255) + { + darkColorBytes = new byte[] { darkColor.R, darkColor.G, darkColor.B, darkColor.A }; + lightColorBytes = new byte[] { lightColor.R, lightColor.G, lightColor.B, lightColor.A }; + } + else + { + darkColorBytes = new byte[] { darkColor.R, darkColor.G, darkColor.B }; + lightColorBytes = new byte[] { lightColor.R, lightColor.G, lightColor.B }; + } + pngData = pngCoder.GetGraphic(pixelsPerModule, darkColorBytes, lightColorBytes, drawQuietZones); + } + + return Convert.ToBase64String(pngData, Base64FormattingOptions.None); + + byte[] ToRgba(Color color) + { + return new byte[] { color.R, color.G, color.B, color.A }; + } } - return base64; + +#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS + if ( +#if NET6_0_OR_GREATER + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) +#else + true +#endif + ) + { + var qr = new QRCode(QrCodeData); + var base64 = string.Empty; + using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones)) + { + base64 = BitmapToBase64(bmp, imgType); + } + return base64; + } + else + { + throw new PlatformNotSupportedException("The specified image type is not supported on this platform."); + } +#else + throw new PlatformNotSupportedException("The specified image type is not supported on this platform."); +#endif } +#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS +#if NET6_0_WINDOWS + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png) { + var qr = new QRCode(QrCodeData); var base64 = string.Empty; using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones)) { @@ -60,13 +109,18 @@ public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, } return base64; } +#endif - +#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS +#if NET6_0_WINDOWS + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif private string BitmapToBase64(Bitmap bmp, ImageType imgType) { var base64 = string.Empty; ImageFormat iFormat; - switch (imgType) { + switch (imgType) + { case ImageType.Png: iFormat = ImageFormat.Png; break; @@ -87,19 +141,23 @@ private string BitmapToBase64(Bitmap bmp, ImageType imgType) } return base64; } +#endif public enum ImageType { +#if NET6_0_WINDOWS + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif Gif, +#if NET6_0_WINDOWS + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif Jpeg, Png } } -#if NET6_0_WINDOWS - [System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif public static class Base64QRCodeHelper { public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, ImageType imgType = ImageType.Png) diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index d67a7989..fe44a53f 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -4,7 +4,7 @@ net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows false $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS false true diff --git a/QRCoderTests/Base64QRCodeRendererTests.cs b/QRCoderTests/Base64QRCodeRendererTests.cs new file mode 100644 index 00000000..e53a1c12 --- /dev/null +++ b/QRCoderTests/Base64QRCodeRendererTests.cs @@ -0,0 +1,87 @@ +#if !NETCOREAPP1_1 +using Xunit; +using QRCoder; +using Shouldly; +using QRCoderTests.Helpers.XUnitExtenstions; +using QRCoderTests.Helpers; +using System; + +using System.Drawing; +using System.IO; +using System.Security.Policy; + +namespace QRCoderTests +{ + /**************************************************************************************************** + * Note: Test cases compare the outcome visually even if it's slower than a byte-wise compare. + * This is necessary, because the Deflate implementation differs on the different target + * platforms and thus the outcome, even if visually identical, differs. Thus only a visual + * test method makes sense. In addition bytewise differences shouldn't be important, if the + * visual outcome is identical and thus the qr code is identical/scannable. + ****************************************************************************************************/ + public class Base64QRCodeRendererTests + { + private readonly QRCodeData data; + + public Base64QRCodeRendererTests() + { + var gen = new QRCodeGenerator(); + data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + } + + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_blackwhite() + { + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5); + var base64QRCode = new Base64QRCode(data).GetGraphic(5); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } + + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_noquietzones() + { + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, false); + var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Black, Color.White, false); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } + + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_color() + { + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 }); + var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Red, Color.Blue); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } + + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_transparent() + { + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 0, 255, 0, 255 }, new byte[] { 255, 255, 255, 0 }); + var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Lime, Color.Transparent); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } + +#if NETFRAMEWORK || NETCOREAPP2_0 || NET5_0 || NET6_0_WINDOWS + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_jpeg() + { + var ms = new MemoryStream(); + using (var bitmap = new QRCode(data).GetGraphic(5, Color.Black, Color.White, true)) + { + bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); + } + ms.Position = 0; + var jpgString = Convert.ToBase64String(ms.ToArray()); + var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Black, Color.White, true, Base64QRCode.ImageType.Jpeg); + base64QRCode.ShouldBe(jpgString); + } +#endif + } +} + +#endif diff --git a/QRCoderTests/Helpers/HelperFunctions.cs b/QRCoderTests/Helpers/HelperFunctions.cs index 5fd9aa13..1f337865 100644 --- a/QRCoderTests/Helpers/HelperFunctions.cs +++ b/QRCoderTests/Helpers/HelperFunctions.cs @@ -5,7 +5,7 @@ #if !NETCOREAPP1_1 using System.Drawing; #endif -#if NETFRAMEWORK || NET5_0_WINDOWS +#if NETFRAMEWORK || NET5_0_WINDOWS || NET6_0_WINDOWS using SW = System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -17,7 +17,7 @@ namespace QRCoderTests.Helpers public static class HelperFunctions { -#if NETFRAMEWORK || NET5_0_WINDOWS +#if NETFRAMEWORK || NET5_0_WINDOWS || NET6_0_WINDOWS public static BitmapSource ToBitmapSource(DrawingImage source) { DrawingVisual drawingVisual = new DrawingVisual(); diff --git a/QRCoderTests/QRCoderTests.csproj b/QRCoderTests/QRCoderTests.csproj index 3f1aa274..e5c01ba9 100644 --- a/QRCoderTests/QRCoderTests.csproj +++ b/QRCoderTests/QRCoderTests.csproj @@ -4,6 +4,7 @@ true true $(DefineConstants);NET5_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS false true false From 46dbbe7b3b7fbd858ead56c7283da1a3fb7f771c Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 13 Apr 2024 14:57:09 -0400 Subject: [PATCH 2/7] Update --- QRCoder/Base64QRCode.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index 711785c5..2bc0884b 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -14,12 +14,10 @@ public class Base64QRCode : AbstractQRCode, IDisposable /// /// Constructor without params to be used in COM Objects connections /// - public Base64QRCode() - { + public Base64QRCode() { } - public Base64QRCode(QRCodeData data) : base(data) - { + public Base64QRCode(QRCodeData data) : base(data) { } public string GetGraphic(int pixelsPerModule) From 1fc85ef687768c633ed6bff56a42b8f8da81de17 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 13 Apr 2024 14:57:57 -0400 Subject: [PATCH 3/7] Update --- QRCoder/Base64QRCode.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index 2bc0884b..073e26ee 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -60,11 +60,6 @@ public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, } return Convert.ToBase64String(pngData, Base64FormattingOptions.None); - - byte[] ToRgba(Color color) - { - return new byte[] { color.R, color.G, color.B, color.A }; - } } #if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS From 833e47ed8aab8588fbae7275e947e3f6d897a993 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 13 Apr 2024 14:59:13 -0400 Subject: [PATCH 4/7] Update --- QRCoder/Base64QRCode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index 073e26ee..e67af09b 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -112,8 +112,7 @@ private string BitmapToBase64(Bitmap bmp, ImageType imgType) { var base64 = string.Empty; ImageFormat iFormat; - switch (imgType) - { + switch (imgType) { case ImageType.Png: iFormat = ImageFormat.Png; break; From 0471429f7c5e02569ec33936fc352b5373b38b20 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 13 Apr 2024 15:00:17 -0400 Subject: [PATCH 5/7] update --- QRCoder/QRCoder.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index fe44a53f..9b37e4c6 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -4,7 +4,7 @@ net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows false $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS false true From c9b102b2a6b0b3d1154967708b15f816b4a040b4 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Sat, 13 Apr 2024 15:01:41 -0400 Subject: [PATCH 6/7] update --- QRCoderTests/Base64QRCodeRendererTests.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/QRCoderTests/Base64QRCodeRendererTests.cs b/QRCoderTests/Base64QRCodeRendererTests.cs index e53a1c12..58011fba 100644 --- a/QRCoderTests/Base64QRCodeRendererTests.cs +++ b/QRCoderTests/Base64QRCodeRendererTests.cs @@ -1,24 +1,14 @@ #if !NETCOREAPP1_1 -using Xunit; using QRCoder; -using Shouldly; using QRCoderTests.Helpers.XUnitExtenstions; -using QRCoderTests.Helpers; +using Shouldly; using System; - using System.Drawing; using System.IO; -using System.Security.Policy; +using Xunit; namespace QRCoderTests { - /**************************************************************************************************** - * Note: Test cases compare the outcome visually even if it's slower than a byte-wise compare. - * This is necessary, because the Deflate implementation differs on the different target - * platforms and thus the outcome, even if visually identical, differs. Thus only a visual - * test method makes sense. In addition bytewise differences shouldn't be important, if the - * visual outcome is identical and thus the qr code is identical/scannable. - ****************************************************************************************************/ public class Base64QRCodeRendererTests { private readonly QRCodeData data; From 70b2855d9154f6823f15378b88e516092e0df299 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Wed, 24 Apr 2024 18:36:57 -0400 Subject: [PATCH 7/7] Update --- QRCoder/Base64QRCode.cs | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index e67af09b..0c9d119d 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -63,33 +63,22 @@ public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, } #if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS - if ( -#if NET6_0_OR_GREATER - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) -#else - true -#endif - ) - { - var qr = new QRCode(QrCodeData); - var base64 = string.Empty; - using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones)) - { - base64 = BitmapToBase64(bmp, imgType); - } - return base64; - } - else +#pragma warning disable CA1416 // Validate platform compatibility + var qr = new QRCode(QrCodeData); + var base64 = string.Empty; + using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones)) { - throw new PlatformNotSupportedException("The specified image type is not supported on this platform."); + base64 = BitmapToBase64(bmp, imgType); } + return base64; +#pragma warning restore CA1416 // Validate platform compatibility #else - throw new PlatformNotSupportedException("The specified image type is not supported on this platform."); + throw new PlatformNotSupportedException("Only the PNG image type is supported on this platform."); #endif } #if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS -#if NET6_0_WINDOWS +#if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png) @@ -105,7 +94,7 @@ public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, #endif #if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS -#if NET6_0_WINDOWS +#if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif private string BitmapToBase64(Bitmap bmp, ImageType imgType) @@ -137,11 +126,11 @@ private string BitmapToBase64(Bitmap bmp, ImageType imgType) public enum ImageType { -#if NET6_0_WINDOWS +#if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif Gif, -#if NET6_0_WINDOWS +#if NET6_0_OR_GREATER [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif Jpeg,