Skip to content

Commit fd548fb

Browse files
authored
Prep DataSegment for multi mode QR codes (#683)
* Enhance alphanumeric and numeric data segment encoding with new bit length calculations and writing methods * Refactor WriteTo method parameters for clarity and consistency in AlphanumericDataSegment * Refactor GetBitLength methods for parameter clarity and consistency in AlphanumericEncoder * Refactor GetBitLength and WriteToBitArray methods for parameter consistency and clarity * Refactor WriteToBitArray method parameters for clarity and consistency * Refactor PlainTextToBinaryNumeric method to handle remaining digits more efficiently
1 parent 80448a1 commit fd548fb

File tree

3 files changed

+100
-40
lines changed

3 files changed

+100
-40
lines changed

QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,22 @@ public AlphanumericDataSegment(string alphanumericText)
2727
/// <param name="version">The QR code version (1-40, or -1 to -4 for Micro QR)</param>
2828
/// <returns>The total number of bits required for this segment</returns>
2929
public override int GetBitLength(int version)
30+
{
31+
return GetBitLength(Text.Length, version);
32+
}
33+
34+
/// <summary>
35+
/// Calculates the total bit length for encoding alphanumeric text of a given length for a specific QR code version.
36+
/// Includes mode indicator, count indicator, and data bits.
37+
/// </summary>
38+
/// <param name="textLength">The length of the alphanumeric text</param>
39+
/// <param name="version">The QR code version (1-40, or -1 to -4 for Micro QR)</param>
40+
/// <returns>The total number of bits required</returns>
41+
public static int GetBitLength(int textLength, int version)
3042
{
3143
int modeIndicatorLength = 4;
3244
int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric);
33-
int dataLength = AlphanumericEncoder.GetBitLength(Text);
45+
int dataLength = AlphanumericEncoder.GetBitLength(textLength);
3446
int length = modeIndicatorLength + countIndicatorLength + dataLength;
3547

3648
return length;
@@ -45,19 +57,33 @@ public override int GetBitLength(int version)
4557
/// <returns>The next index in the BitArray after the last bit written</returns>
4658
public override int WriteTo(BitArray bitArray, int startIndex, int version)
4759
{
48-
var index = startIndex;
60+
return WriteTo(Text, 0, Text.Length, bitArray, startIndex, version);
61+
}
4962

63+
/// <summary>
64+
/// Writes a portion of alphanumeric text to a BitArray at the specified index.
65+
/// Includes mode indicator, count indicator, and data bits.
66+
/// </summary>
67+
/// <param name="text">The full alphanumeric text</param>
68+
/// <param name="offset">The starting index in the text to encode from</param>
69+
/// <param name="length">The number of characters to encode</param>
70+
/// <param name="bitArray">The target BitArray to write to</param>
71+
/// <param name="bitIndex">The starting index in the BitArray</param>
72+
/// <param name="version">The QR code version (1-40, or -1 to -4 for Micro QR)</param>
73+
/// <returns>The next index in the BitArray after the last bit written</returns>
74+
public static int WriteTo(string text, int offset, int length, BitArray bitArray, int bitIndex, int version)
75+
{
5076
// write mode indicator
51-
index = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, index);
77+
bitIndex = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, bitIndex);
5278

5379
// write count indicator
5480
int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric);
55-
index = DecToBin(Text.Length, countIndicatorLength, bitArray, index);
81+
bitIndex = DecToBin(length, countIndicatorLength, bitArray, bitIndex);
5682

5783
// write data - encode alphanumeric text
58-
index = AlphanumericEncoder.WriteToBitArray(Text, bitArray, index);
84+
bitIndex = AlphanumericEncoder.WriteToBitArray(text, offset, length, bitArray, bitIndex);
5985

60-
return index;
86+
return bitIndex;
6187
}
6288
}
6389
}

QRCoder/QRCodeGenerator/AlphanumericEncoder.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ private static Dictionary<char, int> CreateAlphanumEncDict(char[] alphanumEncTab
4141
public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0;
4242

4343
/// <summary>
44-
/// Calculates the bit length required to encode the given alphanumeric text.
44+
/// Calculates the bit length required to encode alphanumeric text of a given length.
4545
/// </summary>
46-
/// <param name="plainText">The alphanumeric text to be encoded.</param>
46+
/// <param name="textLength">The length of the alphanumeric text to be encoded.</param>
4747
/// <returns>The number of bits required to encode the text.</returns>
48-
public static int GetBitLength(string plainText)
48+
public static int GetBitLength(int textLength)
4949
{
50-
return (plainText.Length / 2) * 11 + (plainText.Length & 1) * 6;
50+
return (textLength / 2) * 11 + (textLength & 1) * 6;
5151
}
5252

5353
/// <summary>
@@ -59,25 +59,24 @@ public static int GetBitLength(string plainText)
5959
/// <returns>A BitArray representing the binary data of the encoded alphanumeric text.</returns>
6060
public static BitArray GetBitArray(string plainText)
6161
{
62-
var codeText = new BitArray(GetBitLength(plainText));
63-
WriteToBitArray(plainText, codeText, 0);
62+
var codeText = new BitArray(GetBitLength(plainText.Length));
63+
WriteToBitArray(plainText, 0, plainText.Length, codeText, 0);
6464
return codeText;
6565
}
6666

6767
/// <summary>
68-
/// Writes alphanumeric plain text directly into an existing BitArray at the specified index.
68+
/// Writes a portion of alphanumeric plain text directly into an existing BitArray at the specified index.
6969
/// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters,
7070
/// and 6 bits for a single remaining character if the total count is odd.
7171
/// </summary>
7272
/// <param name="plainText">The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode.</param>
73+
/// <param name="index">The starting index in the text to encode from.</param>
74+
/// <param name="count">The number of characters to encode.</param>
7375
/// <param name="codeText">The target BitArray to write to.</param>
7476
/// <param name="codeIndex">The starting index in the BitArray where writing should begin.</param>
7577
/// <returns>The next index in the BitArray after the last bit written.</returns>
76-
public static int WriteToBitArray(string plainText, BitArray codeText, int codeIndex)
78+
public static int WriteToBitArray(string plainText, int index, int count, BitArray codeText, int codeIndex)
7779
{
78-
var index = 0;
79-
var count = plainText.Length;
80-
8180
// Process each pair of characters.
8281
while (count >= 2)
8382
{

QRCoder/QRCodeGenerator/NumericDataSegment.cs

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,22 @@ public NumericDataSegment(string numericText)
2727
/// <param name="version">The QR code version (1-40, or -1 to -4 for Micro QR)</param>
2828
/// <returns>The total number of bits required for this segment</returns>
2929
public override int GetBitLength(int version)
30+
{
31+
return GetBitLength(Text.Length, version);
32+
}
33+
34+
/// <summary>
35+
/// Calculates the total bit length for encoding numeric text of a given length for a specific QR code version.
36+
/// Includes mode indicator, count indicator, and data bits.
37+
/// </summary>
38+
/// <param name="textLength">The length of the numeric text</param>
39+
/// <param name="version">The QR code version (1-40, or -1 to -4 for Micro QR)</param>
40+
/// <returns>The total number of bits required</returns>
41+
public static int GetBitLength(int textLength, int version)
3042
{
3143
int modeIndicatorLength = 4;
3244
int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Numeric);
33-
int dataLength = Text.Length / 3 * 10 + (Text.Length % 3 == 1 ? 4 : Text.Length % 3 == 2 ? 7 : 0);
45+
int dataLength = textLength / 3 * 10 + (textLength % 3 == 1 ? 4 : textLength % 3 == 2 ? 7 : 0);
3446
int length = modeIndicatorLength + countIndicatorLength + dataLength;
3547

3648
return length;
@@ -45,19 +57,33 @@ public override int GetBitLength(int version)
4557
/// <returns>The next index in the BitArray after the last bit written</returns>
4658
public override int WriteTo(BitArray bitArray, int startIndex, int version)
4759
{
48-
var index = startIndex;
60+
return WriteTo(Text, 0, Text.Length, bitArray, startIndex, version);
61+
}
62+
63+
/// <summary>
64+
/// Writes a portion of numeric text to a BitArray at the specified index.
65+
/// Includes mode indicator, count indicator, and data bits.
66+
/// </summary>
67+
/// <param name="text">The full numeric text</param>
68+
/// <param name="startIndex">The starting index in the text to encode from</param>
69+
/// <param name="length">The number of characters to encode</param>
70+
/// <param name="bitArray">The target BitArray to write to</param>
71+
/// <param name="bitIndex">The starting index in the BitArray</param>
72+
/// <param name="version">The QR code version (1-40, or -1 to -4 for Micro QR)</param>
73+
/// <returns>The next index in the BitArray after the last bit written</returns>
74+
public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version)
75+
{
76+
var index = bitIndex;
4977

5078
// write mode indicator
5179
index = DecToBin((int)EncodingMode.Numeric, 4, bitArray, index);
5280

5381
// write count indicator
5482
int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Numeric);
55-
index = DecToBin(Text.Length, countIndicatorLength, bitArray, index);
83+
index = DecToBin(length, countIndicatorLength, bitArray, index);
5684

5785
// write data - encode numeric text
58-
var data = PlainTextToBinaryNumeric(Text);
59-
data.CopyTo(bitArray, 0, index, data.Length);
60-
index += data.Length;
86+
index = PlainTextToBinaryNumeric(text, startIndex, length, bitArray, index);
6187

6288
return index;
6389
}
@@ -74,10 +100,26 @@ private static BitArray PlainTextToBinaryNumeric(string plainText)
74100
// Calculate the length of the BitArray needed to encode the text.
75101
// Groups of three digits are encoded in 10 bits, remaining groups of two or one digits take 7 or 4 bits respectively.
76102
var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0));
77-
var index = 0;
103+
PlainTextToBinaryNumeric(plainText, 0, plainText.Length, bitArray, 0);
104+
return bitArray;
105+
}
106+
107+
/// <summary>
108+
/// Converts a portion of numeric plain text into a binary format specifically optimized for QR codes, writing directly to an existing BitArray.
109+
/// Numeric compression groups up to 3 digits into 10 bits, less for remaining digits if they do not complete a group of three.
110+
/// </summary>
111+
/// <param name="plainText">The numeric text to be encoded, which should only contain digit characters.</param>
112+
/// <param name="offset">The starting index in the text to encode from.</param>
113+
/// <param name="length">The number of characters to encode.</param>
114+
/// <param name="bitArray">The target BitArray to write to.</param>
115+
/// <param name="bitIndex">The starting index in the BitArray where bits will be written.</param>
116+
/// <returns>The next index in the BitArray after the last bit written.</returns>
117+
private static int PlainTextToBinaryNumeric(string plainText, int offset, int length, BitArray bitArray, int bitIndex)
118+
{
119+
var endIndex = offset + length;
78120

79121
// Process each group of three digits.
80-
for (int i = 0; i < plainText.Length - 2; i += 3)
122+
for (int i = offset; i < endIndex - 2; i += 3)
81123
{
82124
// Parse the next three characters as a decimal integer.
83125
#if HAS_SPAN
@@ -86,29 +128,22 @@ private static BitArray PlainTextToBinaryNumeric(string plainText)
86128
var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture);
87129
#endif
88130
// Convert the decimal to binary and store it in the BitArray.
89-
index = DecToBin(dec, 10, bitArray, index);
131+
bitIndex = DecToBin(dec, 10, bitArray, bitIndex);
132+
offset += 3;
133+
length -= 3;
90134
}
91135

92136
// Handle any remaining digits if the total number is not a multiple of three.
93-
if (plainText.Length % 3 == 2) // Two remaining digits are encoded in 7 bits.
137+
if (length > 0) // Two remaining digits are encoded in 7 bits; one remaining digit is encoded in 4 bits.
94138
{
95139
#if HAS_SPAN
96-
var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture);
140+
var dec = int.Parse(plainText.AsSpan(offset, length), NumberStyles.None, CultureInfo.InvariantCulture);
97141
#else
98-
var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture);
142+
var dec = int.Parse(plainText.Substring(offset, length), NumberStyles.None, CultureInfo.InvariantCulture);
99143
#endif
100-
index = DecToBin(dec, 7, bitArray, index);
101-
}
102-
else if (plainText.Length % 3 == 1) // One remaining digit is encoded in 4 bits.
103-
{
104-
#if HAS_SPAN
105-
var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture);
106-
#else
107-
var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture);
108-
#endif
109-
index = DecToBin(dec, 4, bitArray, index);
144+
bitIndex = DecToBin(dec, length == 2 ? 7 : 4, bitArray, bitIndex);
110145
}
111146

112-
return bitArray;
147+
return bitIndex;
113148
}
114149
}

0 commit comments

Comments
 (0)