Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into speed2
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane32 committed May 19, 2024
2 parents 6b3e6a0 + 14a83f2 commit 6407949
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 7 deletions.
2 changes: 1 addition & 1 deletion QRCoder/PayloadGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
14 changes: 10 additions & 4 deletions QRCoder/QRCodeGenerator.ECCLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,35 @@ public partial class QRCodeGenerator
/// </summary>
public enum ECCLevel
{
/// <summary>
/// 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.
/// </summary>
Default = -1,

/// <summary>
/// Level L: Low error correction (approximately 7% of data can be recovered).
/// This level allows the highest data density.
/// </summary>
L,
L = 0,

/// <summary>
/// Level M: Medium error correction (approximately 15% of data can be recovered).
/// Offers a balance between data capacity and error recovery.
/// </summary>
M,
M = 1,

/// <summary>
/// Level Q: Quartile error correction (approximately 25% of data can be recovered).
/// More robust error correction at the cost of reduced data capacity.
/// </summary>
Q,
Q = 2,

/// <summary>
/// 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.
/// </summary>
H
H = 3
}
}
}
28 changes: 27 additions & 1 deletion QRCoder/QRCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload)
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
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);
}

Expand All @@ -121,6 +125,7 @@ public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLev
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
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);
Expand Down Expand Up @@ -165,7 +170,6 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
return GenerateQrCode(completeBitArray, eccLevel, version);
}


/// <summary>
/// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation.
/// </summary>
Expand All @@ -175,6 +179,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo
/// <returns>Returns the raw QR code data which can be used for rendering.</returns>
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);
Expand All @@ -187,6 +192,27 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel)
return GenerateQrCode(bitArray, eccLevel, version);
}

/// <summary>
/// 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.
/// </summary>
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 });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,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,
Expand Down
1 change: 1 addition & 0 deletions QRCoderApiTests/net60-windows/QRCoder.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,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,
Expand Down
1 change: 1 addition & 0 deletions QRCoderApiTests/net60/QRCoder.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,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,
Expand Down
1 change: 1 addition & 0 deletions QRCoderApiTests/netstandard13/QRCoder.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,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,
Expand Down
75 changes: 74 additions & 1 deletion QRCoderTests/QRGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Xunit;
using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel;


namespace QRCoderTests
{

Expand Down Expand Up @@ -93,7 +94,7 @@ public void validate_antilogtable()
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWX", "C9LlmjRV+TPDkR03MlgDvo/DP+U", 29)]
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXY", "+EtALGm0mrDrnZVW54WdXG612P0", 29)]
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ789012345", "3nFUvZ/Aa2wUdAj1zlMmSu9x4kU", 29)]
// versino 4 alphanumeric
// version 4 alphanumeric
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ7890123456", "9K6EinxynS2KRum46iQsVoPgM7k", 33)]

// version 1 binary
Expand Down Expand Up @@ -552,6 +553,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<QRCodeGenerator.ECCLevel>())
{
if (ecc == eccLevel || ecc == QRCodeGenerator.ECCLevel.Default)
Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)).ShouldBe(expected);
else
Should.Throw<ArgumentOutOfRangeException>(() => 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
Expand Down

0 comments on commit 6407949

Please sign in to comment.