From 9a9d53966ae425f5c36552a02f6f4737270d5be4 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:30:02 -0400 Subject: [PATCH 1/6] Enhance alphanumeric and numeric data segment encoding with new bit length calculations and writing methods --- .../AlphanumericDataSegment.cs | 36 ++++++++- .../QRCodeGenerator/AlphanumericEncoder.cs | 33 +++++++- QRCoder/QRCodeGenerator/NumericDataSegment.cs | 80 ++++++++++++++----- 3 files changed, 124 insertions(+), 25 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs index 98fcd1c9..b5ee76c0 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs @@ -27,10 +27,22 @@ public AlphanumericDataSegment(string alphanumericText) /// The QR code version (1-40, or -1 to -4 for Micro QR) /// The total number of bits required for this segment public override int GetBitLength(int version) + { + return GetBitLength(Text.Length, version); + } + + /// + /// Calculates the total bit length for encoding alphanumeric text of a given length for a specific QR code version. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The length of the alphanumeric text + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The total number of bits required + public static int GetBitLength(int textLength, int version) { int modeIndicatorLength = 4; int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric); - int dataLength = AlphanumericEncoder.GetBitLength(Text); + int dataLength = AlphanumericEncoder.GetBitLength(textLength); int length = modeIndicatorLength + countIndicatorLength + dataLength; return length; @@ -45,17 +57,33 @@ public override int GetBitLength(int version) /// The next index in the BitArray after the last bit written public override int WriteTo(BitArray bitArray, int startIndex, int version) { - var index = startIndex; + return WriteTo(Text, 0, Text.Length, bitArray, startIndex, version); + } + + /// + /// Writes a portion of alphanumeric text to a BitArray at the specified index. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The full alphanumeric text + /// The starting index in the text to encode from + /// The number of characters to encode + /// The target BitArray to write to + /// The starting index in the BitArray + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The next index in the BitArray after the last bit written + public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version) + { + var index = bitIndex; // write mode indicator index = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, index); // write count indicator int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric); - index = DecToBin(Text.Length, countIndicatorLength, bitArray, index); + index = DecToBin(length, countIndicatorLength, bitArray, index); // write data - encode alphanumeric text - index = AlphanumericEncoder.WriteToBitArray(Text, bitArray, index); + index = AlphanumericEncoder.WriteToBitArray(text, startIndex, length, bitArray, index); return index; } diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index 11b3da70..cd62e4d7 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -40,6 +40,16 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab /// public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; + /// + /// Calculates the bit length required to encode alphanumeric text of a given length. + /// + /// The length of the alphanumeric text to be encoded. + /// The number of bits required to encode the text. + public static int GetBitLength(int textLength) + { + return (textLength / 2) * 11 + (textLength & 1) * 6; + } + /// /// Calculates the bit length required to encode the given alphanumeric text. /// @@ -47,7 +57,7 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab /// The number of bits required to encode the text. public static int GetBitLength(string plainText) { - return (plainText.Length / 2) * 11 + (plainText.Length & 1) * 6; + return GetBitLength(plainText.Length); } /// @@ -75,8 +85,25 @@ public static BitArray GetBitArray(string plainText) /// The next index in the BitArray after the last bit written. public static int WriteToBitArray(string plainText, BitArray codeText, int codeIndex) { - var index = 0; - var count = plainText.Length; + return WriteToBitArray(plainText, 0, plainText.Length, codeText, codeIndex); + } + + /// + /// Writes a portion of alphanumeric plain text directly into an existing BitArray at the specified index. + /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, + /// and 6 bits for a single remaining character if the total count is odd. + /// + /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. + /// The starting index in the text to encode from. + /// The number of characters to encode. + /// The target BitArray to write to. + /// The starting index in the BitArray where writing should begin. + /// The next index in the BitArray after the last bit written. + public static int WriteToBitArray(string plainText, int startIndex, int length, BitArray codeText, int codeIndex) + { + var index = startIndex; + var count = length; + var endIndex = startIndex + length; // Process each pair of characters. while (count >= 2) diff --git a/QRCoder/QRCodeGenerator/NumericDataSegment.cs b/QRCoder/QRCodeGenerator/NumericDataSegment.cs index 61935736..68932885 100644 --- a/QRCoder/QRCodeGenerator/NumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/NumericDataSegment.cs @@ -27,10 +27,22 @@ public NumericDataSegment(string numericText) /// The QR code version (1-40, or -1 to -4 for Micro QR) /// The total number of bits required for this segment public override int GetBitLength(int version) + { + return GetBitLength(Text.Length, version); + } + + /// + /// Calculates the total bit length for encoding numeric text of a given length for a specific QR code version. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The length of the numeric text + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The total number of bits required + public static int GetBitLength(int textLength, int version) { int modeIndicatorLength = 4; int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Numeric); - int dataLength = Text.Length / 3 * 10 + (Text.Length % 3 == 1 ? 4 : Text.Length % 3 == 2 ? 7 : 0); + int dataLength = textLength / 3 * 10 + (textLength % 3 == 1 ? 4 : textLength % 3 == 2 ? 7 : 0); int length = modeIndicatorLength + countIndicatorLength + dataLength; return length; @@ -45,19 +57,33 @@ public override int GetBitLength(int version) /// The next index in the BitArray after the last bit written public override int WriteTo(BitArray bitArray, int startIndex, int version) { - var index = startIndex; + return WriteTo(Text, 0, Text.Length, bitArray, startIndex, version); + } + + /// + /// Writes a portion of numeric text to a BitArray at the specified index. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The full numeric text + /// The starting index in the text to encode from + /// The number of characters to encode + /// The target BitArray to write to + /// The starting index in the BitArray + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The next index in the BitArray after the last bit written + public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version) + { + var index = bitIndex; // write mode indicator index = DecToBin((int)EncodingMode.Numeric, 4, bitArray, index); // write count indicator int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Numeric); - index = DecToBin(Text.Length, countIndicatorLength, bitArray, index); + index = DecToBin(length, countIndicatorLength, bitArray, index); // write data - encode numeric text - var data = PlainTextToBinaryNumeric(Text); - data.CopyTo(bitArray, 0, index, data.Length); - index += data.Length; + index = PlainTextToBinaryNumeric(text, startIndex, length, bitArray, index); return index; } @@ -74,10 +100,26 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) // Calculate the length of the BitArray needed to encode the text. // Groups of three digits are encoded in 10 bits, remaining groups of two or one digits take 7 or 4 bits respectively. var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0)); - var index = 0; + PlainTextToBinaryNumeric(plainText, 0, plainText.Length, bitArray, 0); + return bitArray; + } + + /// + /// Converts a portion of numeric plain text into a binary format specifically optimized for QR codes, writing directly to an existing BitArray. + /// Numeric compression groups up to 3 digits into 10 bits, less for remaining digits if they do not complete a group of three. + /// + /// The numeric text to be encoded, which should only contain digit characters. + /// The starting index in the text to encode from. + /// The number of characters to encode. + /// The target BitArray to write to. + /// The starting index in the BitArray where bits will be written. + /// The next index in the BitArray after the last bit written. + private static int PlainTextToBinaryNumeric(string plainText, int offset, int length, BitArray bitArray, int bitIndex) + { + var endIndex = offset + length; // Process each group of three digits. - for (int i = 0; i < plainText.Length - 2; i += 3) + for (int i = offset; i < endIndex - 2; i += 3) { // Parse the next three characters as a decimal integer. #if HAS_SPAN @@ -86,29 +128,31 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); #endif // Convert the decimal to binary and store it in the BitArray. - index = DecToBin(dec, 10, bitArray, index); + bitIndex = DecToBin(dec, 10, bitArray, bitIndex); + offset += 3; + length -= 3; } // Handle any remaining digits if the total number is not a multiple of three. - if (plainText.Length % 3 == 2) // Two remaining digits are encoded in 7 bits. + if (length == 2) // Two remaining digits are encoded in 7 bits. { #if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); #endif - index = DecToBin(dec, 7, bitArray, index); + bitIndex = DecToBin(dec, 7, bitArray, bitIndex); } - else if (plainText.Length % 3 == 1) // One remaining digit is encoded in 4 bits. + else if (length == 1) // One remaining digit is encoded in 4 bits. { #if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); #endif - index = DecToBin(dec, 4, bitArray, index); + bitIndex = DecToBin(dec, 4, bitArray, bitIndex); } - return bitArray; + return bitIndex; } } From 0ebc8e4ca9ecbe038e4aa36229adc73dfb4dbe92 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:36:55 -0400 Subject: [PATCH 2/6] Refactor WriteTo method parameters for clarity and consistency in AlphanumericDataSegment --- QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs index b5ee76c0..4025698c 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs @@ -65,27 +65,25 @@ public override int WriteTo(BitArray bitArray, int startIndex, int version) /// Includes mode indicator, count indicator, and data bits. /// /// The full alphanumeric text - /// The starting index in the text to encode from + /// The starting index in the text to encode from /// The number of characters to encode /// The target BitArray to write to /// The starting index in the BitArray /// The QR code version (1-40, or -1 to -4 for Micro QR) /// The next index in the BitArray after the last bit written - public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version) + public static int WriteTo(string text, int offset, int length, BitArray bitArray, int bitIndex, int version) { - var index = bitIndex; - // write mode indicator - index = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, index); + bitIndex = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, bitIndex); // write count indicator int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric); - index = DecToBin(length, countIndicatorLength, bitArray, index); + bitIndex = DecToBin(length, countIndicatorLength, bitArray, bitIndex); // write data - encode alphanumeric text - index = AlphanumericEncoder.WriteToBitArray(text, startIndex, length, bitArray, index); + bitIndex = AlphanumericEncoder.WriteToBitArray(text, offset, length, bitArray, bitIndex); - return index; + return bitIndex; } } } From 74eaa58a0bb9d57812230ef94b648ad93e02d259 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:37:57 -0400 Subject: [PATCH 3/6] Refactor GetBitLength methods for parameter clarity and consistency in AlphanumericEncoder --- QRCoder/QRCodeGenerator/AlphanumericEncoder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index cd62e4d7..529420e0 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -41,23 +41,23 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; /// - /// Calculates the bit length required to encode alphanumeric text of a given length. + /// Calculates the bit length required to encode the given alphanumeric text. /// - /// The length of the alphanumeric text to be encoded. + /// The alphanumeric text to be encoded. /// The number of bits required to encode the text. - public static int GetBitLength(int textLength) + public static int GetBitLength(string plainText) { - return (textLength / 2) * 11 + (textLength & 1) * 6; + return GetBitLength(plainText.Length); } /// - /// Calculates the bit length required to encode the given alphanumeric text. + /// Calculates the bit length required to encode alphanumeric text of a given length. /// - /// The alphanumeric text to be encoded. + /// The length of the alphanumeric text to be encoded. /// The number of bits required to encode the text. - public static int GetBitLength(string plainText) + public static int GetBitLength(int textLength) { - return GetBitLength(plainText.Length); + return (textLength / 2) * 11 + (textLength & 1) * 6; } /// From ff57ed9b616d68914a733ca74160c54efe8923a0 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:39:37 -0400 Subject: [PATCH 4/6] Refactor GetBitLength and WriteToBitArray methods for parameter consistency and clarity --- .../QRCodeGenerator/AlphanumericEncoder.cs | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index 529420e0..b1c3f39d 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -40,16 +40,6 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab /// public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; - /// - /// Calculates the bit length required to encode the given alphanumeric text. - /// - /// The alphanumeric text to be encoded. - /// The number of bits required to encode the text. - public static int GetBitLength(string plainText) - { - return GetBitLength(plainText.Length); - } - /// /// Calculates the bit length required to encode alphanumeric text of a given length. /// @@ -69,25 +59,11 @@ public static int GetBitLength(int textLength) /// A BitArray representing the binary data of the encoded alphanumeric text. public static BitArray GetBitArray(string plainText) { - var codeText = new BitArray(GetBitLength(plainText)); - WriteToBitArray(plainText, codeText, 0); + var codeText = new BitArray(GetBitLength(plainText.Length)); + WriteToBitArray(plainText, 0, plainText.Length, codeText, 0); return codeText; } - /// - /// Writes alphanumeric plain text directly into an existing BitArray at the specified index. - /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, - /// and 6 bits for a single remaining character if the total count is odd. - /// - /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. - /// The target BitArray to write to. - /// The starting index in the BitArray where writing should begin. - /// The next index in the BitArray after the last bit written. - public static int WriteToBitArray(string plainText, BitArray codeText, int codeIndex) - { - return WriteToBitArray(plainText, 0, plainText.Length, codeText, codeIndex); - } - /// /// Writes a portion of alphanumeric plain text directly into an existing BitArray at the specified index. /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, From e1bf23d6f019b09c97d9a02dda0c4a8a9a1606db Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:40:49 -0400 Subject: [PATCH 5/6] Refactor WriteToBitArray method parameters for clarity and consistency --- QRCoder/QRCodeGenerator/AlphanumericEncoder.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index b1c3f39d..a2e8969f 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -70,17 +70,13 @@ public static BitArray GetBitArray(string plainText) /// and 6 bits for a single remaining character if the total count is odd. /// /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. - /// The starting index in the text to encode from. - /// The number of characters to encode. + /// The starting index in the text to encode from. + /// The number of characters to encode. /// The target BitArray to write to. /// The starting index in the BitArray where writing should begin. /// The next index in the BitArray after the last bit written. - public static int WriteToBitArray(string plainText, int startIndex, int length, BitArray codeText, int codeIndex) + public static int WriteToBitArray(string plainText, int index, int count, BitArray codeText, int codeIndex) { - var index = startIndex; - var count = length; - var endIndex = startIndex + length; - // Process each pair of characters. while (count >= 2) { From 3dabc79ed4048bbde7f5ae3fb0e3d49c054553da Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:44:36 -0400 Subject: [PATCH 6/6] Refactor PlainTextToBinaryNumeric method to handle remaining digits more efficiently --- QRCoder/QRCodeGenerator/NumericDataSegment.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/QRCoder/QRCodeGenerator/NumericDataSegment.cs b/QRCoder/QRCodeGenerator/NumericDataSegment.cs index 68932885..3337af62 100644 --- a/QRCoder/QRCodeGenerator/NumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/NumericDataSegment.cs @@ -134,23 +134,14 @@ private static int PlainTextToBinaryNumeric(string plainText, int offset, int le } // Handle any remaining digits if the total number is not a multiple of three. - if (length == 2) // Two remaining digits are encoded in 7 bits. + if (length > 0) // Two remaining digits are encoded in 7 bits; one remaining digit is encoded in 4 bits. { #if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(offset, length), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(offset, length), NumberStyles.None, CultureInfo.InvariantCulture); #endif - bitIndex = DecToBin(dec, 7, bitArray, bitIndex); - } - else if (length == 1) // One remaining digit is encoded in 4 bits. - { -#if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); -#else - var dec = int.Parse(plainText.Substring(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); -#endif - bitIndex = DecToBin(dec, 4, bitArray, bitIndex); + bitIndex = DecToBin(dec, length == 2 ? 7 : 4, bitArray, bitIndex); } return bitIndex;