using System; using System.Text; using System.Drawing; using System.ComponentModel; using FastReport.Utils; namespace FastReport.Barcode { /// /// Generates the Code128 barcode. /// /// /// This barcode supports three code pages: A, B and C. You need to set appropriate code page /// in the barcode text, or use the auto encode feature. See the property /// for more details. /// /// This example shows how to configure the BarcodeObject to display Code128 barcode. /// /// BarcodeObject barcode; /// ... /// barcode.Barcode = new Barcode128(); /// (barcode.Barcode as Barcode128).AutoEncode = false; /// /// public class Barcode128 : LinearBarcodeBase { #region Fields #if READONLY_STRUCTS private readonly struct Code128 #else private struct Code128 #endif { #pragma warning disable FR0006 // Field name of struct must be longer than 2 characters. public readonly string a; public readonly string b; public readonly string c; #pragma warning restore FR0006 // Field name of struct must be longer than 2 characters. public readonly string data; public Code128(string a, string b, string c, string data) { this.a = a; this.b = b; this.c = c; this.data = data; } } private static Code128[] tabelle_128 = { new Code128(" ", " ", "00", "212222"), new Code128("!", "!", "01", "222122"), new Code128("\"", "\"", "02", "222221"), new Code128("#", "#", "03", "121223"), new Code128("$", "$", "04", "121322"), new Code128("%", "%", "05", "131222"), new Code128("&", "&", "06", "122213"), new Code128("'", "'", "07", "122312"), new Code128("(", "(", "08", "132212"), new Code128(")", ")", "09", "221213"), new Code128("*", "*", "10", "221312"), new Code128("+", "+", "11", "231212"), new Code128(",", ",", "12", "112232"), new Code128("-", "-", "13", "122132"), new Code128(".", ".", "14", "122231"), new Code128("/", "/", "15", "113222"), new Code128("0", "0", "16", "123122"), new Code128("1", "1", "17", "123221"), new Code128("2", "2", "18", "223211"), new Code128("3", "3", "19", "221132"), new Code128("4", "4", "20", "221231"), new Code128("5", "5", "21", "213212"), new Code128("6", "6", "22", "223112"), new Code128("7", "7", "23", "312131"), new Code128("8", "8", "24", "311222"), new Code128("9", "9", "25", "321122"), new Code128(":", ":", "26", "321221"), new Code128(";", ";", "27", "312212"), new Code128("<", "<", "28", "322112"), new Code128("=", "=", "29", "322211"), new Code128(">", ">", "30", "212123"), new Code128("?", "?", "31", "212321"), new Code128("@", "@", "32", "232121"), new Code128("A", "A", "33", "111323"), new Code128("B", "B", "34", "131123"), new Code128("C", "C", "35", "131321"), new Code128("D", "D", "36", "112313"), new Code128("E", "E", "37", "132113"), new Code128("F", "F", "38", "132311"), new Code128("G", "G", "39", "211313"), new Code128("H", "H", "40", "231113"), new Code128("I", "I", "41", "231311"), new Code128("J", "J", "42", "112133"), new Code128("K", "K", "43", "112331"), new Code128("L", "L", "44", "132131"), new Code128("M", "M", "45", "113123"), new Code128("N", "N", "46", "113321"), new Code128("O", "O", "47", "133121"), new Code128("P", "P", "48", "313121"), new Code128("Q", "Q", "49", "211331"), new Code128("R", "R", "50", "231131"), new Code128("S", "S", "51", "213113"), new Code128("T", "T", "52", "213311"), new Code128("U", "U", "53", "213131"), new Code128("V", "V", "54", "311123"), new Code128("W", "W", "55", "311321"), new Code128("X", "X", "56", "331121"), new Code128("Y", "Y", "57", "312113"), new Code128("Z", "Z", "58", "312311"), new Code128("[", "[", "59", "332111"), new Code128("\\", "\\", "60", "314111"), new Code128("]", "]", "61", "221411"), new Code128("^", "^", "62", "431111"), new Code128("_", "_", "63", "111224"), new Code128("\x00", "`", "64", "111422"), new Code128("\x01", "a", "65", "121124"), new Code128("\x02", "b", "66", "121421"), new Code128("\x03", "c", "67", "141122"), new Code128("\x04", "d", "68", "141221"), new Code128("\x05", "e", "69", "112214"), new Code128("\x06", "f", "70", "112412"), new Code128("\x07", "g", "71", "122114"), new Code128("\x08", "h", "72", "122411"), new Code128("\x09", "i", "73", "142112"), new Code128("\x0A", "j", "74", "142211"), new Code128("\x0B", "k", "75", "241211"), new Code128("\x0C", "l", "76", "221114"), new Code128("\x0D", "m", "77", "413111"), new Code128("\x0E", "n", "78", "241112"), new Code128("\x0F", "o", "79", "134111"), new Code128("\x10", "p", "80", "111242"), new Code128("\x11", "q", "81", "121142"), new Code128("\x12", "r", "82", "121241"), new Code128("\x13", "s", "83", "114212"), new Code128("\x14", "t", "84", "124112"), new Code128("\x15", "u", "85", "124211"), new Code128("\x16", "v", "86", "411212"), new Code128("\x17", "w", "87", "421112"), new Code128("\x18", "x", "88", "421211"), new Code128("\x19", "y", "89", "212141"), new Code128("\x1A", "z", "90", "214121"), new Code128("\x1B", "{", "91", "412121"), new Code128("\x1C", "|", "92", "111143"), new Code128("\x1D", "}", "93", "111341"), new Code128("\x1E", "~", "94", "131141"), new Code128("\x1F", "\x7F", "95", "114113"), new Code128(" ", " ", "96", "114311"), // FNC3 new Code128(" ", " ", "97", "411113"), // FNC2 new Code128(" ", " ", "98", "411311"), // SHIFT new Code128(" ", " ", "99", "113141"), // CODE C new Code128(" ", " ", " ", "114131"), // FNC4, CODE B new Code128(" ", " ", " ", "311141"), // FNC4, CODE A new Code128(" ", " ", " ", "411131"), // FNC1 new Code128(" ", " ", " ", "211412"), // START A new Code128(" ", " ", " ", "211214"), // START B new Code128(" ", " ", " ", "211232") // START C }; private enum Encoding { A, B, C, AorB, None } private bool autoEncode; #endregion #region Properties /// /// Gets or sets a value that determines whether the barcode should automatically /// use appropriate encoding. /// /// /// You may use this property to encode data automatically. If you set it to false, /// you must specify the code page inside the data string. The following control codes are available: /// /// /// Sequence /// Code128 control code /// /// /// &A; /// START A / CODE A /// /// /// &B; /// START B / CODE B /// /// /// &C; /// START C / CODE C /// /// /// /// &S; /// SHIFT /// /// /// &1; /// FNC1 /// /// /// &2; /// FNC2 /// /// /// &3; /// FNC3 /// /// /// &4; /// FNC4 /// /// /// The following example shows how to specify control codes: /// /// BarcodeObject barcode; /// barcode.Barcode = new Barcode128(); /// (barcode.Barcode as Barcode128).AutoEncode = false; /// barcode.Text = "&C;1234&A;ABC"; /// /// [DefaultValue(true)] public bool AutoEncode { get { return autoEncode; } set { autoEncode = value; } } /// public override bool IsNumeric { get { return false; } } #endregion #region Private Methods private bool IsDigit(char c) { return c >= '0' && c <= '9'; } private bool IsFourOrMoreDigits(string code, int index, out int numDigits) { numDigits = 0; if (IsDigit(code[index]) && index + 4 < code.Length) { while (index + numDigits < code.Length && IsDigit(code[index + numDigits])) { numDigits++; } } return numDigits >= 4; } private int FindCodeA(char c) { for (int i = 0; i < tabelle_128.Length; i++) { if (c == tabelle_128[i].a[0]) return i; } return -1; } private int FindCodeB(char c) { for (int i = 0; i < tabelle_128.Length; i++) { if (c == tabelle_128[i].b[0]) return i; } return -1; } private int FindCodeC(string c) { for (int i = 0; i < tabelle_128.Length; i++) { if (c == tabelle_128[i].c) return i; } return -1; } // Returns a group of characters with the same encoding. Updates encoding and index parameters. private string GetNextPortion(string code, ref int index, ref Encoding encoding) { if (index >= code.Length) return ""; string result = ""; // determine the first character encoding int aIndex = FindCodeA(code[index]); int bIndex = FindCodeB(code[index]); Encoding firstCharEncoding = Encoding.A; if (aIndex == -1 && bIndex != -1) firstCharEncoding = Encoding.B; else if (aIndex != -1 && bIndex != -1) firstCharEncoding = Encoding.AorB; // if we have four or more digits in the current position, use C encoding. int numDigits = 0; if (IsFourOrMoreDigits(code, index, out numDigits)) firstCharEncoding = Encoding.C; // if encoding = C, we have found the group, just return it. if (firstCharEncoding == Encoding.C) { // we need digit pairs, so round it to even value numDigits = (numDigits / 2) * 2; result = code.Substring(index, numDigits); index += numDigits; encoding = Encoding.C; return "&C;" + result; } // search for next characters with the same encoding. Calculate numChars with the same encoding. int numChars = 1; while (index + numChars < code.Length) { // same as above... aIndex = FindCodeA(code[index + numChars]); bIndex = FindCodeB(code[index + numChars]); Encoding nextCharEncoding = Encoding.A; if (aIndex == -1 && bIndex != -1) nextCharEncoding = Encoding.B; else if (aIndex != -1 && bIndex != -1) nextCharEncoding = Encoding.AorB; if (IsFourOrMoreDigits(code, index + numChars, out numDigits)) nextCharEncoding = Encoding.C; // switch to particular encoding from AorB if (nextCharEncoding != Encoding.C && nextCharEncoding != firstCharEncoding) { if (firstCharEncoding == Encoding.AorB) firstCharEncoding = nextCharEncoding; else if (nextCharEncoding == Encoding.AorB) nextCharEncoding = firstCharEncoding; } if (firstCharEncoding != nextCharEncoding) break; numChars++; } // give precedence to B encoding if (firstCharEncoding == Encoding.AorB) firstCharEncoding = Encoding.B; string prefix = firstCharEncoding == Encoding.A ? "&A;" : "&B;"; // if we have only one character, use SHIFT code to switch encoding. Do not change current encoding. if (encoding != firstCharEncoding && numChars == 1 && (encoding == Encoding.A || encoding == Encoding.B) && (firstCharEncoding == Encoding.A || firstCharEncoding == Encoding.B)) prefix = "&S;"; else encoding = firstCharEncoding; result = prefix + code.Substring(index, numChars); index += numChars; return result; } private string StripControlCodes(string code, bool stripFNCodes) { string result = ""; int index = 0; while (index < code.Length) { string nextChar = GetNextChar(code, ref index, Encoding.None); if (nextChar != "&A;" && nextChar != "&B;" && nextChar != "&C;" && nextChar != "&S;") { if (!stripFNCodes || (nextChar != "&1;" && nextChar != "&2;" && nextChar != "&3;" && nextChar != "&4;")) result += nextChar; } } return result; } private string Encode(string code) { code = StripControlCodes(code, false); string result = ""; int index = 0; Encoding encoding = Encoding.None; while (index < code.Length) { result += GetNextPortion(code, ref index, ref encoding); } return result; } private string GetNextChar(string code, ref int index, Encoding encoding) { if (index >= code.Length) return ""; string result = ""; // check special codes: // "&A;" means START A / CODE A // "&B;" means START B / CODE B // "&C;" means START C / CODE C // "&S;" means SHIFT // "&1;" means FNC1 // "&2;" means FNC2 // "&3;" means FNC3 // "&4;" means FNC4 if (code[index] == '&' && index + 2 < code.Length && code[index + 2] == ';') { char c = code[index + 1].ToString().ToUpper()[0]; if (c == 'A' || c == 'B' || c == 'C' || c == 'S' || c == '1' || c == '2' || c == '3' || c == '4') { index += 3; return "&" + c + ";"; } } // if encoding is C, get next two chars if (encoding == Encoding.C && index + 1 < code.Length) { result = code.Substring(index, 2); index += 2; return result; } result = code.Substring(index, 1); index++; return result; } #endregion #region Protected Methods internal override string StripControlCodes(string data) { return StripControlCodes(data, true); } internal override string GetPattern() { string code = text; if (AutoEncode) code = Encode(code); // get first char to determine encoding Encoding encoding = Encoding.None; int index = 0; string nextChar = GetNextChar(code, ref index, encoding); int checksum = 0; string startCode = ""; // setup encoding switch (nextChar) { case "&A;": encoding = Encoding.A; checksum = 103; startCode = tabelle_128[103].data; break; case "&B;": encoding = Encoding.B; checksum = 104; startCode = tabelle_128[104].data; break; case "&C;": encoding = Encoding.C; checksum = 105; startCode = tabelle_128[105].data; break; default: throw new Exception(Res.Get("Messages,InvalidBarcode1")); } string result = startCode; // Startcode int codeword_pos = 1; while (index < code.Length) { nextChar = GetNextChar(code, ref index, encoding); int idx = 0; switch (nextChar) { case "&A;": encoding = Encoding.A; idx = 101; break; case "&B;": encoding = Encoding.B; idx = 100; break; case "&C;": encoding = Encoding.C; idx = 99; break; case "&S;": if (encoding == Encoding.A) encoding = Encoding.B; else encoding = Encoding.A; idx = 98; break; case "&1;": idx = 102; break; case "&2;": idx = 97; break; case "&3;": idx = 96; break; case "&4;": idx = encoding == Encoding.A ? 101 : 100; break; default: if (encoding == Encoding.A) idx = FindCodeA(nextChar[0]); else if (encoding == Encoding.B) idx = FindCodeB(nextChar[0]); else idx = FindCodeC(nextChar); break; } if (idx < 0) throw new Exception(Res.Get("Messages,InvalidBarcode2")); result += tabelle_128[idx].data; checksum += idx * codeword_pos; codeword_pos++; // switch encoding back after the SHIFT if (nextChar == "&S;") { if (encoding == Encoding.A) encoding = Encoding.B; else encoding = Encoding.A; } } checksum = checksum % 103; result += tabelle_128[checksum].data; // stop code result += "2331112"; return DoConvert(result); } #endregion #region Public Methods /// public override void Assign(BarcodeBase source) { base.Assign(source); AutoEncode = (source as Barcode128).AutoEncode; } internal override void Serialize(FRWriter writer, string prefix, BarcodeBase diff) { base.Serialize(writer, prefix, diff); Barcode128 c = diff as Barcode128; if (c == null || AutoEncode != c.AutoEncode) writer.WriteBool(prefix + "AutoEncode", AutoEncode); } #endregion /// /// Initializes a new instance of the class with default settings. /// public Barcode128() { AutoEncode = true; } } }