Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASCIQRCode Small #384

Merged
merged 8 commits into from
Apr 22, 2024
Merged
71 changes: 69 additions & 2 deletions QRCoder/ASCIIQRCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@ public AsciiQRCode(QRCodeData data) : base(data) { }


/// <summary>
/// Returns a strings that contains the resulting QR code as ASCII chars.
/// Returns a strings that contains the resulting QR code as textual representation.
/// </summary>
/// <param name="repeatPerModule">Number of repeated darkColorString/whiteSpaceString per module.</param>
/// <param name="darkColorString">String for use as dark color modules. In case of string make sure whiteSpaceString has the same length.</param>
/// <param name="whiteSpaceString">String for use as white modules (whitespace). In case of string make sure darkColorString has the same length.</param>
/// <param name="drawQuietZones">Bool that defines if quiet zones around the QR code shall be drawn</param>
/// <param name="endOfLine">End of line separator. (Default: \n)</param>
/// <returns></returns>
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));
}


/// <summary>
/// Returns an array of strings that contains each line of the resulting QR code as ASCII chars.
/// </summary>
/// <param name="repeatPerModule">Number of repeated darkColorString/whiteSpaceString per module.</param>
/// <param name="darkColorString">String for use as dark color modules. In case of string make sure whiteSpaceString has the same length.</param>
/// <param name="whiteSpaceString">String for use as white modules (whitespace). In case of string make sure darkColorString has the same length.</param>
/// <param name="drawQuietZones">Bool that defines if quiet zones around the QR code shall be drawn</param>
/// <returns></returns>
public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true)
{
Expand Down Expand Up @@ -62,6 +66,61 @@ public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString
}
return qrCode.ToArray();
}

/// <summary>
/// Returns a strings that contains the resulting QR code as minified textual representation.
/// </summary>
/// <param name="drawQuietZones">Bool that defines if quiet zones around the QR code shall be drawn</param>
/// <param name="invert">If set to true, dark and light colors will be inverted</param>
/// <param name="endOfLine">End of line separator. (Default: \n)</param>
/// <returns></returns>
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();
}
}


Expand All @@ -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);
}
}
}
56 changes: 56 additions & 0 deletions QRCoderTests/AsciiQRCodeRendererTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading