Skip to content

Commit 2eac6ad

Browse files
authored
Merge branch 'master' into support_base64_linux
2 parents dd49148 + efc73cf commit 2eac6ad

File tree

8 files changed

+198
-22
lines changed

8 files changed

+198
-22
lines changed

QRCoder/ASCIIQRCode.cs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,29 @@ public AsciiQRCode(QRCodeData data) : base(data) { }
1616

1717

1818
/// <summary>
19-
/// Returns a strings that contains the resulting QR code as ASCII chars.
19+
/// Returns a strings that contains the resulting QR code as textual representation.
2020
/// </summary>
2121
/// <param name="repeatPerModule">Number of repeated darkColorString/whiteSpaceString per module.</param>
2222
/// <param name="darkColorString">String for use as dark color modules. In case of string make sure whiteSpaceString has the same length.</param>
2323
/// <param name="whiteSpaceString">String for use as white modules (whitespace). In case of string make sure darkColorString has the same length.</param>
24+
/// <param name="drawQuietZones">Bool that defines if quiet zones around the QR code shall be drawn</param>
2425
/// <param name="endOfLine">End of line separator. (Default: \n)</param>
2526
/// <returns></returns>
2627
public string GetGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true, string endOfLine = "\n")
2728
{
29+
if (repeatPerModule < 1)
30+
throw new Exception("The repeatPerModule-parameter must be 1 or greater.");
2831
return string.Join(endOfLine, GetLineByLineGraphic(repeatPerModule, darkColorString, whiteSpaceString, drawQuietZones));
2932
}
30-
33+
3134

3235
/// <summary>
3336
/// Returns an array of strings that contains each line of the resulting QR code as ASCII chars.
3437
/// </summary>
3538
/// <param name="repeatPerModule">Number of repeated darkColorString/whiteSpaceString per module.</param>
3639
/// <param name="darkColorString">String for use as dark color modules. In case of string make sure whiteSpaceString has the same length.</param>
3740
/// <param name="whiteSpaceString">String for use as white modules (whitespace). In case of string make sure darkColorString has the same length.</param>
41+
/// <param name="drawQuietZones">Bool that defines if quiet zones around the QR code shall be drawn</param>
3842
/// <returns></returns>
3943
public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true)
4044
{
@@ -62,6 +66,61 @@ public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString
6266
}
6367
return qrCode.ToArray();
6468
}
69+
70+
/// <summary>
71+
/// Returns a strings that contains the resulting QR code as minified textual representation.
72+
/// </summary>
73+
/// <param name="drawQuietZones">Bool that defines if quiet zones around the QR code shall be drawn</param>
74+
/// <param name="invert">If set to true, dark and light colors will be inverted</param>
75+
/// <param name="endOfLine">End of line separator. (Default: \n)</param>
76+
/// <returns></returns>
77+
public string GetGraphicSmall(bool drawQuietZones = true, bool invert = false, string endOfLine = "\n")
78+
{
79+
bool BLACK = true, WHITE = false;
80+
81+
var palette = new
82+
{
83+
WHITE_ALL = "\u2588",
84+
WHITE_BLACK = "\u2580",
85+
BLACK_WHITE = "\u2584",
86+
BLACK_ALL = " ",
87+
};
88+
89+
var moduleData = QrCodeData.ModuleMatrix;
90+
var sbSize = (moduleData.Count + endOfLine.Length) * (int)Math.Ceiling(moduleData.Count / 2.0) - 1;
91+
var lineBuilder = new StringBuilder(sbSize);
92+
93+
var quietZonesModifier = (drawQuietZones ? 0 : 8);
94+
var quietZonesOffset = (int)(quietZonesModifier * 0.5);
95+
var sideLength = (moduleData.Count - quietZonesModifier);
96+
97+
for (var row = 0; row < sideLength; row += 2)
98+
{
99+
for (var col = 0; col < sideLength; col++)
100+
{
101+
var current = moduleData[col + quietZonesOffset][row + quietZonesOffset] ^ invert;
102+
var nextRowId = row + quietZonesOffset + 1;
103+
104+
// Set next to whitespace "color"
105+
var next = BLACK;
106+
// Fill next with value, if in data range
107+
if (nextRowId < QrCodeData.ModuleMatrix.Count)
108+
next = moduleData[col + quietZonesOffset][nextRowId] ^ invert;
109+
110+
if (current == WHITE && next == WHITE)
111+
lineBuilder.Append(palette.WHITE_ALL);
112+
else if (current == WHITE && next == BLACK)
113+
lineBuilder.Append(palette.WHITE_BLACK);
114+
else if (current == BLACK && next == WHITE)
115+
lineBuilder.Append(palette.BLACK_WHITE);
116+
else
117+
lineBuilder.Append(palette.BLACK_ALL);
118+
}
119+
if (row + 2 < sideLength)
120+
lineBuilder.Append(endOfLine);
121+
}
122+
return lineBuilder.ToString();
123+
}
65124
}
66125

67126

@@ -74,5 +133,13 @@ public static string GetQRCode(string plainText, int pixelsPerModule, string dar
74133
using (var qrCode = new AsciiQRCode(qrCodeData))
75134
return qrCode.GetGraphic(pixelsPerModule, darkColorString, whiteSpaceString, drawQuietZones, endOfLine);
76135
}
136+
137+
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)
138+
{
139+
using (var qrGenerator = new QRCodeGenerator())
140+
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
141+
using (var qrCode = new AsciiQRCode(qrCodeData))
142+
return qrCode.GetGraphicSmall(drawQuietZones, invert, endOfLine);
143+
}
77144
}
78145
}

QRCoder/ArtQRCode.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor,
120120
if (finderPatternImage != null)
121121
{
122122
var finderPatternSize = 7 * pixelsPerModule;
123-
graphics.DrawImage(finderPatternImage, new Rectangle(0, 0, finderPatternSize, finderPatternSize));
124-
graphics.DrawImage(finderPatternImage, new Rectangle(size - finderPatternSize, 0, finderPatternSize, finderPatternSize));
125-
graphics.DrawImage(finderPatternImage, new Rectangle(0, size - finderPatternSize, finderPatternSize, finderPatternSize));
123+
var finderPatternOffset = drawQuietZones ? 4 * pixelsPerModule : 0;
124+
graphics.DrawImage(finderPatternImage, new Rectangle(finderPatternOffset, finderPatternOffset, finderPatternSize, finderPatternSize));
125+
graphics.DrawImage(finderPatternImage, new Rectangle(size - finderPatternOffset - finderPatternSize, finderPatternOffset, finderPatternSize, finderPatternSize));
126+
graphics.DrawImage(finderPatternImage, new Rectangle(finderPatternOffset, size - finderPatternOffset - finderPatternSize, finderPatternSize, finderPatternSize));
126127
}
127128
graphics.Save();
128129
}

QRCoder/PayloadGenerator.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,7 @@ public static Contact WithCombinedAddress(string name, string country, string ad
987987
private Contact(string name, string zipCode, string city, string country, string streetOrAddressline1, string houseNumberOrAddressline2, AddressType addressType)
988988
{
989989
//Pattern extracted from https://qr-validation.iso-payments.ch as explained in https://github.com/codebude/QRCoder/issues/97
990-
var charsetPattern = @"^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ]|[!""#%&<>÷=@_$£]|[àáâäçèéêëìíîïñòóôöùúûüýßÀÁÂÄÇÈÉÊËÌÍÎÏÒÓÔÖÙÚÛÜÑ])*$";
990+
var charsetPattern = @"^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$";
991991

992992
this.adrType = addressType;
993993

@@ -1051,7 +1051,7 @@ private Contact(string name, string zipCode, string city, string country, string
10511051
}
10521052

10531053
if (!IsValidTwoLetterCode(country))
1054-
throw new SwissQrCodeContactException("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't.");
1054+
throw new SwissQrCodeContactException("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't.");
10551055

10561056
this.country = country;
10571057
}
@@ -1060,7 +1060,7 @@ private Contact(string name, string zipCode, string city, string country, string
10601060

10611061
private static HashSet<string> ValidTwoLetterCodes()
10621062
{
1063-
string[] codes = new string[]{ "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "CV", "KH", "CM", "CA", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "SZ", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "MK", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", "AX" };
1063+
string[] codes = new string[]{ "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "CV", "KH", "CM", "CA", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "SZ", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "MK", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", "AX", "XK" };
10641064
return new HashSet<string>(codes, StringComparer.OrdinalIgnoreCase);
10651065
}
10661066

@@ -1140,7 +1140,8 @@ public override string ToString()
11401140
//AddInf "logical" element
11411141
SwissQrCodePayload += (!string.IsNullOrEmpty(additionalInformation.UnstructureMessage) ? additionalInformation.UnstructureMessage : string.Empty) + br; //Ustrd
11421142
SwissQrCodePayload += additionalInformation.Trailer + br; //Trailer
1143-
SwissQrCodePayload += (!string.IsNullOrEmpty(additionalInformation.BillInformation) ? additionalInformation.BillInformation : string.Empty) + br; //StrdBkgInf
1143+
// Bugfix PR #399 If BillInformation is empty, insert no linebreak
1144+
SwissQrCodePayload += (!string.IsNullOrEmpty(additionalInformation.BillInformation) ? additionalInformation.BillInformation + br : string.Empty); //StrdBkgInf
11441145

11451146
//AltPmtInf "logical" element
11461147
if (!string.IsNullOrEmpty(alternativeProcedure1))

QRCoder/QRCodeGenerator.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
150150
}
151151
modeIndicator += DecToBin((int)encoding, 4);
152152
var countIndicator = DecToBin(dataInputLength, GetCountIndicatorLength(version, encoding));
153-
var bitString = modeIndicator + countIndicator;
154-
155-
bitString += codedText;
153+
var bitString = modeIndicator + countIndicator + codedText;
156154

157155
return GenerateQrCode(bitString, eccLevel, version);
158156
}
@@ -170,13 +168,18 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
170168
int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel);
171169

172170
string modeIndicator = DecToBin((int)EncodingMode.Byte, 4);
173-
string countIndicator = DecToBin(binaryData.Length, GetCountIndicatorLength(version, EncodingMode.Byte));
171+
int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte);
172+
string countIndicator = DecToBin(binaryData.Length, countIndicatorLen);
174173

175-
string bitString = modeIndicator + countIndicator;
174+
StringBuilder sb = new StringBuilder(capacity: 4 + countIndicatorLen + (binaryData.Length*8));
175+
sb.Append(modeIndicator).Append(countIndicator);
176176
foreach (byte b in binaryData)
177177
{
178-
bitString += DecToBin(b, 8);
178+
sb.Append(DecToBin(b, 8));
179+
179180
}
181+
string bitString = sb.ToString();
182+
180183

181184
return GenerateQrCode(bitString, eccLevel, version);
182185
}

QRCoderTests/ArtQRCodeRendererTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,15 @@ public void can_create_standard_qrcode_graphic_with_custom_finder()
3232
{
3333
var gen = new QRCodeGenerator();
3434
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
35-
var finder = new Bitmap(15, 15);
35+
var finder = new Bitmap(70, 70);
36+
using (var g = Graphics.FromImage(finder))
37+
{
38+
g.FillRectangle(Brushes.Red, 0, 0, 70, 70);
39+
}
3640
var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, finderPatternImage: finder);
3741

3842
var result = HelperFunctions.BitmapToHash(bmp);
39-
result.ShouldBe("1102c0c6f235eaf4c3ac639f82f17bfa");
43+
result.ShouldBe("5df3f2892eeb01e9c282ad10f642dec2");
4044
}
4145

4246
[Fact]

0 commit comments

Comments
 (0)