Barcode128.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. using System;
  2. using System.Text;
  3. using System.Drawing;
  4. using System.ComponentModel;
  5. using FastReport.Utils;
  6. namespace FastReport.Barcode
  7. {
  8. /// <summary>
  9. /// Generates the Code128 barcode.
  10. /// </summary>
  11. /// <remarks>
  12. /// This barcode supports three code pages: A, B and C. You need to set appropriate code page
  13. /// in the barcode text, or use the auto encode feature. See the <see cref="AutoEncode"/> property
  14. /// for more details.
  15. /// </remarks>
  16. /// <example>This example shows how to configure the BarcodeObject to display Code128 barcode.
  17. /// <code>
  18. /// BarcodeObject barcode;
  19. /// ...
  20. /// barcode.Barcode = new Barcode128();
  21. /// (barcode.Barcode as Barcode128).AutoEncode = false;
  22. /// </code>
  23. /// </example>
  24. public class Barcode128 : LinearBarcodeBase
  25. {
  26. #region Fields
  27. #if READONLY_STRUCTS
  28. private readonly struct Code128
  29. #else
  30. private struct Code128
  31. #endif
  32. {
  33. #pragma warning disable FR0006 // Field name of struct must be longer than 2 characters.
  34. public readonly string a;
  35. public readonly string b;
  36. public readonly string c;
  37. #pragma warning restore FR0006 // Field name of struct must be longer than 2 characters.
  38. public readonly string data;
  39. public Code128(string a, string b, string c, string data)
  40. {
  41. this.a = a;
  42. this.b = b;
  43. this.c = c;
  44. this.data = data;
  45. }
  46. }
  47. private static Code128[] tabelle_128 = {
  48. new Code128(" ", " ", "00", "212222"),
  49. new Code128("!", "!", "01", "222122"),
  50. new Code128("\"", "\"", "02", "222221"),
  51. new Code128("#", "#", "03", "121223"),
  52. new Code128("$", "$", "04", "121322"),
  53. new Code128("%", "%", "05", "131222"),
  54. new Code128("&", "&", "06", "122213"),
  55. new Code128("'", "'", "07", "122312"),
  56. new Code128("(", "(", "08", "132212"),
  57. new Code128(")", ")", "09", "221213"),
  58. new Code128("*", "*", "10", "221312"),
  59. new Code128("+", "+", "11", "231212"),
  60. new Code128(",", ",", "12", "112232"),
  61. new Code128("-", "-", "13", "122132"),
  62. new Code128(".", ".", "14", "122231"),
  63. new Code128("/", "/", "15", "113222"),
  64. new Code128("0", "0", "16", "123122"),
  65. new Code128("1", "1", "17", "123221"),
  66. new Code128("2", "2", "18", "223211"),
  67. new Code128("3", "3", "19", "221132"),
  68. new Code128("4", "4", "20", "221231"),
  69. new Code128("5", "5", "21", "213212"),
  70. new Code128("6", "6", "22", "223112"),
  71. new Code128("7", "7", "23", "312131"),
  72. new Code128("8", "8", "24", "311222"),
  73. new Code128("9", "9", "25", "321122"),
  74. new Code128(":", ":", "26", "321221"),
  75. new Code128(";", ";", "27", "312212"),
  76. new Code128("<", "<", "28", "322112"),
  77. new Code128("=", "=", "29", "322211"),
  78. new Code128(">", ">", "30", "212123"),
  79. new Code128("?", "?", "31", "212321"),
  80. new Code128("@", "@", "32", "232121"),
  81. new Code128("A", "A", "33", "111323"),
  82. new Code128("B", "B", "34", "131123"),
  83. new Code128("C", "C", "35", "131321"),
  84. new Code128("D", "D", "36", "112313"),
  85. new Code128("E", "E", "37", "132113"),
  86. new Code128("F", "F", "38", "132311"),
  87. new Code128("G", "G", "39", "211313"),
  88. new Code128("H", "H", "40", "231113"),
  89. new Code128("I", "I", "41", "231311"),
  90. new Code128("J", "J", "42", "112133"),
  91. new Code128("K", "K", "43", "112331"),
  92. new Code128("L", "L", "44", "132131"),
  93. new Code128("M", "M", "45", "113123"),
  94. new Code128("N", "N", "46", "113321"),
  95. new Code128("O", "O", "47", "133121"),
  96. new Code128("P", "P", "48", "313121"),
  97. new Code128("Q", "Q", "49", "211331"),
  98. new Code128("R", "R", "50", "231131"),
  99. new Code128("S", "S", "51", "213113"),
  100. new Code128("T", "T", "52", "213311"),
  101. new Code128("U", "U", "53", "213131"),
  102. new Code128("V", "V", "54", "311123"),
  103. new Code128("W", "W", "55", "311321"),
  104. new Code128("X", "X", "56", "331121"),
  105. new Code128("Y", "Y", "57", "312113"),
  106. new Code128("Z", "Z", "58", "312311"),
  107. new Code128("[", "[", "59", "332111"),
  108. new Code128("\\", "\\", "60", "314111"),
  109. new Code128("]", "]", "61", "221411"),
  110. new Code128("^", "^", "62", "431111"),
  111. new Code128("_", "_", "63", "111224"),
  112. new Code128("\x00", "`", "64", "111422"),
  113. new Code128("\x01", "a", "65", "121124"),
  114. new Code128("\x02", "b", "66", "121421"),
  115. new Code128("\x03", "c", "67", "141122"),
  116. new Code128("\x04", "d", "68", "141221"),
  117. new Code128("\x05", "e", "69", "112214"),
  118. new Code128("\x06", "f", "70", "112412"),
  119. new Code128("\x07", "g", "71", "122114"),
  120. new Code128("\x08", "h", "72", "122411"),
  121. new Code128("\x09", "i", "73", "142112"),
  122. new Code128("\x0A", "j", "74", "142211"),
  123. new Code128("\x0B", "k", "75", "241211"),
  124. new Code128("\x0C", "l", "76", "221114"),
  125. new Code128("\x0D", "m", "77", "413111"),
  126. new Code128("\x0E", "n", "78", "241112"),
  127. new Code128("\x0F", "o", "79", "134111"),
  128. new Code128("\x10", "p", "80", "111242"),
  129. new Code128("\x11", "q", "81", "121142"),
  130. new Code128("\x12", "r", "82", "121241"),
  131. new Code128("\x13", "s", "83", "114212"),
  132. new Code128("\x14", "t", "84", "124112"),
  133. new Code128("\x15", "u", "85", "124211"),
  134. new Code128("\x16", "v", "86", "411212"),
  135. new Code128("\x17", "w", "87", "421112"),
  136. new Code128("\x18", "x", "88", "421211"),
  137. new Code128("\x19", "y", "89", "212141"),
  138. new Code128("\x1A", "z", "90", "214121"),
  139. new Code128("\x1B", "{", "91", "412121"),
  140. new Code128("\x1C", "|", "92", "111143"),
  141. new Code128("\x1D", "}", "93", "111341"),
  142. new Code128("\x1E", "~", "94", "131141"),
  143. new Code128("\x1F", "\x7F", "95", "114113"),
  144. new Code128(" ", " ", "96", "114311"), // FNC3
  145. new Code128(" ", " ", "97", "411113"), // FNC2
  146. new Code128(" ", " ", "98", "411311"), // SHIFT
  147. new Code128(" ", " ", "99", "113141"), // CODE C
  148. new Code128(" ", " ", " ", "114131"), // FNC4, CODE B
  149. new Code128(" ", " ", " ", "311141"), // FNC4, CODE A
  150. new Code128(" ", " ", " ", "411131"), // FNC1
  151. new Code128(" ", " ", " ", "211412"), // START A
  152. new Code128(" ", " ", " ", "211214"), // START B
  153. new Code128(" ", " ", " ", "211232") // START C
  154. };
  155. private enum Encoding { A, B, C, AorB, None }
  156. private bool autoEncode;
  157. #endregion
  158. #region Properties
  159. /// <summary>
  160. /// Gets or sets a value that determines whether the barcode should automatically
  161. /// use appropriate encoding.
  162. /// </summary>
  163. /// <remarks>
  164. /// You may use this property to encode data automatically. If you set it to <b>false</b>,
  165. /// you must specify the code page inside the data string. The following control codes are available:
  166. /// <list type="table">
  167. /// <listheader>
  168. /// <term>Sequence</term>
  169. /// <description>Code128 control code</description>
  170. /// </listheader>
  171. /// <item>
  172. /// <term>&amp;A;</term>
  173. /// <description>START A / CODE A</description>
  174. /// </item>
  175. /// <item>
  176. /// <term>&amp;B;</term>
  177. /// <description>START B / CODE B</description>
  178. /// </item>
  179. /// <item>
  180. /// <term>&amp;C;</term>
  181. /// <description>START C / CODE C</description>
  182. /// </item>
  183. /// </list>
  184. /// <item>
  185. /// <term>&amp;S;</term>
  186. /// <description>SHIFT</description>
  187. /// </item>
  188. /// <item>
  189. /// <term>&amp;1;</term>
  190. /// <description>FNC1</description>
  191. /// </item>
  192. /// <item>
  193. /// <term>&amp;2;</term>
  194. /// <description>FNC2</description>
  195. /// </item>
  196. /// <item>
  197. /// <term>&amp;3;</term>
  198. /// <description>FNC3</description>
  199. /// </item>
  200. /// <item>
  201. /// <term>&amp;4;</term>
  202. /// <description>FNC4</description>
  203. /// </item>
  204. /// </remarks>
  205. /// <example>The following example shows how to specify control codes:
  206. /// <code>
  207. /// BarcodeObject barcode;
  208. /// barcode.Barcode = new Barcode128();
  209. /// (barcode.Barcode as Barcode128).AutoEncode = false;
  210. /// barcode.Text = "&amp;C;1234&amp;A;ABC";
  211. /// </code>
  212. /// </example>
  213. [DefaultValue(true)]
  214. public bool AutoEncode
  215. {
  216. get { return autoEncode; }
  217. set { autoEncode = value; }
  218. }
  219. /// <inheritdoc/>
  220. public override bool IsNumeric
  221. {
  222. get { return false; }
  223. }
  224. #endregion
  225. #region Private Methods
  226. private bool IsDigit(char c)
  227. {
  228. return c >= '0' && c <= '9';
  229. }
  230. private bool IsFourOrMoreDigits(string code, int index, out int numDigits)
  231. {
  232. numDigits = 0;
  233. if (IsDigit(code[index]) && index + 4 < code.Length)
  234. {
  235. while (index + numDigits < code.Length && IsDigit(code[index + numDigits]))
  236. {
  237. numDigits++;
  238. }
  239. }
  240. return numDigits >= 4;
  241. }
  242. private int FindCodeA(char c)
  243. {
  244. for (int i = 0; i < tabelle_128.Length; i++)
  245. {
  246. if (c == tabelle_128[i].a[0])
  247. return i;
  248. }
  249. return -1;
  250. }
  251. private int FindCodeB(char c)
  252. {
  253. for (int i = 0; i < tabelle_128.Length; i++)
  254. {
  255. if (c == tabelle_128[i].b[0])
  256. return i;
  257. }
  258. return -1;
  259. }
  260. private int FindCodeC(string c)
  261. {
  262. for (int i = 0; i < tabelle_128.Length; i++)
  263. {
  264. if (c == tabelle_128[i].c)
  265. return i;
  266. }
  267. return -1;
  268. }
  269. // Returns a group of characters with the same encoding. Updates encoding and index parameters.
  270. private string GetNextPortion(string code, ref int index, ref Encoding encoding)
  271. {
  272. if (index >= code.Length)
  273. return "";
  274. string result = "";
  275. // determine the first character encoding
  276. int aIndex = FindCodeA(code[index]);
  277. int bIndex = FindCodeB(code[index]);
  278. Encoding firstCharEncoding = Encoding.A;
  279. if (aIndex == -1 && bIndex != -1)
  280. firstCharEncoding = Encoding.B;
  281. else if (aIndex != -1 && bIndex != -1)
  282. firstCharEncoding = Encoding.AorB;
  283. // if we have four or more digits in the current position, use C encoding.
  284. int numDigits = 0;
  285. if (IsFourOrMoreDigits(code, index, out numDigits))
  286. firstCharEncoding = Encoding.C;
  287. // if encoding = C, we have found the group, just return it.
  288. if (firstCharEncoding == Encoding.C)
  289. {
  290. // we need digit pairs, so round it to even value
  291. numDigits = (numDigits / 2) * 2;
  292. result = code.Substring(index, numDigits);
  293. index += numDigits;
  294. encoding = Encoding.C;
  295. return "&C;" + result;
  296. }
  297. // search for next characters with the same encoding. Calculate numChars with the same encoding.
  298. int numChars = 1;
  299. while (index + numChars < code.Length)
  300. {
  301. // same as above...
  302. aIndex = FindCodeA(code[index + numChars]);
  303. bIndex = FindCodeB(code[index + numChars]);
  304. Encoding nextCharEncoding = Encoding.A;
  305. if (aIndex == -1 && bIndex != -1)
  306. nextCharEncoding = Encoding.B;
  307. else if (aIndex != -1 && bIndex != -1)
  308. nextCharEncoding = Encoding.AorB;
  309. if (IsFourOrMoreDigits(code, index + numChars, out numDigits))
  310. nextCharEncoding = Encoding.C;
  311. // switch to particular encoding from AorB
  312. if (nextCharEncoding != Encoding.C && nextCharEncoding != firstCharEncoding)
  313. {
  314. if (firstCharEncoding == Encoding.AorB)
  315. firstCharEncoding = nextCharEncoding;
  316. else if (nextCharEncoding == Encoding.AorB)
  317. nextCharEncoding = firstCharEncoding;
  318. }
  319. if (firstCharEncoding != nextCharEncoding)
  320. break;
  321. numChars++;
  322. }
  323. // give precedence to B encoding
  324. if (firstCharEncoding == Encoding.AorB)
  325. firstCharEncoding = Encoding.B;
  326. string prefix = firstCharEncoding == Encoding.A ? "&A;" : "&B;";
  327. // if we have only one character, use SHIFT code to switch encoding. Do not change current encoding.
  328. if (encoding != firstCharEncoding &&
  329. numChars == 1 &&
  330. (encoding == Encoding.A || encoding == Encoding.B) &&
  331. (firstCharEncoding == Encoding.A || firstCharEncoding == Encoding.B))
  332. prefix = "&S;";
  333. else
  334. encoding = firstCharEncoding;
  335. result = prefix + code.Substring(index, numChars);
  336. index += numChars;
  337. return result;
  338. }
  339. private string StripControlCodes(string code, bool stripFNCodes)
  340. {
  341. string result = "";
  342. int index = 0;
  343. while (index < code.Length)
  344. {
  345. string nextChar = GetNextChar(code, ref index, Encoding.None);
  346. if (nextChar != "&A;" && nextChar != "&B;" && nextChar != "&C;" && nextChar != "&S;")
  347. {
  348. if (!stripFNCodes || (nextChar != "&1;" && nextChar != "&2;" && nextChar != "&3;" && nextChar != "&4;"))
  349. result += nextChar;
  350. }
  351. }
  352. return result;
  353. }
  354. private string Encode(string code)
  355. {
  356. code = StripControlCodes(code, false);
  357. string result = "";
  358. int index = 0;
  359. Encoding encoding = Encoding.None;
  360. while (index < code.Length)
  361. {
  362. result += GetNextPortion(code, ref index, ref encoding);
  363. }
  364. return result;
  365. }
  366. private string GetNextChar(string code, ref int index, Encoding encoding)
  367. {
  368. if (index >= code.Length)
  369. return "";
  370. string result = "";
  371. // check special codes:
  372. // "&A;" means START A / CODE A
  373. // "&B;" means START B / CODE B
  374. // "&C;" means START C / CODE C
  375. // "&S;" means SHIFT
  376. // "&1;" means FNC1
  377. // "&2;" means FNC2
  378. // "&3;" means FNC3
  379. // "&4;" means FNC4
  380. if (code[index] == '&' && index + 2 < code.Length && code[index + 2] == ';')
  381. {
  382. char c = code[index + 1].ToString().ToUpper()[0];
  383. if (c == 'A' || c == 'B' || c == 'C' || c == 'S' || c == '1' || c == '2' || c == '3' || c == '4')
  384. {
  385. index += 3;
  386. return "&" + c + ";";
  387. }
  388. }
  389. // if encoding is C, get next two chars
  390. if (encoding == Encoding.C && index + 1 < code.Length)
  391. {
  392. result = code.Substring(index, 2);
  393. index += 2;
  394. return result;
  395. }
  396. result = code.Substring(index, 1);
  397. index++;
  398. return result;
  399. }
  400. #endregion
  401. #region Protected Methods
  402. internal override string StripControlCodes(string data)
  403. {
  404. return StripControlCodes(data, true);
  405. }
  406. internal override string GetPattern()
  407. {
  408. string code = text;
  409. if (AutoEncode)
  410. code = Encode(code);
  411. // get first char to determine encoding
  412. Encoding encoding = Encoding.None;
  413. int index = 0;
  414. string nextChar = GetNextChar(code, ref index, encoding);
  415. int checksum = 0;
  416. string startCode = "";
  417. // setup encoding
  418. switch (nextChar)
  419. {
  420. case "&A;":
  421. encoding = Encoding.A;
  422. checksum = 103;
  423. startCode = tabelle_128[103].data;
  424. break;
  425. case "&B;":
  426. encoding = Encoding.B;
  427. checksum = 104;
  428. startCode = tabelle_128[104].data;
  429. break;
  430. case "&C;":
  431. encoding = Encoding.C;
  432. checksum = 105;
  433. startCode = tabelle_128[105].data;
  434. break;
  435. default:
  436. throw new Exception(Res.Get("Messages,InvalidBarcode1"));
  437. }
  438. string result = startCode; // Startcode
  439. int codeword_pos = 1;
  440. while (index < code.Length)
  441. {
  442. nextChar = GetNextChar(code, ref index, encoding);
  443. int idx = 0;
  444. switch (nextChar)
  445. {
  446. case "&A;":
  447. encoding = Encoding.A;
  448. idx = 101;
  449. break;
  450. case "&B;":
  451. encoding = Encoding.B;
  452. idx = 100;
  453. break;
  454. case "&C;":
  455. encoding = Encoding.C;
  456. idx = 99;
  457. break;
  458. case "&S;":
  459. if (encoding == Encoding.A)
  460. encoding = Encoding.B;
  461. else
  462. encoding = Encoding.A;
  463. idx = 98;
  464. break;
  465. case "&1;":
  466. idx = 102;
  467. break;
  468. case "&2;":
  469. idx = 97;
  470. break;
  471. case "&3;":
  472. idx = 96;
  473. break;
  474. case "&4;":
  475. idx = encoding == Encoding.A ? 101 : 100;
  476. break;
  477. default:
  478. if (encoding == Encoding.A)
  479. idx = FindCodeA(nextChar[0]);
  480. else if (encoding == Encoding.B)
  481. idx = FindCodeB(nextChar[0]);
  482. else
  483. idx = FindCodeC(nextChar);
  484. break;
  485. }
  486. if (idx < 0)
  487. throw new Exception(Res.Get("Messages,InvalidBarcode2"));
  488. result += tabelle_128[idx].data;
  489. checksum += idx * codeword_pos;
  490. codeword_pos++;
  491. // switch encoding back after the SHIFT
  492. if (nextChar == "&S;")
  493. {
  494. if (encoding == Encoding.A)
  495. encoding = Encoding.B;
  496. else
  497. encoding = Encoding.A;
  498. }
  499. }
  500. checksum = checksum % 103;
  501. result += tabelle_128[checksum].data;
  502. // stop code
  503. result += "2331112";
  504. return DoConvert(result);
  505. }
  506. #endregion
  507. #region Public Methods
  508. /// <inheritdoc/>
  509. public override void Assign(BarcodeBase source)
  510. {
  511. base.Assign(source);
  512. AutoEncode = (source as Barcode128).AutoEncode;
  513. }
  514. internal override void Serialize(FRWriter writer, string prefix, BarcodeBase diff)
  515. {
  516. base.Serialize(writer, prefix, diff);
  517. Barcode128 c = diff as Barcode128;
  518. if (c == null || AutoEncode != c.AutoEncode)
  519. writer.WriteBool(prefix + "AutoEncode", AutoEncode);
  520. }
  521. #endregion
  522. /// <summary>
  523. /// Initializes a new instance of the <see cref="Barcode128"/> class with default settings.
  524. /// </summary>
  525. public Barcode128()
  526. {
  527. AutoEncode = true;
  528. }
  529. }
  530. }