diff --git a/QRCoder/ASCIIQRCode.cs b/QRCoder/ASCIIQRCode.cs index bffe7cd0..6f07c447 100644 --- a/QRCoder/ASCIIQRCode.cs +++ b/QRCoder/ASCIIQRCode.cs @@ -16,18 +16,21 @@ public AsciiQRCode(QRCodeData data) : base(data) { } /// - /// Returns a strings that contains the resulting QR code as ASCII chars. + /// Returns a strings that contains the resulting QR code as textual representation. /// /// Number of repeated darkColorString/whiteSpaceString per module. /// String for use as dark color modules. In case of string make sure whiteSpaceString has the same length. /// String for use as white modules (whitespace). In case of string make sure darkColorString has the same length. + /// Bool that defines if quiet zones around the QR code shall be drawn /// End of line separator. (Default: \n) /// public string GetGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true, string endOfLine = "\n") { + if (repeatPerModule < 1) + throw new Exception("The repeatPerModule-parameter must be 1 or greater."); return string.Join(endOfLine, GetLineByLineGraphic(repeatPerModule, darkColorString, whiteSpaceString, drawQuietZones)); } - + /// /// Returns an array of strings that contains each line of the resulting QR code as ASCII chars. @@ -35,6 +38,7 @@ public string GetGraphic(int repeatPerModule, string darkColorString = "██", /// Number of repeated darkColorString/whiteSpaceString per module. /// String for use as dark color modules. In case of string make sure whiteSpaceString has the same length. /// String for use as white modules (whitespace). In case of string make sure darkColorString has the same length. + /// Bool that defines if quiet zones around the QR code shall be drawn /// public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true) { @@ -62,6 +66,61 @@ public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString } return qrCode.ToArray(); } + + /// + /// Returns a strings that contains the resulting QR code as minified textual representation. + /// + /// Bool that defines if quiet zones around the QR code shall be drawn + /// If set to true, dark and light colors will be inverted + /// End of line separator. (Default: \n) + /// + public string GetGraphicSmall(bool drawQuietZones = true, bool invert = false, string endOfLine = "\n") + { + bool BLACK = true, WHITE = false; + + var palette = new + { + WHITE_ALL = "\u2588", + WHITE_BLACK = "\u2580", + BLACK_WHITE = "\u2584", + BLACK_ALL = " ", + }; + + var moduleData = QrCodeData.ModuleMatrix; + var sbSize = (moduleData.Count + endOfLine.Length) * (int)Math.Ceiling(moduleData.Count / 2.0) - 1; + var lineBuilder = new StringBuilder(sbSize); + + var quietZonesModifier = (drawQuietZones ? 0 : 8); + var quietZonesOffset = (int)(quietZonesModifier * 0.5); + var sideLength = (moduleData.Count - quietZonesModifier); + + for (var row = 0; row < sideLength; row += 2) + { + for (var col = 0; col < sideLength; col++) + { + var current = moduleData[col + quietZonesOffset][row + quietZonesOffset] ^ invert; + var nextRowId = row + quietZonesOffset + 1; + + // Set next to whitespace "color" + var next = BLACK; + // Fill next with value, if in data range + if (nextRowId < QrCodeData.ModuleMatrix.Count) + next = moduleData[col + quietZonesOffset][nextRowId] ^ invert; + + if (current == WHITE && next == WHITE) + lineBuilder.Append(palette.WHITE_ALL); + else if (current == WHITE && next == BLACK) + lineBuilder.Append(palette.WHITE_BLACK); + else if (current == BLACK && next == WHITE) + lineBuilder.Append(palette.BLACK_WHITE); + else + lineBuilder.Append(palette.BLACK_ALL); + } + if (row + 2 < sideLength) + lineBuilder.Append(endOfLine); + } + return lineBuilder.ToString(); + } } @@ -74,5 +133,13 @@ public static string GetQRCode(string plainText, int pixelsPerModule, string dar using (var qrCode = new AsciiQRCode(qrCodeData)) return qrCode.GetGraphic(pixelsPerModule, darkColorString, whiteSpaceString, drawQuietZones, endOfLine); } + + 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 = true) + { + using (var qrGenerator = new QRCodeGenerator()) + using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) + using (var qrCode = new AsciiQRCode(qrCodeData)) + return qrCode.GetGraphicSmall(drawQuietZones, invert, endOfLine); + } } } \ No newline at end of file diff --git a/QRCoderTests/AsciiQRCodeRendererTests.cs b/QRCoderTests/AsciiQRCodeRendererTests.cs index 7ac2656b..75152911 100644 --- a/QRCoderTests/AsciiQRCodeRendererTests.cs +++ b/QRCoderTests/AsciiQRCodeRendererTests.cs @@ -24,6 +24,62 @@ public void can_render_ascii_qrcode() asciiCode.ShouldBe(targetCode); } + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_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 asciiCode = new AsciiQRCode(data).GetGraphicSmall(); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_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 asciiCode = new AsciiQRCode(data).GetGraphicSmall(drawQuietZones: false); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_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("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphicSmall(invert: true); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_qrcode_with_custom_eol() + { + var targetCode = "█████████████████████████████\r\n█████████████████████████████\r\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\r\n████ █ █ █▄█ █▄█ █ █ ████\r\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\r\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\r\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\r\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\r\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\r\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\r\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\r\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\r\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\r\n█████████████████████████████\r\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphicSmall(endOfLine: "\r\n"); + + asciiCode.ShouldBe(targetCode); + } + [Fact] [Category("QRRenderer/AsciiQRCode")] public void can_render_ascii_qrcode_without_quietzones()