Skip to content

Commit 6407949

Browse files
committed
Merge remote-tracking branch 'origin/master' into speed2
2 parents 6b3e6a0 + 14a83f2 commit 6407949

File tree

8 files changed

+116
-7
lines changed

8 files changed

+116
-7
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: 27 additions & 1 deletion
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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,7 @@ namespace QRCoder
896896
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) { }
897897
public enum ECCLevel
898898
{
899+
Default = -1,
899900
L = 0,
900901
M = 1,
901902
Q = 2,

QRCoderApiTests/net60-windows/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ namespace QRCoder
904904
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) { }
905905
public enum ECCLevel
906906
{
907+
Default = -1,
907908
L = 0,
908909
M = 1,
909910
Q = 2,

QRCoderApiTests/net60/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ namespace QRCoder
838838
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) { }
839839
public enum ECCLevel
840840
{
841+
Default = -1,
841842
L = 0,
842843
M = 1,
843844
Q = 2,

QRCoderApiTests/netstandard13/QRCoder.approved.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,7 @@ namespace QRCoder
803803
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) { }
804804
public enum ECCLevel
805805
{
806+
Default = -1,
806807
L = 0,
807808
M = 1,
808809
Q = 2,

QRCoderTests/QRGeneratorTests.cs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using Xunit;
1111
using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel;
1212

13+
1314
namespace QRCoderTests
1415
{
1516

@@ -93,7 +94,7 @@ public void validate_antilogtable()
9394
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWX", "C9LlmjRV+TPDkR03MlgDvo/DP+U", 29)]
9495
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXY", "+EtALGm0mrDrnZVW54WdXG612P0", 29)]
9596
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ789012345", "3nFUvZ/Aa2wUdAj1zlMmSu9x4kU", 29)]
96-
// versino 4 alphanumeric
97+
// version 4 alphanumeric
9798
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ7890123456", "9K6EinxynS2KRum46iQsVoPgM7k", 33)]
9899

99100
// version 1 binary
@@ -552,6 +553,78 @@ bool IsValidISO(string input)
552553
}
553554
}
554555
}
556+
557+
[Fact]
558+
[Category("QRGenerator/EccLevel")]
559+
public void ecc_level_from_payload_works()
560+
{
561+
var stringValue = "this is a test";
562+
563+
// set up baselines
564+
var expectedL = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.L));
565+
var expectedM = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.M));
566+
var expectedQ = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.Q));
567+
var expectedH = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.H));
568+
569+
// ensure that the baselines are different from each other
570+
expectedL.ShouldNotBe(expectedM);
571+
expectedL.ShouldNotBe(expectedQ);
572+
expectedL.ShouldNotBe(expectedH);
573+
expectedM.ShouldNotBe(expectedQ);
574+
expectedM.ShouldNotBe(expectedH);
575+
expectedQ.ShouldNotBe(expectedH);
576+
577+
// validate that any ECC level can be used when the payload specifies a default ECC level
578+
var payloadDefault = new SamplePayload(stringValue, QRCodeGenerator.ECCLevel.Default);
579+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault)).ShouldBe(expectedM);
580+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Default)).ShouldBe(expectedM);
581+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.L)).ShouldBe(expectedL);
582+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.M)).ShouldBe(expectedM);
583+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Q)).ShouldBe(expectedQ);
584+
Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.H)).ShouldBe(expectedH);
585+
586+
// validate that the ECC level specified in the payload is used when default is specified,
587+
// or checks that the selected ECC level matches the payload ECC level, throwing an exception otherwise
588+
Verify(QRCodeGenerator.ECCLevel.L, expectedL);
589+
Verify(QRCodeGenerator.ECCLevel.M, expectedM);
590+
Verify(QRCodeGenerator.ECCLevel.Q, expectedQ);
591+
Verify(QRCodeGenerator.ECCLevel.H, expectedH);
592+
593+
594+
void Verify(QRCodeGenerator.ECCLevel eccLevel, string expected)
595+
{
596+
var payload = new SamplePayload(stringValue, eccLevel);
597+
Encode(QRCodeGenerator.GenerateQrCode(payload)).ShouldBe(expected);
598+
foreach (var ecc in Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast<QRCodeGenerator.ECCLevel>())
599+
{
600+
if (ecc == eccLevel || ecc == QRCodeGenerator.ECCLevel.Default)
601+
Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)).ShouldBe(expected);
602+
else
603+
Should.Throw<ArgumentOutOfRangeException>(() => Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)));
604+
}
605+
}
606+
607+
string Encode(QRCodeData qrData)
608+
{
609+
return string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray());
610+
}
611+
}
612+
613+
private class SamplePayload : PayloadGenerator.Payload
614+
{
615+
private string _data;
616+
private QRCodeGenerator.ECCLevel _eccLevel;
617+
618+
public SamplePayload(string data, QRCodeGenerator.ECCLevel eccLevel)
619+
{
620+
_data = data;
621+
_eccLevel = eccLevel;
622+
}
623+
624+
public override QRCodeGenerator.ECCLevel EccLevel => _eccLevel;
625+
626+
public override string ToString() => _data;
627+
}
555628
}
556629

557630
public static class ExtensionMethods

0 commit comments

Comments
 (0)