Skip to content

Commit 04cb78b

Browse files
Shane32gfoidl
andauthored
Split alphanumeric encoding from QRCodeGenerator, split encoding tables (#590)
* Split QRCodeGenerator further * update * update * update * update * update test * update * update * update * Separate GaloisField from CapacityTables * update * remove using * Update QRCoder/QRCodeGenerator/GaloisField.cs Co-authored-by: Günther Foidl <gue@korporal.at> --------- Co-authored-by: Günther Foidl <gue@korporal.at>
1 parent e6a4159 commit 04cb78b

File tree

9 files changed

+428
-303
lines changed

9 files changed

+428
-303
lines changed

QRCoder/QRCodeGenerator.cs

Lines changed: 13 additions & 296 deletions
Large diffs are not rendered by default.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System.Collections.Generic;
2+
3+
namespace QRCoder;
4+
5+
public partial class QRCodeGenerator
6+
{
7+
/// <summary>
8+
/// This class contains the alignment patterns used in QR codes.
9+
/// </summary>
10+
private static class AlignmentPatterns
11+
{
12+
/// <summary>
13+
/// A lookup table mapping QR code versions to their corresponding alignment patterns.
14+
/// </summary>
15+
private static readonly Dictionary<int, AlignmentPattern> _alignmentPatternTable = CreateAlignmentPatternTable();
16+
17+
/// <summary>
18+
/// Retrieves the alignment pattern for a specific QR code version.
19+
/// </summary>
20+
public static AlignmentPattern FromVersion(int version) => _alignmentPatternTable[version];
21+
22+
/// <summary>
23+
/// Creates a lookup table mapping QR code versions to their corresponding alignment patterns.
24+
/// Alignment patterns are used in QR codes to help scanners accurately read the code at high speeds and when partially obscured.
25+
/// This table provides the necessary patterns based on the QR code version which dictates the size and complexity of the QR code.
26+
/// </summary>
27+
/// <returns>A dictionary where keys are QR code version numbers and values are AlignmentPattern structures detailing the positions of alignment patterns for each version.</returns>
28+
private static Dictionary<int, AlignmentPattern> CreateAlignmentPatternTable()
29+
{
30+
var alignmentPatternBaseValues = new int[] { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 };
31+
var localAlignmentPatternTable = new Dictionary<int, AlignmentPattern>(40);
32+
33+
for (var i = 0; i < (7 * 40); i += 7)
34+
{
35+
var points = new List<Point>(50);
36+
for (var x = 0; x < 7; x++)
37+
{
38+
if (alignmentPatternBaseValues[i + x] != 0)
39+
{
40+
for (var y = 0; y < 7; y++)
41+
{
42+
if (alignmentPatternBaseValues[i + y] != 0)
43+
{
44+
var p = new Point(alignmentPatternBaseValues[i + x] - 2, alignmentPatternBaseValues[i + y] - 2);
45+
if (!points.Contains(p))
46+
points.Add(p);
47+
}
48+
}
49+
}
50+
}
51+
52+
var version = (i + 7) / 7;
53+
localAlignmentPatternTable.Add(version, new AlignmentPattern()
54+
{
55+
Version = version,
56+
PatternPositions = points
57+
});
58+
}
59+
return localAlignmentPatternTable;
60+
}
61+
}
62+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
5+
namespace QRCoder;
6+
7+
public partial class QRCodeGenerator
8+
{
9+
/// <summary>
10+
/// Encodes alphanumeric characters (<c>0–9</c>, <c>A–Z</c> (uppercase), space, <c>$</c>, <c>%</c>, <c>*</c>, <c>+</c>, <c>-</c>, period, <c>/</c>, colon) into a binary format suitable for QR codes.
11+
/// </summary>
12+
private static class AlphanumericEncoder
13+
{
14+
private static readonly char[] _alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' };
15+
16+
/// <summary>
17+
/// A dictionary mapping alphanumeric characters to their respective positions used in QR code encoding.
18+
/// This includes digits 0-9, uppercase letters A-Z, and some special characters.
19+
/// </summary>
20+
private static readonly Dictionary<char, int> _alphanumEncDict = CreateAlphanumEncDict(_alphanumEncTable);
21+
22+
/// <summary>
23+
/// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding.
24+
/// This includes digits 0-9, uppercase letters A-Z, and some special characters.
25+
/// </summary>
26+
/// <returns>A dictionary mapping each supported alphanumeric character to its corresponding value.</returns>
27+
private static Dictionary<char, int> CreateAlphanumEncDict(char[] alphanumEncTable)
28+
{
29+
var localAlphanumEncDict = new Dictionary<char, int>(45);
30+
// Add 0-9
31+
for (char c = '0'; c <= '9'; c++)
32+
localAlphanumEncDict.Add(c, c - '0');
33+
// Add uppercase alphabetic characters.
34+
for (char c = 'A'; c <= 'Z'; c++)
35+
localAlphanumEncDict.Add(c, localAlphanumEncDict.Count);
36+
// Add special characters from a predefined table.
37+
for (int i = 0; i < _alphanumEncTable.Length; i++)
38+
localAlphanumEncDict.Add(alphanumEncTable[i], localAlphanumEncDict.Count);
39+
return localAlphanumEncDict;
40+
}
41+
42+
/// <summary>
43+
/// Checks if a character is present in the alphanumeric encoding table.
44+
/// </summary>
45+
public static bool CanEncode(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0;
46+
47+
/// <summary>
48+
/// Converts alphanumeric plain text into a binary format optimized for QR codes.
49+
/// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters,
50+
/// and 6 bits for a single remaining character if the total count is odd.
51+
/// </summary>
52+
/// <param name="plainText">The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode.</param>
53+
/// <returns>A BitArray representing the binary data of the encoded alphanumeric text.</returns>
54+
public static BitArray GetBitArray(string plainText)
55+
{
56+
// Calculate the length of the BitArray needed based on the number of character pairs.
57+
var codeText = new BitArray((plainText.Length / 2) * 11 + (plainText.Length & 1) * 6);
58+
var codeIndex = 0;
59+
var index = 0;
60+
var count = plainText.Length;
61+
62+
// Process each pair of characters.
63+
while (count >= 2)
64+
{
65+
// Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating.
66+
var dec = _alphanumEncDict[plainText[index++]] * 45 + _alphanumEncDict[plainText[index++]];
67+
// Convert the number to binary and store it in the BitArray.
68+
codeIndex = DecToBin(dec, 11, codeText, codeIndex);
69+
count -= 2;
70+
}
71+
72+
// Handle the last character if the length is odd.
73+
if (count > 0)
74+
{
75+
DecToBin(_alphanumEncDict[plainText[index]], 6, codeText, codeIndex);
76+
}
77+
78+
return codeText;
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)