Skip to content

Commit b111fd0

Browse files
authored
Merge branch 'master' into add_more_tests
2 parents c732d19 + 02baa95 commit b111fd0

File tree

8 files changed

+235
-88
lines changed

8 files changed

+235
-88
lines changed

QRCoder/PayloadGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public static class PayloadGenerator
1515
public abstract class Payload
1616
{
1717
public virtual int Version { get { return -1; } }
18-
public virtual QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.M; } }
18+
public virtual QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.Default; } }
1919
public virtual QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Default; } }
2020
public abstract override string ToString();
2121
}

QRCoder/QRCodeGenerator.ECCLevel.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,35 @@ public partial class QRCodeGenerator
88
/// </summary>
99
public enum ECCLevel
1010
{
11+
/// <summary>
12+
/// Default error correction level, which will select Level M (Medium) unless otherwise specified by the payload.
13+
/// Level M allows approximately 15% of data to be recovered, offering a balance between data capacity and error recovery.
14+
/// </summary>
15+
Default = -1,
16+
1117
/// <summary>
1218
/// Level L: Low error correction (approximately 7% of data can be recovered).
1319
/// This level allows the highest data density.
1420
/// </summary>
15-
L,
21+
L = 0,
1622

1723
/// <summary>
1824
/// Level M: Medium error correction (approximately 15% of data can be recovered).
1925
/// Offers a balance between data capacity and error recovery.
2026
/// </summary>
21-
M,
27+
M = 1,
2228

2329
/// <summary>
2430
/// Level Q: Quartile error correction (approximately 25% of data can be recovered).
2531
/// More robust error correction at the cost of reduced data capacity.
2632
/// </summary>
27-
Q,
33+
Q = 2,
2834

2935
/// <summary>
3036
/// Level H: High error correction (approximately 30% of data can be recovered).
3137
/// Provides the highest level of error recovery, ideal for environments with high risk of data loss.
3238
/// </summary>
33-
H
39+
H = 3
3440
}
3541
}
3642
}

QRCoder/QRCodeGenerator.cs

Lines changed: 147 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload)
105105
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
106106
public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel)
107107
{
108+
if (eccLevel == ECCLevel.Default)
109+
eccLevel = payload.EccLevel;
110+
else if (payload.EccLevel != ECCLevel.Default && eccLevel != payload.EccLevel)
111+
throw new ArgumentOutOfRangeException(nameof(eccLevel), $"The provided payload requires a ECC level of {eccLevel}.");
108112
return GenerateQrCode(payload.ToString(), eccLevel, false, false, payload.EciMode, payload.Version);
109113
}
110114

@@ -121,6 +125,7 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLev
121125
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
122126
public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1)
123127
{
128+
eccLevel = ValidateECCLevel(eccLevel);
124129
EncodingMode encoding = GetEncodingFromPlaintext(plainText, forceUtf8);
125130
var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8);
126131
var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8);
@@ -165,7 +170,6 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
165170
return GenerateQrCode(completeBitArray, eccLevel, version);
166171
}
167172

168-
169173
/// <summary>
170174
/// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation.
171175
/// </summary>
@@ -175,6 +179,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
175179
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
176180
public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
177181
{
182+
eccLevel = ValidateECCLevel(eccLevel);
178183
int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel);
179184

180185
int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte);
@@ -187,6 +192,27 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
187192
return GenerateQrCode(bitArray, eccLevel, version);
188193
}
189194

195+
/// <summary>
196+
/// Validates the specified error correction level.
197+
/// Returns the provided level if it is valid, or the level M if the provided level is Default.
198+
/// Throws an exception if an invalid level is provided.
199+
/// </summary>
200+
private static ECCLevel ValidateECCLevel(ECCLevel eccLevel)
201+
{
202+
switch (eccLevel)
203+
{
204+
case ECCLevel.L:
205+
case ECCLevel.M:
206+
case ECCLevel.Q:
207+
case ECCLevel.H:
208+
return eccLevel;
209+
case ECCLevel.Default:
210+
return ECCLevel.M;
211+
default:
212+
throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Invalid error correction level.");
213+
}
214+
}
215+
190216
private static readonly BitArray _repeatingPattern = new BitArray(
191217
new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true });
192218

@@ -200,109 +226,147 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
200226
/// <returns>A QRCodeData structure containing the full QR code matrix, which can be used for rendering or analysis.</returns>
201227
private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, int version)
202228
{
203-
//Fill up data code word
204229
var eccInfo = capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel);
205-
var dataLength = eccInfo.TotalDataCodewords * 8;
206-
var lengthDiff = dataLength - bitArray.Length;
207-
if (lengthDiff > 0)
230+
231+
// Fill up data code word
232+
PadData();
233+
234+
// Calculate error correction blocks
235+
var codeWordWithECC = CalculateECCBlocks();
236+
237+
// Calculate interleaved code word lengths
238+
var interleavedLength = CalculateInterleavedLength();
239+
240+
// Interleave code words
241+
var interleavedData = InterleaveData();
242+
243+
// Place interleaved data on module matrix
244+
var qrData = PlaceModules();
245+
246+
return qrData;
247+
248+
249+
// fills the bit array with a repeating pattern to reach the required length
250+
void PadData()
208251
{
209-
// set 'write index' to end of existing bit array
210-
var index = bitArray.Length;
211-
// extend bit array to required length
212-
bitArray.Length = dataLength;
213-
// pad with 4 zeros (or less if lengthDiff < 4)
214-
index += Math.Min(lengthDiff, 4);
215-
// pad to nearest 8 bit boundary
216-
if ((uint)index % 8 != 0)
217-
index += 8 - (int)((uint)index % 8);
218-
// pad with repeating pattern
219-
var repeatingPatternIndex = 0;
220-
while (index < dataLength)
252+
var dataLength = eccInfo.TotalDataCodewords * 8;
253+
var lengthDiff = dataLength - bitArray.Length;
254+
if (lengthDiff > 0)
221255
{
222-
bitArray[index++] = _repeatingPattern[repeatingPatternIndex++];
223-
if (repeatingPatternIndex >= _repeatingPattern.Length)
224-
repeatingPatternIndex = 0;
256+
// set 'write index' to end of existing bit array
257+
var index = bitArray.Length;
258+
// extend bit array to required length
259+
bitArray.Length = dataLength;
260+
// pad with 4 zeros (or less if lengthDiff < 4)
261+
index += Math.Min(lengthDiff, 4);
262+
// pad to nearest 8 bit boundary
263+
if ((uint)index % 8 != 0)
264+
index += 8 - (int)((uint)index % 8);
265+
// pad with repeating pattern
266+
var repeatingPatternIndex = 0;
267+
while (index < dataLength)
268+
{
269+
bitArray[index++] = _repeatingPattern[repeatingPatternIndex++];
270+
if (repeatingPatternIndex >= _repeatingPattern.Length)
271+
repeatingPatternIndex = 0;
272+
}
225273
}
226274
}
227275

228-
// Generate the generator polynomial using the number of ECC words.
229-
List<CodewordBlock> codeWordWithECC;
230-
using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock))
276+
List<CodewordBlock> CalculateECCBlocks()
231277
{
232-
//Calculate error correction words
233-
codeWordWithECC = new List<CodewordBlock>(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2);
234-
AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom);
235-
int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8;
236-
AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom);
237-
}
278+
List<CodewordBlock> codewordBlocks;
279+
// Generate the generator polynomial using the number of ECC words.
280+
using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock))
281+
{
282+
//Calculate error correction words
283+
codewordBlocks = new List<CodewordBlock>(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2);
284+
AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom);
285+
int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8;
286+
AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom);
287+
return codewordBlocks;
288+
}
238289

239-
//Calculate interleaved code word lengths
240-
int interleavedLength = 0;
241-
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
242-
{
243-
foreach (var codeBlock in codeWordWithECC)
244-
if ((uint)codeBlock.CodeWordsLength / 8 > i)
245-
interleavedLength += 8;
246-
}
247-
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
248-
{
249-
foreach (var codeBlock in codeWordWithECC)
250-
if (codeBlock.ECCWords.Length > i)
251-
interleavedLength += 8;
290+
void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom)
291+
{
292+
var groupLength = codewordsInGroup * 8;
293+
for (var i = 0; i < blocksInGroup; i++)
294+
{
295+
var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom);
296+
codewordBlocks.Add(new CodewordBlock(offset2, groupLength, eccWordList));
297+
offset2 += groupLength;
298+
}
299+
}
252300
}
253-
interleavedLength += remainderBits[version - 1];
254301

255-
//Interleave code words
256-
var interleavedData = new BitArray(interleavedLength);
257-
int pos = 0;
258-
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
302+
// Calculate the length of the interleaved data
303+
int CalculateInterleavedLength()
259304
{
260-
foreach (var codeBlock in codeWordWithECC)
305+
var length = 0;
306+
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
261307
{
262-
if ((uint)codeBlock.CodeWordsLength / 8 > i)
263-
pos = bitArray.CopyTo(interleavedData, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8);
308+
foreach (var codeBlock in codeWordWithECC)
309+
if ((uint)codeBlock.CodeWordsLength / 8 > i)
310+
length += 8;
264311
}
265-
}
266-
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
267-
{
268-
foreach (var codeBlock in codeWordWithECC)
269-
if (codeBlock.ECCWords.Length > i)
270-
pos = DecToBin(codeBlock.ECCWords[i], 8, interleavedData, pos);
312+
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
313+
{
314+
foreach (var codeBlock in codeWordWithECC)
315+
if (codeBlock.ECCWords.Length > i)
316+
length += 8;
317+
}
318+
length += remainderBits[version - 1];
319+
return length;
271320
}
272321

273-
//Place interleaved data on module matrix
274-
var qr = new QRCodeData(version);
275-
var blockedModules = new List<Rectangle>(17);
276-
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
277-
ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, blockedModules);
278-
ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules);
279-
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
280-
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
281-
ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, blockedModules);
282-
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
283-
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
284-
var formatStr = GetFormatString(eccLevel, maskVersion);
285-
286-
ModulePlacer.PlaceFormat(qr, formatStr);
287-
if (version >= 7)
322+
// Interleave the data
323+
BitArray InterleaveData()
288324
{
289-
var versionString = GetVersionString(version);
290-
ModulePlacer.PlaceVersion(qr, versionString);
291-
}
292-
325+
var data = new BitArray(interleavedLength);
326+
int pos = 0;
327+
for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++)
328+
{
329+
foreach (var codeBlock in codeWordWithECC)
330+
{
331+
if ((uint)codeBlock.CodeWordsLength / 8 > i)
332+
pos = bitArray.CopyTo(data, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8);
333+
}
334+
}
335+
for (var i = 0; i < eccInfo.ECCPerBlock; i++)
336+
{
337+
foreach (var codeBlock in codeWordWithECC)
338+
if (codeBlock.ECCWords.Length > i)
339+
pos = DecToBin(codeBlock.ECCWords[i], 8, data, pos);
340+
}
293341

294-
ModulePlacer.AddQuietZone(qr);
295-
return qr;
342+
return data;
343+
}
296344

297-
void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom)
345+
// Place the modules on the QR code matrix
346+
QRCodeData PlaceModules()
298347
{
299-
var groupLength = codewordsInGroup * 8;
300-
for (var i = 0; i < blocksInGroup; i++)
348+
var qr = new QRCodeData(version);
349+
var blockedModules = new List<Rectangle>(17);
350+
ModulePlacer.PlaceFinderPatterns(qr, blockedModules);
351+
ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, blockedModules);
352+
ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules);
353+
ModulePlacer.PlaceTimingPatterns(qr, blockedModules);
354+
ModulePlacer.PlaceDarkModule(qr, version, blockedModules);
355+
ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, blockedModules);
356+
ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules);
357+
var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel);
358+
var formatStr = GetFormatString(eccLevel, maskVersion);
359+
360+
ModulePlacer.PlaceFormat(qr, formatStr);
361+
if (version >= 7)
301362
{
302-
var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom);
303-
codeWordWithECC.Add(new CodewordBlock(offset2, groupLength, eccWordList));
304-
offset2 += groupLength;
363+
var versionString = GetVersionString(version);
364+
ModulePlacer.PlaceVersion(qr, versionString);
305365
}
366+
367+
ModulePlacer.AddQuietZone(qr);
368+
369+
return qr;
306370
}
307371
}
308372

QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ namespace QRCoder
895895
public static QRCoder.QRCodeData GenerateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
896896
public enum ECCLevel
897897
{
898+
Default = -1,
898899
L = 0,
899900
M = 1,
900901
Q = 2,

QRCoderApiTests/net60-windows/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ namespace QRCoder
903903
public static QRCoder.QRCodeData GenerateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
904904
public enum ECCLevel
905905
{
906+
Default = -1,
906907
L = 0,
907908
M = 1,
908909
Q = 2,

QRCoderApiTests/net60/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ namespace QRCoder
837837
public static QRCoder.QRCodeData GenerateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
838838
public enum ECCLevel
839839
{
840+
Default = -1,
840841
L = 0,
841842
M = 1,
842843
Q = 2,

QRCoderApiTests/netstandard13/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ namespace QRCoder
802802
public static QRCoder.QRCodeData GenerateQrCode(string plainText, QRCoder.QRCodeGenerator.ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, QRCoder.QRCodeGenerator.EciMode eciMode = 0, int requestedVersion = -1) { }
803803
public enum ECCLevel
804804
{
805+
Default = -1,
805806
L = 0,
806807
M = 1,
807808
Q = 2,

0 commit comments

Comments
 (0)