From 70959768a45a11740e7dfae30b3949deddfc0ca1 Mon Sep 17 00:00:00 2001 From: Gunnar Liljas Date: Wed, 2 Nov 2022 01:23:58 +0100 Subject: [PATCH] Added UTF8 (block character) support --- QRCoder/Utf8QRCode.cs | 77 +++++++++++++++++++++++++++ QRCoderTests/Utf8CodeRendererTests.cs | 74 +++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 QRCoder/Utf8QRCode.cs create mode 100644 QRCoderTests/Utf8CodeRendererTests.cs diff --git a/QRCoder/Utf8QRCode.cs b/QRCoder/Utf8QRCode.cs new file mode 100644 index 00000000..38883c4d --- /dev/null +++ b/QRCoder/Utf8QRCode.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Text; +using static QRCoder.QRCodeGenerator; + +namespace QRCoder +{ + public class Utf8QRCode : AbstractQRCode, IDisposable + { + /// + /// Constructor without params to be used in COM Objects connections + /// + public Utf8QRCode() { } + + public Utf8QRCode(QRCodeData data) : base(data) { } + + + /// + /// Returns a string that contains the resulting QR code, represented by the UTF8 chars SPACE, FULL BLOCK, UPPER HALF BLOCK and LOWER HALF BLOCK. + /// + /// If true, the mandatory space around the QR code is included + /// End of line separator. (Default: \n) + /// If true, the returned QR code will be drawn inverted + public string GetGraphic(bool drawQuietZones = true, string endOfLine = "\n", bool invert = false) + { + return string.Join(endOfLine, GetLineByLineGraphic(drawQuietZones, invert)); + } + + private IEnumerable GetLineByLineGraphic(bool drawQuietZones = true, bool invert = false) + { + var quietZonesModifier = (drawQuietZones ? 0 : 8); + var quietZonesOffset = (int)(quietZonesModifier * 0.5); + var sideLength = QrCodeData.ModuleMatrix.Count - quietZonesModifier; + + var lineBuilder = new StringBuilder(sideLength); + + for (var y = 0; y < sideLength; y += 2) + { + for (var x = 0; x < sideLength; x++) + { + var module1 = QrCodeData.ModuleMatrix[x + quietZonesOffset][y + quietZonesOffset] ^ invert; + var module2 = (y + quietZonesOffset + 1 < QrCodeData.ModuleMatrix.Count && QrCodeData.ModuleMatrix[x + quietZonesOffset][y + quietZonesOffset + 1]) ^ invert; + + if (module1 && module2) + { + lineBuilder.Append('\u2588'); + } + else if (module1) + { + lineBuilder.Append('\u2580'); + } + else if (module2) + { + lineBuilder.Append('\u2584'); + } + else + { + lineBuilder.Append(' '); + } + } + yield return lineBuilder.ToString(); + lineBuilder.Clear(); + } + } + } + + public static class Utf8QRCodeHelper + { + public static string GetQRCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true, bool invert = false) + { + using (var qrGenerator = new QRCodeGenerator()) + using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) + using (var qrCode = new Utf8QRCode(qrCodeData)) + return qrCode.GetGraphic(drawQuietZones, endOfLine, invert); + } + } +} \ No newline at end of file diff --git a/QRCoderTests/Utf8CodeRendererTests.cs b/QRCoderTests/Utf8CodeRendererTests.cs new file mode 100644 index 00000000..3054a2ff --- /dev/null +++ b/QRCoderTests/Utf8CodeRendererTests.cs @@ -0,0 +1,74 @@ +using QRCoder; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; + + +namespace QRCoderTests +{ + + public class Utf8CodeRendererTests + { + [Fact] + [Category("QRRenderer/Utf8QRCode")] + public void can_render_utf8_qrcode() + { + var targetCode = " \n \n █▀▀▀▀▀█ ▄▀ █▄ █▀▀▀▀▀█ \n █ ███ █ ▀ █ ▀ █ ███ █ \n █ ▀▀▀ █ ▀▄▄▄▄ █ ▀▀▀ █ \n ▀▀▀▀▀▀▀ █ █▄▀ ▀▀▀▀▀▀▀ \n ██▀▀█ ▀█ ▄█▀▀▀▄█▄█▀▄ \n ▄ ▀ █ ▀██▀█▄▀▄█ ▀ ▀▀ \n ▀▀▀▀ ▀▀▀ ▄▄██ ▀ ▀ \n █▀▀▀▀▀█ ▀▀ ▀▀▄█▄█▀ ▀▀ \n █ ███ █ █▄█ ▀▄ █ ▀ ▀ \n █ ▀▀▀ █ █▄▀▄█ ▀ ▀█ █▀ \n ▀▀▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀ \n \n "; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var utf8Code = new Utf8QRCode(data).GetGraphic(); + + utf8Code.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/Utf8QRCode")] + public void can_render_utf8_qrcode_without_quietzones() + { + var targetCode = "█▀▀▀▀▀█ ▄▀ █▄ █▀▀▀▀▀█\n█ ███ █ ▀ █ ▀ █ ███ █\n█ ▀▀▀ █ ▀▄▄▄▄ █ ▀▀▀ █\n▀▀▀▀▀▀▀ █ █▄▀ ▀▀▀▀▀▀▀\n██▀▀█ ▀█ ▄█▀▀▀▄█▄█▀▄\n▄ ▀ █ ▀██▀█▄▀▄█ ▀ ▀▀ \n ▀▀▀▀ ▀▀▀ ▄▄██ ▀ ▀\n█▀▀▀▀▀█ ▀▀ ▀▀▄█▄█▀ ▀▀\n█ ███ █ █▄█ ▀▄ █ ▀ ▀\n█ ▀▀▀ █ █▄▀▄█ ▀ ▀█ █▀\n▀▀▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var utf8Code = new Utf8QRCode(data).GetGraphic(drawQuietZones: false); + + utf8Code.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/Utf8QRCode")] + public void can_render_utf8_qrcode_inverted() + { + var targetCode = "█████████████████████████████\n█████████████████████████████\n████ ▄▄▄▄▄ █▀▀▀▄ █ ▄▄▄▄▄ ████\n████ █ █ ██ ▄▄█ █ █ ████\n████ █▄▄▄█ █▄▀█▄██ █▄▄▄█ ████\n████▄▄▄▄▄▄▄█▄▀ ▀ █▄▄▄▄▄▄▄████\n████▀█ ▀ ▄▄▀ ▄ ▀▀ ██ ▀▀▄▀████\n█████▀████▄▀▄█▄█▀▄▀█ ▀ ██████\n█████▄█▄▄▄▄█ ▄█ ▀▀ ▄▄▄█▀ ████\n████ ▄▄▄▄▄ █▀▀ ▄▄▄█▀ ▀ ▄▄████\n████ █ █ █▄▀ ▄███▀ ▀ ▄█████\n████ █▄▄▄█ █▄▀▄▄▄▄██ ▀▄▄ ████\n████▄▄▄▄▄▄▄██▄██▄▄▄█▄▄█▄▄████\n█████████████████████████████\n█████████████████████████████"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A", QRCodeGenerator.ECCLevel.Q); + var utf8Code = new Utf8QRCode(data).GetGraphic(invert: true); + + utf8Code.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/Utf8QRCode")] + public void can_instantate_parameterless() + { + var utf8Code = new Utf8QRCode(); + utf8Code.ShouldNotBeNull(); + utf8Code.ShouldBeOfType(); + } + + [Fact] + [Category("QRRenderer/Utf8QRCode")] + public void can_render_utf8_qrcode_from_helper() + { + var targetCode = " \n \n █▀▀▀▀▀█ ▄▄▄▀█ █▀▀▀▀▀█ \n █ ███ █ ██▀▀ █ ███ █ \n █ ▀▀▀ █ ▀▄ ▀ █ ▀▀▀ █ \n ▀▀▀▀▀▀▀ ▀▄█▄█ ▀▀▀▀▀▀▀ \n ▄ █▄█▀▀▄█▀█▄▄█ █▄▄▀▄ \n ▄ ▀▄▀ ▀ ▄▀▄ █▄█ \n ▀ ▀▀▀▀ █▀ █▄▄█▀▀▀ ▄█ \n █▀▀▀▀▀█ ▄▄█▀▀▀ ▄█▄█▀▀ \n █ ███ █ ▀▄█▀ ▄█▄█▀ \n █ ▀▀▀ █ ▀▄▀▀▀▀ █▄▀▀█ \n ▀▀▀▀▀▀▀ ▀ ▀▀▀ ▀▀ ▀▀ \n \n "; + + //Create QR code + var utf8Code = Utf8QRCodeHelper.GetQRCode("A", QRCodeGenerator.ECCLevel.Q); + utf8Code.ShouldBe(targetCode); + } + } +} \ No newline at end of file