using FastReport.Utils; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text; namespace FastReport.Barcode { /// /// Generates the 2D MaxiCode barcode. /// public class BarcodeMaxiCode : Barcode2DBase { /// /// Sets the MaxiCode mode to use. Only modes 2 to 6 are supported. /// [DefaultValue(4)] public int Mode { get { return mode; } set { mode = value; } } //public int ECI { get; set; } const float FieldSizeFactor = 2.47f; const float PenSizeFactor = 4.5f; MaxiCodeImpl maxiCodeImpl; int mode; /// /// Initializes a new instance of the class with default settings. /// public BarcodeMaxiCode() { Mode = 4; } internal override void Initialize(string text, bool showText, int angle, float zoom) { base.Initialize(text, showText, angle, zoom); maxiCodeImpl = new MaxiCodeImpl(base.text); maxiCodeImpl.setMode(Mode); maxiCodeImpl.encode(); } internal override SizeF CalcBounds() { int textAdd = showText ? 18 : 0; SizeF s = new SizeF(); foreach(MaxiCodeImpl.Hexagon hex in maxiCodeImpl.hexagons) { for (int i = 0; i < hex.pointX.Length; i++) { float pointX = FieldSizeFactor * (float)hex.pointX[i]; float pointY = FieldSizeFactor * (float)hex.pointY[i]; if (pointX > s.Width) s.Width = pointX; if (pointY > s.Height) s.Height = pointY; } } if (s.Width <= 0) s.Width = 100; if (s.Height <= 0) s.Height = 100; s.Height += textAdd; return s; } internal override void Draw2DBarcode(IGraphics g, float kx, float ky) { Brush b = new SolidBrush(Color); Pen p = new Pen(Color, PenSizeFactor * ((kx + ky) / 2)); foreach (MaxiCodeImpl.Hexagon hex in maxiCodeImpl.hexagons) { PointF[] points = new PointF[hex.pointX.Length]; for (int i = 0; i < hex.pointX.Length; i++) points[i] = new PointF(FieldSizeFactor * kx * (float)hex.pointX[i], FieldSizeFactor * ky * (float)hex.pointY[i]); g.FillPolygon(b, points); } foreach (MaxiCodeImpl.Ellipse circle in maxiCodeImpl.target) { g.DrawEllipse(p, FieldSizeFactor * kx * (float)circle.x, FieldSizeFactor * ky * (float)circle.y, FieldSizeFactor * kx * ((float)circle.w - (float)circle.x), FieldSizeFactor * ky * ((float)circle.h - (float)circle.y)); } b.Dispose(); p.Dispose(); } /// public override void Assign(BarcodeBase source) { base.Assign(source); BarcodeMaxiCode src = source as BarcodeMaxiCode; Mode = src.Mode; } internal override void Serialize(FastReport.Utils.FRWriter writer, string prefix, BarcodeBase diff) { base.Serialize(writer, prefix, diff); BarcodeMaxiCode c = diff as BarcodeMaxiCode; if (c == null || Mode != c.Mode) writer.WriteInt(prefix + "Mode", Mode); } } /* * Copyright 2014-2015 Robin Stuart, Daniel Gredler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Implements MaxiCode according to ISO 16023:2000. * * MaxiCode employs a pattern of hexagons around a central 'bulls-eye' * finder pattern. Encoding in several modes is supported, but encoding in * Mode 2 and 3 require primary messages to be set. Input characters can be * any from the ISO 8859-1 (Latin-1) character set. * * TODO: Add ECI functionality. * * @author Robin Stuart * @author Daniel Gredler */ class MaxiCodeImpl { public class ReedSolomon { private int logmod; private int rlen; private int[] logt; private int[] alog; private int[] rspoly; public int[] res; public int getResult(int count) { return res[count]; } public void init_gf(int poly) { int m, b, p, v; // Find the top bit, and hence the symbol size for (b = 1, m = 0; b <= poly; b <<= 1) { m++; } b >>= 1; m--; // Calculate the log/alog tables logmod = (1 << m) - 1; logt = new int[logmod + 1]; alog = new int[logmod]; for (p = 1, v = 0; v < logmod; v++) { alog[v] = p; logt[p] = v; p <<= 1; if ((p & b) != 0) { p ^= poly; } } } public void init_code(int nsym, int index) { int i, k; rspoly = new int[nsym + 1]; rlen = nsym; rspoly[0] = 1; for (i = 1; i <= nsym; i++) { rspoly[i] = 1; for (k = i - 1; k > 0; k--) { if (rspoly[k] != 0) { rspoly[k] = alog[(logt[rspoly[k]] + index) % logmod]; } rspoly[k] ^= rspoly[k - 1]; } rspoly[0] = alog[(logt[rspoly[0]] + index) % logmod]; index++; } } public void encode(int len, int[] data) { int i, k, m; res = new int[rlen]; for (i = 0; i < rlen; i++) { res[i] = 0; } for (i = 0; i < len; i++) { m = res[rlen - 1] ^ data[i]; for (k = rlen - 1; k > 0; k--) { if ((m != 0) && (rspoly[k] != 0)) { res[k] = res[k - 1] ^ alog[(logt[m] + logt[rspoly[k]]) % logmod]; } else { res[k] = res[k - 1]; } } if ((m != 0) && (rspoly[0] != 0)) { res[0] = alog[(logt[m] + logt[rspoly[0]]) % logmod]; } else { res[0] = 0; } } } } #if READONLY_STRUCTS public readonly struct Hexagon #else public struct Hexagon #endif { const double INK_SPREAD = 1.25; static readonly double[] OFFSET_X = new double[] { 0.0, 0.86, 0.86, 0.0, -0.86, -0.86 }; static readonly double[] OFFSET_Y = new double[] { 1.0, 0.5, -0.5, -1.0, -0.5, 0.5 }; public readonly double[] pointX; public readonly double[] pointY; public Hexagon(double centreX, double centreY) { pointX = new double[6]; pointY = new double[6]; for (int i = 0; i < 6; i++) { pointX[i] = centreX + (OFFSET_X[i] * INK_SPREAD); pointY[i] = centreY + (OFFSET_Y[i] * INK_SPREAD); } } } #if READONLY_STRUCTS public readonly struct Ellipse #else public struct Ellipse #endif { #pragma warning disable FR0006 // Field name of struct must be longer than 2 characters. public readonly double x; public readonly double y; public readonly double w; public readonly double h; #pragma warning restore FR0006 // Field name of struct must be longer than 2 characters. public Ellipse(double x, double y, double w, double h) { this.x = x; this.y = y; this.w = w; this.h = h; } } private int eciMode = 3; private byte[] inputBytes; //string error_msg; public List hexagons = new List(); public List target = new List(); private string text; public MaxiCodeImpl(string text) { this.text = text; } //======================================================================== /** MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). */ static int[] MAXICODE_GRID = { 122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0, 124, 123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0, 126, 125, 132, 131, 138, 137, 144, 143, 150, 149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818, 284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, 236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0, 286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, 214, 213, 208, 207, 822, 821, 288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0, 290, 289, 296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824, 292, 291, 298, 297, 304, 303, 310, 309, 316, 315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0, 294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, 341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827, 410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, 829, 0, 412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830, 414, 413, 408, 407, 402, 401, 396, 395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0, 416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, 439, 446, 445, 834, 833, 418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0, 420, 419, 426, 425, 432, 431, 108, 107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836, 482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, 457, 452, 451, 838, 0, 484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839, 486, 485, 480, 479, 474, 473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0, 488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, 517, 843, 842, 490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0, 492, 491, 498, 497, 504, 503, 102, 101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845, 560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, 529, 524, 523, 847, 0, 562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848, 564, 563, 558, 557, 552, 551, 546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0, 566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, 613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851, 568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, 640, 639, 646, 645, 853, 0, 570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854, 728, 727, 722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0, 730, 729, 724, 723, 718, 717, 712, 711, 706, 705, 700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857, 732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, 683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0, 734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, 805, 812, 811, 861, 860, 736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0, 738, 737, 744, 743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863 }; /** * ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A. * 1 = Set A, 2 = Set B, 3 = Set C, 4 = Set D, 5 = Set E. * 0 refers to special characters that fit into more than one set (e.g. GS). */ static int[] MAXICODE_SET = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4, 3, 3, 3, 4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; /** ASCII character to symbol value, from ISO/IEC 16023 Appendix A. */ static int[] MAXICODE_SYMBOL_CHAR = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37, 38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, 37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41, 42, 42, 47, 43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36 }; int mode; int structuredAppendPosition = 1; int structuredAppendTotal = 1; string primaryData = ""; int[] codewords; int[] source; int[] set = new int[144]; int[] character = new int[144]; bool[,] grid = new bool[33,30]; /** * Sets the MaxiCode mode to use. Only modes 2 to 6 are supported. * * @param mode the MaxiCode mode to use */ public void setMode(int mode) { if (mode < 2 || mode > 6) { throw new ArgumentOutOfRangeException("Invalid MaxiCode mode: " + mode); } this.mode = mode; } /** * Returns the MaxiCode mode being used. Only modes 2 to 6 are supported. * * @return the MaxiCode mode being used */ public int getMode() { return mode; } /** * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the * position of this symbol in the series. Valid values are 1 through 8 inclusive. * * @param position the position of this MaxiCode symbol in the structured append series */ public void setStructuredAppendPosition(int position) { if (position < 1 || position > 8) { throw new ArgumentOutOfRangeException("Invalid MaxiCode structured append position: " + position); } this.structuredAppendPosition = position; } /** * Returns the position of this MaxiCode symbol in a series of symbols using structured append. If this symbol is not part of * such a series, this method will return 1. * * @return the position of this MaxiCode symbol in a series of symbols using structured append */ public int getStructuredAppendPosition() { return structuredAppendPosition; } /** * If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the total * number of symbols in the series. Valid values are 1 through 8 inclusive. A value of 1 indicates that this symbol is not * part of a structured append series. * * @param total the total number of MaxiCode symbols in the structured append series */ public void setStructuredAppendTotal(int total) { if (total < 1 || total > 8) { throw new ArgumentOutOfRangeException("Invalid MaxiCode structured append total: " + total); } this.structuredAppendTotal = total; } /** * Returns the size of the series of MaxiCode symbols using structured append that this symbol is part of. If this symbol is * not part of a structured append series, this method will return 1. * * @return size of the series that this symbol is part of */ public int getStructuredAppendTotal() { return structuredAppendTotal; } /** * Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following structure: * * * * * * *
CharactersMeaning
1-9Postal code data which can consist of up to 9 digits (for mode 2) or up to 6 * alphanumeric characters (for mode 3). Remaining unused characters should be * filled with the SPACE character (ASCII 32).
10-12Three-digit country code according to ISO-3166.
13-15Three digit service code. This depends on your parcel courier.
* * @param primary the primary data */ public void setPrimary(String primary) { primaryData = primary; } /** * Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3. * * @return the primary data for this MaxiCode symbol */ public String getPrimary() { return primaryData; } /** {@inheritDoc} */ //@Override public bool encode() { inputBytes = Encoding.UTF8.GetBytes(text); // copy input data over into source int sourcelen = text.Length; source = new int[sourcelen]; //eciProcess(); for (int i = 0; i < sourcelen; i++) { source[i] = inputBytes[i] & 0xFF; } // mode 2 -> mode 3 if postal code isn't strictly numeric if (mode == 2) { for (int i = 0; i < 10 && i < primaryData.Length; i++) { if ((primaryData[i] < '0') || (primaryData[i] > '9')) { mode = 3; break; } } } // initialize the set and character arrays if (!processText()) { //error_msg = "Input data too long"; //return false; throw new Exception("Input data too long"); } // start building the codeword array, starting with a copy of the character data // insert primary message if this is a structured carrier message; insert mode otherwise codewords = new int[character.Length]; character.CopyTo(codewords, 0); if (mode == 2 || mode == 3) { int[] _primary = getPrimaryCodewords(); if (_primary == null) { return false; } codewords = insert(codewords, 0, _primary); } else { codewords = insert(codewords, 0, new int[] { mode }); } // insert structured append flag if necessary if (structuredAppendTotal > 1) { int[] flag = new int[2]; flag[0] = 33; // padding flag[1] = ((structuredAppendPosition - 1) << 3) | (structuredAppendTotal - 1); // position + total int index; if (mode == 2 || mode == 3) { index = 10; // first two data symbols in the secondary message } else { index = 1; // first two data symbols in the primary message (first symbol at index 0 isn't a data symbol) } codewords = insert(codewords, index, flag); } int secondaryMax, secondaryECMax; if (mode == 5) { // 68 data codewords, 56 error corrections in secondary message secondaryMax = 68; secondaryECMax = 56; } else { // 84 data codewords, 40 error corrections in secondary message secondaryMax = 84; secondaryECMax = 40; } // truncate data codewords to maximum data space available int totalMax = secondaryMax + 10; if (codewords.Length > totalMax) { //codewords = codewords.Take(totalMax).ToArray(); int[] __codewords = new int[totalMax]; Array.Copy(codewords, __codewords, totalMax); codewords = __codewords; } // insert primary error correction between primary message and secondary message (always EEC) //int[] primary = codewords.Take(10).ToArray(); int[] primary = new int[10]; Array.Copy(codewords, 0, primary, 0, 10); int[] primaryCheck = getErrorCorrection(primary, 10); codewords = insert(codewords, 10, primaryCheck); // calculate secondary error correction //int[] secondary = codewords.Skip(20).ToArray(); int[] secondary = new int[codewords.Length - 20]; Array.Copy(codewords, 20, secondary, 0, secondary.Length); int[] secondaryOdd = new int[secondary.Length / 2]; int[] secondaryEven = new int[secondary.Length / 2]; for (int i = 0; i < secondary.Length; i++) { if ((i & 1) != 0) { // odd secondaryOdd[(i - 1) / 2] = secondary[i]; } else { // even secondaryEven[i / 2] = secondary[i]; } } int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2); int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2); // add secondary error correction after secondary message int[] _codewords = codewords; codewords = new int[codewords.Length + secondaryECOdd.Length + secondaryECEven.Length]; Array.Copy(_codewords, codewords, _codewords.Length); //codewords = Arrays.copyOf(codewords, codewords.Length + secondaryECOdd.Length + secondaryECEven.Length); for (int i = 0; i < secondaryECOdd.Length; i++) { codewords[20 + secondaryMax + (2 * i) + 1] = secondaryECOdd[i]; } for (int i = 0; i < secondaryECEven.Length; i++) { codewords[20 + secondaryMax + (2 * i)] = secondaryECEven[i]; } //encodeInfo += "Mode: " + mode + "\n"; //encodeInfo += "ECC Codewords: " + secondaryECMax + "\n"; //encodeInfo += "Codewords: "; //for (int i = 0; i < codewords.Length; i++) //{ // encodeInfo += Integer.toString(codewords[i]) + " "; //} //encodeInfo += "\n"; // copy data into symbol grid int[] bit_pattern = new int[7]; for (int i = 0; i < 33; i++) { for (int j = 0; j < 30; j++) { int block = (MAXICODE_GRID[(i * 30) + j] + 5) / 6; int bit = (MAXICODE_GRID[(i * 30) + j] + 5) % 6; if (block != 0) { bit_pattern[0] = (codewords[block - 1] & 0x20) >> 5; bit_pattern[1] = (codewords[block - 1] & 0x10) >> 4; bit_pattern[2] = (codewords[block - 1] & 0x8) >> 3; bit_pattern[3] = (codewords[block - 1] & 0x4) >> 2; bit_pattern[4] = (codewords[block - 1] & 0x2) >> 1; bit_pattern[5] = (codewords[block - 1] & 0x1); if (bit_pattern[bit] != 0) { grid[i,j] = true; } else { grid[i,j] = false; } } } } // add orientation markings grid[0,28] = true; // top right filler grid[0, 29] = true; grid[9, 10] = true; // top left marker grid[9, 11] = true; grid[10, 11] = true; grid[15, 7] = true; // left hand marker grid[16, 8] = true; grid[16, 20] = true; // right hand marker grid[17, 20] = true; grid[22, 10] = true; // bottom left marker grid[23, 10] = true; grid[22, 17] = true; // bottom right marker grid[23, 17] = true; // the following is provided for compatibility, but the results are not useful //row_count = 33; //readable = ""; //pattern = new String[33]; //row_height = new int[33]; //for (int i = 0; i < 33; i++) //{ // StringBuilder bin = new StringBuilder(30); // for (int j = 0; j < 30; j++) // { // if (grid[i][j]) // { // bin.append("1"); // } // else // { // bin.append("0"); // } // } // pattern[i] = bin2pat(bin.toString()); // row_height[i] = 1; //} //symbol_height = 72; //symbol_width = 74; plotSymbol(); return true; } /** * Extracts the postal code, country code and service code from the primary data and returns the corresponding primary message * codewords. * * @return the primary message codewords */ int[] getPrimaryCodewords() { //assert mode == 2 || mode == 3; if (primaryData.Length != 15) { //error_msg = "Invalid Primary String"; //return null; throw new Exception("Invalid Primary String"); } for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */ if ((primaryData[i] < '0') || (primaryData[i] > '9')) { //error_msg = "Invalid Primary String"; //return null; throw new Exception("Invalid Primary String"); } } String postcode; if (mode == 2) { postcode = primaryData.Substring(0, 9); int index = postcode.IndexOf(' '); if (index != -1) { postcode = postcode.Substring(0, index); } } else { // if (mode == 3) postcode = primaryData.Substring(0, 6); } int country = int.Parse(primaryData.Substring(9, 3)); int service = int.Parse(primaryData.Substring(12, 3)); //if (debug) //{ // System.out.println("Using mode " + mode); // System.out.println(" Postcode: " + postcode); // System.out.println(" Country Code: " + country); // System.out.println(" Service: " + service); //} if (mode == 2) { return getMode2PrimaryCodewords(postcode, country, service); } else { // mode == 3 return getMode3PrimaryCodewords(postcode, country, service); } } /** * Returns the primary message codewords for mode 2. * * @param postcode the postal code * @param country the country code * @param service the service code * @return the primary message, as codewords */ static int[] getMode2PrimaryCodewords(String postcode, int country, int service) { for (int i = 0; i < postcode.Length; i++) { if (postcode[i] < '0' || postcode[i] > '9') { postcode = postcode.Substring(0, i); break; } } int postcodeNum = int.Parse(postcode); int[] primary = new int[10]; primary[0] = ((postcodeNum & 0x03) << 4) | 2; primary[1] = ((postcodeNum & 0xfc) >> 2); primary[2] = ((postcodeNum & 0x3f00) >> 8); primary[3] = ((postcodeNum & 0xfc000) >> 14); primary[4] = ((postcodeNum & 0x3f00000) >> 20); primary[5] = ((postcodeNum & 0x3c000000) >> 26) | ((postcode.Length & 0x3) << 4); primary[6] = ((postcode.Length & 0x3c) >> 2) | ((country & 0x3) << 4); primary[7] = (country & 0xfc) >> 2; primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); primary[9] = ((service & 0x3f0) >> 4); return primary; } /** * Returns the primary message codewords for mode 3. * * @param postcode the postal code * @param country the country code * @param service the service code * @return the primary message, as codewords */ static int[] getMode3PrimaryCodewords(String postcode, int country, int service) { int[] postcodeNums = new int[postcode.Length]; postcode = postcode.ToUpper(); for (int i = 0; i < postcodeNums.Length; i++) { postcodeNums[i] = postcode[i]; if (postcode[i] >= 'A' && postcode[i] <= 'Z') { // (Capital) letters shifted to Code Set A values postcodeNums[i] -= 64; } if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) { // Not a valid postal code character, use space instead postcodeNums[i] = 32; } // Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital // letters in Code Set A (e.g. LF becomes 'J') } int[] primary = new int[10]; primary[0] = ((postcodeNums[5] & 0x03) << 4) | 3; primary[1] = ((postcodeNums[4] & 0x03) << 4) | ((postcodeNums[5] & 0x3c) >> 2); primary[2] = ((postcodeNums[3] & 0x03) << 4) | ((postcodeNums[4] & 0x3c) >> 2); primary[3] = ((postcodeNums[2] & 0x03) << 4) | ((postcodeNums[3] & 0x3c) >> 2); primary[4] = ((postcodeNums[1] & 0x03) << 4) | ((postcodeNums[2] & 0x3c) >> 2); primary[5] = ((postcodeNums[0] & 0x03) << 4) | ((postcodeNums[1] & 0x3c) >> 2); primary[6] = ((postcodeNums[0] & 0x3c) >> 2) | ((country & 0x3) << 4); primary[7] = (country & 0xfc) >> 2; primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2); primary[9] = ((service & 0x3f0) >> 4); return primary; } /** * Formats text according to Appendix A, populating the {@link #set} and {@link #character} arrays. * * @return true if the content fits in this symbol and was formatted; false otherwise */ bool processText() { int Length = text.Length; int i, j, count, current_set; if (Length > 138) { return false; } for (i = 0; i < 144; i++) { set[i] = -1; character[i] = 0; } for (i = 0; i < Length; i++) { /* Look up characters in table from Appendix A - this gives value and code set for most characters */ set[i] = MAXICODE_SET[source[i]]; character[i] = MAXICODE_SYMBOL_CHAR[source[i]]; } // If a character can be represented in more than one code set, pick which version to use. if (set[0] == 0) { if (character[0] == 13) { character[0] = 0; } set[0] = 1; } for (i = 1; i < Length; i++) { if (set[i] == 0) { /* Special character that can be represented in more than one code set. */ if (character[i] == 13) { /* Carriage Return */ set[i] = bestSurroundingSet(i, Length, 1, 5); if (set[i] == 5) { character[i] = 13; } else { character[i] = 0; } } else if (character[i] == 28) { /* FS */ set[i] = bestSurroundingSet(i, Length, 1, 2, 3, 4, 5); if (set[i] == 5) { character[i] = 32; } } else if (character[i] == 29) { /* GS */ set[i] = bestSurroundingSet(i, Length, 1, 2, 3, 4, 5); if (set[i] == 5) { character[i] = 33; } } else if (character[i] == 30) { /* RS */ set[i] = bestSurroundingSet(i, Length, 1, 2, 3, 4, 5); if (set[i] == 5) { character[i] = 34; } } else if (character[i] == 32) { /* Space */ set[i] = bestSurroundingSet(i, Length, 1, 2, 3, 4, 5); if (set[i] == 1) { character[i] = 32; } else if (set[i] == 2) { character[i] = 47; } else { character[i] = 59; } } else if (character[i] == 44) { /* Comma */ set[i] = bestSurroundingSet(i, Length, 1, 2); if (set[i] == 2) { character[i] = 48; } } else if (character[i] == 46) { /* Full Stop */ set[i] = bestSurroundingSet(i, Length, 1, 2); if (set[i] == 2) { character[i] = 49; } } else if (character[i] == 47) { /* Slash */ set[i] = bestSurroundingSet(i, Length, 1, 2); if (set[i] == 2) { character[i] = 50; } } else if (character[i] == 58) { /* Colon */ set[i] = bestSurroundingSet(i, Length, 1, 2); if (set[i] == 2) { character[i] = 51; } } } } for (i = Length; i < set.Length; i++) { /* Add the padding */ if (set[Length - 1] == 2) { set[i] = 2; } else { set[i] = 1; } character[i] = 33; } /* Find candidates for number compression (not allowed in primary message in modes 2 and 3). */ if (mode == 2 || mode == 3) { j = 9; } else { j = 0; } count = 0; for (i = j; i < 143; i++) { if (set[i] == 1 && character[i] >= 48 && character[i] <= 57) { /* Character is a number */ count++; } else { count = 0; } if (count == 9) { /* Nine digits in a row can be compressed */ set[i] = 6; set[i - 1] = 6; set[i - 2] = 6; set[i - 3] = 6; set[i - 4] = 6; set[i - 5] = 6; set[i - 6] = 6; set[i - 7] = 6; set[i - 8] = 6; count = 0; } } /* Add shift and latch characters */ current_set = 1; i = 0; do { if ((set[i] != current_set) && (set[i] != 6)) { switch (set[i]) { case 1: if (i + 1 < set.Length && set[i + 1] == 1) { if (i + 2 < set.Length && set[i + 2] == 1) { if (i + 3 < set.Length && set[i + 3] == 1) { /* Latch A */ insert(i, 63); current_set = 1; Length++; i += 3; } else { /* 3 Shift A */ insert(i, 57); Length++; i += 2; } } else { /* 2 Shift A */ insert(i, 56); Length++; i++; } } else { /* Shift A */ insert(i, 59); Length++; } break; case 2: if (i + 1 < set.Length && set[i + 1] == 2) { /* Latch B */ insert(i, 63); current_set = 2; Length++; i++; } else { /* Shift B */ insert(i, 59); Length++; } break; case 3: if (i + 3 < set.Length && set[i + 1] == 3 && set[i + 2] == 3 && set[i + 3] == 3) { /* Lock In C */ insert(i, 60); insert(i, 60); current_set = 3; Length++; i += 3; } else { /* Shift C */ insert(i, 60); Length++; } break; case 4: if (i + 3 < set.Length && set[i + 1] == 4 && set[i + 2] == 4 && set[i + 3] == 4) { /* Lock In D */ insert(i, 61); insert(i, 61); current_set = 4; Length++; i += 3; } else { /* Shift D */ insert(i, 61); Length++; } break; case 5: if (i + 3 < set.Length && set[i + 1] == 5 && set[i + 2] == 5 && set[i + 3] == 5) { /* Lock In E */ insert(i, 62); insert(i, 62); current_set = 5; Length++; i += 3; } else { /* Shift E */ insert(i, 62); Length++; } break; default: throw new Exception("Unexpected set " + set[i] + " at index " + i + "."); } i++; } i++; } while (i < set.Length); /* Number compression has not been forgotten! It's handled below. */ i = 0; do { if (set[i] == 6) { /* Number compression */ int value = 0; for (j = 0; j < 9; j++) { value *= 10; value += (character[i + j] - '0'); } character[i] = 31; /* NS */ character[i + 1] = (value & 0x3f000000) >> 24; character[i + 2] = (value & 0xfc0000) >> 18; character[i + 3] = (value & 0x3f000) >> 12; character[i + 4] = (value & 0xfc0) >> 6; character[i + 5] = (value & 0x3f); i += 6; for (j = i; j < 140; j++) { set[j] = set[j + 3]; character[j] = character[j + 3]; } Length -= 3; } else { i++; } } while (i < set.Length); /* Inject ECI codes to beginning of data, according to Table 3 */ if (eciMode != 3) { insert(0, 27); // ECI if ((eciMode >= 0) && (eciMode <= 31)) { insert(1, eciMode & 0x1F); Length += 2; } if ((eciMode >= 32) && (eciMode <= 1023)) { insert(1, 0x20 + (eciMode >> 6)); insert(2, eciMode & 0x3F); Length += 3; } if ((eciMode >= 1024) && (eciMode <= 32767)) { insert(1, 0x30 + (eciMode >> 12)); insert(2, (eciMode >> 6) & 0x3F); insert(3, eciMode & 0x3F); Length += 4; } if ((eciMode >= 32768) && (eciMode <= 999999)) { insert(1, 0x38 + (eciMode >> 18)); insert(2, (eciMode >> 12) & 0x3F); insert(3, (eciMode >> 6) & 0x3F); insert(4, eciMode & 0x3F); Length += 5; } } /* Make sure we haven't exceeded the maximum data Length. */ if ((mode == 2 || mode == 3) && Length > 84) { return false; } if ((mode == 4 || mode == 6) && Length > 93) { return false; } if (mode == 5 && Length > 77) { return false; } return true; } /** * Guesses the best set to use at the specified index by looking at the surrounding sets. In general, characters in * lower-numbered sets are more common, so we choose them if we can. If no good surrounding sets can be found, the default * value returned is the first value from the valid set. * * @param index the current index * @param Length the maximum Length to look at * @param valid the valid sets for this index * @return the best set to use at the specified index */ int bestSurroundingSet(int index, int Length, params int[] valid) { int option1 = set[index - 1]; if (index + 1 < Length) { // we have two options to check int option2 = set[index + 1]; if (contains(valid, option1) && contains(valid, option2)) { return Math.Min(option1, option2); } else if (contains(valid, option1)) { return option1; } else if (contains(valid, option2)) { return option2; } else { return valid[0]; } } else { // we only have one option to check if (contains(valid, option1)) { return option1; } else { return valid[0]; } } } /** * Moves everything up so that the specified shift or latch character can be inserted. * * @param position the position beyond which everything needs to be shifted * @param c the latch or shift character to insert at the specified position, after everything has been shifted */ void insert(int position, int c) { for (int i = 143; i > position; i--) { set[i] = set[i - 1]; character[i] = character[i - 1]; } character[position] = c; } /** * Returns the error correction codewords for the specified data codewords. * * @param codewords the codewords that we need error correction codewords for * @param ecclen the number of error correction codewords needed * @return the error correction codewords for the specified data codewords */ static int[] getErrorCorrection(int[] codewords, int ecclen) { ReedSolomon rs = new ReedSolomon(); rs.init_gf(0x43); rs.init_code(ecclen, 1); rs.encode(codewords.Length, codewords); int[] results = new int[ecclen]; for (int i = 0; i < ecclen; i++) { results[i] = rs.getResult(results.Length - 1 - i); } return results; } /** {@inheritDoc} */ //@Override protected void plotSymbol() { // hexagons for (int row = 0; row < 33; row++) { for (int col = 0; col < 30; col++) { if (grid[row,col]) { double x = (2.46 * col) + 1.23; if ((row & 1) != 0) { x += 1.23; } double y = (2.135 * row) + 1.43; hexagons.Add(new Hexagon(x, y)); } } } // circles //double[] radii = { 10.85, 8.97, 7.10, 5.22, 3.31, 1.43 }; double[] radii = { 9.91, 6.16, 2.37 }; for (int i = 0; i < radii.Length; i++) { target.Add(new Ellipse( 35.76 - radii[i], 35.60 - radii[i], 35.76 + radii[i], 35.60 + radii[i])); } } /** {@inheritDoc} */ //@Override protected int[] getCodewords() { return codewords; } //======================================================================== int[] insert(int[] original, int index, int[] inserted) { int[] modified = new int[original.Length + inserted.Length]; Array.Copy(original, 0, modified, 0, index); Array.Copy(inserted, 0, modified, index, inserted.Length); Array.Copy(original, index, modified, index + inserted.Length, modified.Length - index - inserted.Length); return modified; } bool contains(int[] values, int value) { for (int i = 0; i < values.Length; i++) { if (values[i] == value) { return true; } } return false; } } }