diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs
index 39bae5f0..b4b0fd76 100644
--- a/QRCoder/PayloadGenerator.cs
+++ b/QRCoder/PayloadGenerator.cs
@@ -15,7 +15,7 @@ public static class PayloadGenerator
public abstract class Payload
{
public virtual int Version { get { return -1; } }
- public virtual QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.M; } }
+ public virtual QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.Default; } }
public virtual QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Default; } }
public abstract override string ToString();
}
diff --git a/QRCoder/QRCodeGenerator.ECCLevel.cs b/QRCoder/QRCodeGenerator.ECCLevel.cs
index aedfa3c9..d8a0ad57 100644
--- a/QRCoder/QRCodeGenerator.ECCLevel.cs
+++ b/QRCoder/QRCodeGenerator.ECCLevel.cs
@@ -8,29 +8,35 @@ public partial class QRCodeGenerator
///
public enum ECCLevel
{
+ ///
+ /// Default error correction level, which will select Level M (Medium) unless otherwise specified by the payload.
+ /// Level M allows approximately 15% of data to be recovered, offering a balance between data capacity and error recovery.
+ ///
+ Default = -1,
+
///
/// Level L: Low error correction (approximately 7% of data can be recovered).
/// This level allows the highest data density.
///
- L,
+ L = 0,
///
/// Level M: Medium error correction (approximately 15% of data can be recovered).
/// Offers a balance between data capacity and error recovery.
///
- M,
+ M = 1,
///
/// Level Q: Quartile error correction (approximately 25% of data can be recovered).
/// More robust error correction at the cost of reduced data capacity.
///
- Q,
+ Q = 2,
///
/// Level H: High error correction (approximately 30% of data can be recovered).
/// Provides the highest level of error recovery, ideal for environments with high risk of data loss.
///
- H
+ H = 3
}
}
}
diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs
index 43a69768..7f52921a 100644
--- a/QRCoder/QRCodeGenerator.cs
+++ b/QRCoder/QRCodeGenerator.cs
@@ -105,6 +105,10 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload)
/// Returns the raw QR code data which can be used for rendering.
public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel)
{
+ if (eccLevel == ECCLevel.Default)
+ eccLevel = payload.EccLevel;
+ else if (payload.EccLevel != ECCLevel.Default && eccLevel != payload.EccLevel)
+ throw new ArgumentOutOfRangeException(nameof(eccLevel), $"The provided payload requires a ECC level of {eccLevel}.");
return GenerateQrCode(payload.ToString(), eccLevel, false, false, payload.EciMode, payload.Version);
}
@@ -121,6 +125,7 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLev
/// Returns the raw QR code data which can be used for rendering.
public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1)
{
+ eccLevel = ValidateECCLevel(eccLevel);
EncodingMode encoding = GetEncodingFromPlaintext(plainText, forceUtf8);
var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8);
var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8);
@@ -165,7 +170,6 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
return GenerateQrCode(completeBitArray, eccLevel, version);
}
-
///
/// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation.
///
@@ -175,6 +179,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
/// Returns the raw QR code data which can be used for rendering.
public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
{
+ eccLevel = ValidateECCLevel(eccLevel);
int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel);
int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte);
@@ -187,6 +192,27 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
return GenerateQrCode(bitArray, eccLevel, version);
}
+ ///
+ /// Validates the specified error correction level.
+ /// Returns the provided level if it is valid, or the level M if the provided level is Default.
+ /// Throws an exception if an invalid level is provided.
+ ///
+ private static ECCLevel ValidateECCLevel(ECCLevel eccLevel)
+ {
+ switch (eccLevel)
+ {
+ case ECCLevel.L:
+ case ECCLevel.M:
+ case ECCLevel.Q:
+ case ECCLevel.H:
+ return eccLevel;
+ case ECCLevel.Default:
+ return ECCLevel.M;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Invalid error correction level.");
+ }
+ }
+
private static readonly BitArray _repeatingPattern = new BitArray(
new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true });
@@ -371,7 +397,7 @@ void WriteEccLevelAndVersion()
#endif
private static void TrimLeadingZeros(BitArray fStrEcc, ref int index, ref int count)
{
- while (!fStrEcc[index])
+ while (count > 0 && !fStrEcc[index])
{
index++;
count--;
diff --git a/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt b/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
index 0573589b..60b1ecc2 100644
--- a/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
+++ b/QRCoderApiTests/net35+net40+net50+net50-windows+netstandard20/QRCoder.approved.txt
@@ -895,6 +895,7 @@ namespace QRCoder
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) { }
public enum ECCLevel
{
+ Default = -1,
L = 0,
M = 1,
Q = 2,
diff --git a/QRCoderApiTests/net60-windows/QRCoder.approved.txt b/QRCoderApiTests/net60-windows/QRCoder.approved.txt
index 3f03e15b..ff07d0df 100644
--- a/QRCoderApiTests/net60-windows/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60-windows/QRCoder.approved.txt
@@ -903,6 +903,7 @@ namespace QRCoder
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) { }
public enum ECCLevel
{
+ Default = -1,
L = 0,
M = 1,
Q = 2,
diff --git a/QRCoderApiTests/net60/QRCoder.approved.txt b/QRCoderApiTests/net60/QRCoder.approved.txt
index 7b3c4ef1..e558ea6f 100644
--- a/QRCoderApiTests/net60/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60/QRCoder.approved.txt
@@ -837,6 +837,7 @@ namespace QRCoder
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) { }
public enum ECCLevel
{
+ Default = -1,
L = 0,
M = 1,
Q = 2,
diff --git a/QRCoderApiTests/netstandard13/QRCoder.approved.txt b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
index 311ba68f..34362e64 100644
--- a/QRCoderApiTests/netstandard13/QRCoder.approved.txt
+++ b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
@@ -802,6 +802,7 @@ namespace QRCoder
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) { }
public enum ECCLevel
{
+ Default = -1,
L = 0,
M = 1,
Q = 2,
diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs
index 8d0818cd..87b61fec 100644
--- a/QRCoderTests/QRGeneratorTests.cs
+++ b/QRCoderTests/QRGeneratorTests.cs
@@ -7,6 +7,7 @@
using System.Linq;
using System.Collections;
using System.Text;
+using System;
namespace QRCoderTests
{
@@ -191,6 +192,16 @@ public void can_generate_from_bytes()
result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011001011111110000000010000010010010100000100000000101110101010101011101000000001011101010010010111010000000010111010111000101110100000000100000100000001000001000000001111111010101011111110000000000000000011000000000000000000111100101010010011101000000001011100001001001001110000000010101011111011111110100000000000101000000110000000000000001011001001010100110000000000000000000110001000101000000000111111100110011011110000000001000001001111110111010000000010111010011100100101100000000101110101110010010010000000001011101011010100011000000000010000010110110101000100000000111111101011100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
}
+ [Fact]
+ [Category("QRGenerator/TextEncoding")]
+ public void trim_leading_zeros_works()
+ {
+ var gen = new QRCodeGenerator();
+ var qrData = gen.CreateQrCode("this is a test", QRCodeGenerator.ECCLevel.M);
+ var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray());
+ result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111001101011111110000000010000010010000100000100000000101110101101101011101000000001011101010001010111010000000010111010101010101110100000000100000101010101000001000000001111111010101011111110000000000000000110010000000000000000101111100011101111100000000001110100011110001100010000000001100011010110010011000000000100111000011010011100000000001001011001101011000100000000000000000100100001001100000000111111100111110001110000000001000001010011000011010000000010111010101110111101100000000101110101000000110100000000001011101011111000010000000000010000010010011010010000000000111111101101111100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+ }
+
[Fact]
[Category("QRGenerator/TextEncoding")]
public void isValidIso_works()
@@ -216,6 +227,78 @@ bool IsValidISO(string input)
}
}
}
+
+ [Fact]
+ [Category("QRGenerator/EccLevel")]
+ public void ecc_level_from_payload_works()
+ {
+ var stringValue = "this is a test";
+
+ // set up baselines
+ var expectedL = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.L));
+ var expectedM = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.M));
+ var expectedQ = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.Q));
+ var expectedH = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.H));
+
+ // ensure that the baselines are different from each other
+ expectedL.ShouldNotBe(expectedM);
+ expectedL.ShouldNotBe(expectedQ);
+ expectedL.ShouldNotBe(expectedH);
+ expectedM.ShouldNotBe(expectedQ);
+ expectedM.ShouldNotBe(expectedH);
+ expectedQ.ShouldNotBe(expectedH);
+
+ // validate that any ECC level can be used when the payload specifies a default ECC level
+ var payloadDefault = new SamplePayload(stringValue, QRCodeGenerator.ECCLevel.Default);
+ Encode(QRCodeGenerator.GenerateQrCode(payloadDefault)).ShouldBe(expectedM);
+ Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Default)).ShouldBe(expectedM);
+ Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.L)).ShouldBe(expectedL);
+ Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.M)).ShouldBe(expectedM);
+ Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Q)).ShouldBe(expectedQ);
+ Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.H)).ShouldBe(expectedH);
+
+ // validate that the ECC level specified in the payload is used when default is specified,
+ // or checks that the selected ECC level matches the payload ECC level, throwing an exception otherwise
+ Verify(QRCodeGenerator.ECCLevel.L, expectedL);
+ Verify(QRCodeGenerator.ECCLevel.M, expectedM);
+ Verify(QRCodeGenerator.ECCLevel.Q, expectedQ);
+ Verify(QRCodeGenerator.ECCLevel.H, expectedH);
+
+
+ void Verify(QRCodeGenerator.ECCLevel eccLevel, string expected)
+ {
+ var payload = new SamplePayload(stringValue, eccLevel);
+ Encode(QRCodeGenerator.GenerateQrCode(payload)).ShouldBe(expected);
+ foreach (var ecc in Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast())
+ {
+ if (ecc == eccLevel || ecc == QRCodeGenerator.ECCLevel.Default)
+ Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)).ShouldBe(expected);
+ else
+ Should.Throw(() => Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)));
+ }
+ }
+
+ string Encode(QRCodeData qrData)
+ {
+ return string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray());
+ }
+ }
+
+ private class SamplePayload : PayloadGenerator.Payload
+ {
+ private string _data;
+ private QRCodeGenerator.ECCLevel _eccLevel;
+
+ public SamplePayload(string data, QRCodeGenerator.ECCLevel eccLevel)
+ {
+ _data = data;
+ _eccLevel = eccLevel;
+ }
+
+ public override QRCodeGenerator.ECCLevel EccLevel => _eccLevel;
+
+ public override string ToString() => _data;
+ }
}
public static class ExtensionMethods