Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
QrEncoder.cs
1using System;
2using System.Collections.Generic;
5
6namespace Waher.Content.QR
7{
11 public class QrEncoder : IDisposable
12 {
13 private readonly static ReedSolomonEC[] ecCalc = new ReedSolomonEC[31];
14 private readonly static byte[][] alignmentMarkers = new byte[][]
15 {
16 new byte[] { 6 },
17 new byte[] { 6, 18 },
18 new byte[] { 6, 22 },
19 new byte[] { 6, 26 },
20 new byte[] { 6, 30 },
21 new byte[] { 6, 34 },
22 new byte[] { 6, 22, 38 },
23 new byte[] { 6, 24, 42 },
24 new byte[] { 6, 26, 46 },
25 new byte[] { 6, 28, 50 },
26 new byte[] { 6, 30, 54 },
27 new byte[] { 6, 32, 58 },
28 new byte[] { 6, 34, 62 },
29 new byte[] { 6, 26, 46, 66 },
30 new byte[] { 6, 26, 48, 70 },
31 new byte[] { 6, 26, 50, 74 },
32 new byte[] { 6, 30, 54, 78 },
33 new byte[] { 6, 30, 56, 82 },
34 new byte[] { 6, 30, 58, 86 },
35 new byte[] { 6, 34, 62, 90 },
36 new byte[] { 6, 28, 50, 72, 94 },
37 new byte[] { 6, 26, 50, 74, 98 },
38 new byte[] { 6, 30, 54, 78, 102 },
39 new byte[] { 6, 28, 54, 80, 106 },
40 new byte[] { 6, 32, 58, 84, 110 },
41 new byte[] { 6, 30, 58, 86, 114 },
42 new byte[] { 6, 34, 62, 90, 118 },
43 new byte[] { 6, 26, 50, 74, 98 , 122 },
44 new byte[] { 6, 30, 54, 78, 102, 126 },
45 new byte[] { 6, 26, 52, 78, 104, 130 },
46 new byte[] { 6, 30, 56, 82, 108, 134 },
47 new byte[] { 6, 34, 60, 86, 112, 138 },
48 new byte[] { 6, 30, 58, 86, 114, 142 },
49 new byte[] { 6, 34, 62, 90, 118, 146 },
50 new byte[] { 6, 30, 54, 78, 102, 126, 150 },
51 new byte[] { 6, 24, 50, 76, 102, 128, 154 },
52 new byte[] { 6, 28, 54, 80, 106, 132, 158 },
53 new byte[] { 6, 32, 58, 84, 110, 136, 162 },
54 new byte[] { 6, 26, 54, 82, 110, 138, 166 },
55 new byte[] { 6, 30, 58, 86, 114, 142, 170 }
56 };
57
61 public QrEncoder()
62 {
63 }
64
68 public void Dispose()
69 {
70 }
71
72 private int BitLengthCharacterCount(int Version, EncodingMode Mode)
73 {
74 switch (Mode)
75 {
76 case EncodingMode.Numeric:
77 if (Version < 10)
78 return 10;
79 else if (Version < 27)
80 return 12;
81 else if (Version < 41)
82 return 14;
83 break;
84
85 case EncodingMode.Alphanumeric:
86 if (Version < 10)
87 return 9;
88 else if (Version < 27)
89 return 11;
90 else if (Version < 41)
91 return 13;
92 break;
93
94 case EncodingMode.Byte:
95 if (Version < 10)
96 return 8;
97 else if (Version < 41)
98 return 16;
99 break;
100
101 case EncodingMode.Kanji:
102 if (Version < 10)
103 return 8;
104 else if (Version < 27)
105 return 10;
106 else if (Version < 41)
107 return 12;
108 break;
109 }
110
111 throw new NotSupportedException("Character Count bit length not supported."); // TODO
112 }
113
121 public byte[] ApplyErrorCorrection(int Version, CorrectionLevel Level, byte[] Message)
122 {
123 return this.ApplyErrorCorrection(Versions.FindVersionInfo(Version, Level), Message);
124 }
125
132 public byte[] ApplyErrorCorrection(VersionInfo Version, byte[] Message)
133 {
134 int SourceIndex = Message.Length;
135 int MessageLen = Version.TotalDataBytes;
136
137 if (SourceIndex > MessageLen)
138 throw new ArgumentException("Message too long for selected version.", nameof(Message));
139 else if (SourceIndex < MessageLen)
140 {
141 Array.Resize(ref Message, MessageLen);
142 while (SourceIndex < MessageLen)
143 {
144 Message[SourceIndex++] = 236;
145 if (SourceIndex < MessageLen)
146 Message[SourceIndex++] = 17;
147 }
148 }
149
150 int i, j, c, d = Version.TotalBlocks;
151 byte[][] DataBlocks = new byte[d][];
152
153 SourceIndex = 0;
154 c = Version.DataBytesPerBlock1;
155 for (i = 0; i < Version.BlocksPerGroup1; i++)
156 {
157 DataBlocks[i] = new byte[c];
158 Array.Copy(Message, SourceIndex, DataBlocks[i], 0, c);
159 SourceIndex += c;
160 }
161
162 if (Version.BlocksPerGroup2 > 0)
163 {
164 c = Version.DataBytesPerBlock2;
165 for (j = 0; j < Version.BlocksPerGroup2; j++)
166 {
167 DataBlocks[i] = new byte[c];
168 Array.Copy(Message, SourceIndex, DataBlocks[i++], 0, c);
169 SourceIndex += c;
170 }
171 }
172
173 ReedSolomonEC EC = ecCalc[Version.EcBytesPerBlock];
174 if (EC is null)
175 {
176 EC = new ReedSolomonEC(Version.EcBytesPerBlock);
177 ecCalc[Version.EcBytesPerBlock] = EC;
178 }
179
180 byte[][] ControlBlocks = new byte[d][];
181
182 for (i = 0; i < d; i++)
183 ControlBlocks[i] = EC.GenerateCorrectionCode(DataBlocks[i]);
184
185 byte[] Result = new byte[Version.TotalBytes];
186 int DestinationIndex = 0;
187
188 for (i = 0, c = Version.DataBytesPerBlock1; i < c; i++)
189 {
190 for (j = 0; j < d; j++)
191 Result[DestinationIndex++] = DataBlocks[j][i];
192 }
193
194 if (Version.DataBytesPerBlock2 > c)
195 {
196 while (i < Version.DataBytesPerBlock2)
197 {
198 for (j = Version.BlocksPerGroup1; j < d; j++)
199 Result[DestinationIndex++] = DataBlocks[j][i];
200
201 i++;
202 }
203 }
204
205 for (i = 0, c = Version.EcBytesPerBlock; i < c; i++)
206 {
207 for (j = 0; j < d; j++)
208 Result[DestinationIndex++] = ControlBlocks[j][i];
209 }
210
211 return Result;
212 }
213
221 public QrMatrix GenerateMatrix(int Version, CorrectionLevel Level, byte[] Message)
222 {
223 return this.GenerateMatrix(Versions.FindVersionInfo(Version, Level), Message, true);
224 }
225
235 public QrMatrix GenerateMatrix(int Version, CorrectionLevel Level, byte[] Message, bool ApplyErrorCorrection)
236 {
237 return this.GenerateMatrix(Versions.FindVersionInfo(Version, Level), Message, ApplyErrorCorrection);
238 }
239
246 public QrMatrix GenerateMatrix(VersionInfo Version, byte[] Message)
247 {
248 return this.GenerateMatrix(Version, Message, true);
249 }
250
259 public QrMatrix GenerateMatrix(VersionInfo Version, byte[] Message, bool ApplyErrorCorrection)
260 {
261 return this.GenerateMatrix(Version, Message, ApplyErrorCorrection, false);
262 }
263
273 public QrMatrix GenerateMatrix(VersionInfo Version, byte[] Message, bool ApplyErrorCorrection, bool AssertMessageFit)
274 {
275 int Size = ((Version.Version - 1) << 2) + 21;
276 QrMatrix M = new QrMatrix(Size);
277 int i, j, c;
278
279 // Finder patterns
280
281 M.DrawFinderMarker(0, 0);
282 M.DrawFinderMarker(Size - 7, 0);
283 M.DrawFinderMarker(0, Size - 7);
284
285 // Separators
286
287 M.HLine(0, 7, 7, DotType.CodeBackground, false);
288 M.VLine(7, 0, 6, DotType.CodeBackground, false);
289
290 M.HLine(0, 7, Size - 8, DotType.CodeBackground, false);
291 M.VLine(7, Size - 7, Size - 1, DotType.CodeBackground, false);
292
293 M.HLine(Size - 8, Size - 1, 7, DotType.CodeBackground, false);
294 M.VLine(Size - 8, 0, 6, DotType.CodeBackground, false);
295
296 // Alignment patterns
297
298 if (Version.Version > 0 && Version.Version <= alignmentMarkers.Length)
299 {
300 byte[] Markers = alignmentMarkers[Version.Version - 1];
301 c = Markers.Length;
302
303 for (i = 0; i < c; i++)
304 {
305 for (j = 0; j < c; j++)
306 {
307 if ((i == 0 && (j == 0 || j == c - 1)) || (j == 0 && i == c - 1))
308 continue;
309
310 M.DrawAlignmentMarker(Markers[i] - 2, Markers[j] - 2);
311 }
312 }
313 }
314
315 // Timing patterns
316
317 M.HLine(8, Size - 9, 6, DotType.CodeForeground, true);
318 M.VLine(6, 8, Size - 9, DotType.CodeForeground, true);
319
320 // Dark module
321
322 M.HLine(8, 8, Size - 8, DotType.CodeForeground, false);
323
324 // Reserve format information area
325
326 M.HLine(0, 5, 8, DotType.CodeBackground, false);
327 M.HLine(7, 8, 8, DotType.CodeBackground, false);
328 M.VLine(8, 0, 5, DotType.CodeBackground, false);
329 M.VLine(8, 7, 7, DotType.CodeBackground, false);
330
331 M.HLine(Size - 8, Size - 1, 8, DotType.CodeBackground, false);
332
333 M.VLine(8, Size - 7, Size - 1, DotType.CodeBackground, false);
334
335 // Reserve version information area
336
337 if (Version.Version >= 7)
338 {
339 for (i = 0; i < 3; i++)
340 {
341 M.HLine(0, 5, Size - 9 - i, DotType.CodeBackground, false);
342 M.VLine(Size - 9 - i, 0, 5, DotType.CodeBackground, false);
343 }
344 }
345
346 // Data bits
347
348 M.SaveMask();
349
350 byte[] FullMessage = ApplyErrorCorrection ? this.ApplyErrorCorrection(Version, Message) : Message;
351 bool Fits = M.EncodeDataBits(FullMessage);
352
353 if (AssertMessageFit && !Fits)
354 throw new NotSupportedException("Message too long for selected QR version & error correction level.");
355
356 // Data masking
357
358 int Mask = -1;
359
360 for (i = 0, c = int.MaxValue; i < 8; i++)
361 {
362 j = M.Penalty(masks[i]);
363 if (j < c)
364 {
365 Mask = i;
366 c = j;
367 }
368 }
369
370 M.ApplyMask(masks[Mask]);
371
372 // Format information
373
374 uint u = (uint)Version.Level;
375 u <<= 3;
376 u |= (uint)Mask;
377 u = typeInformationBits[u];
378
379 u <<= 32 - 15;
380 M.WriteBits(u, 0, 8, 1, 0, 6);
381 M.WriteBits(u, 8, Size - 1, 0, -1, 7);
382 u <<= 6;
383 M.WriteBits(u, 6, 8, 1, 0, 1);
384 u <<= 1;
385 M.WriteBits(u, Size - 8, 8, 1, 0, 8);
386 M.WriteBits(u, 8, 8, 0, -1, 2);
387 u <<= 2;
388 M.WriteBits(u, 8, 5, 0, -1, 5);
389
390 // Version information
391
392 if (Version.Version >= 7)
393 {
394 u = versionInformationBits[Version.Version - 7];
395 u <<= 32 - 18;
396 for (i = 0; i < 6; i++)
397 {
398 M.WriteBits(u, 5 - i, Size - 9, 0, -1, 3);
399 M.WriteBits(u, Size - 9, 5 - i, -1, 0, 3);
400 u <<= 3;
401 }
402 }
403
404 return M;
405 }
406
407 private static readonly MaskFunction[] masks = new MaskFunction[]
408 {
410 QrMatrix.Mask4, QrMatrix.Mask5, QrMatrix.Mask6, QrMatrix.Mask7
411 };
412
413 private static readonly uint[] typeInformationBits = new uint[]
414 {
415 0b111011111000100,
416 0b111001011110011,
417 0b111110110101010,
418 0b111100010011101,
419 0b110011000101111,
420 0b110001100011000,
421 0b110110001000001,
422 0b110100101110110,
423 0b101010000010010,
424 0b101000100100101,
425 0b101111001111100,
426 0b101101101001011,
427 0b100010111111001,
428 0b100000011001110,
429 0b100111110010111,
430 0b100101010100000,
431 0b011010101011111,
432 0b011000001101000,
433 0b011111100110001,
434 0b011101000000110,
435 0b010010010110100,
436 0b010000110000011,
437 0b010111011011010,
438 0b010101111101101,
439 0b001011010001001,
440 0b001001110111110,
441 0b001110011100111,
442 0b001100111010000,
443 0b000011101100010,
444 0b000001001010101,
445 0b000110100001100,
446 0b000100000111011
447 };
448
449 private static readonly uint[] versionInformationBits = new uint[]
450 {
451 0b000111110010010100,
452 0b001000010110111100,
453 0b001001101010011001,
454 0b001010010011010011,
455 0b001011101111110110,
456 0b001100011101100010,
457 0b001101100001000111,
458 0b001110011000001101,
459 0b001111100100101000,
460 0b010000101101111000,
461 0b010001010001011101,
462 0b010010101000010111,
463 0b010011010100110010,
464 0b010100100110100110,
465 0b010101011010000011,
466 0b010110100011001001,
467 0b010111011111101100,
468 0b011000111011000100,
469 0b011001000111100001,
470 0b011010111110101011,
471 0b011011000010001110,
472 0b011100110000011010,
473 0b011101001100111111,
474 0b011110110101110101,
475 0b011111001001010000,
476 0b100000100111010101,
477 0b100001011011110000,
478 0b100010100010111010,
479 0b100011011110011111,
480 0b100100101100001011,
481 0b100101010000101110,
482 0b100110101001100100,
483 0b100111010101000001,
484 0b101000110001101001
485 };
486
493 public KeyValuePair<byte[], VersionInfo> Encode(CorrectionLevel Level, string Message)
494 {
495 BitWriter Output = new BitWriter();
496 ITextEncoder Encoder;
497 EncodingMode Mode;
498 byte[] Bin = null;
499 int ByteLen;
500 int NrCharacters;
501
502 if (NumericEncoder.CanEncode(Message))
503 {
504 Encoder = new NumericEncoder(Output);
505 NrCharacters = Message.Length;
506 ByteLen = NumericEncoder.GetByteLength(NrCharacters);
507 Output.WriteBits(0b0001, 4);
508 Mode = EncodingMode.Numeric;
509 }
510 else if (AlphanumericEncoder.CanEncode(Message))
511 {
512 Encoder = new AlphanumericEncoder(Output);
513 NrCharacters = Message.Length;
514 ByteLen = AlphanumericEncoder.GetByteLength(NrCharacters);
515 Output.WriteBits(0b0010, 4);
516 Mode = EncodingMode.Alphanumeric;
517 }
518 else
519 {
520 ByteEncoder ByteEncoder = new ByteEncoder(Output);
521 Encoder = ByteEncoder;
522
523 Bin = ByteEncoder.GetBytes(Message);
524 NrCharacters = ByteLen = Bin.Length;
525
526 Output.WriteBits(0b0100, 4);
527 Mode = EncodingMode.Byte;
528 }
529
530 VersionInfo[] Options;
531 VersionInfo Version = null;
532
533 switch (Level)
534 {
535 case CorrectionLevel.L:
536 Options = Versions.LowVersions;
537 break;
538
539 case CorrectionLevel.M:
540 Options = Versions.MediumVersions;
541 break;
542
543 case CorrectionLevel.Q:
544 Options = Versions.QuartileVersions;
545 break;
546
547 case CorrectionLevel.H:
548 default:
549 Options = Versions.HighVersions;
550 break;
551 }
552
553 foreach (VersionInfo Option in Options)
554 {
555 if (Option.TotalDataBytes >= ByteLen + 3)
556 {
557 Version = Option;
558 break;
559 }
560 }
561
562 if (Version is null)
563 throw new NotSupportedException("Message too large to fit in any of the recognized QR versions with the error correction level chosen.");
564
565 Output.WriteBits((uint)NrCharacters, this.BitLengthCharacterCount(Version.Version, Mode));
566
567 if (Bin is null)
568 Encoder.Encode(Message);
569 else
570 {
571 foreach (byte b in Bin)
572 Output.WriteBits(b, 8);
573 }
574
575 ByteLen = Output.TotalBits;
576 int i = (Version.TotalDataBytes << 3) - ByteLen;
577 if (i > 0)
578 {
579 if (i > 4)
580 i = 4;
581
582 Output.WriteBits(0, i); // Terminator
583 }
584
585 i = Output.TotalBits & 7;
586 if (i > 0)
587 Output.WriteBits(0, 8 - i); // Byte padding
588
589 i = Output.TotalBits >> 3;
590 ByteLen = Version.TotalDataBytes;
591
592 while (i++ < ByteLen)
593 {
594 Output.WriteBits(236, 8);
595 if (++i < ByteLen)
596 Output.WriteBits(17, 8);
597 }
598
599 return new KeyValuePair<byte[], VersionInfo>(Output.ToArray(), Version);
600 }
601
608 public QrMatrix GenerateMatrix(CorrectionLevel Level, string Message)
609 {
610 KeyValuePair<byte[], VersionInfo> P = this.Encode(Level, Message);
611 return this.GenerateMatrix(P.Value, P.Key, true, true);
612 }
613
614 }
615}
Class used to compute a QR code matrix.
Definition: QrMatrix.cs:29
bool EncodeDataBits(byte[] Data)
Encodes data bits in free positions in the matrix.
Definition: QrMatrix.cs:243
void WriteBits(uint Bits, int X, int Y, int Dx, int Dy, int NrBits)
Writes bits to the matrix.
Definition: QrMatrix.cs:849
void ApplyMask(MaskFunction Mask)
Applies a mask on the matrix.
Definition: QrMatrix.cs:824
void DrawAlignmentMarker(int X, int Y)
Draws a Alignment marker pattern in the matrix.
Definition: QrMatrix.cs:146
static bool Mask4(int x, int y)
Mask function 4
static bool Mask0(int x, int y)
Mask function 0
static bool Mask6(int x, int y)
Mask function 6
int Penalty()
Calculates the total penalty score of the matrix.
Definition: QrMatrix.cs:718
void HLine(int X1, int X2, int Y, DotType Dot, bool Dotted)
Draws a horizontal line in the matrix.
Definition: QrMatrix.cs:168
static bool Mask3(int x, int y)
Mask function 3
void VLine(int X, int Y1, int Y2, DotType Dot, bool Dotted)
Draws a horizontal line in the matrix.
Definition: QrMatrix.cs:193
void DrawFinderMarker(int X, int Y)
Draws a Finder marker pattern in the matrix.
Definition: QrMatrix.cs:127
static bool Mask2(int x, int _)
Mask function 2
static bool Mask5(int x, int y)
Mask function 5
void SaveMask()
Saves the currently defined dots as a mask.
Definition: QrMatrix.cs:815
static bool Mask1(int _, int y)
Mask function 1
Implements Reed-Solomon Error Correction using polynomial division over GF(256)[x].
byte[] GenerateCorrectionCode(byte[] Message)
Computes the Error Correction code for a message.
Contains information about one version of encoding.
Definition: VersionInfo.cs:7
int BlocksPerGroup2
Blocks in Group 2
Definition: VersionInfo.cs:65
int TotalDataBytes
Total number of data bytes
Definition: VersionInfo.cs:85
int TotalBytes
Total number of bytes
Definition: VersionInfo.cs:90
int EcBytesPerBlock
Error Correction bytes per block
Definition: VersionInfo.cs:50
int TotalBlocks
Total number of blocks
Definition: VersionInfo.cs:75
int DataBytesPerBlock2
Data bytes per block in group 2
Definition: VersionInfo.cs:70
int DataBytesPerBlock1
Data bytes per block in group 1
Definition: VersionInfo.cs:60
int BlocksPerGroup1
Blocks in Group 1
Definition: VersionInfo.cs:55
CorrectionLevel Level
Error correction level.
Definition: VersionInfo.cs:40
Internal database of QR versions and properties.
Definition: Versions.cs:9
static readonly VersionInfo[] QuartileVersions
Version information for Quartile Error Correction mode.
Definition: Versions.cs:128
static readonly VersionInfo[] HighVersions
Version information for High Error Correction mode.
Definition: Versions.cs:175
static VersionInfo FindVersionInfo(int Version, CorrectionLevel Level)
Fins information about a version and correction level.
Definition: Versions.cs:16
static readonly VersionInfo[] MediumVersions
Version information for Medium Error Correction mode.
Definition: Versions.cs:81
static readonly VersionInfo[] LowVersions
Version information for Low Error Correction mode.
Definition: Versions.cs:34
QR Code encoder.
Definition: QrEncoder.cs:12
void Dispose()
QR Code encoder.
Definition: QrEncoder.cs:68
KeyValuePair< byte[], VersionInfo > Encode(CorrectionLevel Level, string Message)
Encodes text for purposes of presenting it in the smallest QR Code matrix possible.
Definition: QrEncoder.cs:493
byte[] ApplyErrorCorrection(int Version, CorrectionLevel Level, byte[] Message)
Applies Error Correction to a byte message.
Definition: QrEncoder.cs:121
QrEncoder()
QR Code encoder.
Definition: QrEncoder.cs:61
QrMatrix GenerateMatrix(VersionInfo Version, byte[] Message)
Generates a QR Code matrix from a data message.
Definition: QrEncoder.cs:246
QrMatrix GenerateMatrix(VersionInfo Version, byte[] Message, bool ApplyErrorCorrection)
Generates a QR Code matrix from a data message.
Definition: QrEncoder.cs:259
QrMatrix GenerateMatrix(int Version, CorrectionLevel Level, byte[] Message, bool ApplyErrorCorrection)
Generates a QR Code matrix from a data message.
Definition: QrEncoder.cs:235
QrMatrix GenerateMatrix(int Version, CorrectionLevel Level, byte[] Message)
Generates a QR Code matrix from a data message.
Definition: QrEncoder.cs:221
byte[] ApplyErrorCorrection(VersionInfo Version, byte[] Message)
Applies Error Correction to a byte message.
Definition: QrEncoder.cs:132
QrMatrix GenerateMatrix(VersionInfo Version, byte[] Message, bool ApplyErrorCorrection, bool AssertMessageFit)
Generates a QR Code matrix from a data message.
Definition: QrEncoder.cs:273
QrMatrix GenerateMatrix(CorrectionLevel Level, string Message)
Generates the smallest QR Code matrix for a given data message.
Definition: QrEncoder.cs:608
static int GetByteLength(int NrCharacters)
Gets the number of bytes required to encode an alphanumeric message containing a specific number of c...
static bool CanEncode(string Text)
Checks if a text string can be encoded using the alphanumeric encoding.
Writes a sequence of bits (Most significant bits first).
Definition: BitWriter.cs:10
void WriteBits(uint Value, int NrBits)
Writes several bits to the output stream.
Definition: BitWriter.cs:59
Encodes alphanumeric strings from the ISO 8859-1 character set (by default). If that fails,...
Definition: ByteEncoder.cs:8
byte[] GetBytes(string Text)
Gets the byte representation of a string.
Definition: ByteEncoder.cs:37
static int GetByteLength(int NrCharacters)
Gets the number of bytes required to encode an alphanumeric message containing a specific number of c...
static bool CanEncode(string Text)
Checks if a text string can be encoded using the alphanumeric encoding.
Interface for text encoders.
Definition: ITextEncoder.cs:7
delegate bool MaskFunction(int x, int y)
Delegate for mask functions
CorrectionLevel
QR Code correction level.
Definition: Enumerations.cs:7
EncodingMode
QR Code encoding mode
Definition: Enumerations.cs:33
DotType
Type of dot in code.
Definition: Enumerations.cs:64