Skip to content

Commit c9314f9

Browse files
authored
Merge pull request #350 from codebude/feature/312-add-quietzone-flag-pngbyteqrcode
Feature/312 add quietzone flag pngbyteqrcode
2 parents da3eb22 + 951fb87 commit c9314f9

File tree

4 files changed

+193
-16
lines changed

4 files changed

+193
-16
lines changed

QRCoder/PngByteQRCode.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public PngByteQRCode(QRCodeData data) : base(data)
2020
/// <summary>
2121
/// Creates a black & white PNG of the QR code, using 1-bit grayscale.
2222
/// </summary>
23-
public byte[] GetGraphic(int pixelsPerModule)
23+
public byte[] GetGraphic(int pixelsPerModule, bool drawQuietZones = true)
2424
{
2525
using (var png = new PngBuilder())
2626
{
27-
var size = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule;
27+
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
2828
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Greyscale);
29-
png.WriteScanlines(this.DrawScanlines(pixelsPerModule));
29+
png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones));
3030
png.WriteEnd();
3131
return png.GetBytes();
3232
}
@@ -35,14 +35,14 @@ public byte[] GetGraphic(int pixelsPerModule)
3535
/// <summary>
3636
/// Creates 2-color PNG of the QR code, using 1-bit indexed color. Accepts 3-byte RGB colors for normal images and 4-byte RGBA-colors for transparent images.
3737
/// </summary>
38-
public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba)
38+
public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, bool drawQuietZones = true)
3939
{
4040
using (var png = new PngBuilder())
4141
{
42-
var size = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule;
42+
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
4343
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Indexed);
4444
png.WritePalette(darkColorRgba, lightColorRgba);
45-
png.WriteScanlines(this.DrawScanlines(pixelsPerModule));
45+
png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones));
4646
png.WriteEnd();
4747
return png.GetBytes();
4848
}
@@ -51,22 +51,23 @@ public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] light
5151
/// <summary>
5252
/// Creates a bitmap where each pixel is represented by a single bit, dark = 0 and light = 1.
5353
/// </summary>
54-
private byte[] DrawScanlines(int pixelsPerModule)
54+
private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones)
5555
{
5656
var moduleMatrix = this.QrCodeData.ModuleMatrix;
57-
var matrixSize = moduleMatrix.Count;
57+
var matrixSize = moduleMatrix.Count - (drawQuietZones ? 0 : 8);
58+
var quietZoneOffset = (drawQuietZones ? 0 : 4);
5859
var bytesPerScanline = (matrixSize * pixelsPerModule + 7) / 8 + 1; // A monochrome scanline is one byte for filter type then one bit per pixel.
5960
var scanlines = new byte[bytesPerScanline * matrixSize * pixelsPerModule];
6061

6162
for (var y = 0; y < matrixSize; y++)
6263
{
63-
var modules = moduleMatrix[y];
64+
var modules = moduleMatrix[y+quietZoneOffset];
6465
var scanlineOffset = y * pixelsPerModule * bytesPerScanline;
6566

6667
// Draw a scanline with the modules from the QR code.
6768
for (var x = 0; x < matrixSize; x++)
6869
{
69-
if (modules[x])
70+
if (modules[x + quietZoneOffset])
7071
{
7172
continue;
7273
}
@@ -319,22 +320,22 @@ private static uint Crc32(byte[] data, int index, int length)
319320

320321
public static class PngByteQRCodeHelper
321322
{
322-
public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1)
323+
public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true)
323324
{
324325
using (var qrGenerator = new QRCodeGenerator())
325326
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
326327
using (var qrCode = new PngByteQRCode(qrCodeData))
327-
return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba);
328+
return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba, drawQuietZones);
328329
}
329330

330331

331332

332-
public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size)
333+
public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size, bool drawQuietZones = true)
333334
{
334335
using (var qrGen = new QRCodeGenerator())
335336
using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
336337
using (var qrPng = new PngByteQRCode(qrCode))
337-
return qrPng.GetGraphic(size);
338+
return qrPng.GetGraphic(size, drawQuietZones);
338339
}
339340
}
340341
}

QRCoderTests/Helpers/HelperFunctions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,22 @@ public static string BitmapToHash(Bitmap bmp)
3838
}
3939
return ByteArrayToHash(imgBytes);
4040
}
41+
#endif
4142

4243
public static string ByteArrayToHash(byte[] data)
4344
{
45+
#if !NETCOREAPP1_1
4446
var md5 = new MD5CryptoServiceProvider();
4547
var hash = md5.ComputeHash(data);
48+
#else
49+
var hash = new SshNet.Security.Cryptography.MD5().ComputeHash(data);
50+
#endif
4651
return BitConverter.ToString(hash).Replace("-", "").ToLower();
4752
}
4853

4954
public static string StringToHash(string data)
5055
{
5156
return ByteArrayToHash(Encoding.UTF8.GetBytes(data));
5257
}
53-
#endif
54-
5558
}
5659
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using Xunit;
2+
using QRCoder;
3+
using Shouldly;
4+
using QRCoderTests.Helpers.XUnitExtenstions;
5+
using QRCoderTests.Helpers;
6+
#if !NETCOREAPP1_1
7+
using System.Drawing;
8+
using System.IO;
9+
#endif
10+
11+
namespace QRCoderTests
12+
{
13+
/****************************************************************************************************
14+
* Note: Test cases compare the outcome visually even if it's slower than a byte-wise compare.
15+
* This is necessary, because the Deflate implementation differs on the different target
16+
* platforms and thus the outcome, even if visually identical, differs. Thus only a visual
17+
* test method makes sense. In addition bytewise differences shouldn't be important, if the
18+
* visual outcome is identical and thus the qr code is identical/scannable.
19+
****************************************************************************************************/
20+
public class PngByteQRCodeRendererTests
21+
{
22+
23+
24+
[Fact]
25+
[Category("QRRenderer/PngByteQRCode")]
26+
public void can_render_pngbyte_qrcode_blackwhite()
27+
{
28+
//Create QR code
29+
var gen = new QRCodeGenerator();
30+
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
31+
var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5);
32+
33+
#if NETCOREAPP1_1
34+
var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
35+
result.ShouldBe("1fc35c3bea6fad47427143ce716c83b8");
36+
#else
37+
using (var mStream = new MemoryStream(pngCodeGfx))
38+
{
39+
var bmp = (Bitmap)Image.FromStream(mStream);
40+
var result = HelperFunctions.BitmapToHash(bmp);
41+
result.ShouldBe("18b19e6037cff06ae995d8d487b0e46e");
42+
}
43+
#endif
44+
}
45+
46+
[Fact]
47+
[Category("QRRenderer/PngByteQRCode")]
48+
public void can_render_pngbyte_qrcode_color()
49+
{
50+
//Create QR code
51+
var gen = new QRCodeGenerator();
52+
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
53+
var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 });
54+
55+
#if NETCOREAPP1_1
56+
var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
57+
result.ShouldBe("0144b1d40aa6eeb6cb07df42822ea0a7");
58+
#else
59+
using (var mStream = new MemoryStream(pngCodeGfx))
60+
{
61+
var bmp = (Bitmap)Image.FromStream(mStream);
62+
var result = HelperFunctions.BitmapToHash(bmp);
63+
result.ShouldBe("37ae73e90b66beac317b790be3db24cc");
64+
}
65+
#endif
66+
}
67+
68+
69+
[Fact]
70+
[Category("QRRenderer/PngByteQRCode")]
71+
public void can_render_pngbyte_qrcode_color_with_alpha()
72+
{
73+
//Create QR code
74+
var gen = new QRCodeGenerator();
75+
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
76+
var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 });
77+
78+
#if NETCOREAPP1_1
79+
var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
80+
result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5");
81+
#else
82+
using (var mStream = new MemoryStream(pngCodeGfx))
83+
{
84+
var bmp = (Bitmap)Image.FromStream(mStream);
85+
var result = HelperFunctions.BitmapToHash(bmp);
86+
result.ShouldBe("c56c2a9535fd8e9a92a6ac9709d21e67");
87+
}
88+
#endif
89+
}
90+
91+
[Fact]
92+
[Category("QRRenderer/PngByteQRCode")]
93+
public void can_render_pngbyte_qrcode_color_without_quietzones()
94+
{
95+
//Create QR code
96+
var gen = new QRCodeGenerator();
97+
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
98+
var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, false);
99+
100+
#if NETCOREAPP1_1
101+
var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
102+
result.ShouldBe("07f760b3eb54901840b094d31e299713");
103+
#else
104+
File.WriteAllBytes(@"C:\Temp\pngbyte_35.png", pngCodeGfx);
105+
using (var mStream = new MemoryStream(pngCodeGfx))
106+
{
107+
var bmp = (Bitmap)Image.FromStream(mStream);
108+
bmp.MakeTransparent(Color.Transparent);
109+
var result = HelperFunctions.BitmapToHash(bmp);
110+
#if NET35_OR_GREATER || NET40_OR_GREATER
111+
result.ShouldBe("75be11d582575617d2490c54b69e844e");
112+
#else
113+
result.ShouldBe("fbbc8255ebf3e4f4a1d21f0dd15f76f8");
114+
#endif
115+
}
116+
#endif
117+
}
118+
119+
[Fact]
120+
[Category("QRRenderer/PngByteQRCode")]
121+
public void can_instantate_pngbyte_qrcode_parameterless()
122+
{
123+
var pngCode = new PngByteQRCode();
124+
pngCode.ShouldNotBeNull();
125+
pngCode.ShouldBeOfType<PngByteQRCode>();
126+
}
127+
128+
[Fact]
129+
[Category("QRRenderer/PngByteQRCode")]
130+
public void can_render_pngbyte_qrcode_from_helper()
131+
{
132+
//Create QR code
133+
var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L, 10);
134+
135+
#if NETCOREAPP1_1
136+
var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
137+
result.ShouldBe("c562388f4f3cf13a299b469a3e3b852f");
138+
#else
139+
using (var mStream = new MemoryStream(pngCodeGfx))
140+
{
141+
var bmp = (Bitmap)Image.FromStream(mStream);
142+
var result = HelperFunctions.BitmapToHash(bmp);
143+
result.ShouldBe("1978fb11ce26acf9b6cb7490b4c44ef2");
144+
}
145+
#endif
146+
}
147+
148+
[Fact]
149+
[Category("QRRenderer/PngByteQRCode")]
150+
public void can_render_pngbyte_qrcode_from_helper_2()
151+
{
152+
//Create QR code
153+
var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", 5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, QRCodeGenerator.ECCLevel.L);
154+
155+
#if NETCOREAPP1_1
156+
var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
157+
result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5");
158+
#else
159+
using (var mStream = new MemoryStream(pngCodeGfx))
160+
{
161+
var bmp = (Bitmap)Image.FromStream(mStream);
162+
var result = HelperFunctions.BitmapToHash(bmp);
163+
result.ShouldBe("c56c2a9535fd8e9a92a6ac9709d21e67");
164+
}
165+
#endif
166+
}
167+
168+
}
169+
}
170+
171+
172+

QRCoderTests/QRCoderTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<PrivateAssets>all</PrivateAssets>
3737
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3838
</PackageReference>
39+
<PackageReference Include="SshNet.Security.Cryptography" Version="1.3.0" />
3940
<ProjectReference Include="..\QRCoder\QRCoder.csproj" />
4041
</ItemGroup>
4142
<ItemGroup>

0 commit comments

Comments
 (0)