diff --git a/.gitignore b/.gitignore
index dc122d7..c2710d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,9 @@
-/data
-/work
-/logs
/.idea
-/target
.DS_Store
/.settings
/.classpath
/.project
/.gradle
-/build
+build
*~
/*.iml
\ No newline at end of file
diff --git a/CREDITS.txt b/CREDITS.txt
deleted file mode 100644
index 6e66a0c..0000000
--- a/CREDITS.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a Java 8 version of the project
-
-https://github.com/gredler/jdk9-png-writer-backport
-
-by Daniel Gredler
\ No newline at end of file
diff --git a/barcode/NOTICE.txt b/barcode/NOTICE.txt
new file mode 100644
index 0000000..91af19a
--- /dev/null
+++ b/barcode/NOTICE.txt
@@ -0,0 +1,7 @@
+This work is based on Okapi Barcode Library
+
+https://github.com/woo-j/OkapiBarcode
+
+(Apache License 2.0)
+
+as of 2016
\ No newline at end of file
diff --git a/barcode/build.gradle b/barcode/build.gradle
new file mode 100644
index 0000000..f125b34
--- /dev/null
+++ b/barcode/build.gradle
@@ -0,0 +1,7 @@
+dependencies {
+ testImplementation project(':io-vector')
+ testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.0"
+ testImplementation "junit:junit:4.12"
+ testImplementation "com.google.zxing:javase:${project.property('zxing.version')}"
+ testImplementation "org.reflections:reflections:${project.property('reflections.version')}"
+}
diff --git a/barcode/src/main/java/module-info.java b/barcode/src/main/java/module-info.java
new file mode 100644
index 0000000..1c65a9d
--- /dev/null
+++ b/barcode/src/main/java/module-info.java
@@ -0,0 +1,6 @@
+module org.xbib.graphics.barcode {
+ exports org.xbib.graphics.barcode;
+ exports org.xbib.graphics.barcode.util;
+ exports org.xbib.graphics.barcode.render;
+ requires transitive java.desktop;
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java b/barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java
new file mode 100755
index 0000000..8ee8234
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java
@@ -0,0 +1,363 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements the Australia Post 4-State barcode .
+ */
+public class AustraliaPost extends Symbol {
+
+ private static final char[] CHARACTER_SET = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '#'
+ };
+
+ private static final String[] N_ENCODING_TABLE = {
+ "00", "01", "02", "10", "11", "12", "20", "21", "22", "30"
+ };
+
+ private static final String[] C_ENCODING_TABLE = {
+ "222", "300", "301", "302", "310", "311", "312", "320", "321", "322",
+ "000", "001", "002", "010", "011", "012", "020", "021", "022", "100", "101", "102", "110",
+ "111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221",
+ "023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132", "133", "203",
+ "213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333",
+ "003", "013"
+ };
+
+ private static final String[] BAR_VALUE_TABLE = {
+ "000", "001", "002", "003", "010", "011", "012", "013", "020", "021",
+ "022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110", "111", "112",
+ "113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203",
+ "210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233", "300",
+ "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331",
+ "332", "333"
+ };
+ private ausMode mode;
+
+ ;
+
+ public AustraliaPost() {
+ mode = ausMode.AUSPOST;
+ }
+
+ /**
+ * Specify encoding of Australia Post Standard Customer Barcode,
+ * Customer Barcode 2 or Customer Barcode 3 (37-bar, 52-bar and 67-bar
+ * symbols) depending on input data length. Valid data characters are 0-9,
+ * A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and
+ * should not be included in the input data.
+ *
+ * Input data should include a 8-digit Deliver Point ID
+ * (DPID) optionally followed by customer information as shown below.
+ *
+ * Permitted Australia Post input data
+ *
+ *
+ * Input Length
+ * Required Input Format
+ * Symbol Length
+ * FCC
+ * Encoding Table
+ *
+ *
+ * 8
+ * 99999999
+ * 37-bar
+ * 11
+ * None
+ *
+ *
+ * 13
+ * 99999999AAAAA
+ * 52-bar
+ * 59
+ * C
+ *
+ *
+ * 16
+ * 9999999999999999
+ * 52-bar
+ * 59
+ * N
+ *
+ *
+ * 18
+ * 99999999AAAAAAAAAA
+ * 67-bar
+ * 62
+ * C
+ *
+ *
+ * 23
+ * 99999999999999999999999
+ * 67-bar
+ * 62
+ * N
+ *
+ *
+ *
+ */
+ public void setPostMode() {
+ mode = ausMode.AUSPOST;
+ }
+
+ /**
+ * Specify encoding of a Reply Paid version of the Australia Post
+ * 4-State Barcode (FCC 45) which requires an 8-digit DPID input.
+ */
+ public void setReplyMode() {
+ mode = ausMode.AUSREPLY;
+ }
+
+ /**
+ * Specify encoding of a Routing version of the Australia Post 4-State
+ * Barcode (FCC 87) which requires an 8-digit DPID input.
+ */
+ public void setRouteMode() {
+ mode = ausMode.AUSROUTE;
+ }
+
+ /**
+ * Specify encoding of a Redirection version of the Australia Post 4-State
+ * Barcode (FCC 92) which requires an 8-digit DPID input.
+ */
+ public void setRedirectMode() {
+ mode = ausMode.AUSREDIRECT;
+ }
+
+ @Override
+ public boolean encode() {
+ String formatControlCode = "00";
+ String deliveryPointId;
+ StringBuilder barStateValues;
+ StringBuilder zeroPaddedInput = new StringBuilder();
+ int i;
+
+ switch (mode) {
+ case AUSPOST:
+ switch (content.length()) {
+ case 8:
+ formatControlCode = "11";
+ break;
+ case 13:
+ formatControlCode = "59";
+ break;
+ case 16:
+ formatControlCode = "59";
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+ break;
+ case 18:
+ formatControlCode = "62";
+ break;
+ case 23:
+ formatControlCode = "62";
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+ break;
+ default:
+ errorMsg.append("Auspost input is wrong length");
+ return false;
+ }
+ break;
+ case AUSREPLY:
+ if (content.length() > 8) {
+ errorMsg.append("Auspost input is too long");
+ return false;
+ } else {
+ formatControlCode = "45";
+ }
+ break;
+ case AUSROUTE:
+ if (content.length() > 8) {
+ errorMsg.append("Auspost input is too long");
+ return false;
+ } else {
+ formatControlCode = "87";
+ }
+ break;
+ case AUSREDIRECT:
+ if (content.length() > 8) {
+ errorMsg.append("Auspost input is too long");
+ return false;
+ } else {
+ formatControlCode = "92";
+ }
+ break;
+ }
+
+ encodeInfo.append("FCC: ").append(formatControlCode).append('\n');
+
+ if (mode != ausMode.AUSPOST) {
+ for (i = content.length(); i < 8; i++) {
+ zeroPaddedInput.append("0");
+ }
+ }
+ zeroPaddedInput.append(content);
+
+ if (!(content.matches("[0-9A-Za-z #]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ /* Verify that the first 8 characters are numbers */
+ deliveryPointId = zeroPaddedInput.substring(0, 8);
+
+ if (!(deliveryPointId.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in DPID");
+ return false;
+ }
+
+ encodeInfo.append("DPID: ").append(deliveryPointId).append('\n');
+
+ /* Start */
+ barStateValues = new StringBuilder("13");
+
+ /* Encode the FCC */
+ for (i = 0; i < 2; i++) {
+ barStateValues.append(N_ENCODING_TABLE[formatControlCode.charAt(i) - '0']);
+ }
+
+ /* Delivery Point Identifier (DPID) */
+ for (i = 0; i < 8; i++) {
+ barStateValues.append(N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0']);
+ }
+
+ /* Customer Information */
+ switch (zeroPaddedInput.length()) {
+ case 13:
+ case 18:
+ for (i = 8; i < zeroPaddedInput.length(); i++) {
+ barStateValues.append(C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]);
+ }
+ break;
+ case 16:
+ case 23:
+ for (i = 8; i < zeroPaddedInput.length(); i++) {
+ barStateValues.append(N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]);
+ }
+ break;
+ }
+
+ /* Filler bar */
+ switch (barStateValues.length()) {
+ case 22:
+ case 37:
+ case 52:
+ barStateValues.append("3");
+ break;
+ }
+
+ /* Reed Solomon error correction */
+ barStateValues.append(calcReedSolomon(barStateValues.toString()));
+
+ /* Stop character */
+ barStateValues.append("13");
+
+ encodeInfo.append("Total length: ").append(barStateValues.length()).append('\n');
+ encodeInfo.append("Encoding: ");
+ for (i = 0; i < barStateValues.length(); i++) {
+ switch (barStateValues.charAt(i)) {
+ case '1':
+ encodeInfo.append("A");
+ break;
+ case '2':
+ encodeInfo.append("D");
+ break;
+ case '0':
+ encodeInfo.append("F");
+ break;
+ case '3':
+ encodeInfo.append("T");
+ break;
+ }
+ }
+ encodeInfo.append("\n");
+
+ readable = new StringBuilder();
+ pattern = new String[1];
+ pattern[0] = barStateValues.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ private String calcReedSolomon(String oldBarStateValues) {
+ ReedSolomon rs = new ReedSolomon();
+ StringBuilder newBarStateValues = new StringBuilder();
+
+ /* Adds Reed-Solomon error correction to auspost */
+
+ int barStateCount;
+ int tripleValueCount = 0;
+ int[] tripleValue = new int[31];
+
+ for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) {
+ tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4)
+ + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2)
+ + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0);
+ }
+
+ rs.init_gf(0x43);
+ rs.init_code(4, 1);
+ rs.encode(tripleValueCount, tripleValue);
+
+ for (barStateCount = 4; barStateCount > 0; barStateCount--) {
+ newBarStateValues.append(BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)]);
+ }
+
+ return newBarStateValues.toString();
+ }
+
+ private int barStateToDecimal(char oldBarStateValues, int shift) {
+ return (oldBarStateValues - '0') << shift;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ getRectangles().clear();
+ x = 0;
+ w = 1;
+ y = 0;
+ h = 0;
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ switch (pattern[0].charAt(xBlock)) {
+ case '1':
+ y = 0;
+ h = 5;
+ break;
+ case '2':
+ y = 3;
+ h = 5;
+ break;
+ case '0':
+ y = 0;
+ h = 8;
+ break;
+ case '3':
+ y = 3;
+ h = 2;
+ break;
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ x += 2;
+ }
+ symbolWidth = pattern[0].length() * 3;
+ symbolHeight = 8;
+ }
+
+ private enum ausMode {AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT}
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java
new file mode 100755
index 0000000..bb81d3d
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java
@@ -0,0 +1,1989 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+
+/**
+ * Implements Aztec Code bar code symbology according to ISO/IEC 24778:2008.
+ * Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null
+ * characters) up to a maximum length of approximately 3800 numeric characters,
+ * 3000 alphabetic characters or 1900 bytes of data in a two-dimensional matrix
+ * symbol.
+ */
+public class AztecCode extends Symbol {
+
+ private static final int[] COMPACT_AZTEC_MAP = { //27 x 27 data grid
+ 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433,
+ 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459,
+ 607, 606, 410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432,
+ 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458,
+ 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257, 259, 261,
+ 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461,
+ 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260,
+ 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 462, 463,
+ 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121,
+ 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465,
+ 599, 598, 403, 402, 239, 238, 106, 108, 110, 112, 114, 116, 118, 120,
+ 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467,
+ 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19,
+ 21, 23, 25, 27, 140, 141, 288, 289, 468, 469,
+ 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18,
+ 20, 22, 24, 26, 142, 143, 290, 291, 470, 471,
+ 593, 592, 397, 396, 233, 232, 101, 100, 1, 1, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473,
+ 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 30, 31, 146, 147, 294, 295, 474, 475,
+ 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1,
+ 2007, 32, 33, 148, 149, 296, 297, 476, 477,
+ 587, 586, 391, 390, 227, 226, 95, 94, 2026, 1, 0, 1, 1, 1, 1, 1, 0, 1,
+ 2008, 34, 35, 150, 151, 298, 299, 478, 479,
+ 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+ 2009, 36, 37, 152, 153, 300, 301, 480, 481,
+ 583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ 2010, 38, 39, 154, 155, 302, 303, 482, 483,
+ 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+ 2011, 40, 41, 156, 157, 304, 305, 484, 485,
+ 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1,
+ 2012, 42, 43, 158, 159, 306, 307, 486, 487,
+ 577, 576, 381, 380, 217, 216, 85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1,
+ 2013, 44, 45, 160, 161, 308, 309, 488, 489,
+ 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 46, 47, 162, 163, 310, 311, 490, 491,
+ 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016,
+ 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493,
+ 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68, 66, 64, 62, 60, 58,
+ 56, 54, 50, 51, 166, 167, 314, 315, 494, 495,
+ 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59,
+ 57, 55, 52, 53, 168, 169, 316, 317, 496, 497,
+ 567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186,
+ 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499,
+ 565, 564, 369, 368, 207, 205, 203, 201, 199, 197, 195, 193, 191, 189, 187,
+ 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501,
+ 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342,
+ 340, 338, 336, 334, 332, 330, 328, 326, 322, 323, 502, 503,
+ 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343,
+ 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505,
+ 558, 556, 554, 552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530,
+ 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507,
+ 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, 533, 531,
+ 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509
+ };
+ private static final int[][] AZTEC_MAP = new int[151][151];
+ private static final int[] AztecCodeSet = { /* From Table 2 */
+ 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8,
+ 8, 8, 8, 8, 4, 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, 8, 4, 8, 4, 4, 4, 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, 8, 4, 8, 4, 4
+ };
+ private static final int[] AztecSymbolChar = { /* From Table 2 */
+ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22,
+ 23, 24, 25, 26, 20, 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, 27, 27, 21, 28, 22, 23, 24, 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, 27, 29, 25, 30, 26, 27
+ };
+ /* Problem characters are:
+ 300: Carriage Return (ASCII 13)
+ 301: Comma (ASCII 44)
+ 302: Full Stop (ASCII 46)
+ */
+ private static final String[] pentbit = {
+ "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001",
+ "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", "10010", "10011", "10100", "10101",
+ "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111"
+ };
+ private static final String[] quadbit = {
+ "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001",
+ "1010", "1011", "1100", "1101", "1110", "1111"
+ };
+ private static final String[] tribit = {
+ "000", "001", "010", "011", "100", "101", "110", "111"
+ };
+ private static final int[] AztecSizes = { /* Codewords per symbol */
+ 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790,
+ 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, 1570, 1664
+ };
+ private static final int[] AztecCompactSizes = {
+ 17, 40, 51, 76
+ };
+ private static final int[] Aztec10DataSizes = { /* Data bits per symbol maximum with 10% error correction */
+ 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730,
+ 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, 12324, 13188, 14076,
+ 15000, 15948, 16920, 17940
+ };
+ private static final int[] Aztec23DataSizes = { /* Data bits per symbol maximum with 23% error correction */
+ 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040,
+ 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, 10536, 11280, 12036,
+ 12828, 13644, 14472, 15348
+ };
+ private static final int[] Aztec36DataSizes = { /* Data bits per symbol maximum with 36% error correction */
+ 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350,
+ 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, 8760, 9372, 9996, 10656,
+ 11340, 12024, 12744
+ };
+ private static final int[] Aztec50DataSizes = { /* Data bits per symbol maximum with 50% error correction */
+ 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610,
+ 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, 6828, 7308, 7800, 8316,
+ 8844, 9384, 9948
+ };
+ private static final int[] AztecCompact10DataSizes = {
+ 78, 198, 336, 520
+ };
+ private static final int[] AztecCompact23DataSizes = {
+ 66, 168, 288, 440
+ };
+ private static final int[] AztecCompact36DataSizes = {
+ 48, 138, 232, 360
+ };
+ private static final int[] AztecCompact50DataSizes = {
+ 36, 102, 176, 280
+ };
+ private static final int[] AztecOffset = {
+ 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21,
+ 19, 17, 15, 13, 10, 8, 6, 4, 2, 0
+ };
+ private static final int[] AztecCompactOffset = {
+ 6, 4, 2, 0
+ };
+ private StringBuilder binaryString;
+ private int preferredSize = 0;
+ private int preferredEccLevel = -1;
+
+ /**
+ *
+ */
+ public AztecCode() {
+ int layer, start, length, n, i;
+ int x, y;
+
+ for (x = 0; x < 151; x++) {
+ for (y = 0; y < 151; y++) {
+ AZTEC_MAP[x][y] = 0;
+ }
+ }
+
+ for (layer = 1; layer < 33; layer++) {
+ start = (112 * (layer - 1)) + (16 * (layer - 1) * (layer - 1)) + 2;
+ length = 28 + ((layer - 1) * 4) + (layer * 4);
+ /* Top */
+ i = 0;
+ x = 64 - ((layer - 1) * 2);
+ y = 63 - ((layer - 1) * 2);
+ for (n = start; n < (start + length); n += 2) {
+ AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n;
+ AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1;
+ i++;
+ }
+ /* Right */
+ i = 0;
+ x = 78 + ((layer - 1) * 2);
+ y = 64 - ((layer - 1) * 2);
+ for (n = start + length; n < (start + (length * 2)); n += 2) {
+ AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n;
+ AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1;
+ i++;
+ }
+ /* Bottom */
+ i = 0;
+ x = 77 + ((layer - 1) * 2);
+ y = 78 + ((layer - 1) * 2);
+ for (n = start + (length * 2); n < (start + (length * 3)); n += 2) {
+ AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n;
+ AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1;
+ i++;
+ }
+ /* Left */
+ i = 0;
+ x = 63 - ((layer - 1) * 2);
+ y = 77 + ((layer - 1) * 2);
+ for (n = start + (length * 3); n < (start + (length * 4)); n += 2) {
+ AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n;
+ AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1;
+ i++;
+ }
+ }
+
+ /* Central finder pattern */
+ for (y = 69; y <= 81; y++) {
+ for (x = 69; x <= 81; x++) {
+ AZTEC_MAP[x][y] = 1;
+ }
+ }
+ for (y = 70; y <= 80; y++) {
+ for (x = 70; x <= 80; x++) {
+ AZTEC_MAP[x][y] = 0;
+ }
+ }
+ for (y = 71; y <= 79; y++) {
+ for (x = 71; x <= 79; x++) {
+ AZTEC_MAP[x][y] = 1;
+ }
+ }
+ for (y = 72; y <= 78; y++) {
+ for (x = 72; x <= 78; x++) {
+ AZTEC_MAP[x][y] = 0;
+ }
+ }
+ for (y = 73; y <= 77; y++) {
+ for (x = 73; x <= 77; x++) {
+ AZTEC_MAP[x][y] = 1;
+ }
+ }
+ for (y = 74; y <= 76; y++) {
+ for (x = 74; x <= 76; x++) {
+ AZTEC_MAP[x][y] = 0;
+ }
+ }
+
+ /* Guide bars */
+ for (y = 11; y < 151; y += 16) {
+ for (x = 1; x < 151; x += 2) {
+ AZTEC_MAP[x][y] = 1;
+ AZTEC_MAP[y][x] = 1;
+ }
+ }
+
+ /* Descriptor */
+ for (i = 0; i < 10; i++) { /* Top */
+
+ AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i;
+ }
+ for (i = 0; i < 10; i++) { /* Right */
+
+ AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i;
+ }
+ for (i = 0; i < 10; i++) { /* Bottom */
+
+ AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i;
+ }
+ for (i = 0; i < 10; i++) { /* Left */
+
+ AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i;
+ }
+
+ /* Orientation */
+ AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1;
+ AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1;
+ AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1;
+ AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1;
+ AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1;
+ AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1;
+ }
+
+ /**
+ * Sets a preferred symbol size. This value may be ignored if data string is
+ * too large to fit in the specified symbol size. Values correspond to
+ * symbol sizes as shown in the following table:
+ *
+ * Available Aztec Code symbol sizes
+ *
+ *
+ *
+ * Input
+ *
+ * Symbol Size
+ *
+ * Input
+ *
+ * Symbol Size
+ *
+ *
+ *
+ * 1
+ *
+ * 15 x 15*
+ *
+ * 19
+ *
+ * 79 x 79
+ *
+ *
+ *
+ * 2
+ *
+ * 19 x 19*
+ *
+ * 20
+ *
+ * 83 x 83
+ *
+ *
+ *
+ * 3
+ *
+ * 23 x 23*
+ *
+ * 21
+ *
+ * 87 x 87
+ *
+ *
+ *
+ * 4
+ *
+ * 27 x 27*
+ *
+ * 22
+ *
+ * 91 x 91
+ *
+ *
+ *
+ * 5
+ *
+ * 19 x 19
+ *
+ * 23
+ *
+ * 95 x 95
+ *
+ *
+ *
+ * 6
+ *
+ * 23 x 23
+ *
+ * 24
+ *
+ * 101 x 101
+ *
+ *
+ *
+ * 7
+ *
+ * 27 x 27
+ *
+ * 25
+ *
+ * 105 x 105
+ *
+ *
+ *
+ * 8
+ *
+ * 31 x 31
+ *
+ * 26
+ *
+ * 109 x 109
+ *
+ *
+ *
+ * 9
+ *
+ * 37 x 37
+ *
+ * 27
+ *
+ * 113 x 113
+ *
+ *
+ *
+ * 10
+ *
+ * 41 x 41
+ *
+ * 28
+ *
+ * 117 x 117
+ *
+ *
+ *
+ * 11
+ *
+ * 45 x 45
+ *
+ * 29
+ *
+ * 121 x 121
+ *
+ *
+ *
+ * 12
+ *
+ * 49 x 49
+ *
+ * 30
+ *
+ * 125 x 125
+ *
+ *
+ *
+ * 13
+ *
+ * 53 x 53
+ *
+ * 31
+ *
+ * 131 x 131
+ *
+ *
+ *
+ * 14
+ *
+ * 57 x 57
+ *
+ * 32
+ *
+ * 135 x 135
+ *
+ *
+ *
+ * 15
+ *
+ * 61 x 61
+ *
+ * 33
+ *
+ * 139 x 139
+ *
+ *
+ *
+ * 16
+ *
+ * 67 x 67
+ *
+ * 34
+ *
+ * 143 x 143
+ *
+ *
+ *
+ * 17
+ *
+ * 71 x 71
+ *
+ * 35
+ *
+ * 147 x 147
+ *
+ *
+ *
+ * 18
+ *
+ * 75 x 75
+ *
+ * 36
+ *
+ * 151 x 151
+ *
+ *
+ *
+ *
+ * @param size An integer in the range 1 - 36
+ */
+ public void setPreferredSize(int size) {
+ preferredSize = size;
+ }
+
+ /**
+ * Sets the preferred minimum amount of symbol space dedicated to error
+ * correction. This value will be ignored if a symbol size has been set by
+ * setPreferredSize
Valid options are:
+ * Error correction options
+ *
+ *
+ *
+ * Mode
+ *
+ * Error Correction Capacity
+ *
+ *
+ *
+ * 1
+ *
+ * >10% + 3 codewords
+ *
+ *
+ *
+ * 2
+ *
+ * >23% + 3 codewords
+ *
+ *
+ *
+ * 3
+ *
+ * >36% + 3 codewords
+ *
+ *
+ *
+ * 4
+ *
+ * >50% + 3 codewords
+ *
+ *
+ *
+ *
+ * @param eccLevel An integer in the range 1 - 4
+ */
+ public void setPreferredEccLevel(int eccLevel) {
+ preferredEccLevel = eccLevel;
+ }
+
+ private int avoidReferenceGrid(int input) {
+ int output;
+
+ output = input;
+ if (output > 10) {
+ output++;
+ }
+ if (output > 26) {
+ output++;
+ }
+ if (output > 42) {
+ output++;
+ }
+ if (output > 58) {
+ output++;
+ }
+ if (output > 74) {
+ output++;
+ }
+ if (output > 90) {
+ output++;
+ }
+ if (output > 106) {
+ output++;
+ }
+ if (output > 122) {
+ output++;
+ }
+ if (output > 138) {
+ output++;
+ }
+
+ return output;
+ }
+
+ @Override
+ public boolean encode() {
+ int i, ecc_level, data_length, layers, data_maxsize;
+ int adjustment_size, codeword_size;
+ int j, count, adjusted_length, padbits, remainder;
+ StringBuilder adjusted_string = new StringBuilder();
+ StringBuilder bit_pattern = new StringBuilder();
+ int comp_loop = 4;
+ int data_blocks, ecc_blocks, total_bits;
+ boolean compact;
+ ReedSolomon rs = new ReedSolomon();
+ ReedSolomon rs2 = new ReedSolomon();
+ StringBuilder descriptor = new StringBuilder();
+ int[] desc_data = new int[4];
+ int[] desc_ecc = new int[6];
+ int y, x, weight;
+ StringBuilder bin;
+ int t;
+ boolean done;
+
+ if (readerInit) {
+ comp_loop = 1;
+ }
+
+ eciProcess(); // Get ECI mode
+
+ if ((inputDataType == DataType.GS1) && (readerInit)) {
+ errorMsg.append("Cannot encode in GS1 and Reader Initialisation mode at the same time");
+ return false;
+ }
+
+ if (!generateAztecBinary()) {
+ errorMsg.append("Input too long or too many extended ASCII characters");
+ return false;
+ }
+
+ // Set the error correction level
+ if ((preferredEccLevel <= 0) || (preferredEccLevel > 4)) {
+ ecc_level = 2;
+ } else {
+ ecc_level = preferredEccLevel;
+ }
+
+ data_length = binaryString.length();
+
+ layers = 0; /* Keep compiler happy! */
+
+ data_maxsize = 0; /* Keep compiler happy! */
+
+ adjustment_size = 0;
+
+ if ((preferredSize < 0) || (preferredSize > 36)) {
+ preferredSize = 0;
+ }
+
+ if (preferredSize == 0) { /* The size of the symbol can be determined by Okapi */
+
+ do {
+ /* Decide what size symbol to use - the smallest that fits the data */
+ compact = false; /* 1 = Aztec Compact, 0 = Normal Aztec */
+
+ layers = 0;
+
+ switch (ecc_level) {
+ /* For each level of error correction work out the smallest symbol which
+ the data will fit in */
+ case 1:
+ for (i = 32; i > 0; i--) {
+ if ((data_length + adjustment_size) < Aztec10DataSizes[i - 1]) {
+ layers = i;
+ compact = false;
+ data_maxsize = Aztec10DataSizes[i - 1];
+ }
+ }
+ for (i = comp_loop; i > 0; i--) {
+ if ((data_length + adjustment_size) < AztecCompact10DataSizes[i - 1]) {
+ layers = i;
+ compact = true;
+ data_maxsize = AztecCompact10DataSizes[i - 1];
+ }
+ }
+ break;
+ case 2:
+ for (i = 32; i > 0; i--) {
+ if ((data_length + adjustment_size) < Aztec23DataSizes[i - 1]) {
+ layers = i;
+ compact = false;
+ data_maxsize = Aztec23DataSizes[i - 1];
+ }
+ }
+ for (i = comp_loop; i > 0; i--) {
+ if ((data_length + adjustment_size) < AztecCompact23DataSizes[i - 1]) {
+ layers = i;
+ compact = true;
+ data_maxsize = AztecCompact23DataSizes[i - 1];
+ }
+ }
+ break;
+ case 3:
+ for (i = 32; i > 0; i--) {
+ if ((data_length + adjustment_size) < Aztec36DataSizes[i - 1]) {
+ layers = i;
+ compact = false;
+ data_maxsize = Aztec36DataSizes[i - 1];
+ }
+ }
+ for (i = comp_loop; i > 0; i--) {
+ if ((data_length + adjustment_size) < AztecCompact36DataSizes[i - 1]) {
+ layers = i;
+ compact = true;
+ data_maxsize = AztecCompact36DataSizes[i - 1];
+ }
+ }
+ break;
+ case 4:
+ for (i = 32; i > 0; i--) {
+ if ((data_length + adjustment_size) < Aztec50DataSizes[i - 1]) {
+ layers = i;
+ compact = false;
+ data_maxsize = Aztec50DataSizes[i - 1];
+ }
+ }
+ for (i = comp_loop; i > 0; i--) {
+ if ((data_length + adjustment_size) < AztecCompact50DataSizes[i - 1]) {
+ layers = i;
+ compact = true;
+ data_maxsize = AztecCompact50DataSizes[i - 1];
+ }
+ }
+ break;
+ }
+
+ if (layers == 0) { /* Couldn't find a symbol which fits the data */
+
+ errorMsg.append("Input too long (too many bits for selected ECC)");
+ return false;
+ }
+
+ /* Determine codeword bitlength - Table 3 */
+ codeword_size = 6; /* if (layers <= 2) */
+
+ if ((layers >= 3) && (layers <= 8)) {
+ codeword_size = 8;
+ }
+ if ((layers >= 9) && (layers <= 22)) {
+ codeword_size = 10;
+ }
+ if (layers >= 23) {
+ codeword_size = 12;
+ }
+
+ j = 0;
+ i = 0;
+
+ do {
+ if ((j + 1) % codeword_size == 0) {
+ /* Last bit of codeword */
+ done = false;
+ count = 0;
+
+ /* Discover how many '1's in current codeword */
+ for (t = 0; t < (codeword_size - 1); t++) {
+ if (binaryString.charAt((i - (codeword_size - 1)) + t) == '1') {
+ count++;
+ }
+ }
+
+ if (count == (codeword_size - 1)) {
+ adjusted_string.append('0');
+ j++;
+ done = true;
+ }
+
+ if (count == 0) {
+ adjusted_string.append('1');
+ j++;
+ done = true;
+ }
+
+ if (!done) {
+ adjusted_string.append(binaryString.charAt(i));
+ j++;
+ i++;
+ }
+ } else {
+ adjusted_string.append(binaryString.charAt(i));
+ j++;
+ i++;
+ }
+ } while (i < data_length);
+
+ adjusted_length = adjusted_string.length();
+ adjustment_size = adjusted_length - data_length;
+
+ /* Add padding */
+ remainder = adjusted_length % codeword_size;
+
+ padbits = codeword_size - remainder;
+ if (padbits == codeword_size) {
+ padbits = 0;
+ }
+
+ for (i = 0; i < padbits; i++) {
+ adjusted_string.append("1");
+ }
+ adjusted_length = adjusted_string.length();
+
+ count = 0;
+ for (i = (adjusted_length - codeword_size); i < adjusted_length; i++) {
+ if (adjusted_string.charAt(i) == '1') {
+ count++;
+ }
+ }
+ if (count == codeword_size) {
+ adjusted_string = new StringBuilder(adjusted_string.substring(0, adjusted_length - 1) + '0');
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < (adjusted_length / codeword_size); i++) {
+ int l = 0, m = (1 << (codeword_size - 1));
+ for (j = 0; j < codeword_size; j++) {
+ if (adjusted_string.charAt((i * codeword_size) + j) == '1') {
+ l += m;
+ }
+ m = m >> 1;
+ }
+ encodeInfo.append(Integer.toString(l)).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ } while (adjusted_length > data_maxsize);
+ /* This loop will only repeat on the rare occasions when the rule about not having all 1s or all 0s
+ means that the binary string has had to be lengthened beyond the maximum number of bits that can
+ be encoded in a symbol of the selected size */
+ } else {
+ /* The size of the symbol has been specified by the user */
+ compact = false;
+ if ((readerInit) && ((preferredSize >= 2) && (preferredSize <= 4))) {
+ preferredSize = 5;
+ }
+ if ((preferredSize >= 1) && (preferredSize <= 4)) {
+ compact = true;
+ layers = preferredSize;
+ }
+ if ((preferredSize >= 5) && (preferredSize <= 36)) {
+ layers = preferredSize - 4;
+ }
+
+ /* Determine codeword bitlength - Table 3 */
+ codeword_size = 6;
+ if ((layers >= 3) && (layers <= 8)) {
+ codeword_size = 8;
+ }
+ if ((layers >= 9) && (layers <= 22)) {
+ codeword_size = 10;
+ }
+ if (layers >= 23) {
+ codeword_size = 12;
+ }
+ j = 0;
+ i = 0;
+ do {
+ if (((j + 1) % codeword_size) == 0) {
+ /* Last bit of codeword */
+ done = false;
+ count = 0;
+
+ /* Discover how many '1's in current codeword */
+ for (t = 0; t < (codeword_size - 1); t++) {
+ if (binaryString.charAt((i - (codeword_size - 1)) + t) == '1') {
+ count++;
+ }
+ }
+
+ if (count == (codeword_size - 1)) {
+ adjusted_string.append('0');
+ j++;
+ done = true;
+ }
+
+ if (count == 0) {
+ adjusted_string.append('1');
+ j++;
+ done = true;
+ }
+
+ if (!done) {
+ adjusted_string.append(binaryString.charAt(i));
+ j++;
+ i++;
+ }
+ } else {
+ adjusted_string.append(binaryString.charAt(i));
+ j++;
+ i++;
+ }
+ } while (i < binaryString.length());
+
+ adjusted_length = adjusted_string.length();
+ remainder = adjusted_length % codeword_size;
+ padbits = codeword_size - remainder;
+
+ if (padbits == codeword_size) {
+ padbits = 0;
+ }
+ for (i = 0; i < padbits; i++) {
+ adjusted_string.append("1");
+ }
+
+ adjusted_length = adjusted_string.length();
+ count = 0;
+
+ for (i = (adjusted_length - codeword_size); i < adjusted_length; i++) {
+ if (adjusted_string.charAt(i) == '1') {
+ count++;
+ }
+ }
+
+ if (count == codeword_size) {
+ adjusted_string = new StringBuilder(adjusted_string.substring(0, adjusted_length - 1) + '0');
+ }
+
+ /* Check if the data actually fits into the selected symbol size */
+ if (compact) {
+ data_maxsize = codeword_size * (AztecCompactSizes[layers - 1] - 3);
+ } else {
+ data_maxsize = codeword_size * (AztecSizes[layers - 1] - 3);
+ }
+
+ if (adjusted_length > data_maxsize) {
+ errorMsg.append("Data too long for specified Aztec Code symbol size");
+ return false;
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < (adjusted_length / codeword_size); i++) {
+ int l = 0, m = (1 << (codeword_size - 1));
+ for (j = 0; j < codeword_size; j++) {
+ if (adjusted_string.charAt((i * codeword_size) + j) == '1') {
+ l += m;
+ }
+ m = m >> 1;
+ }
+ encodeInfo.append(Integer.toString(l)).append(" ");
+ }
+ encodeInfo.append("\n");
+ }
+
+ if (readerInit && (layers > 22)) {
+ errorMsg.append("Data too long for reader initialisation symbol");
+ return false;
+ }
+
+ data_blocks = adjusted_length / codeword_size;
+
+ if (compact) {
+ ecc_blocks = AztecCompactSizes[layers - 1] - data_blocks;
+ } else {
+ ecc_blocks = AztecSizes[layers - 1] - data_blocks;
+ }
+
+ encodeInfo.append("Compact Mode: ");
+ if (compact) {
+ encodeInfo.append("TRUE\n");
+ } else {
+ encodeInfo.append("FALSE\n");
+ }
+ encodeInfo.append("Layers: ").append(layers).append('\n');
+ encodeInfo.append("Codeword Length: ").append(codeword_size).append(" bits\n");
+ encodeInfo.append("Data Codewords: ").append(data_blocks).append('\n');
+ encodeInfo.append("ECC Codewords: ").append(ecc_blocks).append('\n');
+
+ int[] data_part = new int[data_blocks + 3];
+ int[] ecc_part = new int[ecc_blocks + 3];
+
+ /* Split into codewords and calculate reed-colomon error correction codes */
+ switch (codeword_size) {
+ case 6:
+ for (i = 0; i < data_blocks; i++) {
+ for (weight = 0; weight < 6; weight++) {
+ if (adjusted_string.charAt((i * codeword_size) + weight) == '1') {
+ data_part[i] += (32 >> weight);
+ }
+ }
+ }
+ rs.init_gf(0x43);
+ rs.init_code(ecc_blocks, 1);
+ rs.encode(data_blocks, data_part);
+ for (i = 0; i < ecc_blocks; i++) {
+ ecc_part[i] = rs.getResult(i);
+ }
+ for (i = (ecc_blocks - 1); i >= 0; i--) {
+ for (weight = 0x20; weight > 0; weight = weight >> 1) {
+ if ((ecc_part[i] & weight) != 0) {
+ adjusted_string.append("1");
+ } else {
+ adjusted_string.append("0");
+ }
+ }
+ }
+ break;
+ case 8:
+ for (i = 0; i < data_blocks; i++) {
+ for (weight = 0; weight < 8; weight++) {
+ if (adjusted_string.charAt((i * codeword_size) + weight) == '1') {
+ data_part[i] += (128 >> weight);
+ }
+ }
+ }
+ rs.init_gf(0x12d);
+ rs.init_code(ecc_blocks, 1);
+ rs.encode(data_blocks, data_part);
+ for (i = 0; i < ecc_blocks; i++) {
+ ecc_part[i] = rs.getResult(i);
+ }
+ for (i = (ecc_blocks - 1); i >= 0; i--) {
+ for (weight = 0x80; weight > 0; weight = weight >> 1) {
+ if ((ecc_part[i] & weight) != 0) {
+ adjusted_string.append("1");
+ } else {
+ adjusted_string.append("0");
+ }
+ }
+ }
+ break;
+ case 10:
+ for (i = 0; i < data_blocks; i++) {
+ for (weight = 0; weight < 10; weight++) {
+ if (adjusted_string.charAt((i * codeword_size) + weight) == '1') {
+ data_part[i] += (512 >> weight);
+ }
+ }
+ }
+ rs.init_gf(0x409);
+ rs.init_code(ecc_blocks, 1);
+ rs.encode(data_blocks, data_part);
+ for (i = 0; i < ecc_blocks; i++) {
+ ecc_part[i] = rs.getResult(i);
+ }
+ for (i = (ecc_blocks - 1); i >= 0; i--) {
+ for (weight = 0x200; weight > 0; weight = weight >> 1) {
+ if ((ecc_part[i] & weight) != 0) {
+ adjusted_string.append("1");
+ } else {
+ adjusted_string.append("0");
+ }
+ }
+ }
+ break;
+ case 12:
+ for (i = 0; i < data_blocks; i++) {
+ for (weight = 0; weight < 12; weight++) {
+ if (adjusted_string.charAt((i * codeword_size) + weight) == '1') {
+ data_part[i] += (2048 >> weight);
+ }
+ }
+ }
+ rs.init_gf(0x1069);
+ rs.init_code(ecc_blocks, 1);
+ rs.encode(data_blocks, data_part);
+ for (i = 0; i < ecc_blocks; i++) {
+ ecc_part[i] = rs.getResult(i);
+ }
+ for (i = (ecc_blocks - 1); i >= 0; i--) {
+ for (weight = 0x800; weight > 0; weight = weight >> 1) {
+ if ((ecc_part[i] & weight) != 0) {
+ adjusted_string.append("1");
+ } else {
+ adjusted_string.append("0");
+ }
+ }
+ }
+ break;
+ }
+
+ /* Invert the data so that actual data is on the outside and reed-solomon on the inside */
+ total_bits = (data_blocks + ecc_blocks) * codeword_size;
+ for (i = 0; i < total_bits; i++) {
+ bit_pattern.append(adjusted_string.charAt(total_bits - i - 1));
+ }
+
+ if (compact) {
+ /* The first 2 bits represent the number of layers minus 1 */
+ if (((layers - 1) & 0x02) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ if (((layers - 1) & 0x01) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ /* The next 6 bits represent the number of data blocks minus 1 */
+ if (readerInit) {
+ descriptor.append('1');
+ } else {
+ if (((data_blocks - 1) & 0x20) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+ for (i = 0x10; i > 0; i = i >> 1) {
+ if (((data_blocks - 1) & i) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+ encodeInfo.append("Mode Message: ").append(descriptor).append("\n");
+ j = 2;
+ } else {
+ /* The first 5 bits represent the number of layers minus 1 */
+ for (i = 0x10; i > 0; i = i >> 1) {
+ if (((layers - 1) & i) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+
+ /* The next 11 bits represent the number of data blocks minus 1 */
+ if (readerInit) {
+ descriptor.append('1');
+ } else {
+ if (((data_blocks - 1) & 0x400) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+ for (i = 0x200; i > 0; i = i >> 1) {
+ if (((data_blocks - 1) & i) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+
+ encodeInfo.append("Mode Message: ").append(descriptor).append("\n");
+ j = 4;
+ }
+
+ /* Split into 4-bit codewords */
+ for (i = 0; i < j; i++) {
+ for (weight = 0; weight < 4; weight++) {
+ if (descriptor.charAt((i * 4) + weight) == '1') {
+ desc_data[i] += (8 >> weight);
+ }
+ }
+ }
+
+ /* Add reed-solomon error correction with Galois field GF(16) and prime modulus
+ x^4 + x + 1 (section 7.2.3)*/
+ rs2.init_gf(0x13);
+ if (compact) {
+ rs2.init_code(5, 1);
+ rs2.encode(2, desc_data);
+ for (j = 0; j < 5; j++) {
+ desc_ecc[j] = rs2.getResult(j);
+ }
+ for (i = 0; i < 5; i++) {
+ for (weight = 0x08; weight > 0; weight = weight >> 1) {
+ if ((desc_ecc[4 - i] & weight) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+ }
+ } else {
+ rs2.init_code(6, 1);
+ rs2.encode(4, desc_data);
+ for (j = 0; j < 6; j++) {
+ desc_ecc[j] = rs2.getResult(j);
+ }
+ for (i = 0; i < 6; i++) {
+ for (weight = 0x08; weight > 0; weight = weight >> 1) {
+ if ((desc_ecc[5 - i] & weight) != 0) {
+ descriptor.append('1');
+ } else {
+ descriptor.append('0');
+ }
+ }
+ }
+ }
+
+ readable = new StringBuilder();
+
+ /* Plot all of the data into the symbol in pre-defined spiral pattern */
+ if (compact) {
+
+ rowCount = 27 - (2 * AztecCompactOffset[layers - 1]);
+ rowHeight = new int[rowCount];
+ rowHeight[0] = -1;
+ pattern = new String[rowCount];
+ bin = new StringBuilder();
+ for (y = AztecCompactOffset[layers - 1]; y < (27 - AztecCompactOffset[layers - 1]); y++) {
+ for (x = AztecCompactOffset[layers - 1]; x < (27 - AztecCompactOffset[layers - 1]); x++) {
+ j = COMPACT_AZTEC_MAP[(y * 27) + x];
+
+ if (j == 0) {
+ bin.append("0");
+ }
+ if (j == 1) {
+ bin.append("1");
+ }
+
+ if (j >= 2) {
+ if ((j - 2) < bit_pattern.length()) {
+ bin.append(bit_pattern.charAt(j - 2));
+ } else {
+ if (j > 2000) {
+ bin.append(descriptor.charAt(j - 2000));
+ } else {
+ bin.append("0");
+ }
+ }
+ }
+ }
+ rowHeight[y - AztecCompactOffset[layers - 1]] = 1;
+ pattern[y - AztecCompactOffset[layers - 1]] = bin2pat(bin.toString());
+ bin = new StringBuilder();
+ }
+
+ } else {
+ rowCount = 151 - (2 * AztecOffset[layers - 1]);
+ rowHeight = new int[rowCount];
+ rowHeight[0] = -1;
+ pattern = new String[rowCount];
+ bin = new StringBuilder();
+ for (y = AztecOffset[layers - 1]; y < (151 - AztecOffset[layers - 1]); y++) {
+ for (x = AztecOffset[layers - 1]; x < (151 - AztecOffset[layers - 1]); x++) {
+ j = AZTEC_MAP[x][y];
+ if (j == 1) {
+ bin.append("1");
+ }
+ if (j == 0) {
+ bin.append("0");
+ }
+ if (j >= 2) {
+ if ((j - 2) < bit_pattern.length()) {
+ bin.append(bit_pattern.charAt(j - 2));
+ } else {
+ if (j > 20000) {
+ bin.append(descriptor.charAt(j - 20000));
+ } else {
+ bin.append("0");
+ }
+ }
+ }
+ }
+ rowHeight[y - AztecOffset[layers - 1]] = 1;
+ pattern[y - AztecOffset[layers - 1]] = bin2pat(bin.toString());
+ bin = new StringBuilder();
+ }
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private boolean generateAztecBinary() {
+ /* Encode input data into a binary string */
+ int i, j, k, bytes;
+ int curtable, newtable, lasttable, chartype, maplength, blocks;
+ int[] charmap = new int[2 * inputBytes.length];
+ int[] typemap = new int[2 * inputBytes.length];
+ int[] blockType = new int[inputBytes.length + 1];
+ int[] blockLength = new int[inputBytes.length + 1];
+ int weight;
+
+ /* Lookup input string in encoding table */
+ maplength = 0;
+
+ if (inputDataType == DataType.GS1) {
+ /* Add FNC1 to beginning of GS1 messages */
+ charmap[maplength] = 0; // FLG
+ typemap[maplength++] = 8; // PUNC
+ charmap[maplength] = 400; // (0)
+ typemap[maplength++] = 8; // PUNC
+ }
+
+ if (eciMode != 3) {
+ int flagNumber;
+
+ charmap[maplength] = 0; // FLG
+ typemap[maplength++] = 8; // PUNC
+
+ flagNumber = 6;
+
+ if (eciMode < 100000) {
+ flagNumber = 5;
+ }
+
+ if (eciMode < 10000) {
+ flagNumber = 4;
+ }
+
+ if (eciMode < 1000) {
+ flagNumber = 3;
+ }
+
+ if (eciMode < 100) {
+ flagNumber = 2;
+ }
+
+ if (eciMode < 10) {
+ flagNumber = 1;
+ }
+
+ charmap[maplength] = 400 + flagNumber;
+ typemap[maplength++] = 8; // PUNC
+ }
+
+ for (i = 0; i < inputBytes.length; i++) {
+ if ((inputDataType == DataType.GS1) && ((inputBytes[i] & 0xFF) == '[')) {
+ /* FNC1 represented by FLG(0) */
+ charmap[maplength] = 0; // FLG
+ typemap[maplength++] = 8; // PUNC
+ charmap[maplength] = 400; // (0)
+ typemap[maplength++] = 8; // PUNC
+ } else {
+ if (((inputBytes[i] & 0xFF) > 0x7F) || ((inputBytes[i] & 0xFF) == 0x00)) {
+ charmap[maplength] = (inputBytes[i] & 0xFF);
+ typemap[maplength++] = 32; //BINARY
+ } else {
+ charmap[maplength] = AztecSymbolChar[(inputBytes[i] & 0xFF)];
+ typemap[maplength++] = AztecCodeSet[(inputBytes[i] & 0xFF)];
+ }
+ }
+ }
+
+ /* Look for double character encoding possibilities */
+ i = 0;
+ do {
+ if (((charmap[i] == 300) && (charmap[i + 1] == 11)) && ((typemap[i] == 8 /*PUNC */) && (typemap[i + 1] == 8 /*PUNC*/))) {
+ /* CR LF combination */
+ charmap[i] = 2;
+ typemap[i] = 8; // PUNC
+ if ((i + 1) != maplength) {
+ for (j = i + 1; j < maplength; j++) {
+ charmap[j] = charmap[j + 1];
+ typemap[j] = typemap[j + 1];
+ }
+ }
+ maplength--;
+ }
+
+ if (((charmap[i] == 302) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) {
+ /* . SP combination */
+ charmap[i] = 3;
+ typemap[i] = 8; // PUNC;
+ if ((i + 1) != maplength) {
+ for (j = i + 1; j < maplength; j++) {
+ charmap[j] = charmap[j + 1];
+ typemap[j] = typemap[j + 1];
+ }
+ }
+ maplength--;
+ }
+
+ if (((charmap[i] == 301) && (charmap[i + 1] == 1)) && ((typemap[i] == 24) && (typemap[i + 1] == 23))) {
+ /* , SP combination */
+ charmap[i] = 4;
+ typemap[i] = 8; //PUNC;
+ if ((i + 1) != maplength) {
+ for (j = i + 1; j < maplength; j++) {
+ charmap[j] = charmap[j + 1];
+ typemap[j] = typemap[j + 1];
+ }
+ }
+ maplength--;
+ }
+
+ if (((charmap[i] == 21) && (charmap[i + 1] == 1)) && ((typemap[i] == 8) && (typemap[i + 1] == 23))) {
+ /* : SP combination */
+ charmap[i] = 5;
+ typemap[i] = 8; //PUNC;
+ if ((i + 1) != maplength) {
+ for (j = i + 1; j < maplength; j++) {
+ charmap[j] = charmap[j + 1];
+ typemap[j] = typemap[j + 1];
+ }
+ }
+ maplength--;
+ }
+
+ i++;
+ } while (i < (maplength - 1));
+
+ /* look for blocks of characters which use the same table */
+ blocks = 1;
+ blockType[0] = typemap[0];
+ blockLength[0] = 1;
+ for (i = 1; i < maplength; i++) {
+ if (typemap[i] == typemap[i - 1]) {
+ blockLength[blocks - 1]++;
+ } else {
+ blocks++;
+ blockType[blocks - 1] = typemap[i];
+ blockLength[blocks - 1] = 1;
+ }
+ }
+
+ if ((blockType[0] & 1) != 0) {
+ blockType[0] = 1;
+ }
+ if ((blockType[0] & 2) != 0) {
+ blockType[0] = 2;
+ }
+ if ((blockType[0] & 4) != 0) {
+ blockType[0] = 4;
+ }
+ if ((blockType[0] & 8) != 0) {
+ blockType[0] = 8;
+ }
+
+ if (blocks > 1) {
+
+ /* look for adjacent blocks which can use the same table (left to right search) */
+ for (i = 1; i < blocks; i++) {
+ if ((blockType[i] & blockType[i - 1]) != 0) {
+ blockType[i] = (blockType[i] & blockType[i - 1]);
+ }
+ }
+
+ if ((blockType[blocks - 1] & 1) != 0) {
+ blockType[blocks - 1] = 1;
+ }
+ if ((blockType[blocks - 1] & 2) != 0) {
+ blockType[blocks - 1] = 2;
+ }
+ if ((blockType[blocks - 1] & 4) != 0) {
+ blockType[blocks - 1] = 4;
+ }
+ if ((blockType[blocks - 1] & 8) != 0) {
+ blockType[blocks - 1] = 8;
+ }
+
+ /* look for adjacent blocks which can use the same table (right to left search) */
+ for (i = blocks - 2; i > 0; i--) {
+ if ((blockType[i] & blockType[i + 1]) != 0) {
+ blockType[i] = (blockType[i] & blockType[i + 1]);
+ }
+ }
+
+ /* determine the encoding table for characters which do not fit with adjacent blocks */
+ for (i = 1; i < blocks; i++) {
+ if ((blockType[i] & 8) != 0) {
+ blockType[i] = 8;
+ }
+ if ((blockType[i] & 4) != 0) {
+ blockType[i] = 4;
+ }
+ if ((blockType[i] & 2) != 0) {
+ blockType[i] = 2;
+ }
+ if ((blockType[i] & 1) != 0) {
+ blockType[i] = 1;
+ }
+ }
+
+ /* if less than 4 characters are preceeded and followed by binary blocks
+ then it is more efficient to also encode these in binary
+ */
+
+ /* Combine blocks of the same type */
+ i = 0;
+ do {
+ if (blockType[i] == blockType[i + 1]) {
+ blockLength[i] += blockLength[i + 1];
+ for (j = i + 1; j < blocks - 1; j++) {
+ blockType[j] = blockType[j + 1];
+ blockLength[j] = blockLength[j + 1];
+ }
+ blocks--;
+ } else {
+ i++;
+ }
+ } while (i < blocks - 1);
+ }
+
+ /* Put the adjusted block data back into typemap */
+ j = 0;
+ for (i = 0; i < blocks; i++) {
+ if ((blockLength[i] < 3) && (blockType[i] != 32)) { /* Shift character(s) needed */
+
+ for (k = 0; k < blockLength[i]; k++) {
+ typemap[j + k] = blockType[i] + 64;
+ }
+ } else { /* Latch character (or byte mode) needed */
+
+ for (k = 0; k < blockLength[i]; k++) {
+ typemap[j + k] = blockType[i];
+ }
+ }
+ j += blockLength[i];
+ }
+
+ /* Don't shift an initial capital letter */
+ if (typemap[0] == 65) {
+ typemap[0] = 1;
+ }
+
+ /* Problem characters (those that appear in different tables with different values) can now be resolved into their tables */
+ for (i = 0; i < maplength; i++) {
+ if ((charmap[i] >= 300) && (charmap[i] < 400)) {
+ curtable = typemap[i];
+ if (curtable > 64) {
+ curtable -= 64;
+ }
+ switch (charmap[i]) {
+ case 300:
+ /* Carriage Return */
+ switch (curtable) {
+ case 8:
+ charmap[i] = 1;
+ break; // PUNC
+ case 4:
+ charmap[i] = 14;
+ break; // PUNC
+ }
+ break;
+ case 301:
+ /* Comma */
+ switch (curtable) {
+ case 8:
+ charmap[i] = 17;
+ break; // PUNC
+ case 16:
+ charmap[i] = 12;
+ break; // DIGIT
+ }
+ break;
+ case 302:
+ /* Full Stop */
+ switch (curtable) {
+ case 8:
+ charmap[i] = 19;
+ break; // PUNC
+ case 16:
+ charmap[i] = 13;
+ break; // DIGIT
+ }
+ break;
+ }
+ }
+ }
+
+ binaryString = new StringBuilder();
+
+ encodeInfo.append("Encoding: ");
+
+ curtable = 1; /* start with 1 table */
+
+ lasttable = 1;
+ for (i = 0; i < maplength; i++) {
+ newtable = curtable;
+ if ((typemap[i] != curtable) && (charmap[i] < 400)) {
+ /* Change table */
+ if (curtable == 32) {
+ /* If ending binary mode the current table is the same as when entering binary mode */
+ curtable = lasttable;
+ newtable = lasttable;
+ }
+ if (typemap[i] > 64) {
+ /* Shift character */
+ switch (typemap[i]) {
+ case (64 + 1):
+ /* To UPPER */
+ switch (curtable) {
+ case 2:
+ /* US */
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("US ");
+ break;
+ case 4:
+ /* UL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("UL ");
+ newtable = 1;
+ break;
+ case 8:
+ /* UL */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ newtable = 1;
+ break;
+ case 16:
+ /* US */
+ binaryString.append(quadbit[15]);
+ encodeInfo.append("US ");
+ break;
+ }
+ break;
+ case (64 + 2):
+ /* To LOWER */
+ switch (curtable) {
+ case 1:
+ /* LL */
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ case 4:
+ /* LL */
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ case 8:
+ /* UL LL */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ case 16:
+ /* UL LL */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ }
+ break;
+ case (64 + 4):
+ /* To MIXED */
+ switch (curtable) {
+ case 1:
+ /* ML */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ case 2:
+ /* ML */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ case 8:
+ /* UL ML */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ case 16:
+ /* UL ML */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ }
+ break;
+ case (64 + 8):
+ /* To PUNC */
+ switch (curtable) {
+ case 1:
+ /* PS */
+ binaryString.append(pentbit[0]);
+ encodeInfo.append("PS ");
+ break;
+ case 2:
+ /* PS */
+ binaryString.append(pentbit[0]);
+ encodeInfo.append("PS ");
+ break;
+ case 4:
+ /* PS */
+ binaryString.append(pentbit[0]);
+ encodeInfo.append("PS ");
+ break;
+ case 16:
+ /* PS */
+ binaryString.append(quadbit[0]);
+ encodeInfo.append("PS ");
+ break;
+ }
+ break;
+ case (64 + 16):
+ /* To DIGIT */
+ switch (curtable) {
+ case 1:
+ /* DL */
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ case 2:
+ /* DL */
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ case 4:
+ /* UL DL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ case 8:
+ /* UL DL */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ }
+ break;
+ }
+ } else {
+ /* Latch character */
+ switch (typemap[i]) {
+ case 1:
+ /* To UPPER */
+ switch (curtable) {
+ case 2:
+ /* ML UL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("UL ");
+ newtable = 1;
+ break;
+ case 4:
+ /* UL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("UL ");
+ newtable = 1;
+ break;
+ case 8:
+ /* UL */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ newtable = 1;
+ break;
+ case 16:
+ /* UL */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ newtable = 1;
+ break;
+ }
+ break;
+ case 2:
+ /* To LOWER */
+ switch (curtable) {
+ case 1:
+ /* LL */
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ case 4:
+ /* LL */
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ case 8:
+ /* UL LL */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ case 16:
+ /* UL LL */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[28]);
+ encodeInfo.append("LL ");
+ newtable = 2;
+ break;
+ }
+ break;
+ case 4:
+ /* To MIXED */
+ switch (curtable) {
+ case 1:
+ /* ML */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ case 2:
+ /* ML */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ case 8:
+ /* UL ML */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ case 16:
+ /* UL ML */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ newtable = 4;
+ break;
+ }
+ break;
+ case 8:
+ /* To PUNC */
+ switch (curtable) {
+ case 1:
+ /* ML PL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("PL ");
+ newtable = 8;
+ break;
+ case 2:
+ /* ML PL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("PL ");
+ newtable = 8;
+ break;
+ case 4:
+ /* PL */
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("PL ");
+ newtable = 8;
+ break;
+ case 16:
+ /* UL ML PL */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("ML ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("PL ");
+ newtable = 8;
+ break;
+ }
+ break;
+ case 16:
+ /* To DIGIT */
+ switch (curtable) {
+ case 1:
+ /* DL */
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ case 2:
+ /* DL */
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ case 4:
+ /* UL DL */
+ binaryString.append(pentbit[29]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ case 8:
+ /* UL DL */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[30]);
+ encodeInfo.append("DL ");
+ newtable = 16;
+ break;
+ }
+ break;
+ case 32:
+ /* To BINARY */
+ lasttable = curtable;
+ switch (curtable) {
+ case 1:
+ /* BS */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("BS ");
+ newtable = 32;
+ break;
+ case 2:
+ /* BS */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("BS ");
+ newtable = 32;
+ break;
+ case 4:
+ /* BS */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("BS ");
+ newtable = 32;
+ break;
+ case 8:
+ /* UL BS */
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("BS ");
+ lasttable = 1;
+ newtable = 32;
+ break;
+ case 16:
+ /* UL BS */
+ binaryString.append(quadbit[14]);
+ encodeInfo.append("UL ");
+ binaryString.append(pentbit[31]);
+ encodeInfo.append("BS ");
+ lasttable = 1;
+ newtable = 32;
+ break;
+ }
+
+ bytes = 0;
+ do {
+ bytes++;
+ } while (typemap[i + (bytes - 1)] == 32);
+ bytes--;
+
+ if (bytes > 2079) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (bytes > 31) { /* Put 00000 followed by 11-bit number of bytes less 31 */
+
+ binaryString.append("00000");
+ for (weight = 0x400; weight > 0; weight = weight >> 1) {
+ if (((bytes - 31) & weight) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ } else { /* Put 5-bit number of bytes */
+
+ for (weight = 0x10; weight > 0; weight = weight >> 1) {
+ if ((bytes & weight) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ /* Add data to the binary string */
+ curtable = newtable;
+ chartype = typemap[i];
+ if (chartype > 64) {
+ chartype -= 64;
+ }
+ switch (chartype) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ if (charmap[i] >= 400) {
+ encodeInfo.append("FLG(").append(Integer.toString(charmap[i] - 400)).append(") ");
+ binaryString.append(tribit[charmap[i] - 400]);
+ if (charmap[i] != 400) {
+ /* ECI */
+ binaryString.append(eciToBinary());
+ }
+ } else {
+ binaryString.append(pentbit[charmap[i]]);
+ encodeInfo.append(Integer.toString(charmap[i])).append(" ");
+ }
+ break;
+ case 16:
+ binaryString.append(quadbit[charmap[i]]);
+ encodeInfo.append(Integer.toString(charmap[i]));
+ break;
+ case 32:
+ for (weight = 0x80; weight > 0; weight = weight >> 1) {
+ if ((charmap[i] & weight) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ encodeInfo.append(Integer.toString(charmap[i]));
+ break;
+ }
+
+ }
+ encodeInfo.append("\n");
+ return true;
+ }
+
+ private String eciToBinary() {
+ StringBuilder binary = new StringBuilder();
+ String eciNumber = Integer.toString(eciMode);
+ int i;
+
+ for (i = 0; i < eciNumber.length(); i++) {
+ binary.append(quadbit[(eciNumber.charAt(i) - '0') + 2]);
+ encodeInfo.append(Character.toString(eciNumber.charAt(i))).append(" ");
+ }
+
+ return binary.toString();
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java b/barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java
new file mode 100755
index 0000000..2077831
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java
@@ -0,0 +1,168 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+
+/**
+ * Implements Aztec Runes bar code symbology.
+ * According to ISO/IEC 24778:2008 Annex A
+ * Aztec Runes is a fixed-size matrix symbology which can encode whole
+ * integer values between 0 and 255.
+ */
+public class AztecRune extends Symbol {
+
+ private int[] bitPlacementMap = {
+ 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9,
+ 28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10,
+ 27, 1, 0, 1, 0, 0, 0, 1, 0, 1, 11,
+ 26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12,
+ 25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13,
+ 24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14,
+ 23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0
+ };
+
+ @Override
+ public boolean encode() {
+ int decimalValue = 0;
+ int i;
+ int row;
+ int column;
+ StringBuilder binaryDataStream;
+ StringBuilder reversedBinaryDataStream;
+ int[] dataCodeword = new int[3];
+ int[] errorCorrectionCodeword = new int[6];
+ ReedSolomon rs = new ReedSolomon();
+ StringBuilder rowBinary;
+
+ if (content.length() > 3) {
+ errorMsg.append("Input too large");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid input data");
+ return false;
+ }
+
+ switch (content.length()) {
+ case 3:
+ decimalValue = 100 * (content.charAt(0) - '0');
+ decimalValue += 10 * (content.charAt(1) - '0');
+ decimalValue += (content.charAt(2) - '0');
+ break;
+ case 2:
+ decimalValue = 10 * (content.charAt(0) - '0');
+ decimalValue += (content.charAt(1) - '0');
+ break;
+ case 1:
+ decimalValue = (content.charAt(0) - '0');
+ break;
+ }
+
+ if (decimalValue > 255) {
+ errorMsg.append("Input too large");
+ return false;
+ }
+
+ binaryDataStream = new StringBuilder();
+ for (i = 0x80; i > 0; i = i >> 1) {
+ if ((decimalValue & i) != 0) {
+ binaryDataStream.append("1");
+ } else {
+ binaryDataStream.append("0");
+ }
+ }
+
+ dataCodeword[0] = 0;
+ dataCodeword[1] = 0;
+
+ for (i = 0; i < 2; i++) {
+ if (binaryDataStream.charAt(i * 4) == '1') {
+ dataCodeword[i] += 8;
+ }
+ if (binaryDataStream.charAt((i * 4) + 1) == '1') {
+ dataCodeword[i] += 4;
+ }
+ if (binaryDataStream.charAt((i * 4) + 2) == '1') {
+ dataCodeword[i] += 2;
+ }
+ if (binaryDataStream.charAt((i * 4) + 3) == '1') {
+ dataCodeword[i] += 1;
+ }
+ }
+
+ rs.init_gf(0x13);
+ rs.init_code(5, 1);
+ rs.encode(2, dataCodeword);
+
+ for (i = 0; i < 5; i++) {
+ errorCorrectionCodeword[i] = rs.getResult(i);
+ }
+
+ for (i = 0; i < 5; i++) {
+ if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) {
+ binaryDataStream.append('1');
+ } else {
+ binaryDataStream.append('0');
+ }
+ if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) {
+ binaryDataStream.append('1');
+ } else {
+ binaryDataStream.append('0');
+ }
+ if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) {
+ binaryDataStream.append('1');
+ } else {
+ binaryDataStream.append('0');
+ }
+ if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) {
+ binaryDataStream.append('1');
+ } else {
+ binaryDataStream.append('0');
+ }
+ }
+
+ reversedBinaryDataStream = new StringBuilder();
+ for (i = 0; i < binaryDataStream.length(); i++) {
+ if ((i & 1) == 0) {
+ if (binaryDataStream.charAt(i) == '0') {
+ reversedBinaryDataStream.append("1");
+ } else {
+ reversedBinaryDataStream.append("0");
+ }
+ } else {
+ reversedBinaryDataStream.append(binaryDataStream.charAt(i));
+ }
+ }
+
+ encodeInfo.append("Binary: ").append(reversedBinaryDataStream).append("\n");
+
+ rowBinary = new StringBuilder();
+ readable = new StringBuilder();
+ pattern = new String[11];
+ rowCount = 11;
+ rowHeight = new int[11];
+ for (row = 0; row < 11; row++) {
+ for (column = 0; column < 11; column++) {
+ if (bitPlacementMap[(row * 11) + column] == 1) {
+ rowBinary.append("1");
+ }
+ if (bitPlacementMap[(row * 11) + column] == 0) {
+ rowBinary.append("0");
+ }
+ if (bitPlacementMap[(row * 11) + column] >= 2) {
+ rowBinary.append(reversedBinaryDataStream.charAt(bitPlacementMap[(row * 11) + column] - 2));
+ }
+ }
+ pattern[row] = bin2pat(rowBinary.toString());
+ rowHeight[row] = 1;
+ rowBinary = new StringBuilder();
+ }
+
+ plotSymbol();
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java
new file mode 100755
index 0000000..ddd7301
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java
@@ -0,0 +1,155 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Channel Code according to ANSI/AIM BC12-1998.
+ * Channel code encodes whole integer values between 0 and 7,742,862.
+ */
+public class ChannelCode extends Symbol {
+ private int[] space = new int[11];
+ private int[] bar = new int[11];
+ private double currentValue;
+ private double targetValue;
+ private String horizontalSpacing;
+ private int requestedNumberOfChannels = 0;
+
+ /**
+ * Set the number of channels used to encode data. This setting will be
+ * ignored if the value to be encoded requires more channels.
+ *
+ * @param channels Number of channels in range 3 - 8
+ */
+ public void setNumberOfChannels(int channels) {
+ if ((channels >= 3) && (channels <= 7)) {
+ requestedNumberOfChannels = channels;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("fallthrough")
+ public boolean encode() {
+ int numberOfChannels;
+ int i;
+ int leadingZeroCount;
+
+ targetValue = 0;
+ horizontalSpacing = "";
+
+ if (content.length() > 7) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if ((requestedNumberOfChannels <= 2) || (requestedNumberOfChannels > 8)) {
+ numberOfChannels = 3;
+ } else {
+ numberOfChannels = requestedNumberOfChannels;
+ }
+
+ for (i = 0; i < content.length(); i++) {
+ targetValue *= 10;
+ targetValue += Character.getNumericValue(content.charAt(i));
+ }
+
+ switch (numberOfChannels) {
+ case 3:
+ if (targetValue > 26) {
+ numberOfChannels++;
+ }
+ case 4:
+ if (targetValue > 292) {
+ numberOfChannels++;
+ }
+ case 5:
+ if (targetValue > 3493) {
+ numberOfChannels++;
+ }
+ case 6:
+ if (targetValue > 44072) {
+ numberOfChannels++;
+ }
+ case 7:
+ if (targetValue > 576688) {
+ numberOfChannels++;
+ }
+ case 8:
+ if (targetValue > 7742862) {
+ numberOfChannels++;
+ }
+ }
+
+ if (numberOfChannels == 9) {
+ errorMsg.append("Value out of range");
+ return false;
+ }
+
+ encodeInfo.append("Channels Used: ").append(numberOfChannels).append('\n');
+
+ for (i = 0; i < 11; i++) {
+ bar[i] = 0;
+ space[i] = 0;
+ }
+
+ bar[0] = space[1] = bar[1] = space[2] = bar[2] = 1;
+ currentValue = 0;
+ nextSpace(numberOfChannels, 3, numberOfChannels, numberOfChannels);
+
+ leadingZeroCount = numberOfChannels - 1 - content.length();
+
+ readable = new StringBuilder();
+ for (i = 0; i < leadingZeroCount; i++) {
+ readable.append("0");
+ }
+ readable.append(content);
+
+ pattern = new String[1];
+ pattern[0] = horizontalSpacing;
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ private void nextSpace(int channels, int i, int maxSpace, int maxBar) {
+ int s;
+
+ for (s = (i < channels + 2) ? 1 : maxSpace; s <= maxSpace; s++) {
+ space[i] = s;
+ nextBar(channels, i, maxBar, maxSpace + 1 - s);
+ }
+ }
+
+ private void nextBar(int channels, int i, int maxBar, int maxSpace) {
+ int b;
+
+ b = (space[i] + bar[i - 1] + space[i - 1] + bar[i - 2] > 4) ? 1 : 2;
+ if (i < channels + 2) {
+ for (; b <= maxBar; b++) {
+ bar[i] = b;
+ nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b);
+ }
+ } else if (b <= maxBar) {
+ bar[i] = maxBar;
+ checkIfDone();
+ currentValue++;
+ }
+ }
+
+ private void checkIfDone() {
+ int i;
+
+ if (currentValue == targetValue) {
+ /* Target reached - save the generated pattern */
+ horizontalSpacing = "11110";
+ for (i = 0; i < 11; i++) {
+ horizontalSpacing += (char) (space[i] + '0');
+ horizontalSpacing += (char) (bar[i] + '0');
+ }
+ }
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java b/barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java
new file mode 100755
index 0000000..87aa226
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java
@@ -0,0 +1,86 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Codabar barcode symbology according to BS EN 798:1996.
+ * Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27.
+ * Codabar can encode any length string starting and ending with the letters
+ * A-D and containing between these letters the numbers 0-9, dash (-), dollar
+ * ($), colon (:), slash (/), full stop (.) or plus (+). No check digit is
+ * generated.
+ */
+public class Codabar extends Symbol {
+
+ private static final String[] CODABAR_TABLE = {
+ "11111221", "11112211", "11121121", "22111111", "11211211",
+ "21111211", "12111121", "12112111", "12211111", "21121111",
+ "11122111", "11221111", "21112121", "21211121", "21212111",
+ "11212121", "11221211", "12121121", "11121221", "11122211"
+ };
+
+ private static final char[] CHARACTER_SET = {
+ '0', '1', '2', '3', '4',
+ '5', '6', '7', '8', '9',
+ '-', '$', ':', '/', '.',
+ '+', 'A', 'B', 'C', 'D'
+ };
+
+ /**
+ * Ratio of wide bar width to narrow bar width.
+ */
+ private double moduleWidthRatio = 2;
+
+ /**
+ * Returns the ratio of wide bar width to narrow bar width.
+ *
+ * @return the ratio of wide bar width to narrow bar width
+ */
+ public double getModuleWidthRatio() {
+ return moduleWidthRatio;
+ }
+
+ /**
+ * Sets the ratio of wide bar width to narrow bar width. Valid values are usually
+ * between {@code 2} and {@code 3}. The default value is {@code 2}.
+ *
+ * @param moduleWidthRatio the ratio of wide bar width to narrow bar width
+ */
+ public void setModuleWidthRatio(double moduleWidthRatio) {
+ this.moduleWidthRatio = moduleWidthRatio;
+ }
+
+ @Override
+ public boolean encode() {
+ if (!(content.matches("[A-D]{1}[0-9:/\\$\\.\\+\u002D]+[A-D]{1}"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder horizontalSpacing = new StringBuilder();
+
+ int l = content.length();
+ for (int i = 0; i < l; i++) {
+ horizontalSpacing.append(CODABAR_TABLE[positionOf(content.charAt(i), CHARACTER_SET)]);
+ }
+
+ readable = new StringBuilder(content);
+ pattern = new String[]{horizontalSpacing.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ plotSymbol();
+ return true;
+ }
+
+ @Override
+ protected double getModuleWidth(int originalWidth) {
+ if (originalWidth == 1) {
+ return 1;
+ } else {
+ return moduleWidthRatio;
+ }
+ }
+
+ @Override
+ protected int[] getCodewords() {
+ return getPatternAsCodewords(8);
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java b/barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java
new file mode 100755
index 0000000..a3df7e4
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java
@@ -0,0 +1,852 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F", 1995.
+ * Codablock F is a multi-row symbology using Code 128 encoding. It can
+ * encode any 8-bit ISO 8859-1 (Latin-1) data up to approximately 1000
+ * alpha-numeric characters or 2000 numeric digits in length.
+ */
+public class CodablockF extends Symbol {
+
+ /* Annex A Table A.1 */
+ private String[] C128Table = {"212222", "222122", "222221", "121223", "121322", "131222", "122213",
+ "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222",
+ "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222",
+ "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323",
+ "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133",
+ "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113",
+ "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111",
+ "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214",
+ "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112",
+ "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112",
+ "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311",
+ "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232",
+ "2331112"};
+ private final int[][] blockmatrix = new int[44][62];
+ private int columns_needed;
+ private int[] source;
+ private int rows_needed;
+ private cfMode final_mode;
+ private final cfMode[] subset_selector = new cfMode[44];
+
+ @Override
+ public boolean encode() {
+ int input_length, i, j, k;
+ int min_module_height;
+ Mode last_mode, this_mode;
+ double estimate_codelength;
+ StringBuilder row_pattern;
+ int[] row_indicator = new int[44];
+ int[] row_check = new int[44];
+ int k1_sum, k2_sum;
+ int k1_check, k2_check;
+
+ input_length = content.length();
+ final_mode = cfMode.MODEA;
+
+ if (input_length > 5450) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ if (!content.matches("[\u0000-\u00FF]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ try {
+ inputBytes = content.getBytes("ISO8859_1");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Character encoding error");
+ return false;
+ }
+
+ source = new int[input_length + 1];
+ for (i = 0; i < input_length; i++) {
+ source[i] = inputBytes[i] & 0xFF;
+ }
+ source[input_length] = 0x00;
+
+ /* Make a guess at how many characters will be needed to encode the data */
+ estimate_codelength = 0.0;
+ last_mode = Mode.AORB; /* Codablock always starts with Code A */
+ for (i = 0; i < input_length; i++) {
+ this_mode = findSubset(source[i]);
+ if (this_mode != last_mode) {
+ estimate_codelength += 1.0;
+ }
+ if (this_mode != Mode.ABORC) {
+ estimate_codelength += 1.0;
+ } else {
+ estimate_codelength += 0.5;
+ }
+ if (source[i] > 127) {
+ estimate_codelength += 1.0;
+ }
+ last_mode = this_mode;
+ }
+
+ /* Decide symbol size based on the above guess */
+ rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45));
+ if (rows_needed < 2) {
+ rows_needed = 2;
+ }
+ if (rows_needed > 44) {
+ rows_needed = 44;
+ }
+ columns_needed = (int) (estimate_codelength + 2) / rows_needed;
+ if (columns_needed < 4) {
+ columns_needed = 4;
+ }
+ if (columns_needed > 62) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ /* Encode the data */
+ if (!(data_encode_blockf())) {
+ return false;
+ }
+
+ /* Add check digits - Annex F */
+ k1_sum = 0;
+ k2_sum = 0;
+ for (i = 0; i < input_length; i++) {
+ if ((inputDataType == DataType.GS1) && source[i] == '[') {
+ k1_sum += (i + 1) * 29; /* GS */
+ k2_sum += i * 29;
+ } else {
+ k1_sum += (i + 1) * source[i];
+ k2_sum += i * source[i];
+ }
+ }
+ k1_check = k1_sum % 86;
+ k2_check = k2_sum % 86;
+ if ((final_mode == cfMode.MODEA) || (final_mode == cfMode.MODEB)) {
+ k1_check = k1_check + 64;
+ if (k1_check > 95) {
+ k1_check -= 96;
+ }
+ k2_check = k2_check + 64;
+ if (k2_check > 95) {
+ k2_check -= 96;
+ }
+ }
+ blockmatrix[rows_needed - 1][columns_needed - 2] = k1_check;
+ blockmatrix[rows_needed - 1][columns_needed - 1] = k2_check;
+
+ /* Calculate row height (4.6.1.a) */
+ min_module_height = (int) (0.55 * (columns_needed + 3)) + 3;
+ if (min_module_height < 8) {
+ min_module_height = 8;
+ }
+
+ /* Encode the Row Indicator in the First Row of the Symbol - Table D2 */
+ if (subset_selector[0] == cfMode.MODEC) {
+ /* Code C */
+ row_indicator[0] = rows_needed - 2;
+ } else {
+ /* Code A or B */
+ row_indicator[0] = rows_needed + 62;
+
+ if (row_indicator[0] > 95) {
+ row_indicator[0] -= 95;
+ }
+ }
+
+ /* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */
+ for (i = 1; i < rows_needed; i++) {
+ /* Note that the second row is row number 1 because counting starts from 0 */
+ if (subset_selector[i] == cfMode.MODEC) {
+ /* Code C */
+ row_indicator[i] = i + 42;
+ } else {
+ /* Code A or B */
+ if (i < 6)
+ row_indicator[i] = i + 10;
+ else
+ row_indicator[i] = i + 20;
+ }
+ }
+
+ /* Calculate row check digits - Annex E */
+ for (i = 0; i < rows_needed; i++) {
+ k = 103;
+ switch (subset_selector[i]) {
+ case MODEA:
+ k += 98;
+ break;
+ case MODEB:
+ k += 100;
+ break;
+ case MODEC:
+ k += 99;
+ break;
+ }
+ k += 2 * row_indicator[i];
+ for (j = 0; j < columns_needed; j++) {
+ k += (j + 3) * blockmatrix[i][j];
+ }
+ row_check[i] = k % 103;
+ }
+
+ readable = new StringBuilder();
+ rowCount = rows_needed;
+ pattern = new String[rowCount];
+ rowHeight = new int[rowCount];
+
+ encodeInfo.append("Grid size: ").append(columns_needed).append(" X ").append(rows_needed).append('\n');
+ encodeInfo.append("K1 Check Digit: ").append(k1_check).append("\n");
+ encodeInfo.append("K2 Check Digit: ").append(k2_check).append("\n");
+
+ /* Resolve the data into patterns and place in symbol structure */
+ encodeInfo.append("Encoding: ");
+ for (i = 0; i < rows_needed; i++) {
+
+ row_pattern = new StringBuilder();
+ /* Start character */
+ row_pattern.append(C128Table[103]); /* Always Start A */
+
+ switch (subset_selector[i]) {
+ case MODEA:
+ row_pattern.append(C128Table[98]);
+ encodeInfo.append("MODEA ");
+ break;
+ case MODEB:
+ row_pattern.append(C128Table[100]);
+ encodeInfo.append("MODEB ");
+ break;
+ case MODEC:
+ row_pattern.append(C128Table[99]);
+ encodeInfo.append("MODEC ");
+ break;
+ }
+ row_pattern.append(C128Table[row_indicator[i]]);
+ encodeInfo.append(Integer.toString(row_indicator[i])).append(" ");
+
+ for (j = 0; j < columns_needed; j++) {
+ row_pattern.append(C128Table[blockmatrix[i][j]]);
+ encodeInfo.append(Integer.toString(blockmatrix[i][j])).append(" ");
+ }
+
+ row_pattern.append(C128Table[row_check[i]]);
+ encodeInfo.append("(").append(Integer.toString(row_check[i])).append(") ");
+
+ /* Stop character */
+ row_pattern.append(C128Table[106]);
+
+ /* Write the information into the symbol */
+ pattern[i] = row_pattern.toString();
+ rowHeight[i] = 15;
+ }
+ encodeInfo.append("\n");
+
+ symbolHeight = rows_needed * 15;
+ plotSymbol();
+ return true;
+ }
+
+ private Mode findSubset(int letter) {
+ Mode mode;
+
+ if (letter <= 31) {
+ mode = Mode.SHIFTA;
+ } else if ((letter >= 48) && (letter <= 57)) {
+ mode = Mode.ABORC;
+ } else if (letter <= 95) {
+ mode = Mode.AORB;
+ } else if (letter <= 127) {
+ mode = Mode.SHIFTB;
+ } else if (letter <= 159) {
+ mode = Mode.SHIFTA;
+ } else if (letter <= 223) {
+ mode = Mode.AORB;
+ } else {
+ mode = Mode.SHIFTB;
+ }
+
+ return mode;
+ }
+
+ private boolean data_encode_blockf() {
+ int i, j, input_position, current_row;
+ int column_position, c;
+ cfMode current_mode;
+ boolean done, exit_status;
+
+ exit_status = false;
+ current_row = 0;
+ current_mode = cfMode.MODEA;
+ column_position = 0;
+ input_position = 0;
+ c = 0;
+
+ do {
+ done = false;
+ /* 'done' ensures that the instructions are followed in the correct order for each input character */
+
+ if (column_position == 0) {
+ /* The Beginning of a row */
+ c = columns_needed;
+ current_mode = character_subset_select(input_position);
+ subset_selector[current_row] = current_mode;
+ if ((current_row == 0) && (inputDataType == DataType.GS1)) {
+ /* Section 4.4.7.1 */
+ blockmatrix[current_row][column_position] = 102; /* FNC1 */
+ column_position++;
+ c--;
+ }
+ }
+
+ if ((inputDataType == DataType.GS1) && (source[input_position] == '[')) {
+ blockmatrix[current_row][column_position] = 102; /* FNC1 */
+ column_position++;
+ c--;
+ input_position++;
+ done = true;
+ }
+
+ if (!done) {
+ if (c <= 2) {
+ /* Annex B section 1 rule 1 */
+ /* Ensure that there is sufficient encodation capacity to continue (using the rules of Annex B.2). */
+ switch (current_mode) {
+ case MODEA: /* Table B1 applies */
+ if (findSubset(source[input_position]) == Mode.ABORC) {
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ done = true;
+ }
+
+ if ((findSubset(source[input_position]) == Mode.SHIFTB) && (c == 1)) {
+ /* Needs two symbols */
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ done = true;
+ }
+
+ if ((source[input_position] >= 244) && (!done)) {
+ /* Needs three symbols */
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ if (c == 1) {
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ }
+ done = true;
+ }
+
+ if ((source[input_position] >= 128) && (!done) && c == 1) {
+ /* Needs two symbols */
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ done = true;
+ }
+ break;
+ case MODEB: /* Table B2 applies */
+ if (findSubset(source[input_position]) == Mode.ABORC) {
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ done = true;
+ }
+
+ if ((findSubset(source[input_position]) == Mode.SHIFTA) && (c == 1)) {
+ /* Needs two symbols */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ done = true;
+ }
+
+ if (((source[input_position] >= 128)
+ && (source[input_position] <= 159)) && (!done)) {
+ /* Needs three symbols */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ if (c == 1) {
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ }
+ done = true;
+ }
+
+ if ((source[input_position] >= 160) && (!done) && c == 1) {
+ /* Needs two symbols */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ done = true;
+ }
+ break;
+ case MODEC: /* Table B3 applies */
+ if ((findSubset(source[input_position]) != Mode.ABORC) && (c == 1)) {
+ /* Needs two symbols */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ done = true;
+ }
+
+ if (((findSubset(source[input_position]) == Mode.ABORC)
+ && (findSubset(source[input_position + 1]) != Mode.ABORC))
+ && (c == 1)) {
+ /* Needs two symbols */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ done = true;
+ }
+
+ if (source[input_position] >= 128) {
+ /* Needs three symbols */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ if (c == 1) {
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (!done) {
+ if (((findSubset(source[input_position]) == Mode.AORB)
+ || (findSubset(source[input_position]) == Mode.SHIFTA))
+ && (current_mode == cfMode.MODEA)) {
+ /* Annex B section 1 rule 2 */
+ /* If in Code Subset A and the next data character can be encoded in Subset A encode the next
+ character. */
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 101; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (((findSubset(source[input_position]) == Mode.AORB)
+ || (findSubset(source[input_position]) == Mode.SHIFTB))
+ && (current_mode == cfMode.MODEB)) {
+ /* Annex B section 1 rule 3 */
+ /* If in Code Subset B and the next data character can be encoded in subset B, encode the next
+ character. */
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 100; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (((findSubset(source[input_position]) == Mode.ABORC)
+ && (findSubset(source[input_position + 1]) == Mode.ABORC))
+ && (current_mode == cfMode.MODEC)) {
+ /* Annex B section 1 rule 4 */
+ /* If in Code Subset C and the next data are 2 digits, encode them. */
+ blockmatrix[current_row][column_position]
+ = ((source[input_position] - '0') * 10)
+ + (source[input_position + 1] - '0');
+ column_position++;
+ c--;
+ input_position += 2;
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (((current_mode == cfMode.MODEA) || (current_mode == cfMode.MODEB))
+ && ((findSubset(source[input_position]) == Mode.ABORC)
+ || ((inputDataType == DataType.GS1) && (source[input_position] == '[')))) {
+ /* Count the number of numeric digits */
+ /* If 4 or more numeric data characters occur together when in subsets A or B:
+ a. If there is an even number of numeric data characters, insert a Code C character before the
+ first numeric digit to change to subset C.
+ b. If there is an odd number of numeric data characters, insert a Code Set C character immedi-
+ ately after the first numeric digit to change to subset C. */
+ i = 0;
+ j = 0;
+ do {
+ i++;
+ if ((inputDataType == DataType.GS1) && (source[input_position + j] == '[')) {
+ i++;
+ }
+ j++;
+ } while ((findSubset(source[input_position + j]) == Mode.ABORC)
+ || ((inputDataType == DataType.GS1) && (source[input_position + j] == '[')));
+ i--;
+
+ if (i >= 4) {
+ /* Annex B section 1 rule 5 */
+ if ((i % 2) == 1) {
+ /* Annex B section 1 rule 5a */
+ blockmatrix[current_row][column_position] = 99; /* Code C */
+ column_position++;
+ c--;
+ blockmatrix[current_row][column_position] = ((source[input_position] - '0') * 10)
+ + (source[input_position + 1] - '0');
+ column_position++;
+ c--;
+ input_position += 2;
+ current_mode = cfMode.MODEC;
+ } else {
+ /* Annex B section 1 rule 5b */
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ }
+ done = true;
+ } else {
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ done = true;
+ }
+ }
+ }
+
+ if (!done) {
+ if ((current_mode == cfMode.MODEB) && (findSubset(source[input_position]) == Mode.SHIFTA)) {
+ /* Annex B section 1 rule 6 */
+ /* When in subset B and an ASCII control character occurs in the data:
+ a. If there is a lower case character immediately following the control character, insert a Shift
+ character before the control character.
+ b. Otherwise, insert a Code A character before the control character to change to subset A. */
+ if ((source[input_position + 1] >= 96) && (source[input_position + 1] <= 127)) {
+ /* Annex B section 1 rule 6a */
+ blockmatrix[current_row][column_position] = 98; /* Shift */
+ column_position++;
+ c--;
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 100; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ } else {
+ /* Annex B section 1 rule 6b */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 100; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ current_mode = cfMode.MODEA;
+ }
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if ((current_mode == cfMode.MODEA) && (findSubset(source[input_position]) == Mode.SHIFTB)) {
+ /* Annex B section 1 rule 7 */
+ /* When in subset A and a lower case character occurs in the data:
+ a. If following that character, a control character occurs in the data before the occurrence of
+ another lower case character, insert a Shift character before the lower case character.
+ b. Otherwise, insert a Code B character before the lower case character to change to subset B. */
+ if ((findSubset(source[input_position + 1]) == Mode.SHIFTA)
+ && (findSubset(source[input_position + 2]) == Mode.SHIFTB)) {
+ /* Annex B section 1 rule 7a */
+ blockmatrix[current_row][column_position] = 98; /* Shift */
+ column_position++;
+ c--;
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 101; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ } else {
+ /* Annex B section 1 rule 7b */
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 101; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ current_mode = cfMode.MODEB;
+ }
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if ((current_mode == cfMode.MODEC) && ((findSubset(source[input_position]) != Mode.ABORC)
+ || (findSubset(source[input_position + 1]) != Mode.ABORC))) {
+ /* Annex B section 1 rule 8 */
+ /* When in subset C and a non-numeric character (or a single digit) occurs in the data, insert a Code
+ A or Code B character before that character, following rules 8a and 8b to determine between code
+ subsets A and B.
+ a. If an ASCII control character (eg NUL) occurs in the data before any lower case character, use
+ Code A.
+ b. Otherwise use Code B. */
+ if (findSubset(source[input_position]) == Mode.SHIFTA) {
+ /* Annex B section 1 rule 8a */
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ column_position++;
+ c--;
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 101; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ current_mode = cfMode.MODEA;
+ } else {
+ /* Annex B section 1 rule 8b */
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ column_position++;
+ c--;
+ if (source[input_position] >= 128) {
+ /* Extended ASCII character */
+ blockmatrix[current_row][column_position] = 100; /* FNC4 */
+ column_position++;
+ c--;
+ }
+ blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
+ column_position++;
+ c--;
+ input_position++;
+ current_mode = cfMode.MODEB;
+ }
+ }
+ }
+
+ if (input_position == content.length()) {
+ /* End of data - Annex B rule 5a */
+ if (c == 1) {
+ if (current_mode == cfMode.MODEA) {
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ current_mode = cfMode.MODEB;
+ } else {
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ current_mode = cfMode.MODEA;
+ }
+ column_position++;
+ c--;
+ }
+
+ if (c == 0) {
+ /* Another row is needed */
+ column_position = 0;
+ c = columns_needed;
+ current_row++;
+ subset_selector[current_row] = cfMode.MODEA;
+ current_mode = cfMode.MODEA;
+ }
+
+ if (c > 2) {
+ /* Fill up the last row */
+ do {
+ if (current_mode == cfMode.MODEA) {
+ blockmatrix[current_row][column_position] = 100; /* Code B */
+ current_mode = cfMode.MODEB;
+ } else {
+ blockmatrix[current_row][column_position] = 101; /* Code A */
+ current_mode = cfMode.MODEA;
+ }
+ column_position++;
+ c--;
+ } while (c > 2);
+ }
+
+ /* If (c == 2) { do nothing } */
+
+ exit_status = true;
+ final_mode = current_mode;
+ } else {
+ if (c <= 0) {
+ /* Start new row - Annex B rule 5b */
+ column_position = 0;
+ current_row++;
+ if (current_row > 43) {
+ return false;
+ }
+ }
+ }
+
+ } while (!exit_status);
+
+ if (current_row == 0) {
+ /* fill up the first row */
+ for (c = column_position; c <= columns_needed; c++) {
+ if (current_mode == cfMode.MODEA) {
+ blockmatrix[current_row][c] = 100; /* Code B */
+ current_mode = cfMode.MODEB;
+ } else {
+ blockmatrix[current_row][c] = 101; /* Code A */
+ current_mode = cfMode.MODEA;
+ }
+ }
+ current_row++;
+ /* add a second row */
+ subset_selector[current_row] = cfMode.MODEA;
+ current_mode = cfMode.MODEA;
+ for (c = 0; c <= columns_needed - 2; c++) {
+ if (current_mode == cfMode.MODEA) {
+ blockmatrix[current_row][c] = 100; /* Code B */
+ current_mode = cfMode.MODEB;
+ } else {
+ blockmatrix[current_row][c] = 101; /* Code A */
+ current_mode = cfMode.MODEA;
+ }
+ }
+ }
+ rows_needed = current_row + 1;
+
+ return true;
+ }
+
+ private cfMode character_subset_select(int input_position) {
+ /* Section 4.5.2 - Determining the Character Subset Selector in a Row */
+
+ if ((source[input_position] >= '0') && (source[input_position + 1] <= '9')) {
+ /* Rule 1 */
+ return cfMode.MODEC;
+ }
+
+ if ((source[input_position] >= 128) && (source[input_position] <= 160)) {
+ /* Rule 2 (i) */
+ return cfMode.MODEA;
+ }
+
+ if ((source[input_position] >= 0) && (source[input_position] <= 31)) {
+ /* Rule 3 */
+ return cfMode.MODEA;
+ }
+
+ /* Rule 4 */
+ return cfMode.MODEB;
+ }
+
+ private int a3_convert(int source) {
+ /* Annex A section 3 */
+ if (source < 32) {
+ return source + 64;
+ }
+ if (source <= 127) {
+ return source - 32;
+ }
+ if (source <= 159) {
+ return (source - 128) + 64;
+ }
+ /* if source >= 160 */
+ return (source - 128) - 32;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock, yBlock;
+ int x, y, w, h;
+ boolean black;
+ getRectangles().clear();
+ y = 1;
+ h = 1;
+ for (yBlock = 0; yBlock < rowCount; yBlock++) {
+ black = true;
+ x = 0;
+ for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) {
+ if (black) {
+ black = false;
+ w = pattern[yBlock].charAt(xBlock) - '0';
+ if (rowHeight[yBlock] == -1) {
+ h = defaultHeight;
+ } else {
+ h = rowHeight[yBlock];
+ }
+ if (w != 0 && h != 0) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ }
+ if ((x + w) > symbolWidth) {
+ symbolWidth = x + w;
+ }
+ } else {
+ black = true;
+ }
+ x += (double) (pattern[yBlock].charAt(xBlock) - '0');
+ }
+ y += h;
+ if ((y + h) > symbolHeight) {
+ symbolHeight = y + h;
+ }
+ /* Add bars between rows */
+ if (yBlock != (rowCount - 1)) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(11, y - 1, (symbolWidth - 24), 2);
+ getRectangles().add(rect);
+ }
+ }
+ Rectangle2D.Double top = new Rectangle2D.Double(0, 0, symbolWidth, 2);
+ getRectangles().add(top);
+ Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, symbolWidth, 2);
+ getRectangles().add(bottom);
+ symbolHeight += 2;
+ mergeVerticalBlocks();
+ }
+
+ private enum Mode {
+ SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
+ }
+
+ private enum cfMode {
+ MODEA, MODEB, MODEC
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code11.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code11.java
new file mode 100755
index 0000000..b4e3dd1
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code11.java
@@ -0,0 +1,208 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Code 11 bar code symbology.
+ * Code 11 can encode any length string consisting of the digits 0-9 and the
+ * dash character (-). One or two modulo-11 check digits are calculated.
+ */
+public class Code11 extends Symbol {
+
+ private static final String[] CODE_11_TABLE = {
+ "111121", "211121", "121121", "221111", "112121", "212111",
+ "122111", "111221", "211211", "211111", "112111"
+ };
+
+ private static final char[] CHARACTER_SET = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'
+ };
+
+ /**
+ * Ratio of wide bar width to narrow bar width.
+ */
+ private double moduleWidthRatio = 2;
+
+ /**
+ * The number of check digits to calculate ({@code 1} or {@code 2}).
+ */
+ private int checkDigitCount = 2;
+
+ /**
+ * Optional start delimiter to be shown in the human-readable text.
+ */
+ private Character startDelimiter;
+
+ /**
+ * Optional stop delimiter to be shown in the human-readable text.
+ */
+ private Character stopDelimiter;
+
+ private static int getCheckDigitC(int[] weight, int length) {
+ int countC = 0;
+ int weightC = 1;
+ for (int i = length - 1; i >= 0; i--) {
+ countC += (weightC * weight[i]);
+ weightC++;
+ if (weightC > 10) {
+ weightC = 1;
+ }
+ }
+ return countC % 11;
+ }
+
+ private static int getCheckDigitK(int[] weight, int length) {
+ int countK = 0;
+ int weightK = 1;
+ for (int i = length - 1; i >= 0; i--) {
+ countK += (weightK * weight[i]);
+ weightK++;
+ if (weightK > 9) {
+ weightK = 1;
+ }
+ }
+ return countK % 11;
+ }
+
+ /**
+ * Returns the ratio of wide bar width to narrow bar width.
+ *
+ * @return the ratio of wide bar width to narrow bar width
+ */
+ public double getModuleWidthRatio() {
+ return moduleWidthRatio;
+ }
+
+ /**
+ * Sets the ratio of wide bar width to narrow bar width. Valid values are usually
+ * between {@code 2} and {@code 3}. The default value is {@code 2}.
+ *
+ * @param moduleWidthRatio the ratio of wide bar width to narrow bar width
+ */
+ public void setModuleWidthRatio(double moduleWidthRatio) {
+ this.moduleWidthRatio = moduleWidthRatio;
+ }
+
+ /**
+ * Returns the number of check digits to calculate (1 or 2).
+ *
+ * @return the number of check digits to calculate
+ */
+ public int getCheckDigitCount() {
+ return checkDigitCount;
+ }
+
+ /**
+ * Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is {@code 2}.
+ *
+ * @param checkDigitCount the number of check digits to calculate
+ */
+ public void setCheckDigitCount(int checkDigitCount) {
+ if (checkDigitCount < 1 || checkDigitCount > 2) {
+ throw new IllegalArgumentException("Check digit count must be 1 or 2.");
+ }
+ this.checkDigitCount = checkDigitCount;
+ }
+
+ /**
+ * Returns the optional start delimiter to be shown in the human-readable text.
+ *
+ * @return the optional start delimiter to be shown in the human-readable text
+ */
+ public Character getStartDelimiter() {
+ return startDelimiter;
+ }
+
+ /**
+ * Sets an optional start delimiter to be shown in the human-readable text (defaults to null
).
+ *
+ * @param startDelimiter an optional start delimiter to be shown in the human-readable text
+ */
+ public void setStartDelimiter(Character startDelimiter) {
+ this.startDelimiter = startDelimiter;
+ }
+
+ /**
+ * Returns the optional stop delimiter to be shown in the human-readable text.
+ *
+ * @return the optional stop delimiter to be shown in the human-readable text
+ */
+ public Character getStopDelimiter() {
+ return stopDelimiter;
+ }
+
+ /**
+ * Sets an optional stop delimiter to be shown in the human-readable text (defaults to null
).
+ *
+ * @param stopDelimiter an optional stop delimiter to be shown in the human-readable text
+ */
+ public void setStopDelimiter(Character stopDelimiter) {
+ this.stopDelimiter = stopDelimiter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean encode() {
+
+ if (!(content.matches("[0-9-]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder horizontalSpacing = new StringBuilder("112211");
+ String humanReadable = content;
+ int length = content.length();
+ int[] weight = new int[length + 1];
+
+ for (int i = 0; i < length; i++) {
+ char c = content.charAt(i);
+ weight[i] = positionOf(c, CHARACTER_SET);
+ horizontalSpacing.append(CODE_11_TABLE[weight[i]]);
+ }
+
+ int checkDigitC = getCheckDigitC(weight, length);
+ horizontalSpacing.append(CODE_11_TABLE[checkDigitC]);
+ humanReadable += CHARACTER_SET[checkDigitC];
+ encodeInfo.append("Check Digit C: ").append(checkDigitC).append("\n");
+
+ if (checkDigitCount == 2) {
+ weight[length] = checkDigitC;
+ int checkDigitK = getCheckDigitK(weight, length + 1);
+ horizontalSpacing.append(CODE_11_TABLE[checkDigitK]);
+ humanReadable += CHARACTER_SET[checkDigitK];
+ encodeInfo.append("Check Digit K: ").append(checkDigitK).append("\n");
+ }
+
+ horizontalSpacing.append("112211");
+
+ readable = new StringBuilder(humanReadable);
+ if (startDelimiter != null) {
+ readable = new StringBuilder(startDelimiter).append(readable);
+ }
+ if (stopDelimiter != null) {
+ readable.append(stopDelimiter);
+ }
+
+ pattern = new String[]{horizontalSpacing.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+
+ plotSymbol();
+
+ return true;
+ }
+
+ @Override
+ protected double getModuleWidth(int originalWidth) {
+ if (originalWidth == 1) {
+ return 1;
+ } else {
+ return moduleWidthRatio;
+ }
+ }
+
+ @Override
+ protected int[] getCodewords() {
+ return getPatternAsCodewords(6);
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code128.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code128.java
new file mode 100755
index 0000000..3272c45
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code128.java
@@ -0,0 +1,843 @@
+package org.xbib.graphics.barcode;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements Code 128 bar code symbology according to ISO/IEC 15417:2007.
+ * Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters.
+ * Setting GS1 mode allows encoding in GS1-128 (also known as UPC/EAN-128).
+ */
+public class Code128 extends Symbol {
+ private String[] code128Table = {
+ "212222", "222122", "222221", "121223", "121322", "131222", "122213",
+ "122312", "132212", "221213", "221312", "231212", "112232", "122132",
+ "122231", "113222", "123122", "123221", "223211", "221132", "221231",
+ "213212", "223112", "312131", "311222", "321122", "321221", "312212",
+ "322112", "322211", "212123", "212321", "232121", "111323", "131123",
+ "131321", "112313", "132113", "132311", "211313", "231113", "231311",
+ "112133", "112331", "132131", "113123", "113321", "133121", "313121",
+ "211331", "231131", "213113", "213311", "213131", "311123", "311321",
+ "331121", "312113", "312311", "332111", "314111", "221411", "431111",
+ "111224", "111422", "121124", "121421", "141122", "141221", "112214",
+ "112412", "122114", "122411", "142112", "142211", "241211", "221114",
+ "413111", "241112", "134111", "111242", "121142", "121241", "114212",
+ "124112", "124211", "411212", "421112", "421211", "212141", "214121",
+ "412121", "111143", "111341", "131141", "114113", "114311", "411113",
+ "411311", "113141", "114131", "311141", "411131", "211412", "211214",
+ "211232", "2331112"
+ };
+ private Mode[] mode_type = new Mode[200];
+ private int[] mode_length = new int[200];
+ private int index_point = 0, read = 0;
+ private boolean modeCSupression;
+ private Composite compositeMode;
+
+ public Code128() {
+ modeCSupression = false;
+ compositeMode = Composite.OFF;
+ }
+
+ /**
+ * Allow the use of subset C (numeric compression) in encoding (default).
+ */
+ public void useModeC() {
+ modeCSupression = false;
+ }
+
+ ;
+
+ /**
+ * Disallow the use of subset C (numeric compression) in encoding.
+ * Numeric values will be encoded using subset B.
+ */
+ public void stopModeC() {
+ modeCSupression = true;
+ }
+
+ protected void setCca() {
+ compositeMode = Composite.CCA;
+ }
+
+ protected void setCcb() {
+ compositeMode = Composite.CCB;
+ }
+
+ protected void setCcc() {
+ compositeMode = Composite.CCC;
+ }
+
+ public void unsetCc() {
+ compositeMode = Composite.OFF;
+ }
+
+ @Override
+ public boolean encode() {
+ int sourcelen = content.length();
+ int i, j, k;
+ int input_point = 0;
+ Mode mode, last_mode;
+ Mode last_set, current_set;
+ double glyph_count;
+ int bar_characters = 0, total_sum = 0;
+ FMode f_state = FMode.LATCHN;
+ int[] values = new int[200];
+ int c;
+ StringBuilder dest = new StringBuilder();
+ int[] inputData;
+ int c_count;
+ int linkage_flag = 0;
+
+ if (!content.matches("[\u0000-\u00FF]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ try {
+ inputBytes = content.getBytes("ISO8859_1");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Character encoding error");
+ return false;
+ }
+
+ inputData = new int[sourcelen];
+ for (i = 0; i < sourcelen; i++) {
+ inputData[i] = inputBytes[i] & 0xFF;
+ }
+
+ FMode[] fset = new FMode[200];
+ Mode[] set = new Mode[200]; /* set[] = Calculated mode for each character */
+
+ if (sourcelen > 170) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ /* Detect extended ASCII characters */
+ for (i = 0; i < sourcelen; i++) {
+ if (inputData[i] >= 128) {
+ fset[i] = FMode.SHIFTF;
+ } else {
+ fset[i] = FMode.LATCHN;
+ }
+ }
+
+ /* Decide when to latch to extended mode - Annex E note 3 */
+ j = 0;
+ for (i = 0; i < sourcelen; i++) {
+ if (fset[i] == FMode.SHIFTF) {
+ j++;
+ } else {
+ j = 0;
+ }
+
+ if (j >= 5) {
+ for (k = i; k > (i - 5); k--) {
+ fset[k] = FMode.LATCHF;
+ }
+ }
+
+ if ((j >= 3) && (i == (sourcelen - 1))) {
+ for (k = i; k > (i - 3); k--) {
+ fset[k] = FMode.LATCHF;
+ }
+ }
+ }
+
+ /* Decide if it is worth reverting to 646 encodation for a few characters as described in 4.3.4.2 (d) */
+ for (i = 1; i < sourcelen; i++) {
+ if ((fset[i - 1] == FMode.LATCHF) && (fset[i] == FMode.LATCHN)) {
+ /* Detected a change from 8859-1 to 646 - count how long for */
+ for (j = 0; (fset[i + j] == FMode.LATCHN) && ((i + j) < sourcelen); j++) ;
+ if ((j < 5) || ((j < 3) && ((i + j) == (sourcelen - 1)))) {
+ /* Uses the same figures recommended by Annex E note 3 */
+ /* Change to shifting back rather than latching back */
+ for (k = 0; k < j; k++) {
+ fset[i + k] = FMode.SHIFTN;
+ }
+ }
+ }
+ }
+
+ /* Decide on mode using same system as PDF417 and rules of ISO 15417 Annex E */
+ mode = findSubset(inputData[input_point]);
+ mode_type[0] = mode;
+ mode_length[0] = 1;
+
+ if (inputDataType == DataType.GS1) {
+ mode = Mode.ABORC;
+ }
+
+ if ((modeCSupression) && (mode == Mode.ABORC)) {
+ mode = Mode.AORB;
+ }
+
+ for (i = 1; i < sourcelen; i++) {
+ last_mode = mode;
+ mode = findSubset(inputData[i]);
+ if ((inputDataType == DataType.GS1) && inputData[i] == '[') {
+ mode = Mode.ABORC;
+ }
+ if ((modeCSupression) && (mode == Mode.ABORC)) {
+ mode = Mode.AORB;
+ }
+ if (mode == last_mode) {
+ mode_length[index_point]++;
+ } else {
+ index_point++;
+ mode_type[index_point] = mode;
+ mode_length[index_point] = 1;
+ }
+ }
+ index_point++;
+
+ reduceSubsetChanges();
+
+
+ if (inputDataType == DataType.GS1) {
+ /* Put set data into set[] */
+ read = 0;
+ for (i = 0; i < index_point; i++) {
+ for (j = 0; j < mode_length[i]; j++) {
+ set[read] = mode_type[i];
+ read++;
+ }
+ }
+
+ /* Resolve odd length LATCHC blocks */
+ c_count = 0;
+ for (i = 0; i < read; i++) {
+ if (set[i] == Mode.LATCHC) {
+ if (inputData[i] == '[') {
+ if ((c_count & 1) != 0) {
+ if ((i - c_count) != 0) {
+ set[i - c_count] = Mode.LATCHB;
+ } else {
+ set[i - 1] = Mode.LATCHB;
+ }
+ }
+ c_count = 0;
+ } else {
+ c_count++;
+ }
+ } else {
+ if ((c_count & 1) != 0) {
+ if ((i - c_count) != 0) {
+ set[i - c_count] = Mode.LATCHB;
+ } else {
+ set[i - 1] = Mode.LATCHB;
+ }
+ }
+ c_count = 0;
+ }
+ }
+ if ((c_count & 1) != 0) {
+ if ((i - c_count) != 0) {
+ set[i - c_count] = Mode.LATCHB;
+ } else {
+ set[i - 1] = Mode.LATCHB;
+ }
+ }
+ for (i = 1; i < read - 1; i++) {
+ if ((set[i] == Mode.LATCHC) && ((set[i - 1] == Mode.LATCHB)
+ && (set[i + 1] == Mode.LATCHB))) {
+ set[i] = Mode.LATCHB;
+ }
+ }
+ } else {
+ /* Resolve odd length LATCHC blocks */
+
+ if ((mode_type[0] == Mode.LATCHC) && ((mode_length[0] & 1) != 0)) {
+ /* Rule 2 */
+ mode_length[1]++;
+ mode_length[0]--;
+ if (index_point == 1) {
+ mode_length[1] = 1;
+ mode_type[1] = Mode.LATCHB;
+ index_point = 2;
+ }
+ }
+ if (index_point > 1) {
+ for (i = 1; i < index_point; i++) {
+ if ((mode_type[i] == Mode.LATCHC) && ((mode_length[i] & 1) != 0)) {
+ /* Rule 3b */
+ mode_length[i - 1]++;
+ mode_length[i]--;
+ }
+ }
+ }
+
+ /* Put set data into set[] */
+ for (i = 0; i < index_point; i++) {
+ for (j = 0; j < mode_length[i]; j++) {
+ set[read] = mode_type[i];
+ read++;
+ }
+ }
+ }
+
+ /* Adjust for strings which start with shift characters - make them latch instead */
+ if (set[0] == Mode.SHIFTA) {
+ i = 0;
+ do {
+ set[i] = Mode.LATCHA;
+ i++;
+ } while (set[i] == Mode.SHIFTA);
+ }
+
+ if (set[0] == Mode.SHIFTB) {
+ i = 0;
+ do {
+ set[i] = Mode.LATCHB;
+ i++;
+ } while (set[i] == Mode.SHIFTB);
+ }
+
+ /* Now we can calculate how long the barcode is going to be - and stop it from
+ being too long */
+ last_set = Mode.NULL;
+ glyph_count = 0.0;
+ for (i = 0; i < sourcelen; i++) {
+ if ((set[i] == Mode.SHIFTA) || (set[i] == Mode.SHIFTB)) {
+ glyph_count += 1.0;
+ }
+ if ((fset[i] == FMode.SHIFTF) || (fset[i] == FMode.SHIFTN)) {
+ glyph_count += 1.0;
+ }
+ if (((set[i] == Mode.LATCHA) || (set[i] == Mode.LATCHB)) || (set[i] == Mode.LATCHC)) {
+ if (set[i] != last_set) {
+ last_set = set[i];
+ glyph_count += 1.0;
+ }
+ }
+ if (i == 0) {
+ if (fset[i] == FMode.LATCHF) {
+ glyph_count += 2.0;
+ }
+ } else {
+ if ((fset[i] == FMode.LATCHF) && (fset[i - 1] != FMode.LATCHF)) {
+ glyph_count += 2.0;
+ }
+ if ((fset[i] != FMode.LATCHF) && (fset[i - 1] == FMode.LATCHF)) {
+ glyph_count += 2.0;
+ }
+ }
+
+ if (set[i] == Mode.LATCHC) {
+ if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) {
+ glyph_count += 1.0;
+ } else {
+ glyph_count += 0.5;
+ }
+ } else {
+ glyph_count += 1.0;
+ }
+ }
+ if (glyph_count > 80.0) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ encodeInfo.append("Encoding: ");
+
+ /* So now we know what start character to use - we can get on with it! */
+ if (readerInit) {
+ /* Reader Initialisation mode */
+ switch (set[0]) {
+ case LATCHA: /* Start A */
+ dest.append(code128Table[103]);
+ values[0] = 103;
+ current_set = Mode.LATCHA;
+ dest.append(code128Table[96]); /* FNC3 */
+ values[1] = 96;
+ bar_characters++;
+ encodeInfo.append("STARTA FNC3 ");
+ break;
+ case LATCHB: /* Start B */
+ dest.append(code128Table[104]);
+ values[0] = 104;
+ current_set = Mode.LATCHB;
+ dest.append(code128Table[96]); /* FNC3 */
+ values[1] = 96;
+ bar_characters++;
+ encodeInfo.append("STARTB FNC3 ");
+ break;
+ default: /* Start C */
+ dest.append(code128Table[104]); /* Start B */
+ values[0] = 105;
+ dest.append(code128Table[96]); /* FNC3 */
+ values[1] = 96;
+ dest.append(code128Table[99]); /* Code C */
+ values[2] = 99;
+ bar_characters += 2;
+ current_set = Mode.LATCHC;
+ encodeInfo.append("STARTB FNC3 CODEC ");
+ break;
+ }
+ } else {
+ /* Normal mode */
+ switch (set[0]) {
+ case LATCHA:
+ /* Start A */
+ dest.append(code128Table[103]);
+ values[0] = 103;
+ current_set = Mode.LATCHA;
+ encodeInfo.append("STARTA ");
+ break;
+ case LATCHB:
+ /* Start B */
+ dest.append(code128Table[104]);
+ values[0] = 104;
+ current_set = Mode.LATCHB;
+ encodeInfo.append("STARTB ");
+ break;
+ default:
+ /* Start C */
+ dest.append(code128Table[105]);
+ values[0] = 105;
+ current_set = Mode.LATCHC;
+ encodeInfo.append("STARTC ");
+ break;
+ }
+ }
+ bar_characters++;
+
+ if (inputDataType == DataType.GS1) {
+ dest.append(code128Table[102]);
+ values[1] = 102;
+ bar_characters++;
+ encodeInfo.append("FNC1 ");
+ }
+
+ if (fset[0] == FMode.LATCHF) {
+ switch (current_set) {
+ case LATCHA:
+ dest.append(code128Table[101]);
+ dest.append(code128Table[101]);
+ values[bar_characters] = 101;
+ values[bar_characters + 1] = 101;
+ encodeInfo.append("FNC4 FNC4 ");
+ break;
+ case LATCHB:
+ dest.append(code128Table[100]);
+ dest.append(code128Table[100]);
+ values[bar_characters] = 100;
+ values[bar_characters + 1] = 100;
+ encodeInfo.append("FNC4 FNC4 ");
+ break;
+ }
+ bar_characters += 2;
+ f_state = FMode.LATCHF;
+ }
+
+ /* Encode the data */
+ read = 0;
+ do {
+
+ if ((read != 0) && (set[read] != current_set)) { /* Latch different code set */
+ switch (set[read]) {
+ case LATCHA:
+ dest.append(code128Table[101]);
+ values[bar_characters] = 101;
+ bar_characters++;
+ current_set = Mode.LATCHA;
+ encodeInfo.append("CODEA ");
+ break;
+ case LATCHB:
+ dest.append(code128Table[100]);
+ values[bar_characters] = 100;
+ bar_characters++;
+ current_set = Mode.LATCHB;
+ encodeInfo.append("CODEB ");
+ break;
+ case LATCHC:
+ dest.append(code128Table[99]);
+ values[bar_characters] = 99;
+ bar_characters++;
+ current_set = Mode.LATCHC;
+ encodeInfo.append("CODEC ");
+ break;
+ }
+ }
+
+ if (read != 0) {
+ if ((fset[read] == FMode.LATCHF) && (f_state == FMode.LATCHN)) {
+ /* Latch beginning of extended mode */
+ switch (current_set) {
+ case LATCHA:
+ dest.append(code128Table[101]);
+ dest.append(code128Table[101]);
+ values[bar_characters] = 101;
+ values[bar_characters + 1] = 101;
+ encodeInfo.append("FNC4 FNC4 ");
+ break;
+ case LATCHB:
+ dest.append(code128Table[100]);
+ dest.append(code128Table[100]);
+ values[bar_characters] = 100;
+ values[bar_characters + 1] = 100;
+ encodeInfo.append("FNC4 FNC4 ");
+ break;
+ }
+ bar_characters += 2;
+ f_state = FMode.LATCHN;
+ }
+ if ((fset[read] == FMode.LATCHN) && (f_state == FMode.LATCHF)) {
+ /* Latch end of extended mode */
+ switch (current_set) {
+ case LATCHA:
+ dest.append(code128Table[101]);
+ dest.append(code128Table[101]);
+ values[bar_characters] = 101;
+ values[bar_characters + 1] = 101;
+ encodeInfo.append("FNC4 FNC4 ");
+ break;
+ case LATCHB:
+ dest.append(code128Table[100]);
+ dest.append(code128Table[100]);
+ values[bar_characters] = 100;
+ values[bar_characters + 1] = 100;
+ encodeInfo.append("FNC4 FNC4 ");
+ break;
+ }
+ bar_characters += 2;
+ f_state = FMode.LATCHN;
+ }
+ }
+
+ if ((fset[read] == FMode.SHIFTF) || (fset[read] == FMode.SHIFTN)) {
+ /* Shift to or from extended mode */
+ switch (current_set) {
+ case LATCHA:
+ dest.append(code128Table[101]); /* FNC 4 */
+ values[bar_characters] = 101;
+ encodeInfo.append("FNC4 ");
+ break;
+ case LATCHB:
+ dest.append(code128Table[100]); /* FNC 4 */
+ values[bar_characters] = 100;
+ encodeInfo.append("FNC4 ");
+ break;
+ }
+ bar_characters++;
+ }
+
+ if ((set[read] == Mode.SHIFTA) || (set[read] == Mode.SHIFTB)) {
+ /* Insert shift character */
+ dest.append(code128Table[98]);
+ values[bar_characters] = 98;
+ encodeInfo.append("SHFT ");
+ bar_characters++;
+ }
+
+ if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) {
+ /* Encode data characters */
+ c = inputData[read];
+ switch (set[read]) {
+ case SHIFTA:
+ case LATCHA:
+ if (c > 127) {
+ if (c < 160) {
+ dest.append(code128Table[(c - 128) + 64]);
+ values[bar_characters] = (c - 128) + 64;
+ } else {
+ dest.append(code128Table[(c - 128) - 32]);
+ values[bar_characters] = (c - 128) - 32;
+ }
+ } else {
+ if (c < 32) {
+ dest.append(code128Table[c + 64]);
+ values[bar_characters] = c + 64;
+ } else {
+ dest.append(code128Table[c - 32]);
+ values[bar_characters] = c - 32;
+ }
+ }
+ encodeInfo.append(Integer.toString(values[bar_characters])).append(" ");
+ bar_characters++;
+ read++;
+ break;
+ case SHIFTB:
+ case LATCHB:
+ if (c > 127) {
+ dest.append(code128Table[c - 32 - 128]);
+ values[bar_characters] = c - 32 - 128;
+ } else {
+ dest.append(code128Table[c - 32]);
+ values[bar_characters] = c - 32;
+ }
+ encodeInfo.append(Integer.toString(values[bar_characters])).append(" ");
+ bar_characters++;
+ read++;
+ break;
+ case LATCHC:
+ int weight;
+ int d = inputData[read + 1];
+
+ weight = (10 * (c - '0')) + (d - '0');
+ dest.append(code128Table[weight]);
+ values[bar_characters] = weight;
+ encodeInfo.append(Integer.toString(values[bar_characters])).append(" ");
+ bar_characters++;
+ read += 2;
+ break;
+ }
+ } else {
+ // FNC1
+ dest.append(code128Table[102]);
+ values[bar_characters] = 102;
+ bar_characters++;
+ read++;
+ encodeInfo.append("FNC1 ");
+ }
+
+ } while (read < sourcelen);
+
+ encodeInfo.append("\n");
+
+ /* "...note that the linkage flag is an extra code set character between
+ the last data character and the Symbol Check Character" (GS1 Specification) */
+
+ /* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */
+
+ switch (compositeMode) {
+ case CCA:
+ case CCB:
+ /* CC-A or CC-B 2D component */
+ switch (set[sourcelen - 1]) {
+ case LATCHA:
+ linkage_flag = 100;
+ break;
+ case LATCHB:
+ linkage_flag = 99;
+ break;
+ case LATCHC:
+ linkage_flag = 101;
+ break;
+ }
+ encodeInfo.append("Linkage flag: ").append(linkage_flag).append('\n');
+ break;
+ case CCC:
+ /* CC-C 2D component */
+ switch (set[sourcelen - 1]) {
+ case LATCHA:
+ linkage_flag = 99;
+ break;
+ case LATCHB:
+ linkage_flag = 101;
+ break;
+ case LATCHC:
+ linkage_flag = 100;
+ break;
+ }
+ encodeInfo.append("Linkage flag: ").append(linkage_flag).append('\n');
+ break;
+ default:
+ break;
+ }
+
+ if (linkage_flag != 0) {
+ dest.append(code128Table[linkage_flag]);
+ values[bar_characters] = linkage_flag;
+ bar_characters++;
+ }
+
+ /* check digit calculation */
+ for (i = 0; i < bar_characters; i++) {
+ if (i > 0) {
+ values[i] *= i;
+ }
+ total_sum += values[i];
+ }
+ dest.append(code128Table[total_sum % 103]);
+ encodeInfo.append("Data Codewords: ").append(bar_characters).append('\n');
+ encodeInfo.append("Check Digit: ").append(total_sum % 103).append('\n');
+
+ /* Stop character */
+ dest.append(code128Table[106]);
+
+ if (!(inputDataType == DataType.GS1)) {
+ readable = new StringBuilder(content);
+ }
+
+ if (inputDataType == DataType.HIBC) {
+ readable.append("*").append(content).append("*");
+ }
+
+ if (compositeMode == Composite.OFF) {
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ } else {
+ /* Add the separator pattern for composite symbols */
+ pattern = new String[2];
+ pattern[0] = "0" + dest;
+ pattern[1] = dest.toString();
+ rowCount = 2;
+ rowHeight = new int[2];
+ rowHeight[0] = 1;
+ rowHeight[1] = -1;
+ }
+ plotSymbol();
+ return true;
+ }
+
+ private Mode findSubset(int letter) {
+ Mode mode;
+
+ if (letter <= 31) {
+ mode = Mode.SHIFTA;
+ } else if ((letter >= 48) && (letter <= 57)) {
+ mode = Mode.ABORC;
+ } else if (letter <= 95) {
+ mode = Mode.AORB;
+ } else if (letter <= 127) {
+ mode = Mode.SHIFTB;
+ } else if (letter <= 159) {
+ mode = Mode.SHIFTA;
+ } else if (letter <= 223) {
+ mode = Mode.AORB;
+ } else {
+ mode = Mode.SHIFTB;
+ }
+
+ return mode;
+ }
+
+ private void reduceSubsetChanges() { /* Implements rules from ISO 15417 Annex E */
+ int i, length;
+ Mode current, last, next;
+
+ for (i = 0; i < index_point; i++) {
+ current = mode_type[i];
+ length = mode_length[i];
+ if (i != 0) {
+ last = mode_type[i - 1];
+ } else {
+ last = Mode.NULL;
+ }
+ if (i != index_point - 1) {
+ next = mode_type[i + 1];
+ } else {
+ next = Mode.NULL;
+ }
+
+ if (i == 0) { /* first block */
+ if ((index_point == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */
+ mode_type[i] = Mode.LATCHC;
+ }
+ if (current == Mode.ABORC) {
+ if (length >= 4) { /* Rule 1b */
+ mode_type[i] = Mode.LATCHC;
+ } else {
+ mode_type[i] = Mode.AORB;
+ current = Mode.AORB;
+ }
+ }
+ if (current == Mode.SHIFTA) { /* Rule 1c */
+ mode_type[i] = Mode.LATCHA;
+ }
+ if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */
+ mode_type[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if (current == Mode.AORB) { /* Rule 1d */
+ mode_type[i] = Mode.LATCHB;
+ }
+ } else {
+ if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */
+ mode_type[i] = Mode.LATCHC;
+ current = Mode.LATCHC;
+ }
+ if (current == Mode.ABORC) {
+ mode_type[i] = Mode.AORB;
+ current = Mode.AORB;
+ }
+ if ((current == Mode.AORB) && (last == Mode.LATCHA)) {
+ mode_type[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.AORB) && (last == Mode.LATCHB)) {
+ mode_type[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.AORB) && (next == Mode.SHIFTA)) {
+ mode_type[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.AORB) && (next == Mode.SHIFTB)) {
+ mode_type[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if (current == Mode.AORB) {
+ mode_type[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */
+ mode_type[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */
+ mode_type[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) {
+ mode_type[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) {
+ mode_type[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) {
+ mode_type[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) {
+ mode_type[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ } /* Rule 2 is implemented elsewhere, Rule 6 is implied */
+ }
+
+ combineSubsetBlocks();
+
+ }
+
+ private void combineSubsetBlocks() {
+ int i, j;
+
+ /* bring together same type blocks */
+ if (index_point > 1) {
+ i = 1;
+ while (i < index_point) {
+ if (mode_type[i - 1] == mode_type[i]) {
+ /* bring together */
+ mode_length[i - 1] = mode_length[i - 1] + mode_length[i];
+ j = i + 1;
+
+ /* decreace the list */
+ while (j < index_point) {
+ mode_length[j - 1] = mode_length[j];
+ mode_type[j - 1] = mode_type[j];
+ j++;
+ }
+ index_point--;
+ i--;
+ }
+ i++;
+ }
+ }
+ }
+
+ private enum Mode {
+ NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC
+ }
+
+ private enum FMode {
+ SHIFTN, LATCHN, SHIFTF, LATCHF
+ }
+
+ private enum Composite {OFF, CCA, CCB, CCC}
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java
new file mode 100755
index 0000000..eb2ae61
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java
@@ -0,0 +1,769 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements Code 16K symbology
+ * According to BS EN 12323:2005
+ * Encodes using a stacked symbology based on Code 128. Supports encoding
+ * of any 8-bit ISO 8859-1 (Latin-1) data with a maximum data capacity of 77
+ * alpha-numeric characters or 154 numerical digits.
+ */
+public class Code16k extends Symbol {
+
+ /* EN 12323 Table 1 - "Code 16K" character encodations */
+ private static final String[] C_16_K_TABLE = {
+ "212222", "222122", "222221", "121223", "121322", "131222", "122213",
+ "122312", "132212", "221213", "221312", "231212", "112232", "122132",
+ "122231", "113222", "123122", "123221", "223211", "221132", "221231",
+ "213212", "223112", "312131", "311222", "321122", "321221", "312212",
+ "322112", "322211", "212123", "212321", "232121", "111323", "131123",
+ "131321", "112313", "132113", "132311", "211313", "231113", "231311",
+ "112133", "112331", "132131", "113123", "113321", "133121", "313121",
+ "211331", "231131", "213113", "213311", "213131", "311123", "311321",
+ "331121", "312113", "312311", "332111", "314111", "221411", "431111",
+ "111224", "111422", "121124", "121421", "141122", "141221", "112214",
+ "112412", "122114", "122411", "142112", "142211", "241211", "221114",
+ "413111", "241112", "134111", "111242", "121142", "121241", "114212",
+ "124112", "124211", "411212", "421112", "421211", "212141", "214121",
+ "412121", "111143", "111341", "131141", "114113", "114311", "411113",
+ "411311", "113141", "114131", "311141", "411131", "211412", "211214",
+ "211232", "211133"
+ };
+ /* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */
+ private static final String[] C_16_K_START_STOP = {
+ "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112"
+ };
+ /* EN 12323 Table 5 - Start and stop values defining row numbers */
+ private static final int[] C_16_K_START_VALUES = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7
+ };
+ private static final int[] C_16_K_STOP_VALUES = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3
+ };
+ private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */
+ private final int[] block_length = new int[170]; /* RENAME block_length */
+
+ @Override
+ public boolean encode() {
+ StringBuilder width_pattern;
+ int current_row, rows_needed, first_check, second_check;
+ int indexchaine, pads_needed;
+ char[] set, fset;
+ Mode mode;
+ char last_set, current_set;
+ int i, j, k, m, read;
+ int[] values;
+ int bar_characters;
+ double glyph_count;
+ int first_sum, second_sum;
+ int input_length;
+ int c_count;
+ boolean f_state;
+ int[] inputData;
+
+ if (!content.matches("[\u0000-\u00FF]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ try {
+ inputBytes = content.getBytes("ISO8859_1");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Character encoding error");
+ return false;
+ }
+
+ input_length = content.length();
+ inputData = new int[input_length];
+ for (i = 0; i < input_length; i++) {
+ inputData[i] = inputBytes[i] & 0xFF;
+ }
+
+ bar_characters = 0;
+ set = new char[160];
+ fset = new char[160];
+ values = new int[160];
+
+ if (input_length > 157) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ /* Detect extended ASCII characters */
+ for (i = 0; i < input_length; i++) {
+ if (inputData[i] >= 128) {
+ fset[i] = 'f';
+ } else {
+ fset[i] = ' ';
+ }
+ }
+
+ /* Decide when to latch to extended mode */
+ for (i = 0; i < input_length; i++) {
+ j = 0;
+ if (fset[i] == 'f') {
+ do {
+ j++;
+ } while (fset[i + j] == 'f');
+ if ((j >= 5) || ((j >= 3) && ((i + j) == (input_length - 1)))) {
+ for (k = 0; k <= j; k++) {
+ fset[i + k] = 'F';
+ }
+ }
+ }
+ }
+
+ /* Decide if it is worth reverting to 646 encodation for a few characters */
+ if (input_length > 1) {
+ for (i = 1; i < input_length; i++) {
+ if ((fset[i - 1] == 'F') && (fset[i] == ' ')) {
+ /* Detected a change from 8859-1 to 646 - count how long for */
+ for (j = 0; (fset[i + j] == ' ') && ((i + j) < input_length); j++)
+ ;
+ if (j < 5) {
+ /* Change to shifting back rather than latching back */
+ for (k = 0; k < j; k++) {
+ fset[i + k] = 'n';
+ }
+ }
+ }
+ }
+ }
+
+ /* Detect mode A, B and C characters */
+ int block_count = 0;
+ indexchaine = 0;
+
+ mode = findSubset(inputData[indexchaine]);
+ if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) {
+ mode = Mode.ABORC;
+ } /* FNC1 */
+
+ for (i = 0; i < 160; i++) {
+ block_length[i] = 0;
+ }
+
+ do {
+ block_mode[block_count] = mode;
+ while ((block_mode[block_count] == mode) && (indexchaine < input_length)) {
+ block_length[block_count]++;
+ indexchaine++;
+ if (indexchaine < input_length) {
+ mode = findSubset(inputData[indexchaine]);
+ if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) {
+ mode = Mode.ABORC;
+ } /* FNC1 */
+ }
+ }
+ block_count++;
+ } while (indexchaine < input_length);
+
+ reduceSubsetChanges(block_count);
+
+
+ /* Put set data into set[] */
+ read = 0;
+ for (i = 0; i < block_count; i++) {
+ for (j = 0; j < block_length[i]; j++) {
+ switch (block_mode[i]) {
+ case SHIFTA:
+ set[read] = 'a';
+ break;
+ case LATCHA:
+ set[read] = 'A';
+ break;
+ case SHIFTB:
+ set[read] = 'b';
+ break;
+ case LATCHB:
+ set[read] = 'B';
+ break;
+ case LATCHC:
+ set[read] = 'C';
+ break;
+ }
+ read++;
+ }
+ }
+
+ /* Adjust for strings which start with shift characters - make them latch instead */
+ if (set[0] == 'a') {
+ i = 0;
+ do {
+ set[i] = 'A';
+ i++;
+ } while (set[i] == 'a');
+ }
+
+ if (set[0] == 'b') {
+ i = 0;
+ do {
+ set[i] = 'B';
+ i++;
+ } while (set[i] == 'b');
+ }
+
+ /* Watch out for odd-length Mode C blocks */
+ c_count = 0;
+ for (i = 0; i < read; i++) {
+ if (set[i] == 'C') {
+ if (inputData[i] == '[') {
+ if ((c_count & 1) != 0) {
+ if ((i - c_count) != 0) {
+ set[i - c_count] = 'B';
+ } else {
+ set[i - 1] = 'B';
+ }
+ }
+ c_count = 0;
+ } else {
+ c_count++;
+ }
+ } else {
+ if ((c_count & 1) != 0) {
+ if ((i - c_count) != 0) {
+ set[i - c_count] = 'B';
+ } else {
+ set[i - 1] = 'B';
+ }
+ }
+ c_count = 0;
+ }
+ }
+ if ((c_count & 1) != 0) {
+ if ((i - c_count) != 0) {
+ set[i - c_count] = 'B';
+ } else {
+ set[i - 1] = 'B';
+ }
+ }
+ for (i = 1; i < read - 1; i++) {
+ if ((set[i] == 'C') && ((set[i - 1] == 'B') && (set[i + 1] == 'B'))) {
+ set[i] = 'B';
+ }
+ }
+
+ /* Make sure the data will fit in the symbol */
+ last_set = ' ';
+ glyph_count = 0.0;
+ for (i = 0; i < input_length; i++) {
+ if ((set[i] == 'a') || (set[i] == 'b')) {
+ glyph_count = glyph_count + 1.0;
+ }
+ if ((fset[i] == 'f') || (fset[i] == 'n')) {
+ glyph_count = glyph_count + 1.0;
+ }
+ if (((set[i] == 'A') || (set[i] == 'B')) || (set[i] == 'C')) {
+ if (set[i] != last_set) {
+ last_set = set[i];
+ glyph_count = glyph_count + 1.0;
+ }
+ }
+ if (i == 0) {
+ if ((set[i] == 'B') && (set[1] == 'C')) {
+ glyph_count = glyph_count - 1.0;
+ }
+ if ((set[i] == 'B') && (set[1] == 'B') && set[2] == 'C') {
+ glyph_count = glyph_count - 1.0;
+ }
+ if (fset[i] == 'F') {
+ glyph_count = glyph_count + 2.0;
+ }
+ } else {
+ if ((fset[i] == 'F') && (fset[i - 1] != 'F')) {
+ glyph_count = glyph_count + 2.0;
+ }
+ if ((fset[i] != 'F') && (fset[i - 1] == 'F')) {
+ glyph_count = glyph_count + 2.0;
+ }
+ }
+
+ if ((set[i] == 'C') && (!((inputDataType == DataType.GS1) && (content.charAt(i) == '[')))) {
+ glyph_count = glyph_count + 0.5;
+ } else {
+ glyph_count = glyph_count + 1.0;
+ }
+ }
+
+ if ((inputDataType == DataType.GS1) && (set[0] != 'A')) {
+ /* FNC1 can be integrated with mode character */
+ glyph_count--;
+ }
+
+ if (glyph_count > 77.0) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ /* Calculate how tall the symbol will be */
+ glyph_count = glyph_count + 2.0;
+ i = (int) glyph_count;
+ rows_needed = (i / 5);
+ if (i % 5 > 0) {
+ rows_needed++;
+ }
+
+ if (rows_needed == 1) {
+ rows_needed = 2;
+ }
+
+ /* start with the mode character - Table 2 */
+ m = 0;
+ switch (set[0]) {
+ case 'A':
+ m = 0;
+ break;
+ case 'B':
+ m = 1;
+ break;
+ case 'C':
+ m = 2;
+ break;
+ }
+
+ if (readerInit) {
+ if (m == 2) {
+ m = 5;
+ }
+ if (inputDataType == DataType.GS1) {
+ errorMsg.append("Cannot use both GS1 mode and Reader Initialisation");
+ return false;
+ } else {
+ if ((set[0] == 'B') && (set[1] == 'C')) {
+ m = 6;
+ }
+ }
+ values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */
+ values[bar_characters + 1] = 96; /* FNC3 */
+ bar_characters += 2;
+ } else {
+ if (inputDataType == DataType.GS1) {
+ /* Integrate FNC1 */
+ switch (set[0]) {
+ case 'B':
+ m = 3;
+ break;
+ case 'C':
+ m = 4;
+ break;
+ }
+ } else {
+ if ((set[0] == 'B') && (set[1] == 'C')) {
+ m = 5;
+ }
+ if (((set[0] == 'B') && (set[1] == 'B')) && (set[2] == 'C')) {
+ m = 6;
+ }
+ }
+ }
+ values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */
+ bar_characters++;
+ //}
+ current_set = set[0];
+ f_state = false;
+ /* f_state remembers if we are in Extended ASCII mode (value 1) or
+ in ISO/IEC 646 mode (value 0) */
+ if (fset[0] == 'F') {
+ switch (current_set) {
+ case 'A':
+ values[bar_characters] = 101;
+ values[bar_characters + 1] = 101;
+ break;
+ case 'B':
+ values[bar_characters] = 100;
+ values[bar_characters + 1] = 100;
+ break;
+ }
+ bar_characters += 2;
+ f_state = true;
+ }
+
+ read = 0;
+
+ /* Encode the data */
+ do {
+
+ if ((read != 0) && (set[read] != set[read - 1])) { /* Latch different code set */
+ switch (set[read]) {
+ case 'A':
+ values[bar_characters] = 101;
+ bar_characters++;
+ current_set = 'A';
+ break;
+ case 'B':
+ values[bar_characters] = 100;
+ bar_characters++;
+ current_set = 'B';
+ break;
+ case 'C':
+ if (!((read == 1) && (set[0] == 'B'))) { /* Not Mode C/Shift B */
+ if (!((read == 2) && ((set[0] == 'B') && (set[1] == 'B')))) {
+ /* Not Mode C/Double Shift B */
+ values[bar_characters] = 99;
+ bar_characters++;
+ }
+ }
+ current_set = 'C';
+ break;
+ }
+ }
+ if (read != 0) {
+ if ((fset[read] == 'F') && !f_state) {
+ /* Latch beginning of extended mode */
+ switch (current_set) {
+ case 'A':
+ values[bar_characters] = 101;
+ values[bar_characters + 1] = 101;
+ break;
+ case 'B':
+ values[bar_characters] = 100;
+ values[bar_characters + 1] = 100;
+ break;
+ }
+ bar_characters += 2;
+ f_state = true;
+ }
+ if ((fset[read] == ' ') && f_state) {
+ /* Latch end of extended mode */
+ switch (current_set) {
+ case 'A':
+ values[bar_characters] = 101;
+ values[bar_characters + 1] = 101;
+ break;
+ case 'B':
+ values[bar_characters] = 100;
+ values[bar_characters + 1] = 100;
+ break;
+ }
+ bar_characters += 2;
+ f_state = false;
+ }
+ }
+
+ if ((fset[i] == 'f') || (fset[i] == 'n')) {
+ /* Shift extended mode */
+ switch (current_set) {
+ case 'A':
+ values[bar_characters] = 101; /* FNC 4 */
+ break;
+ case 'B':
+ values[bar_characters] = 100; /* FNC 4 */
+ break;
+ }
+ bar_characters++;
+ }
+
+ if ((set[i] == 'a') || (set[i] == 'b')) {
+ /* Insert shift character */
+ values[bar_characters] = 98;
+ bar_characters++;
+ }
+
+ if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) {
+ switch (set[read]) { /* Encode data characters */
+ case 'A':
+ case 'a':
+ getValueSubsetA(inputData[read], values, bar_characters);
+ bar_characters++;
+ read++;
+ break;
+ case 'B':
+ case 'b':
+ getValueSubsetB(inputData[read], values, bar_characters);
+ bar_characters++;
+ read++;
+ break;
+ case 'C':
+ getValueSubsetC(inputData[read], inputData[read + 1], values, bar_characters);
+ bar_characters++;
+ read += 2;
+ break;
+ }
+ } else {
+ values[bar_characters] = 102;
+ bar_characters++;
+ read++;
+ }
+
+ } while (read < input_length);
+
+ pads_needed = 5 - ((bar_characters + 2) % 5);
+ if (pads_needed == 5) {
+ pads_needed = 0;
+ }
+ if ((bar_characters + pads_needed) < 8) {
+ pads_needed += 8 - (bar_characters + pads_needed);
+ }
+ for (i = 0; i < pads_needed; i++) {
+ values[bar_characters] = 106;
+ bar_characters++;
+ }
+
+ /* Calculate check digits */
+ first_sum = 0;
+ second_sum = 0;
+ for (i = 0; i < bar_characters; i++) {
+ first_sum += (i + 2) * values[i];
+ second_sum += (i + 1) * values[i];
+ }
+ first_check = first_sum % 107;
+ second_sum += first_check * (bar_characters + 1);
+ second_check = second_sum % 107;
+ values[bar_characters] = first_check;
+ values[bar_characters + 1] = second_check;
+ readable = new StringBuilder();
+ pattern = new String[rows_needed];
+ rowCount = rows_needed;
+ rowHeight = new int[rows_needed];
+ encodeInfo.append("Symbol Rows: ").append(rows_needed).append("\n");
+ encodeInfo.append("First Check Digit: ").append(first_check).append("\n");
+ encodeInfo.append("Second Check Digit: ").append(second_check).append("\n");
+ encodeInfo.append("Codewords: ");
+ for (current_row = 0; current_row < rows_needed; current_row++) {
+ width_pattern = new StringBuilder();
+ width_pattern.append(C_16_K_START_STOP[C_16_K_START_VALUES[current_row]]);
+ width_pattern.append("1");
+ for (i = 0; i < 5; i++) {
+ width_pattern.append(C_16_K_TABLE[values[(current_row * 5) + i]]);
+ encodeInfo.append(values[(current_row * 5) + i]).append(" ");
+ }
+ width_pattern.append(C_16_K_START_STOP[C_16_K_STOP_VALUES[current_row]]);
+
+ pattern[current_row] = width_pattern.toString();
+ rowHeight[current_row] = 10;
+ }
+ encodeInfo.append("\n");
+ plotSymbol();
+ return true;
+ }
+
+ private void getValueSubsetA(int source, int[] values, int bar_chars) {
+ if (source > 127) {
+ if (source < 160) {
+ values[bar_chars] = source + 64 - 128;
+ } else {
+ values[bar_chars] = source - 32 - 128;
+ }
+ } else {
+ if (source < 32) {
+ values[bar_chars] = source + 64;
+ } else {
+ values[bar_chars] = source - 32;
+ }
+ }
+ }
+
+ private void getValueSubsetB(int source, int[] values, int bar_chars) {
+ if (source > 127) {
+ values[bar_chars] = source - 32 - 128;
+ } else {
+ values[bar_chars] = source - 32;
+ }
+ }
+
+ private void getValueSubsetC(int source_a, int source_b, int[] values, int bar_chars) {
+ int weight;
+
+ weight = (10 * Character.getNumericValue(source_a)) + Character.getNumericValue(source_b);
+ values[bar_chars] = weight;
+ }
+
+ private Mode findSubset(int letter) {
+ Mode mode;
+
+ if (letter <= 31) {
+ mode = Mode.SHIFTA;
+ } else if ((letter >= 48) && (letter <= 57)) {
+ mode = Mode.ABORC;
+ } else if (letter <= 95) {
+ mode = Mode.AORB;
+ } else if (letter <= 127) {
+ mode = Mode.SHIFTB;
+ } else if (letter <= 159) {
+ mode = Mode.SHIFTA;
+ } else if (letter <= 223) {
+ mode = Mode.AORB;
+ } else {
+ mode = Mode.SHIFTB;
+ }
+
+ return mode;
+ }
+
+ private void reduceSubsetChanges(int block_count) { /* Implements rules from ISO 15417 Annex E */
+ int i, length;
+ Mode current, last, next;
+
+ for (i = 0; i < block_count; i++) {
+ current = block_mode[i];
+ length = block_length[i];
+ if (i != 0) {
+ last = block_mode[i - 1];
+ } else {
+ last = Mode.NULL;
+ }
+ if (i != block_count - 1) {
+ next = block_mode[i + 1];
+ } else {
+ next = Mode.NULL;
+ }
+
+ if (i == 0) { /* first block */
+ if ((block_count == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */
+ block_mode[i] = Mode.LATCHC;
+ }
+ if (current == Mode.ABORC) {
+ if (length >= 4) { /* Rule 1b */
+ block_mode[i] = Mode.LATCHC;
+ } else {
+ block_mode[i] = Mode.AORB;
+ current = Mode.AORB;
+ }
+ }
+ if (current == Mode.SHIFTA) { /* Rule 1c */
+ block_mode[i] = Mode.LATCHA;
+ }
+ if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */
+ block_mode[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if (current == Mode.AORB) { /* Rule 1d */
+ block_mode[i] = Mode.LATCHB;
+ }
+ } else {
+ if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */
+ block_mode[i] = Mode.LATCHC;
+ current = Mode.LATCHC;
+ }
+ if (current == Mode.ABORC) {
+ block_mode[i] = Mode.AORB;
+ current = Mode.AORB;
+ }
+ if ((current == Mode.AORB) && (last == Mode.LATCHA)) {
+ block_mode[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.AORB) && (last == Mode.LATCHB)) {
+ block_mode[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.AORB) && (next == Mode.SHIFTA)) {
+ block_mode[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.AORB) && (next == Mode.SHIFTB)) {
+ block_mode[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if (current == Mode.AORB) {
+ block_mode[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */
+ block_mode[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */
+ block_mode[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) {
+ block_mode[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) {
+ block_mode[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) {
+ block_mode[i] = Mode.LATCHA;
+ current = Mode.LATCHA;
+ }
+ if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) {
+ block_mode[i] = Mode.LATCHB;
+ current = Mode.LATCHB;
+ }
+ }
+ }
+ combineSubsetBlocks(block_count);
+ }
+
+ private void combineSubsetBlocks(int block_count) {
+ int i, j;
+ if (block_count > 1) {
+ i = 1;
+ while (i < block_count) {
+ if (block_mode[i - 1] == block_mode[i]) {
+ block_length[i - 1] = block_length[i - 1] + block_length[i];
+ j = i + 1;
+ while (j < block_count) {
+ block_length[j - 1] = block_length[j];
+ block_mode[j - 1] = block_mode[j];
+ j++;
+ }
+ block_count = block_count - 1;
+ i--;
+ }
+ i++;
+ }
+ }
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock, yBlock;
+ int x, y, w, h;
+ boolean black;
+ getRectangles().clear();
+ y = 1;
+ h = 1;
+ for (yBlock = 0; yBlock < rowCount; yBlock++) {
+ black = true;
+ x = 15;
+ for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) {
+ if (black) {
+ black = false;
+ w = pattern[yBlock].charAt(xBlock) - '0';
+ if (rowHeight[yBlock] == -1) {
+ h = defaultHeight;
+ } else {
+ h = rowHeight[yBlock];
+ }
+ if (w != 0 && h != 0) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ }
+ if ((x + w) > symbolWidth) {
+ symbolWidth = x + w;
+ }
+ } else {
+ black = true;
+ }
+ x += (double) (pattern[yBlock].charAt(xBlock) - '0');
+ }
+ y += h;
+ if ((y + h) > symbolHeight) {
+ symbolHeight = y + h;
+ }
+ /* Add bars between rows */
+ if (yBlock != (rowCount - 1)) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, (symbolWidth - 15), 2);
+ getRectangles().add(rect);
+ }
+ }
+ Rectangle2D.Double top = new Rectangle2D.Double(0, 0, (symbolWidth + 15), 2);
+ getRectangles().add(top);
+ Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, (symbolWidth + 15), 2);
+ getRectangles().add(bottom);
+ symbolWidth += 30;
+ symbolHeight += 2;
+ mergeVerticalBlocks();
+ }
+
+ private enum Mode {
+ NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
+ }
+
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java
new file mode 100755
index 0000000..6dba07d
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java
@@ -0,0 +1,493 @@
+package org.xbib.graphics.barcode;
+
+import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
+import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements the Code 2 of 5 family of barcode standards.
+ */
+public class Code2Of5 extends Symbol {
+
+ private static final String[] C25_MATRIX_TABLE = {
+ "113311", "311131", "131131", "331111", "113131", "313111", "133111", "111331", "311311", "131311"
+ };
+ private static final String[] C25_INDUSTRIAL_TABLE = {
+ "1111313111", "3111111131", "1131111131", "3131111111", "1111311131", "3111311111", "1131311111", "1111113131", "3111113111", "1131113111"
+ };
+ private static final String[] C25_INTERLEAVED_TABLE = {
+ "11331", "31113", "13113", "33111", "11313", "31311", "13311", "11133", "31131", "13131"
+ };
+ /**
+ * The 2-of-5 mode.
+ */
+ private ToFMode mode = ToFMode.MATRIX;
+ /**
+ * Ratio of wide bar width to narrow bar width.
+ */
+ private double moduleWidthRatio = 3;
+
+ /**
+ * Returns the ratio of wide bar width to narrow bar width.
+ *
+ * @return the ratio of wide bar width to narrow bar width
+ */
+ public double getModuleWidthRatio() {
+ return moduleWidthRatio;
+ }
+
+ /**
+ * Sets the ratio of wide bar width to narrow bar width. Valid values are usually
+ * between {@code 2} and {@code 3}. The default value is {@code 3}.
+ *
+ * @param moduleWidthRatio the ratio of wide bar width to narrow bar width
+ */
+ public void setModuleWidthRatio(double moduleWidthRatio) {
+ this.moduleWidthRatio = moduleWidthRatio;
+ }
+
+ /**
+ * Select Standard Code 2 of 5 mode, also known as Code 2 of 5 Matrix. (default)
+ * Encodes any length numeric input (digits 0-9).
+ */
+ public void setMatrixMode() {
+ mode = ToFMode.MATRIX;
+ }
+
+ /**
+ * Select Industrial Code 2 of 5 which can encode any length numeric input
+ * (digits 0-9) and does not include a check digit.
+ */
+ public void setIndustrialMode() {
+ mode = ToFMode.INDUSTRIAL;
+ }
+
+ /**
+ * Select International Air Transport Agency variation of Code 2 of 5.
+ * Encodes any length numeric input (digits 0-9) and does not include
+ * a check digit.
+ */
+ public void setIATAMode() {
+ mode = ToFMode.IATA;
+ }
+
+ /**
+ * Select Code 2 of 5 Data Logic. Encodes any length numeric input
+ * (digits 0-9) and does not include a check digit.
+ */
+ public void setDataLogicMode() {
+ mode = ToFMode.DATA_LOGIC;
+ }
+
+ /**
+ * Select Interleaved Code 2 of 5. encodes pairs of numbers, and so can
+ * only encode an even number of digits (0-9). If an odd number of digits
+ * is entered a leading zero is added. No check digit is calculated.
+ */
+ public void setInterleavedMode() {
+ mode = ToFMode.INTERLEAVED;
+ }
+
+ /**
+ * Select ITF-14, also known as UPC Shipping Container Symbol or Case Code.
+ * Requires a 13 digit numeric input (digits 0-9). One modulo-10 check
+ * digit is calculated.
+ */
+ public void setITF14Mode() {
+ mode = ToFMode.ITF14;
+ }
+
+ /**
+ * Select Deutsche Post Leitcode. Requires a 13-digit numerical input.
+ * Check digit is calculated.
+ */
+ public void setDPLeitMode() {
+ mode = ToFMode.DPLEIT;
+ }
+
+ /**
+ * Select Deutsche Post Identcode. Requires an 11-digit numerical input.
+ * Check digit is calculated.
+ */
+ public void setDPIdentMode() {
+ mode = ToFMode.DPIDENT;
+ }
+
+ @Override
+ public boolean encode() {
+ boolean retval = false;
+
+ switch (mode) {
+ case MATRIX:
+ retval = dataMatrixTof();
+ break;
+ case INDUSTRIAL:
+ retval = industrialTof();
+ break;
+ case IATA:
+ retval = iataTof();
+ break;
+ case INTERLEAVED:
+ retval = interleavedTof();
+ break;
+ case DATA_LOGIC:
+ retval = dataLogic();
+ break;
+ case ITF14:
+ retval = itf14();
+ break;
+ case DPLEIT:
+ retval = deutschePostLeitcode();
+ break;
+ case DPIDENT:
+ retval = deutschePostIdentcode();
+ break;
+ }
+
+ if (retval) {
+ plotSymbol();
+ }
+
+ return retval;
+ }
+
+ private boolean dataMatrixTof() {
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder dest = new StringBuilder("311111");
+
+ for (int i = 0; i < content.length(); i++) {
+ dest.append(C25_MATRIX_TABLE[Character.getNumericValue(content.charAt(i))]);
+ }
+ dest.append("31111");
+
+ readable = new StringBuilder(content);
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private boolean industrialTof() {
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder dest = new StringBuilder("313111");
+ readable = new StringBuilder(content);
+ for (int i = 0; i < readable.length(); i++) {
+ dest.append(C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))]);
+ }
+ dest.append("31113");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private boolean iataTof() {
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder dest = new StringBuilder("1111");
+ readable = new StringBuilder(content);
+ for (int i = 0; i < readable.length(); i++) {
+ dest.append(C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))]);
+ }
+ dest.append("311");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private boolean dataLogic() {
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder dest = new StringBuilder("1111");
+ readable = new StringBuilder(content);
+
+ for (int i = 0; i < readable.length(); i++) {
+ dest.append(C25_MATRIX_TABLE[Character.getNumericValue(readable.charAt(i))]);
+ }
+
+ dest.append("311");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private boolean interleavedTof() {
+ int i;
+ StringBuilder dest;
+
+ if ((content.length() & 1) == 0) {
+ readable = new StringBuilder(content);
+ } else {
+ readable = new StringBuilder("0").append(content);
+ }
+ if (!(readable.toString().matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ dest = new StringBuilder("1111");
+
+ for (i = 0; i < readable.length(); i += 2) {
+ dest.append(interlace(i, i + 1));
+ }
+
+ dest.append("311");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private String interlace(int x, int y) {
+ char a = readable.charAt(x);
+ char b = readable.charAt(y);
+
+ String one = C25_INTERLEAVED_TABLE[Character.getNumericValue(a)];
+ String two = C25_INTERLEAVED_TABLE[Character.getNumericValue(b)];
+ String f = "";
+
+ for (int i = 0; i < 5; i++) {
+ f += one.charAt(i);
+ f += two.charAt(i);
+ }
+
+ return f;
+ }
+
+ private boolean itf14() {
+ int i, count = 0;
+ int input_length = content.length();
+ StringBuilder dest;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (input_length > 13) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ readable = new StringBuilder();
+ for (i = input_length; i < 13; i++) {
+ readable.append("0");
+ }
+
+ readable.append(content);
+ for (i = 12; i >= 0; i--) {
+ count += readable.charAt(i) - '0';
+
+ if ((i & 1) == 0) {
+ count += 2 * (readable.charAt(i) - '0');
+ }
+ }
+
+ readable.append((char) (((10 - (count % 10)) % 10) + '0'));
+ encodeInfo.append("Check Digit: ").append((char) (((10 - (count % 10)) % 10) + '0'));
+ encodeInfo.append('\n');
+
+ dest = new StringBuilder("1111");
+
+ for (i = 0; i < readable.length(); i += 2) {
+ dest.append(interlace(i, i + 1));
+ }
+
+ dest.append("311");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private boolean deutschePostLeitcode() {
+ int i, count = 0;
+ int input_length = content.length();
+ StringBuilder dest;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (input_length > 13) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ readable = new StringBuilder();
+ for (i = input_length; i < 13; i++) {
+ readable.append("0");
+ }
+
+ readable.append(content);
+
+ for (i = 12; i >= 0; i--) {
+ count += 4 * (readable.charAt(i) - '0');
+
+ if ((i & 1) != 0) {
+ count += 5 * (readable.charAt(i) - '0');
+ }
+ }
+
+ readable.append((char) (((10 - (count % 10)) % 10) + '0'));
+ encodeInfo.append("Check digit: ").append((char) (((10 - (count % 10)) % 10) + '0'));
+ encodeInfo.append('\n');
+
+ dest = new StringBuilder("1111");
+
+ for (i = 0; i < readable.length(); i += 2) {
+ dest.append(interlace(i, i + 1));
+ }
+
+ dest.append("311");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ private boolean deutschePostIdentcode() {
+ int i, count = 0;
+ int input_length = content.length();
+ StringBuilder dest;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (input_length > 11) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ readable = new StringBuilder();
+ for (i = input_length; i < 11; i++) {
+ readable.append("0");
+ }
+
+ readable.append(content);
+ for (i = 10; i >= 0; i--) {
+ count += 4 * (readable.charAt(i) - '0');
+
+ if ((i & 1) != 0) {
+ count += 5 * (readable.charAt(i) - '0');
+ }
+ }
+
+ readable.append((char) (((10 - (count % 10)) % 10) + '0'));
+ encodeInfo.append("Check Digit: ").append((char) (((10 - (count % 10)) % 10) + '0'));
+ encodeInfo.append('\n');
+
+ dest = new StringBuilder("1111");
+
+ for (i = 0; i < readable.length(); i += 2) {
+ dest.append(interlace(i, i + 1));
+ }
+
+ dest.append("311");
+
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ getRectangles().clear();
+ getTexts().clear();
+ int baseY;
+ if (getHumanReadableLocation() == TOP) {
+ baseY = getTheoreticalHumanReadableHeight();
+ } else {
+ baseY = 0;
+ }
+ double x = 0;
+ int y = baseY;
+ int h = 0;
+ boolean black = true;
+ int offset = 0;
+ if (mode == ToFMode.ITF14) {
+ offset = 20;
+ }
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ char c = pattern[0].charAt(xBlock);
+ double w = getModuleWidth(c - '0') * moduleWidth;
+ if (black) {
+ if (rowHeight[0] == -1) {
+ h = defaultHeight;
+ } else {
+ h = rowHeight[0];
+ }
+ if (w != 0 && h != 0) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h);
+ getRectangles().add(rect);
+ }
+ symbolWidth = (int) Math.ceil(x + w + (2 * offset));
+ }
+ black = !black;
+ x += w;
+ }
+ symbolHeight = h;
+ if (mode == ToFMode.ITF14) {
+ Rectangle2D.Double topBar = new Rectangle2D.Double(0, baseY, symbolWidth, 4);
+ Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, baseY + symbolHeight - 4, symbolWidth, 4);
+ Rectangle2D.Double leftBar = new Rectangle2D.Double(0, baseY, 4, symbolHeight);
+ Rectangle2D.Double rightBar = new Rectangle2D.Double(symbolWidth - 4, baseY, 4, symbolHeight);
+ getRectangles().add(topBar);
+ getRectangles().add(bottomBar);
+ getRectangles().add(leftBar);
+ getRectangles().add(rightBar);
+ }
+ if (getHumanReadableLocation() != NONE && readable.length() > 0) {
+ double baseline;
+ if (getHumanReadableLocation() == TOP) {
+ baseline = fontSize;
+ } else {
+ baseline = getHeight() + fontSize;
+ }
+ double centerX = getWidth() / 2.0;
+ getTexts().add(new TextBox(centerX, baseline, readable.toString()));
+ }
+ }
+
+ @Override
+ protected double getModuleWidth(int originalWidth) {
+ if (originalWidth == 1) {
+ return 1;
+ } else {
+ return moduleWidthRatio;
+ }
+ }
+
+ private enum ToFMode {
+ MATRIX, INDUSTRIAL, IATA, DATA_LOGIC, INTERLEAVED, ITF14, DPLEIT, DPIDENT
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code32.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code32.java
new file mode 100755
index 0000000..6c35717
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code32.java
@@ -0,0 +1,103 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Code 32, also known as Italian Pharmacode, A variation of Code
+ * 39 used by the Italian Ministry of Health ("Ministero della Sanità ")
+ * Requires a numeric input up to 8 digits in length. Check digit is
+ * calculated.
+ */
+public class Code32 extends Symbol {
+ private char[] tabella = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F',
+ 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z'
+ };
+
+ @Override
+ public boolean encode() {
+ int i, checksum, checkpart, checkdigit;
+ int pharmacode, remainder, devisor;
+ StringBuilder localstr;
+ StringBuilder risultante;
+ int[] codeword = new int[6];
+ Code3Of9 c39 = new Code3Of9();
+
+ if (content.length() > 8) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ /* Add leading zeros as required */
+ localstr = new StringBuilder();
+ for (i = content.length(); i < 8; i++) {
+ localstr.append("0");
+ }
+ localstr.append(content);
+
+ /* Calculate the check digit */
+ checksum = 0;
+ checkpart = 0;
+ for (i = 0; i < 4; i++) {
+ checkpart = Character.getNumericValue(localstr.charAt(i * 2));
+ checksum += checkpart;
+ checkpart = 2 * Character.getNumericValue(localstr.charAt((i * 2) + 1));
+ if (checkpart >= 10) {
+ checksum += (checkpart - 10) + 1;
+ } else {
+ checksum += checkpart;
+ }
+ }
+
+ /* Add check digit to data string */
+ checkdigit = checksum % 10;
+ localstr.append((char) (checkdigit + '0'));
+ encodeInfo.append("Check Digit: ").append((char) (checkdigit + '0'));
+ encodeInfo.append('\n');
+
+ /* Convert string into an integer value */
+ pharmacode = 0;
+ for (i = 0; i < localstr.length(); i++) {
+ pharmacode *= 10;
+ pharmacode += Character.getNumericValue(localstr.charAt(i));
+ }
+
+ /* Convert from decimal to base-32 */
+ devisor = 33554432;
+ for (i = 5; i >= 0; i--) {
+ codeword[i] = pharmacode / devisor;
+ remainder = pharmacode % devisor;
+ pharmacode = remainder;
+ devisor /= 32;
+ }
+
+ /* Look up values in 'Tabella di conversione' */
+ risultante = new StringBuilder();
+ for (i = 5; i >= 0; i--) {
+ risultante.append(tabella[codeword[i]]);
+ }
+
+ /* Plot the barcode using Code 39 */
+
+ readable = new StringBuilder("A").append(localstr);
+ pattern = new String[1];
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ encodeInfo.append("Code 39 Equivalent: ").append(risultante).append('\n');
+ try {
+ c39.setContent(risultante.toString());
+ } catch (Exception e) {
+ errorMsg.append(e.getMessage());
+ return false;
+ }
+
+ this.pattern[0] = c39.pattern[0];
+ this.plotSymbol();
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java
new file mode 100755
index 0000000..7fb0378
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java
@@ -0,0 +1,155 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Code 39 bar code symbology according to ISO/IEC 16388:2007.
+ * Input data can be of any length and supports the characters 0-9, A-Z, dash
+ * (-), full stop (.), space, asterisk (*), dollar ($), slash (/), plus (+)
+ * and percent (%). The standard does not require a check digit but a
+ * modulo-43 check digit can be added if required.
+ */
+public class Code3Of9 extends Symbol {
+
+ private static final String[] CODE_39 = {
+ "1112212111", "2112111121", "1122111121", "2122111111", "1112211121",
+ "2112211111", "1122211111", "1112112121", "2112112111", "1122112111",
+ "2111121121", "1121121121", "2121121111", "1111221121", "2111221111",
+ "1121221111", "1111122121", "2111122111", "1121122111", "1111222111",
+ "2111111221", "1121111221", "2121111211", "1111211221", "2111211211",
+ "1121211211", "1111112221", "2111112211", "1121112211", "1111212211",
+ "2211111121", "1221111121", "2221111111", "1211211121", "2211211111",
+ "1221211111", "1211112121", "2211112111", "1221112111", "1212121111",
+ "1212111211", "1211121211", "1112121211"
+ };
+ private static final char[] LOOKUP = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+',
+ '%'
+ };
+ private CheckDigit checkOption = CheckDigit.NONE;
+ /**
+ * Ratio of wide bar width to narrow bar width.
+ */
+ private double moduleWidthRatio = 2;
+
+ /**
+ * Returns the ratio of wide bar width to narrow bar width.
+ *
+ * @return the ratio of wide bar width to narrow bar width
+ */
+ public double getModuleWidthRatio() {
+ return moduleWidthRatio;
+ }
+
+ /**
+ * Sets the ratio of wide bar width to narrow bar width. Valid values are usually
+ * between {@code 2} and {@code 3}. The default value is {@code 2}.
+ *
+ * @param moduleWidthRatio the ratio of wide bar width to narrow bar width
+ */
+ public void setModuleWidthRatio(double moduleWidthRatio) {
+ this.moduleWidthRatio = moduleWidthRatio;
+ }
+
+ /**
+ * Select addition of optional Modulo-43 check digit or encoding without
+ * check digit.
+ *
+ * @param checkMode Check digit option.
+ */
+ public void setCheckDigit(CheckDigit checkMode) {
+ checkOption = checkMode;
+ }
+
+ @Override
+ public boolean encode() {
+ if (!(content.matches("[0-9A-Z\\. \\-$/+%]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+ StringBuilder p = new StringBuilder();
+ StringBuilder dest = new StringBuilder();
+ int l = content.length();
+ int charval;
+ char thischar;
+ int counter = 0;
+ char check_digit = ' ';
+ dest.append("1211212111");
+ for (int i = 0; i < l; i++) {
+ thischar = content.charAt(i);
+ charval = positionOf(thischar, LOOKUP);
+ counter += charval;
+ p.append(CODE_39[charval]);
+ }
+ dest.append(p);
+ if (checkOption == CheckDigit.MOD43) {
+ counter = counter % 43;
+ if (counter < 10) {
+ check_digit = (char) (counter + '0');
+ } else {
+ if (counter < 36) {
+ check_digit = (char) ((counter - 10) + 'A');
+ } else {
+ switch (counter) {
+ case 36:
+ check_digit = '-';
+ break;
+ case 37:
+ check_digit = '.';
+ break;
+ case 38:
+ check_digit = ' ';
+ break;
+ case 39:
+ check_digit = '$';
+ break;
+ case 40:
+ check_digit = '/';
+ break;
+ case 41:
+ check_digit = '+';
+ break;
+ default:
+ check_digit = 37;
+ break;
+ }
+ }
+ }
+ charval = positionOf(check_digit, LOOKUP);
+ p.append(CODE_39[charval]);
+ if (check_digit == ' ') {
+ check_digit = '_';
+ }
+ }
+ dest.append("121121211");
+ if (checkOption == CheckDigit.MOD43) {
+ readable = new StringBuilder("*").append(content).append(check_digit).append("*");
+ encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
+ } else {
+ readable = new StringBuilder("*").append(content).append("*");
+ }
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ plotSymbol();
+ return true;
+ }
+
+ @Override
+ protected double getModuleWidth(int originalWidth) {
+ if (originalWidth == 1) {
+ return 1;
+ } else {
+ return moduleWidthRatio;
+ }
+ }
+
+ @Override
+ protected int[] getCodewords() {
+ return getPatternAsCodewords(10);
+ }
+
+ public enum CheckDigit {
+ NONE, MOD43
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java
new file mode 100755
index 0000000..30d9e63
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java
@@ -0,0 +1,79 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Code 3 of 9 Extended, also known as Code 39e and Code39+.
+ * Supports encoding of all characters in the 7-bit ASCII table. A
+ * modulo-43 check digit can be added if required.
+ */
+public class Code3Of9Extended extends Symbol {
+
+ private final String[] ECode39 = {
+ "%U", "$A", "$B", "$C", "$D", "$E", "$F", "$G", "$H", "$I", "$J", "$K",
+ "$L", "$M", "$N", "$O", "$P", "$Q", "$R", "$S", "$T", "$U", "$V", "$W",
+ "$X", "$Y", "$Z", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C",
+ "/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "/Z", "%F", "%G",
+ "%H", "%I", "%J", "%V", "A", "B", "C", "D", "E", "F", "G", "H", "I",
+ "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
+ "X", "Y", "Z", "%K", "%L", "%M", "%N", "%O", "%W", "+A", "+B", "+C",
+ "+D", "+E", "+F", "+G", "+H", "+I", "+J", "+K", "+L", "+M", "+N", "+O",
+ "+P", "+Q", "+R", "+S", "+T", "+U", "+V", "+W", "+X", "+Y", "+Z", "%P",
+ "%Q", "%R", "%S", "%T"
+ };
+ private CheckDigit checkOption;
+
+ public Code3Of9Extended() {
+ checkOption = CheckDigit.NONE;
+ }
+
+ /**
+ * Select addition of optional Modulo-43 check digit or encoding without
+ * check digit.
+ *
+ * @param checkMode Check digit option
+ */
+ public void setCheckDigit(CheckDigit checkMode) {
+ checkOption = checkMode;
+ }
+
+ @Override
+ public boolean encode() {
+ StringBuilder buffer = new StringBuilder();
+ int l = content.length();
+ int asciicode;
+ Code3Of9 c = new Code3Of9();
+
+ if (checkOption == CheckDigit.MOD43) {
+ c.setCheckDigit(Code3Of9.CheckDigit.MOD43);
+ }
+
+ if (!content.matches("[\u0000-\u007F]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ for (int i = 0; i < l; i++) {
+ asciicode = content.charAt(i);
+ buffer.append(ECode39[asciicode]);
+ }
+
+ try {
+ c.setContent(buffer.toString());
+ } catch (Exception e) {
+ errorMsg.append(e.getMessage());
+ return false;
+ }
+ readable = new StringBuilder(content);
+ pattern = new String[1];
+ pattern[0] = c.pattern[0];
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ public enum CheckDigit {
+ NONE, MOD43
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code49.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code49.java
new file mode 100755
index 0000000..ef55083
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code49.java
@@ -0,0 +1,1345 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements Code 49 according to ANSI/AIM-BC6-2000.
+ * Encoding supports full 7-bit ASCII input up to a maximum of 49 characters
+ * or 81 numeric digits. GS1 data encoding is also supported.
+ */
+public class Code49 extends Symbol {
+
+ private final String[] c49_table7 = {
+ /* Table 7: Code 49 ASCII Chart */
+ "! ", "!A", "!B", "!C", "!D", "!E", "!F", "!G", "!H", "!I", "!J", "!K",
+ "!L", "!M", "!N", "!O", "!P", "!Q", "!R", "!S", "!T", "!U", "!V", "!W",
+ "!X", "!Y", "!Z", "!1", "!2", "!3", "!4", "!5", " ", "!6", "!7", "!8",
+ "$", "%", "!9", "!0", "!-", "!.", "!$", "+", "!/", "-", ".", "/", "0",
+ "1", "2", "3", "4", "5", "6", "7", "8", "9", "!+", "&1", "&2", "&3",
+ "&4", "&5", "&6", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
+ "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
+ "Z", "&7", "&8", "&9", "&0", "&-", "&.", "&A", "&B", "&C", "&D", "&E",
+ "&F", "&G", "&H", "&I", "&J", "&K", "&L", "&M", "&N", "&O", "&P", "&Q",
+ "&R", "&S", "&T", "&U", "&V", "&W", "&X", "&Y", "&Z", "&$", "&/", "&+",
+ "&%", "& "
+ };
+
+ /* Table 5: Check Character Weighting Values */
+ private final int[] c49_x_weight = {
+ 1, 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13,
+ 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10
+ };
+
+ private final int[] c49_y_weight = {
+ 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5,
+ 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24
+ };
+
+ private final int[] c49_z_weight = {
+ 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41,
+ 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24, 30
+ };
+
+ private final String[] c49_table4 = {
+ /* Table 4: Row Parity Pattern for Code 49 Symbols */
+ "OEEO", "EOEO", "OOEE", "EEOO", "OEOE", "EOOE", "OOOO", "EEEE"
+ };
+
+ private final String[] c49_appxe_even = {
+ /* Appendix E - Code 49 Encodation Patterns (Even Symbol Character Parity) */
+ /* Column 1 */
+ "11521132", "25112131", "14212132", "25121221", "14221222", "12412132",
+ "23321221", "12421222", "21521221", "15112222", "15121312", "13312222",
+ "24221311", "13321312", "11512222", "22421311", "11521312", "25112311",
+ "14212312", "23312311", "12412312", "21512311", "16121131", "14321131",
+ "12521131", "15212131", "15221221", "13412131", "13421221", "11612131",
+ "16112221", "16121311", "14312221", "14321311", "12512221", "12521311",
+ "15212311", "13412311", "11612311", "11131135", "31131133", "51131131",
+ "21122134", "41122132", "21131224", "41131222", "11113135", "31113133",
+ "51113131", "11122225", "31122223", "51122221", "11131315", "31131313",
+ "51131311", "21113224", "41113222", "21122314",
+ /* Column 2 */
+ "41122312", "11113315", "31113313", "51113311", "12131134", "32131132",
+ "21231133", "41231131", "22122133", "42122131", "11222134", "22131223",
+ "42131221", "11231224", "31231222", "12113134", "32113132", "12122224",
+ "32122222", "12131314", "32131312", "21231313", "41231311", "22113223",
+ "42113221", "11213224", "22122313", "42122311", "11222314", "31222312",
+ "12113314", "32113312", "21213313", "41213311", "13131133", "33131131",
+ "22231132", "11331133", "31331131", "23122132", "12222133", "23131222",
+ "12231223", "32231221", "21331222", "13113133", "33113131", "13122223",
+ "33122221", "11313133", "13131313", "33131311", "11322223", "22231312",
+ "11331313", "31331311", "23113222", "12213223",
+ /* Column 3 */
+ "23122312", "12222313", "32222311", "21322312", "13113313", "33113311",
+ "22213312", "11313313", "31313311", "14131132", "23231131", "12331132",
+ "21431131", "24122131", "13222132", "24131221", "13231222", "11422132",
+ "22331221", "11431222", "14113132", "14122222", "12313132", "14131312",
+ "12322222", "23231311", "12331312", "21431311", "24113221", "13213222",
+ "24122311", "13222312", "11413222", "22322311", "11422312", "14113312",
+ "23213311", "12313312", "21413311", "15131131", "13331131", "14222131",
+ "14231221", "12422131", "12431221", "15113131", "15122221", "13313131",
+ "15131311", "13322221", "11513131", "13331311", "11522221", "14213221",
+ "14222311", "12413221", "12422311", "15113311",
+ /* Column 4 */
+ "13313311", "11513311", "11141134", "31141132", "21132133", "41132131",
+ "21141223", "41141221", "11123134", "31123132", "11132224", "31132222",
+ "11141314", "31141312", "21114133", "41114131", "21123223", "41123221",
+ "21132313", "41132311", "11114224", "31114222", "11123314", "31123312",
+ "21114313", "41114311", "12141133", "32141131", "21241132", "22132132",
+ "11232133", "22141222", "11241223", "31241221", "12123133", "32123131",
+ "12132223", "32132221", "12141313", "32141311", "21241312", "22114132",
+ "11214133", "22123222", "11223223", "22132312", "11232313", "31232311",
+ "12114223", "32114221", "12123313", "32123311", "21223312", "22114312",
+ "11214313", "31214311", "13141132", "22241131",
+ /* Column 5 */
+ "11341132", "23132131", "12232132", "23141221", "12241222", "21341221",
+ "13123132", "13132222", "11323132", "13141312", "11332222", "22241311",
+ "11341312", "23114131", "12214132", "23123221", "12223222", "23132311",
+ "12232312", "21332311", "13114222", "13123312", "11314222", "22223311",
+ "11323312", "23114311", "12214312", "21314311", "14141131", "12341131",
+ "13232131", "13241221", "11432131", "14123131", "14132221", "12323131",
+ "14141311", "12332221", "12341311", "13214131", "13223221", "11414131",
+ "13232311", "11423221", "11432311", "14114221", "14123311", "12314221",
+ "12323311", "13214311", "11414311", "11151133", "31151131", "21142132",
+ "21151222", "11133133", "31133131", "11142223",
+ /* Column 6 */
+ "31142221", "11151313", "31151311", "21124132", "21133222", "21142312",
+ "11115133", "31115131", "11124223", "31124221", "11133313", "31133311",
+ "21115222", "21124312", "12151132", "21251131", "22142131", "11242132",
+ "22151221", "11251222", "12133132", "12142222", "12151312", "21251311",
+ "22124131", "11224132", "22133221", "11233222", "22142311", "11242312",
+ "12115132", "12124222", "12133312", "21233311", "22115221", "11215222",
+ "22124311", "11224312", "13151131", "12242131", "12251221", "13133131",
+ "13142221", "11333131", "13151311", "11342221", "12224131", "12233221",
+ "12242311", "13115131", "13124221", "11315131", "13133311", "11324221",
+ "11333311", "12215221", "12224311", "11161132",
+ /* Column 7 */
+ "21152131", "21161221", "11143132", "11152222", "11161312", "21134131",
+ "21143221", "21152311", "11125132", "11134222", "11143312", "21116131",
+ "21125221", "21134311", "12161131", "11252131", "12143131", "12152221",
+ "12161311", "11234131", "11243221", "11252311", "12125131", "12134221",
+ "12143311", "11216131", "11225221", "11234311", "11111236", "31111234",
+ "51111232", "21111325", "41111323", "61111321", "11111416", "31111414",
+ "51111412", "31211143", "51211141", "12111235", "32111233", "52111231",
+ "21211234", "41211232", "22111324", "42111322", "11211325", "31211323",
+ "51211321", "12111415", "32111413", "52111411", "21211414", "41211412",
+ "12211144", "32211142", "21311143", "41311141",
+ /* Column 8 */
+ "13111234", "33111232", "22211233", "42211231", "11311234", "31311232",
+ "23111323", "43111321", "12211324", "32211322", "21311323", "41311321",
+ "13111414", "33111412", "22211413", "42211411", "11311414", "31311412",
+ "13211143", "33211141", "22311142", "11411143", "31411141", "14111233",
+ "34111231", "23211232", "12311233", "32311231", "21411232", "24111322",
+ "13211323", "33211321", "22311322", "11411323", "31411321", "14111413",
+ "34111411", "23211412", "12311413", "32311411", "21411412", "14211142",
+ "23311141", "12411142", "21511141", "15111232", "24211231", "13311232",
+ "22411231", "11511232", "25111321", "14211322", "23311321", "12411322",
+ "21511321", "15111412", "24211411", "13311412",
+ /* Column 9 */
+ "22411411", "11511412", "15211141", "13411141", "11611141", "16111231",
+ "14311231", "12511231", "15211321", "13411321", "11611321", "16111411",
+ "14311411", "12511411", "21121144", "41121142", "11112145", "31112143",
+ "51112141", "11121235", "31121233", "51121231", "21112234", "41112232",
+ "21121324", "41121322", "11112325", "31112323", "51112321", "11121415",
+ "31121413", "51121411", "21112414", "41112412", "22121143", "42121141",
+ "11221144", "31221142", "12112144", "32112142", "12121234", "32121232",
+ "21221233", "41221231", "22112233", "42112231", "11212234", "22121323",
+ "42121321", "11221324", "31221322", "12112324", "32112322", "12121414",
+ "32121412", "21221413", "41221411", "22112413",
+ /* Column 10 */
+ "42112411", "11212414", "31212412", "23121142", "12221143", "32221141",
+ "21321142", "13112143", "33112141", "13121233", "33121231", "11312143",
+ "22221232", "11321233", "31321231", "23112232", "12212233", "23121322",
+ "12221323", "32221321", "21321322", "13112323", "33112321", "13121413",
+ "33121411", "11312323", "22221412", "11321413", "31321411", "23112412",
+ "12212413", "32212411", "21312412", "24121141", "13221142", "22321141",
+ "11421142", "14112142", "14121232", "12312142", "23221231", "12321232",
+ "21421231", "24112231", "13212232", "24121321", "13221322", "11412232",
+ "22321321", "11421322", "14112322", "14121412", "12312322", "23221411",
+ "12321412", "21421411", "24112411", "13212412",
+ /* Column 11 */
+ "22312411", "11412412", "14221141", "12421141", "15112141", "15121231",
+ "13312141", "13321231", "11512141", "11521231", "14212231", "14221321",
+ "12412231", "12421321", "15112321", "15121411", "13312321", "13321411",
+ "11512321", "11521411", "14212411", "12412411", "21131143", "41131141",
+ "11122144", "31122142", "11131234", "31131232", "21113143", "41113141",
+ "21122233", "41122231", "21131323", "41131321", "11113234", "31113232",
+ "11122324", "31122322", "11131414", "31131412", "21113323", "41113321",
+ "21122413", "41122411", "11113414", "31113412", "22131142", "11231143",
+ "31231141", "12122143", "32122141", "12131233", "32131231", "21231232",
+ "22113142", "11213143", "22122232", "11222233",
+ /* Column 12 */
+ "22131322", "11231323", "31231321", "12113233", "32113231", "12122323",
+ "32122321", "12131413", "32131411", "21231412", "22113322", "11213323",
+ "22122412", "11222413", "31222411", "12113413", "32113411", "21213412",
+ "23131141", "12231142", "21331141", "13122142", "13131232", "11322142",
+ "22231231", "11331232", "23113141", "12213142", "23122231", "12222232",
+ "23131321", "12231322", "21331321", "13113232", "13122322", "11313232",
+ "13131412", "11322322", "22231411", "11331412", "23113321", "12213322",
+ "23122411", "12222412", "21322411", "13113412", "22213411", "11313412",
+ "13231141", "11431141", "14122141", "14131231", "12322141", "12331231",
+ "13213141", "13222231", "11413141", "13231321",
+ /* Column 13 */
+ "11422231", "11431321", "14113231", "14122321", "12313231", "14131411",
+ "12322321", "12331411", "13213321", "13222411", "11413321", "11422411",
+ "14113411", "12313411", "21141142", "11132143", "31132141", "11141233",
+ "31141231", "21123142", "21132232", "21141322", "11114143", "31114141",
+ "11123233", "31123231", "11132323", "31132321", "11141413", "31141411",
+ "21114232", "21123322", "21132412", "11114323", "31114321", "11123413",
+ "31123411", "22141141", "11241142", "12132142", "12141232", "21241231",
+ "22123141", "11223142", "22132231", "11232232", "22141321", "11241322",
+ "12114142", "12123232", "12132322", "12141412", "21241411", "22114231",
+ "11214232", "22123321", "11223322", "22132411",
+ /* Column 14 */
+ "11232412", "12114322", "12123412", "21223411", "12241141", "13132141",
+ "13141231", "11332141", "11341231", "12223141", "12232231", "12241321",
+ "13114141", "13123231", "11314141", "13132321", "11323231", "13141411",
+ "11332321", "11341411", "12214231", "12223321", "12232411", "13114321",
+ "13123411", "11314321", "11323411", "21151141", "11142142", "11151232",
+ "21133141", "21142231", "21151321", "11124142", "11133232", "11142322",
+ "11151412", "21115141", "21124231", "21133321", "21142411", "11115232",
+ "11124322", "11133412", "11251141", "12142141", "12151231", "11233141",
+ "11242231", "11251321", "12124141", "12133231", "12142321", "12151411",
+ "11215141", "11224231", "11233321", "11242411",
+ /* Column 15 */
+ "12115231", "12124321", "12133411", "11152141", "11161231", "11134141",
+ "11143231", "11152321", "11161411", "11116141", "11125231", "11134321",
+ "11143411", "21111244", "41111242", "11111335", "31111333", "51111331",
+ "21111424", "41111422", "11111515", "31111513", "51111511", "21211153",
+ "41211151", "22111243", "42111241", "11211244", "31211242", "12111334",
+ "32111332", "21211333", "41211331", "22111423", "42111421", "11211424",
+ "31211422", "12111514", "32111512", "21211513", "41211511", "22211152",
+ "11311153", "31311151", "23111242", "12211243", "32211241", "21311242",
+ "13111333", "33111331", "22211332", "11311333", "31311331", "23111422",
+ "12211423", "32211421", "21311422", "13111513",
+ /* Column 16 */
+ "33111511", "22211512", "11311513", "31311511", "23211151", "12311152",
+ "21411151", "24111241", "13211242", "22311241", "11411242", "14111332",
+ "23211331", "12311332", "21411331", "24111421", "13211422", "22311421",
+ "11411422", "14111512", "23211511", "12311512", "21411511", "13311151",
+ "11511151", "14211241", "12411241", "15111331", "13311331", "11511331",
+ "14211421", "12411421", "15111511", "13311511", "11511511", "31121152",
+ "21112153", "41112151", "21121243", "41121241", "11112244", "31112242",
+ "11121334", "31121332", "21112333", "41112331", "21121423", "41121421",
+ "11112424", "31112422", "11121514", "31121512", "21112513", "41112511",
+ "12121153", "32121151", "21221152", "22112152",
+ /* Column 17 */
+ "11212153", "22121242", "11221243", "31221241", "12112243", "32112241",
+ "12121333", "32121331", "21221332", "22112332", "11212333", "22121422",
+ "11221423", "31221421", "12112423", "32112421", "12121513", "32121511",
+ "21221512", "22112512", "11212513", "31212511", "13121152", "22221151",
+ "11321152", "23112151", "12212152", "23121241", "12221242", "21321241",
+ "13112242", "13121332", "11312242", "22221331", "11321332", "23112331",
+ "12212332", "23121421", "12221422", "21321421", "13112422", "13121512",
+ "11312422", "22221511", "11321512", "23112511", "12212512", "21312511",
+ "14121151", "12321151", "13212151", "13221241", "11412151", "11421241",
+ "14112241", "14121331", "12312241", "12321331",
+ /* Column 18 */
+ "13212331", "13221421", "11412331", "11421421", "14112421", "14121511",
+ "12312421", "12321511", "13212511", "11412511", "11131153", "31131151",
+ "21122152", "21131242", "11113153", "31113151", "11122243", "31122241",
+ "11131333", "31131331", "21113242", "21122332", "21131422", "11113333",
+ "31113331", "11122423", "31122421", "11131513", "31131511", "21113422",
+ "21122512", "12131152", "21231151", "22122151", "11222152", "22131241",
+ "11231242", "12113152", "12122242", "12131332", "21231331", "22113241",
+ "11213242", "22122331", "11222332", "22131421", "11231422", "12113332",
+ "12122422", "12131512", "21231511", "22113421", "11213422", "22122511",
+ "11222512", "13131151", "11331151", "12222151",
+ /* Column 19 */
+ "12231241", "13113151", "13122241", "11313151", "13131331", "11322241",
+ "11331331", "12213241", "12222331", "12231421", "13113331", "13122421",
+ "11313331", "13131511", "11322421", "11331511", "12213421", "12222511",
+ "11141152", "21132151", "21141241", "11123152", "11132242", "11141332",
+ "21114151", "21123241", "21132331", "21141421", "11114242", "11123332",
+ "11132422", "11141512", "21114331", "21123421", "21132511", "12141151",
+ "11232151", "11241241", "12123151", "12132241", "12141331", "11214151",
+ "11223241", "11232331", "11241421", "12114241", "12123331", "12132421",
+ "12141511", "11214331", "11223421", "11232511", "11151151", "11133151",
+ "11142241", "11151331", "11115151", "11124241",
+ /* Column 20 */
+ "11133331", "11142421", "11151511", "11111254", "31111252", "21111343",
+ "41111341", "11111434", "31111432", "21111523", "41111521", "11111614",
+ "31111612", "31211161", "12111253", "32111251", "21211252", "22111342",
+ "11211343", "31211341", "12111433", "32111431", "21211432", "22111522",
+ "11211523", "31211521", "12111613", "32111611", "21211612", "12211162",
+ "21311161", "13111252", "22211251", "11311252", "23111341", "12211342",
+ "21311341", "13111432", "22211431", "11311432", "23111521", "12211522",
+ "21311521", "13111612", "22211611", "11311612", "13211161", "11411161",
+ "14111251", "12311251", "13211341", "11411341", "14111431", "12311431",
+ "13211521", "11411521", "14111611", "12311611",
+ /* Column 21 */
+ "21121162", "11112163", "31112161", "11121253", "31121251", "21112252",
+ "21121342", "11112343", "31112341", "11121433", "31121431", "21112432",
+ "21121522", "11112523", "31112521", "11121613", "31121611", "22121161",
+ "11221162", "12112162", "12121252", "21221251", "22112251", "11212252",
+ "22121341", "11221342", "12112342", "12121432", "21221431", "22112431",
+ "11212432", "22121521", "11221522", "12112522", "12121612", "21221611",
+ "12221161", "13112161", "13121251", "11312161", "11321251", "32121115",
+ "52121113", "21221116", "41221114", "61221112", "22112116", "42112114",
+ "31212115", "51212113", "13121116", "33121114", "22221115", "42221113",
+ "11321116", "31321114", "51321112", "23112115",
+ /* Column 22 */
+ "43112113", "12212116", "32212114", "52212112", "21312115", "41312113",
+ "61312111", "14121115", "34121113", "23221114", "43221112", "12321115",
+ "32321113", "52321111", "21421114", "41421112", "24112114", "13212115",
+ "33212113", "22312114", "42312112", "11412115", "31412113", "51412111",
+ "15121114", "24221113", "13321114", "33321112", "22421113", "42421111",
+ "11521114", "31521112", "25112113", "14212114", "34212112", "23312113",
+ "43312111", "12412114", "32412112", "21512113", "41512111", "16121113",
+ "25221112", "14321113", "34321111", "23421112", "12521113", "32521111",
+ "15212113", "24312112", "13412113", "33412111", "22512112", "11612113",
+ "31612111", "31131115", "51131113", "21122116",
+ /* Column 23 */
+ "41122114", "61122112", "31113115", "51113113", "12131116", "32131114",
+ "52131112", "21231115", "41231113", "61231111", "22122115", "42122113",
+ "11222116", "31222114", "51222112", "12113116", "32113114", "52113112",
+ "21213115", "41213113", "61213111", "13131115", "33131113", "22231114",
+ "42231112", "11331115", "31331113", "51331111", "23122114", "43122112",
+ "12222115", "32222113", "52222111", "21322114", "41322112", "13113115",
+ "33113113", "22213114", "42213112", "11313115", "31313113", "51313111",
+ "14131114", "34131112", "23231113", "43231111", "12331114", "32331112",
+ "21431113", "41431111", "24122113", "13222114", "33222112", "22322113",
+ "42322111", "11422114", "31422112", "14113114",
+ /* Column 24 */
+ "34113112", "23213113", "43213111", "12313114", "32313112", "21413113",
+ "41413111", "15131113", "24231112", "13331113", "33331111", "22431112",
+ "25122112", "14222113", "34222111", "23322112", "12422113", "32422111",
+ "21522112", "15113113", "24213112", "13313113", "33313111", "22413112",
+ "11513113", "31513111", "16131112", "25231111", "14331112", "23431111",
+ "15222112", "24322111", "13422112", "22522111", "16113112", "25213111",
+ "14313112", "23413111", "12513112", "21613111", "11141116", "31141114",
+ "51141112", "21132115", "41132113", "61132111", "11123116", "31123114",
+ "51123112", "21114115", "41114113", "61114111", "12141115", "32141113",
+ "52141111", "21241114", "41241112", "22132114",
+ /* Column 25 */
+ "42132112", "11232115", "31232113", "51232111", "12123115", "32123113",
+ "52123111", "21223114", "41223112", "22114114", "42114112", "11214115",
+ "31214113", "51214111", "13141114", "33141112", "22241113", "42241111",
+ "11341114", "31341112", "23132113", "43132111", "12232114", "32232112",
+ "21332113", "41332111", "13123114", "33123112", "22223113", "42223111",
+ "11323114", "31323112", "23114113", "43114111", "12214114", "32214112",
+ "21314113", "41314111", "14141113", "34141111", "23241112", "12341113",
+ "32341111", "24132112", "13232113", "33232111", "22332112", "11432113",
+ "31432111", "14123113", "34123111", "23223112", "12323113", "32323111",
+ "21423112", "24114112", "13214113", "33214111",
+ /* Column 26 */
+ "22314112", "11414113", "31414111", "15141112", "24241111", "13341112",
+ "25132111", "14232112", "23332111", "12432112", "15123112", "24223111",
+ "13323112", "22423111", "11523112", "25114111", "14214112", "23314111",
+ "12414112", "21514111", "16141111", "14341111", "15232111", "13432111",
+ "16123111", "14323111", "12523111", "15214111", "13414111", "11614111",
+ "11151115", "31151113", "51151111", "21142114", "41142112", "11133115",
+ "31133113", "51133111", "21124114", "41124112", "11115115", "31115113",
+ "51115111", "12151114", "32151112", "21251113", "41251111", "22142113",
+ "42142111", "11242114", "31242112", "12133114", "32133112", "21233113",
+ "41233111", "22124113", "42124111", "11224114",
+ /* Column 27 */
+ "31224112", "12115114", "32115112", "21215113", "41215111", "13151113",
+ "33151111", "22251112", "23142112", "12242113", "32242111", "21342112",
+ "13133113", "33133111", "22233112", "11333113", "31333111", "23124112",
+ "12224113", "32224111", "21324112", "13115113", "33115111", "22215112",
+ "11315113", "31315111", "14151112", "23251111", "24142111", "13242112",
+ "22342111", "14133112", "23233111", "12333112", "21433111", "24124111",
+ "13224112", "22324111", "11424112", "14115112", "23215111", "12315112",
+ "21415111", "15151111", "14242111", "15133111", "13333111", "14224111",
+ "12424111", "15115111", "13315111", "11515111", "11161114", "31161112",
+ "21152113", "41152111", "11143114", "31143112",
+ /* Column 28 */
+ "21134113", "41134111", "11125114", "31125112", "21116113", "41116111",
+ "12161113", "32161111", "22152112", "11252113", "31252111", "12143113",
+ "32143111", "21243112", "22134112", "11234113", "31234111", "12125113",
+ "32125111", "21225112", "22116112", "11216113", "31216111", "13161112",
+ "23152111", "12252112", "13143112", "22243111", "11343112", "23134111",
+ "12234112", "21334111", "13125112", "22225111", "11325112", "23116111",
+ "12216112", "21316111", "14161111", "13252111", "14143111", "12343111",
+ "13234111", "11434111", "14125111", "12325111", "13216111", "11416111",
+ "31111216", "51111214", "31211125", "51211123", "32111215", "52111213",
+ "21211216", "41211214", "61211212", "12211126",
+ /* Column 29 */
+ "32211124", "52211122", "21311125", "41311123", "61311121", "13111216",
+ "33111214", "22211215", "42211213", "11311216", "31311214", "51311212",
+ "13211125", "33211123", "22311124", "42311122", "11411125", "31411123",
+ "51411121", "14111215", "34111213", "23211214", "43211212", "12311215",
+ "32311213", "52311211", "21411214", "41411212", "14211124", "34211122",
+ "23311123", "43311121", "12411124", "32411122", "21511123", "41511121",
+ "15111214", "24211213", "13311214", "33311212", "22411213", "42411211",
+ "11511214", "31511212", "15211123", "24311122", "13411123", "33411121",
+ "22511122", "11611123", "31611121", "16111213", "25211212", "14311213",
+ "34311211", "23411212", "12511213", "32511211",
+ /* Column 30 */
+ "21611212", "21121126", "41121124", "61121122", "31112125", "51112123",
+ "31121215", "51121213", "21112216", "41112214", "61112212", "22121125",
+ "42121123", "11221126", "31221124", "51221122", "12112126", "32112124",
+ "52112122", "12121216", "32121214", "52121212", "21221215", "41221213",
+ "61221211", "22112215", "42112213", "11212216", "31212214", "51212212",
+ "23121124", "43121122", "12221125", "32221123", "52221121", "21321124",
+ "41321122", "13112125", "33112123", "13121215", "33121213", "11312125",
+ "22221214", "42221212", "11321215", "31321213", "51321211", "23112214",
+ "43112212", "12212215", "32212213", "52212211", "21312214", "41312212",
+ "24121123", "13221124", "33221122", "22321123",
+ /* Column 31 */
+ "42321121", "11421124", "31421122", "14112124", "34112122", "14121214",
+ "34121212", "12312124", "23221213", "43221211", "12321214", "32321212",
+ "21421213", "41421211", "24112213", "13212214", "33212212", "22312213",
+ "42312211", "11412214", "31412212", "25121122", "14221123", "34221121",
+ "23321122", "12421123", "32421121", "21521122", "15112123", "15121213",
+ "13312123", "24221212", "13321213", "33321211", "11512123", "22421212",
+ "11521213", "31521211", "25112212", "14212213", "34212211", "23312212",
+ "12412213", "32412211", "21512212", "15221122", "24321121", "13421122",
+ "22521121", "16112122", "16121212", "14312122", "25221211", "14321212",
+ "12512122", "23421211", "12521212", "15212212",
+ /* Column 32 */
+ "24312211", "13412212", "22512211", "11612212", "21131125", "41131123",
+ "61131121", "11122126", "31122124", "51122122", "11131216", "31131214",
+ "51131212", "21113125", "41113123", "61113121", "21122215", "41122213",
+ "61122211", "11113216", "31113214", "51113212", "22131124", "42131122",
+ "11231125", "31231123", "51231121", "12122125", "32122123", "52122121",
+ "12131215", "32131213", "52131211", "21231214", "41231212", "22113124",
+ "42113122", "11213125", "22122214", "42122212", "11222215", "31222213",
+ "51222211", "12113215", "32113213", "52113211", "21213214", "41213212",
+ "23131123", "43131121", "12231124", "32231122", "21331123", "41331121",
+ "13122124", "33122122", "13131214", "33131212",
+ /* Column 33 */
+ "11322124", "22231213", "42231211", "11331214", "31331212", "23113123",
+ "43113121", "12213124", "23122213", "43122211", "12222214", "32222212",
+ "21322213", "41322211", "13113214", "33113212", "22213213", "42213211",
+ "11313214", "31313212", "24131122", "13231123", "33231121", "22331122",
+ "11431123", "31431121", "14122123", "34122121", "14131213", "34131211",
+ "12322123", "23231212", "12331213", "32331211", "21431212", "24113122",
+ "13213123", "24122212", "13222213", "33222211", "11413123", "22322212",
+ "11422213", "31422211", "14113213", "34113211", "23213212", "12313213",
+ "32313211", "21413212", "25131121", "14231122", "23331121", "12431122",
+ "15122122", "15131212", "13322122", "24231211",
+ /* Column 34 */
+ "13331212", "11522122", "22431211", "25113121", "14213122", "25122211",
+ "14222212", "12413122", "23322211", "12422212", "21522211", "15113212",
+ "24213211", "13313212", "22413211", "11513212", "15231121", "13431121",
+ "16122121", "16131211", "14322121", "14331211", "12522121", "15213121",
+ "15222211", "13413121", "13422211", "11613121", "16113211", "14313211",
+ "12513211", "21141124", "41141122", "11132125", "31132123", "51132121",
+ "11141215", "31141213", "51141211", "21123124", "41123122", "21132214",
+ "41132212", "11114125", "31114123", "51114121", "11123215", "31123213",
+ "51123211", "21114214", "41114212", "22141123", "42141121", "11241124",
+ "31241122", "12132124", "32132122", "12141214",
+ /* Column 35 */
+ "32141212", "21241213", "41241211", "22123123", "42123121", "11223124",
+ "22132213", "42132211", "11232214", "31232212", "12114124", "32114122",
+ "12123214", "32123212", "21223213", "41223211", "22114213", "42114211",
+ "11214214", "31214212", "23141122", "12241123", "32241121", "21341122",
+ "13132123", "33132121", "13141213", "33141211", "11332123", "22241212",
+ "11341213", "31341211", "23123122", "12223123", "23132212", "12232213",
+ "32232211", "21332212", "13114123", "33114121", "13123213", "33123211",
+ "11314123", "22223212", "11323213", "31323211", "23114212", "12214213",
+ "32214211", "21314212", "24141121", "13241122", "22341121", "14132122",
+ "14141212", "12332122", "23241211", "12341212",
+ /* Column 36 */
+ "24123121", "13223122", "24132211", "13232212", "11423122", "22332211",
+ "11432212", "14114122", "14123212", "12314122", "23223211", "12323212",
+ "21423211", "24114211", "13214212", "22314211", "11414212", "14241121",
+ "15132121", "15141211", "13332121", "13341211", "14223121", "14232211",
+ "12423121", "12432211", "15114121", "15123211", "13314121", "13323211",
+ "11514121", "11523211", "14214211", "12414211", "21151123", "41151121",
+ "11142124", "31142122", "11151214", "31151212", "21133123", "41133121",
+ "21142213", "41142211", "11124124", "31124122", "11133214", "31133212",
+ "21115123", "41115121", "21124213", "41124211", "11115214", "31115212",
+ "22151122", "11251123", "31251121", "12142123",
+ /* Column 37 */
+ "32142121", "12151213", "32151211", "21251212", "22133122", "11233123",
+ "22142212", "11242213", "31242211", "12124123", "32124121", "12133213",
+ "32133211", "21233212", "22115122", "11215123", "22124212", "11224213",
+ "31224211", "12115213", "32115211", "21215212", "23151121", "12251122",
+ "13142122", "13151212", "11342122", "22251211", "23133121", "12233122",
+ "23142211", "12242212", "21342211", "13124122", "13133212", "11324122",
+ "22233211", "11333212", "23115121", "12215122", "23124211", "12224212",
+ "21324211", "13115212", "22215211", "11315212", "13251121", "14142121",
+ "14151211", "12342121", "13233121", "13242211", "11433121", "14124121",
+ "14133211", "12324121", "12333211", "13215121",
+ /* Column 38 */
+ "13224211", "11415121", "11424211", "14115211", "12315211", "21161122",
+ "11152123", "31152121", "11161213", "31161211", "21143122", "21152212",
+ "11134123", "31134121", "11143213", "31143211", "21125122", "21134212",
+ "11116123", "31116121", "11125213", "31125211", "22161121", "12152122",
+ "12161212", "22143121", "11243122", "22152211", "11252212", "12134122",
+ "12143212", "21243211", "22125121", "11225122", "22134211", "11234212",
+ "12116122", "12125212", "21225211", "13152121", "13161211", "12243121",
+ "12252211", "13134121", "13143211", "11334121", "11343211", "12225121",
+ "12234211", "13116121", "13125211", "11316121", "11325211", "21111226",
+ "41111224", "61111222", "31111315", "51111313",
+ /* Column 39 */
+ "21211135", "41211133", "61211131", "22111225", "42111223", "11211226",
+ "31211224", "51211222", "12111316", "32111314", "52111312", "21211315",
+ "41211313", "61211311", "22211134", "42211132", "11311135", "31311133",
+ "51311131", "23111224", "43111222", "12211225", "32211223", "52211221",
+ "21311224", "41311222", "13111315", "33111313", "22211314", "42211312",
+ "11311315", "31311313", "51311311", "23211133", "43211131", "12311134",
+ "32311132", "21411133", "41411131", "24111223", "13211224", "33211222",
+ "22311223", "42311221", "11411224", "31411222", "14111314", "34111312",
+ "23211313", "43211311", "12311314", "32311312", "21411313", "41411311",
+ "24211132", "13311133", "33311131", "22411132",
+ /* Column 40 */
+ "11511133", "31511131", "25111222", "14211223", "34211221", "23311222",
+ "12411223", "32411221", "21511222", "15111313", "24211312", "13311313",
+ "33311311", "22411312", "11511313", "31511311", "25211131", "14311132",
+ "23411131", "12511132", "21611131", "15211222", "24311221", "13411222",
+ "22511221", "11611222", "16111312", "25211311", "14311312", "23411311",
+ "12511312", "21611311", "31121134", "51121132", "21112135", "41112133",
+ "61112131", "21121225", "41121223", "61121221", "11112226", "31112224",
+ "51112222", "11121316", "31121314", "51121312", "21112315", "41112313",
+ "61112311", "12121135", "32121133", "52121131", "21221134", "41221132",
+ "22112134", "42112132", "11212135", "22121224",
+ /* Column 41 */
+ "42121222", "11221225", "31221223", "51221221", "12112225", "32112223",
+ "52112221", "12121315", "32121313", "52121311", "21221314", "41221312",
+ "22112314", "42112312", "11212315", "31212313", "51212311", "13121134",
+ "33121132", "22221133", "42221131", "11321134", "31321132", "23112133",
+ "43112131", "12212134", "23121223", "43121221", "12221224", "32221222",
+ "21321223", "41321221", "13112224", "33112222", "13121314", "33121312",
+ "11312224", "22221313", "42221311", "11321314", "31321312",
+ /* Column 42 */
+ "23112313", "43112311", "12212314", "32212312", "21312313", "41312311",
+ "14121133", "34121131", "23221132", "12321133", "32321131", "21421132",
+ "24112132", "13212133", "24121222", "13221223", "33221221", "11412133",
+ "22321222", "11421223", "31421221", "14112223", "34112221", "14121313",
+ "34121311", "12312223", "23221312", "12321313", "32321311", "21421312",
+ "24112312", "13212313", "33212311", "22312312", "11412313", "31412311",
+ "15121132", "24221131", "13321132", "22421131"
+ };
+
+ private String[] c49_appxe_odd = {
+ /* Appendix E - Code 49 Encodation Patterns (Odd Symbol Character Parity) */
+ /* Column 1 */
+ "22121116", "42121114", "31221115", "51221113", "32112115", "52112113",
+ "21212116", "41212114", "61212112", "23121115", "43121113", "12221116",
+ "32221114", "52221112", "21321115", "41321113", "61321111", "13112116",
+ "33112114", "22212115", "42212113", "11312116", "31312114", "51312112",
+ "24121114", "13221115", "33221113", "22321114", "42321112", "11421115",
+ "31421113", "51421111", "14112115", "34112113", "23212114", "43212112",
+ "12312115", "32312113", "52312111", "21412114", "41412112", "25121113",
+ "14221114", "34221112", "23321113", "43321111", "12421114", "32421112",
+ "21521113", "41521111", "15112114", "24212113", "13312114", "33312112",
+ "22412113", "42412111", "11512114", "31512112",
+ /* Column 2 */
+ "15221113", "24321112", "13421113", "33421111", "22521112", "16112113",
+ "25212112", "14312113", "34312111", "23412112", "12512113", "32512111",
+ "21612112", "21131116", "41131114", "61131112", "31122115", "51122113",
+ "21113116", "41113114", "61113112", "22131115", "42131113", "11231116",
+ "31231114", "51231112", "12122116", "32122114", "52122112", "21222115",
+ "41222113", "61222111", "22113115", "42113113", "11213116", "31213114",
+ "51213112", "23131114", "43131112", "12231115", "32231113", "52231111",
+ "21331114", "41331112", "13122115", "33122113", "22222114", "42222112",
+ "11322115", "31322113", "51322111", "23113114", "43113112", "12213115",
+ "32213113", "52213111", "21313114", "41313112",
+ /* Column 3 */
+ "24131113", "13231114", "33231112", "22331113", "42331111", "11431114",
+ "31431112", "14122114", "34122112", "23222113", "43222111", "12322114",
+ "32322112", "21422113", "41422111", "24113113", "13213114", "33213112",
+ "22313113", "42313111", "11413114", "31413112", "25131112", "14231113",
+ "34231111", "23331112", "12431113", "32431111", "15122113", "24222112",
+ "13322113", "33322111", "22422112", "11522113", "31522111", "25113112",
+ "14213113", "34213111", "23313112", "12413113", "32413111", "21513112",
+ "15231112", "24331111", "13431112", "16122112", "25222111", "14322112",
+ "23422111", "12522112", "15213112", "24313111", "13413112", "22513111",
+ "11613112", "21141115", "41141113", "61141111",
+ /* Column 4 */
+ "11132116", "31132114", "51132112", "21123115", "41123113", "61123111",
+ "11114116", "31114114", "51114112", "22141114", "42141112", "11241115",
+ "31241113", "51241111", "12132115", "32132113", "52132111", "21232114",
+ "41232112", "22123114", "42123112", "11223115", "31223113", "51223111",
+ "12114115", "32114113", "52114111", "21214114", "41214112", "23141113",
+ "43141111", "12241114", "32241112", "21341113", "41341111", "13132114",
+ "33132112", "22232113", "42232111", "11332114", "31332112", "23123113",
+ "43123111", "12223114", "32223112", "21323113", "41323111", "13114114",
+ "33114112", "22214113", "42214111", "11314114", "31314112", "24141112",
+ "13241113", "33241111", "22341112", "14132113",
+ /* Column 5 */
+ "34132111", "23232112", "12332113", "32332111", "21432112", "24123112",
+ "13223113", "33223111", "22323112", "11423113", "31423111", "14114113",
+ "34114111", "23214112", "12314113", "32314111", "21414112", "25141111",
+ "14241112", "23341111", "15132112", "24232111", "13332112", "22432111",
+ "25123111", "14223112", "23323111", "12423112", "21523111", "15114112",
+ "24214111", "13314112", "22414111", "11514112", "15241111", "16132111",
+ "14332111", "15223111", "13423111", "16114111", "14314111", "12514111",
+ "21151114", "41151112", "11142115", "31142113", "51142111", "21133114",
+ "41133112", "11124115", "31124113", "51124111", "21115114", "41115112",
+ "22151113", "42151111", "11251114", "31251112",
+ /* Column 6 */
+ "12142114", "32142112", "21242113", "41242111", "22133113", "42133111",
+ "11233114", "31233112", "12124114", "32124112", "21224113", "41224111",
+ "22115113", "42115111", "11215114", "31215112", "23151112", "12251113",
+ "32251111", "13142113", "33142111", "22242112", "11342113", "31342111",
+ "23133112", "12233113", "32233111", "21333112", "13124113", "33124111",
+ "22224112", "11324113", "31324111", "23115112", "12215113", "32215111",
+ "21315112", "24151111", "13251112", "14142112", "23242111", "12342112",
+ "24133111", "13233112", "22333111", "11433112", "14124112", "23224111",
+ "12324112", "21424111", "24115111", "13215112", "22315111", "11415112",
+ "14251111", "15142111", "13342111", "14233111",
+ /* Column 7 */
+ "12433111", "15124111", "13324111", "11524111", "14215111", "12415111",
+ "21161113", "41161111", "11152114", "31152112", "21143113", "41143111",
+ "11134114", "31134112", "21125113", "41125111", "11116114", "31116112",
+ "22161112", "12152113", "32152111", "21252112", "22143112", "11243113",
+ "31243111", "12134113", "32134111", "21234112", "22125112", "11225113",
+ "31225111", "12116113", "32116111", "21216112", "23161111", "13152112",
+ "22252111", "23143111", "12243112", "21343111", "13134112", "22234111",
+ "11334112", "23125111", "12225112", "21325111", "13116112", "22216111",
+ "11316112", "14152111", "13243111", "14134111", "12334111", "13225111",
+ "11425111", "14116111", "12316111", "41111215",
+ /* Column 8 */
+ "61111213", "21211126", "41211124", "61211122", "22111216", "42111214",
+ "31211215", "51211213", "22211125", "42211123", "11311126", "31311124",
+ "51311122", "23111215", "43111213", "12211216", "32211214", "52211212",
+ "21311215", "41311213", "61311211", "23211124", "43211122", "12311125",
+ "32311123", "52311121", "21411124", "41411122", "24111214", "13211215",
+ "33211213", "22311214", "42311212", "11411215", "31411213", "51411211",
+ "24211123", "13311124", "33311122", "22411123", "42411121", "11511124",
+ "31511122", "25111213", "14211214", "34211212", "23311213", "43311211",
+ "12411214", "32411212", "21511213", "41511211", "25211122", "14311123",
+ "34311121", "23411122", "12511123", "32511121",
+ /* Column 9 */
+ "21611122", "15211213", "24311212", "13411213", "33411211", "22511212",
+ "11611213", "31611211", "31121125", "51121123", "21112126", "41112124",
+ "61112122", "21121216", "41121214", "61121212", "31112215", "51112213",
+ "12121126", "32121124", "52121122", "21221125", "41221123", "61221121",
+ "22112125", "42112123", "11212126", "22121215", "42121213", "11221216",
+ "31221214", "51221212", "12112216", "32112214", "52112212", "21212215",
+ "41212213", "61212211", "13121125", "33121123", "22221124", "42221122",
+ "11321125", "31321123", "51321121", "23112124", "43112122", "12212125",
+ "23121214", "43121212", "12221215", "32221213", "52221211", "21321214",
+ "41321212", "13112215", "33112213", "22212214",
+ /* Column 10 */
+ "42212212", "11312215", "31312213", "51312211", "14121124", "34121122",
+ "23221123", "43221121", "12321124", "32321122", "21421123", "41421121",
+ "24112123", "13212124", "24121213", "13221214", "33221212", "11412124",
+ "22321213", "42321211", "11421214", "31421212", "14112214", "34112212",
+ "23212213", "43212211", "12312214", "32312212", "21412213", "41412211",
+ "15121123", "24221122", "13321123", "33321121", "22421122", "11521123",
+ "31521121", "25112122", "14212123", "25121212", "14221213", "34221211",
+ "12412123", "23321212", "12421213", "32421211", "21521212", "15112213",
+ "24212212", "13312213", "33312211", "22412212", "11512213", "31512211",
+ "16121122", "25221121", "14321122", "23421121",
+ /* Column 11 */
+ "12521122", "15212122", "15221212", "13412122", "24321211", "13421212",
+ "11612122", "22521211", "16112212", "25212211", "14312212", "23412211",
+ "12512212", "21612211", "11131126", "31131124", "51131122", "21122125",
+ "41122123", "61122121", "21131215", "41131213", "61131211", "11113126",
+ "31113124", "51113122", "11122216", "31122214", "51122212", "21113215",
+ "41113213", "61113211", "12131125", "32131123", "52131121", "21231124",
+ "41231122", "22122124", "42122122", "11222125", "22131214", "42131212",
+ "11231215", "31231213", "51231211", "12113125", "32113123", "52113121",
+ "12122215", "32122213", "52122211", "21222214", "41222212", "22113214",
+ "42113212", "11213215", "31213213", "51213211",
+ /* Column 12 */
+ "13131124", "33131122", "22231123", "42231121", "11331124", "31331122",
+ "23122123", "43122121", "12222124", "23131213", "43131211", "12231214",
+ "32231212", "21331213", "41331211", "13113124", "33113122", "13122214",
+ "33122212", "11313124", "22222213", "42222211", "11322214", "31322212",
+ "23113213", "43113211", "12213214", "32213212", "21313213", "41313211",
+ "14131123", "34131121", "23231122", "12331123", "32331121", "21431122",
+ "24122122", "13222123", "24131212", "13231213", "33231211", "11422123",
+ "22331212", "11431213", "31431211", "14113123", "34113121", "14122213",
+ "34122211", "12313123", "23222212", "12322213", "32322211", "21422212",
+ "24113212", "13213213", "33213211", "22313212",
+ /* Column 13 */
+ "11413213", "31413211", "15131122", "24231121", "13331122", "22431121",
+ "25122121", "14222122", "25131211", "14231212", "12422122", "23331211",
+ "12431212", "15113122", "15122212", "13313122", "24222211", "13322212",
+ "11513122", "22422211", "11522212", "25113211", "14213212", "23313211",
+ "12413212", "21513211", "16131121", "14331121", "15222121", "15231211",
+ "13422121", "13431211", "16113121", "16122211", "14313121", "14322211",
+ "12513121", "12522211", "15213211", "13413211", "11613211", "11141125",
+ "31141123", "51141121", "21132124", "41132122", "21141214", "41141212",
+ "11123125", "31123123", "51123121", "11132215", "31132213", "51132211",
+ "21114124", "41114122", "21123214", "41123212",
+ /* Column 14 */
+ "11114215", "31114213", "51114211", "12141124", "32141122", "21241123",
+ "41241121", "22132123", "42132121", "11232124", "22141213", "42141211",
+ "11241214", "31241212", "12123124", "32123122", "12132214", "32132212",
+ "21232213", "41232211", "22114123", "42114121", "11214124", "22123213",
+ "42123211", "11223214", "31223212", "12114214", "32114212", "21214213",
+ "41214211", "13141123", "33141121", "22241122", "11341123", "31341121",
+ "23132122", "12232123", "23141212", "12241213", "32241211", "21341212",
+ "13123123", "33123121", "13132213", "33132211", "11323123", "22232212",
+ "11332213", "31332211", "23114122", "12214123", "23123212", "12223213",
+ "32223211", "21323212", "13114213", "33114211",
+ /* Column 15 */
+ "22214212", "11314213", "31314211", "14141122", "23241121", "12341122",
+ "24132121", "13232122", "24141211", "13241212", "11432122", "22341211",
+ "14123122", "14132212", "12323122", "23232211", "12332212", "21432211",
+ "24114121", "13214122", "24123211", "13223212", "11414122", "22323211",
+ "11423212", "14114212", "23214211", "12314212", "21414211", "15141121",
+ "13341121", "14232121", "14241211", "12432121", "15123121", "15132211",
+ "13323121", "13332211", "11523121", "14214121", "14223211", "12414121",
+ "12423211", "15114211", "13314211", "11514211", "11151124", "31151122",
+ "21142123", "41142121", "21151213", "41151211", "11133124", "31133122",
+ "11142214", "31142212", "21124123", "41124121",
+ /* Column 16 */
+ "21133213", "41133211", "11115124", "31115122", "11124214", "31124212",
+ "21115213", "41115211", "12151123", "32151121", "21251122", "22142122",
+ "11242123", "22151212", "11251213", "31251211", "12133123", "32133121",
+ "12142213", "32142211", "21242212", "22124122", "11224123", "22133212",
+ "11233213", "31233211", "12115123", "32115121", "12124213", "32124211",
+ "21224212", "22115212", "11215213", "31215211", "13151122", "22251121",
+ "23142121", "12242122", "23151211", "12251212", "13133122", "13142212",
+ "11333122", "22242211", "11342212", "23124121", "12224122", "23133211",
+ "12233212", "21333211", "13115122", "13124212", "11315122", "22224211",
+ "11324212", "23115211", "12215212", "21315211",
+ /* Column 17 */
+ "14151121", "13242121", "13251211", "14133121", "14142211", "12333121",
+ "12342211", "13224121", "13233211", "11424121", "11433211", "14115121",
+ "14124211", "12315121", "12324211", "13215211", "11415211", "11161123",
+ "31161121", "21152122", "21161212", "11143123", "31143121", "11152213",
+ "31152211", "21134122", "21143212", "11125123", "31125121", "11134213",
+ "31134211", "21116122", "21125212", "12161122", "22152121", "11252122",
+ "22161211", "12143122", "12152212", "21252211", "22134121", "11234122",
+ "22143211", "11243212", "12125122", "12134212", "21234211", "22116121",
+ "11216122", "22125211", "11225212", "13161121", "12252121", "13143121",
+ "13152211", "11343121", "12234121", "12243211",
+ /* Column 18 */
+ "13125121", "13134211", "11325121", "11334211", "12216121", "12225211",
+ "31111225", "51111223", "21111316", "41111314", "61111312", "31211134",
+ "51211132", "12111226", "32111224", "52111222", "21211225", "41211223",
+ "61211221", "22111315", "42111313", "11211316", "31211314", "51211312",
+ "12211135", "32211133", "52211131", "21311134", "41311132", "13111225",
+ "33111223", "22211224", "42211222", "11311225", "31311223", "51311221",
+ "23111314", "43111312", "12211315", "32211313", "52211311", "21311314",
+ "41311312", "13211134", "33211132", "22311133", "42311131", "11411134",
+ "31411132", "14111224", "34111222", "23211223", "43211221", "12311224",
+ "32311222", "21411223", "41411221", "24111313",
+ /* Column 19 */
+ "13211314", "33211312", "22311313", "42311311", "11411314", "31411312",
+ "14211133", "34211131", "23311132", "12411133", "32411131", "21511132",
+ "15111223", "24211222", "13311223", "33311221", "22411222", "11511223",
+ "31511221", "25111312", "14211313", "34211311", "23311312", "12411313",
+ "32411311", "21511312", "15211132", "24311131", "13411132", "22511131",
+ "11611132", "16111222", "25211221", "14311222", "23411221", "12511222",
+ "21611221", "15211312", "24311311", "13411312", "22511311", "11611312",
+ "21121135", "41121133", "61121131", "11112136", "31112134", "51112132",
+ "11121226", "31121224", "51121222", "21112225", "41112223", "61112221",
+ "21121315", "41121313", "61121311", "11112316",
+ /* Column 20 */
+ "31112314", "51112312", "22121134", "42121132", "11221135", "31221133",
+ "51221131", "12112135", "32112133", "52112131", "12121225", "32121223",
+ "52121221", "21221224", "41221222", "22112224", "42112222", "11212225",
+ "22121314", "42121312", "11221315", "31221313", "51221311", "12112315",
+ "32112313", "52112311", "21212314", "41212312", "23121133", "43121131",
+ "12221134", "32221132", "21321133", "41321131", "13112134", "33112132",
+ "13121224", "33121222", "11312134", "22221223", "42221221", "11321224",
+ "31321222", "23112223", "43112221", "12212224", "23121313", "43121311",
+ "12221314", "32221312", "21321313", "41321311", "13112314", "33112312",
+ "22212313", "42212311", "11312314", "31312312",
+ /* Column 21 */
+ "24121132", "13221133", "33221131", "22321132", "11421133", "31421131",
+ "14112133", "34112131", "14121223", "34121221", "12312133", "23221222",
+ "12321223", "32321221", "21421222", "24112222", "13212223", "24121312",
+ "13221313", "33221311", "11412223", "22321312", "11421313", "31421311",
+ "14112313", "34112311", "23212312", "12312313", "32312311", "21412312",
+ "25121131", "14221132", "23321131", "12421132", "21521131", "15112132",
+ "15121222", "13312132", "24221221", "13321222", "11512132", "22421221",
+ "11521222", "25112221", "14212222", "25121311", "14221312", "12412222",
+ "23321311", "12421312", "21521311", "15112312", "24212311", "13312312",
+ "22412311", "11512312", "15221131", "13421131",
+ /* Column 22 */
+ "16112131", "16121221", "14312131", "14321221", "12512131", "12521221",
+ "15212221", "15221311", "13412221", "13421311", "11612221", "16112311",
+ "14312311", "12512311", "21131134", "41131132", "11122135", "31122133",
+ "51122131", "11131225", "31131223", "51131221", "21113134", "41113132",
+ "21122224", "41122222", "21131314", "41131312", "11113225", "31113223",
+ "51113221", "11122315", "31122313", "51122311", "21113314", "41113312",
+ "22131133", "42131131", "11231134", "31231132", "12122134", "32122132",
+ "12131224", "32131222", "21231223", "41231221", "22113133", "42113131",
+ "11213134", "22122223", "42122221", "11222224", "22131313", "42131311",
+ "11231314", "31231312", "12113224", "32113222",
+ /* Column 23 */
+ "12122314", "32122312", "21222313", "41222311", "22113313", "42113311",
+ "11213314", "31213312", "23131132", "12231133", "32231131", "21331132",
+ "13122133", "33122131", "13131223", "33131221", "11322133", "22231222",
+ "11331223", "31331221", "23113132", "12213133", "23122222", "12222223",
+ "23131312", "12231313", "32231311", "21331312", "13113223", "33113221",
+ "13122313", "33122311", "11313223", "22222312", "11322313", "31322311",
+ "23113312", "12213313", "32213311", "21313312", "24131131", "13231132",
+ "22331131", "11431132", "14122132", "14131222", "12322132", "23231221",
+ "12331222", "21431221", "24113131", "13213132", "24122221", "13222222",
+ "24131311", "11413132", "13231312", "11422222",
+ /* Column 24 */
+ "22331311", "11431312", "14113222", "14122312", "12313222", "23222311",
+ "12322312", "21422311", "24113311", "13213312", "22313311", "11413312",
+ "14231131", "12431131", "15122131", "15131221", "13322131", "13331221",
+ "11522131", "14213131", "14222221", "12413131", "14231311", "12422221",
+ "12431311", "15113221", "15122311", "13313221", "13322311", "11513221",
+ "11522311", "14213311", "12413311", "21141133", "41141131", "11132134",
+ "31132132", "11141224", "31141222", "21123133", "41123131", "21132223",
+ "41132221", "21141313", "41141311", "11114134", "31114132", "11123224",
+ "31123222", "11132314", "31132312", "21114223", "41114221", "21123313",
+ "41123311", "11114314", "31114312", "22141132",
+ /* Column 25 */
+ "11241133", "31241131", "12132133", "32132131", "12141223", "32141221",
+ "21241222", "22123132", "11223133", "22132222", "11232223", "22141312",
+ "11241313", "31241311", "12114133", "32114131", "12123223", "32123221",
+ "12132313", "32132311", "21232312", "22114222", "11214223", "22123312",
+ "11223313", "31223311", "12114313", "32114311", "21214312", "23141131",
+ "12241132", "21341131", "13132132", "13141222", "11332132", "22241221",
+ "11341222", "23123131", "12223132", "23132221", "12232222", "23141311",
+ "12241312", "21341311", "13114132", "13123222", "11314132", "13132312",
+ "11323222", "22232311", "11332312", "23114221", "12214222", "23123311",
+ "12223312", "21323311", "13114312", "22214311",
+ /* Column 26 */
+ "11314312", "13241131", "14132131", "14141221", "12332131", "12341221",
+ "13223131", "13232221", "11423131", "13241311", "11432221", "14114131",
+ "14123221", "12314131", "14132311", "12323221", "12332311", "13214221",
+ "13223311", "11414221", "11423311", "14114311", "12314311", "21151132",
+ "11142133", "31142131", "11151223", "31151221", "21133132", "21142222",
+ "21151312", "11124133", "31124131", "11133223", "31133221", "11142313",
+ "31142311", "21115132", "21124222", "21133312", "11115223", "31115221",
+ "11124313", "31124311", "22151131", "11251132", "12142132", "12151222",
+ "21251221", "22133131", "11233132", "22142221", "11242222", "22151311",
+ "11251312", "12124132", "12133222", "12142312",
+ /* Column 27 */
+ "21242311", "22115131", "11215132", "22124221", "11224222", "22133311",
+ "11233312", "12115222", "12124312", "21224311", "12251131", "13142131",
+ "13151221", "11342131", "12233131", "12242221", "12251311", "13124131",
+ "13133221", "11324131", "13142311", "11333221", "11342311", "12215131",
+ "12224221", "12233311", "13115221", "13124311", "11315221", "11324311",
+ "21161131", "11152132", "11161222", "21143131", "21152221", "21161311",
+ "11134132", "11143222", "11152312", "21125131", "21134221", "21143311",
+ "11116132", "11125222", "11134312", "12152131", "12161221", "11243131",
+ "11252221", "12134131", "12143221", "12152311", "11225131", "11234221",
+ "11243311", "12116131", "12125221", "12134311",
+ /* Column 28 */
+ "21111235", "41111233", "61111231", "11111326", "31111324", "51111322",
+ "21111415", "41111413", "61111411", "21211144", "41211142", "22111234",
+ "42111232", "11211235", "31211233", "51211231", "12111325", "32111323",
+ "52111321", "21211324", "41211322", "22111414", "42111412", "11211415",
+ "31211413", "51211411", "22211143", "42211141", "11311144", "31311142",
+ "23111233", "43111231", "12211234", "32211232", "21311233", "41311231",
+ "13111324", "33111322", "22211323", "42211321", "11311324", "31311322",
+ "23111413", "43111411", "12211414", "32211412", "21311413", "41311411",
+ "23211142", "12311143", "32311141", "21411142", "24111232", "13211233",
+ "33211231", "22311232", "11411233", "31411231",
+ /* Column 29 */
+ "14111323", "34111321", "23211322", "12311323", "32311321", "21411322",
+ "24111412", "13211413", "33211411", "22311412", "11411413", "31411411",
+ "24211141", "13311142", "22411141", "11511142", "25111231", "14211232",
+ "23311231", "12411232", "21511231", "15111322", "24211321", "13311322",
+ "22411321", "11511322", "25111411", "14211412", "23311411", "12411412",
+ "21511411", "14311141", "12511141", "15211231", "13411231", "11611231",
+ "16111321", "14311321", "12511321", "15211411", "13411411", "11611411",
+ "31121143", "51121141", "21112144", "41112142", "21121234", "41121232",
+ "11112235", "31112233", "51112231", "11121325", "31121323", "51121321",
+ "21112324", "41112322", "21121414", "41121412",
+ /* Column 30 */
+ "11112415", "31112413", "51112411", "12121144", "32121142", "21221143",
+ "41221141", "22112143", "42112141", "11212144", "22121233", "42121231",
+ "11221234", "31221232", "12112234", "32112232", "12121324", "32121322",
+ "21221323", "41221321", "22112323", "42112321", "11212324", "22121413",
+ "42121411", "11221414", "31221412", "12112414", "32112412", "21212413",
+ "41212411", "13121143", "33121141", "22221142", "11321143", "31321141",
+ "23112142", "12212143", "23121232", "12221233", "32221231", "21321232",
+ "13112233", "33112231", "13121323", "33121321", "11312233", "22221322",
+ "11321323", "31321321", "23112322", "12212323", "23121412", "12221413",
+ "32221411", "21321412", "13112413", "33112411",
+ /* Column 31 */
+ "22212412", "11312413", "31312411", "14121142", "23221141", "12321142",
+ "21421141", "24112141", "13212142", "24121231", "13221232", "11412142",
+ "22321231", "11421232", "14112232", "14121322", "12312232", "23221321",
+ "12321322", "21421321", "24112321", "13212322", "24121411", "13221412",
+ "11412322", "22321411", "11421412", "14112412", "23212411", "12312412",
+ "21412411", "15121141", "13321141", "11521141", "14212141", "14221231",
+ "12412141", "12421231", "15112231", "15121321", "13312231", "13321321",
+ "11512231", "11521321", "14212321", "14221411", "12412321", "12421411",
+ "15112411", "13312411", "11512411", "11131144", "31131142", "21122143",
+ "41122141", "21131233", "41131231", "11113144",
+ /* Column 32 */
+ "31113142", "11122234", "31122232", "11131324", "31131322", "21113233",
+ "41113231", "21122323", "41122321", "21131413", "41131411", "11113324",
+ "31113322", "11122414", "31122412", "21113413", "41113411", "12131143",
+ "32131141", "21231142", "22122142", "11222143", "22131232", "11231233",
+ "31231231", "12113143", "32113141", "12122233", "32122231", "12131323",
+ "32131321", "21231322", "22113232", "11213233", "22122322", "11222323",
+ "22131412", "11231413", "31231411", "12113323", "32113321", "12122413",
+ "32122411", "21222412", "22113412", "11213413", "31213411", "13131142",
+ "22231141", "11331142", "23122141", "12222142", "23131231", "12231232",
+ "21331231", "13113142", "13122232", "11313142",
+ /* Column 33 */
+ "13131322", "11322232", "22231321", "11331322", "23113231", "12213232",
+ "23122321", "12222322", "23131411", "12231412", "21331411", "13113322",
+ "13122412", "11313322", "22222411", "11322412", "23113411", "12213412",
+ "21313411", "14131141", "12331141", "13222141", "13231231", "11422141",
+ "11431231", "14113141", "14122231", "12313141", "14131321", "12322231",
+ "12331321", "13213231", "13222321", "11413231", "13231411", "11422321",
+ "11431411", "14113321", "14122411", "12313321", "12322411", "13213411",
+ "11413411", "11141143", "31141141", "21132142", "21141232", "11123143",
+ "31123141", "11132233", "31132231", "11141323", "31141321", "21114142",
+ "21123232", "21132322", "21141412", "11114233",
+ /* Column 34 */
+ "31114231", "11123323", "31123321", "11132413", "31132411", "21114322",
+ "21123412", "12141142", "21241141", "22132141", "11232142", "22141231",
+ "11241232", "12123142", "12132232", "12141322", "21241321", "22114141",
+ "11214142", "22123231", "11223232", "22132321", "11232322", "22141411",
+ "11241412", "12114232", "12123322", "12132412", "21232411", "22114321",
+ "11214322", "22123411", "11223412", "13141141", "11341141", "12232141",
+ "12241231", "13123141", "13132231", "11323141", "13141321", "11332231",
+ "11341321", "12214141", "12223231", "12232321", "12241411", "13114231",
+ "13123321", "11314231", "13132411", "11323321", "11332411", "12214321",
+ "12223411", "11151142", "21142141", "21151231",
+ /* Column 35 */
+ "11133142", "11142232", "11151322", "21124141", "21133231", "21142321",
+ "21151411", "11115142", "11124232", "11133322", "11142412", "21115231",
+ "21124321", "21133411", "12151141", "11242141", "11251231", "12133141",
+ "12142231", "12151321", "11224141", "11233231", "11242321", "11251411",
+ "12115141", "12124231", "12133321", "12142411", "11215231", "11224321",
+ "11233411", "11161141", "11143141", "11152231", "11161321", "11125141",
+ "11134231", "11143321", "11152411", "11111245", "31111243", "51111241",
+ "21111334", "41111332", "11111425", "31111423", "51111421", "21111514",
+ "41111512", "31211152", "12111244", "32111242", "21211243", "41211241",
+ "22111333", "42111331", "11211334", "31211332",
+ /* Column 36 */
+ "12111424", "32111422", "21211423", "41211421", "22111513", "42111511",
+ "11211514", "31211512", "12211153", "32211151", "21311152", "13111243",
+ "33111241", "22211242", "11311243", "31311241", "23111332", "12211333",
+ "32211331", "21311332", "13111423", "33111421", "22211422", "11311423",
+ "31311421", "23111512", "12211513", "32211511", "21311512", "13211152",
+ "22311151", "11411152", "14111242", "23211241", "12311242", "21411241",
+ "24111331", "13211332", "22311331", "11411332", "14111422", "23211421",
+ "12311422", "21411421", "24111511", "13211512", "22311511", "11411512",
+ "14211151", "12411151", "15111241", "13311241", "11511241", "14211331",
+ "12411331", "15111421", "13311421", "11511421",
+ /* Column 37 */
+ "14211511", "12411511", "21121153", "41121151", "11112154", "31112152",
+ "11121244", "31121242", "21112243", "41112241", "21121333", "41121331",
+ "11112334", "31112332", "11121424", "31121422", "21112423", "41112421",
+ "21121513", "41121511", "11112514", "31112512", "22121152", "11221153",
+ "31221151", "12112153", "32112151", "12121243", "32121241", "21221242",
+ "22112242", "11212243", "22121332", "11221333", "31221331", "12112333",
+ "32112331", "12121423", "32121421", "21221422", "22112422", "11212423",
+ "22121512", "11221513", "31221511", "12112513", "32112511", "21212512",
+ "23121151", "12221152", "21321151", "13112152", "13121242", "11312152",
+ "22221241", "11321242", "23112241", "12212242",
+ /* Column 38 */
+ "23121331", "12221332", "21321331", "13112332", "13121422", "11312332",
+ "22221421", "11321422", "23112421", "12212422", "23121511", "12221512",
+ "21321511", "13112512", "22212511", "11312512", "13221151", "11421151",
+ "14112151", "14121241", "12312151", "12321241", "13212241", "13221331",
+ "11412241", "11421331", "14112331", "14121421", "12312331", "12321421",
+ "13212421", "13221511", "11412421", "11421511", "14112511", "12312511",
+ "21131152", "11122153", "31122151", "11131243", "31131241", "21113152",
+ "21122242", "21131332", "11113243", "31113241", "11122333", "31122331",
+ "11131423", "31131421", "21113332", "21122422", "21131512", "11113423",
+ "31113421", "11122513", "31122511", "22131151",
+ /* Column 39 */
+ "11231152", "12122152", "12131242", "21231241", "22113151", "11213152",
+ "22122241", "11222242", "22131331", "11231332", "12113242", "12122332",
+ "12131422", "21231421", "22113331", "11213332", "22122421", "11222422",
+ "22131511", "11231512", "12113422", "12122512", "21222511", "12231151",
+ "13122151", "13131241", "11322151", "11331241", "12213151", "12222241",
+ "12231331", "13113241", "13122331", "11313241", "13131421", "11322331",
+ "11331421", "12213331", "12222421", "12231511", "13113421", "13122511",
+ "11313421", "11322511", "21141151", "11132152", "11141242", "21123151",
+ "21132241", "21141331", "11114152", "11123242", "11132332", "11141422",
+ "21114241", "21123331", "21132421", "21141511",
+ /* Column 40 */
+ "11114332", "11123422", "11132512", "11241151", "12132151", "12141241",
+ "11223151", "11232241", "11241331", "12114151", "12123241", "12132331",
+ "12141421", "11214241", "11223331", "11232421", "11241511", "12114331",
+ "12123421", "12132511", "11142151", "11151241", "11124151", "11133241",
+ "11142331", "11151421", "11115241", "11124331", "11133421", "11142511",
+ "21111253", "41111251", "11111344", "31111342", "21111433", "41111431",
+ "11111524", "31111522", "21111613", "41111611", "21211162", "22111252",
+ "11211253", "31211251", "12111343", "32111341", "21211342", "22111432",
+ "11211433", "31211431", "12111523", "32111521", "21211522", "22111612",
+ "11211613", "31211611", "22211161", "11311162",
+ /* Column 41 */
+ "23111251", "12211252", "21311251", "13111342", "22211341", "11311342",
+ "23111431", "12211432", "21311431", "13111522", "22211521", "11311522",
+ "23111611", "12211612", "21311611", "12311161", "13211251", "11411251",
+ "14111341", "12311341", "13211431", "11411431", "14111521", "12311521",
+ "13211611", "11411611", "31121161", "21112162", "21121252", "11112253",
+ "31112251", "11121343", "31121341", "21112342", "21121432", "11112433",
+ "31112431", "11121523", "31121521", "21112522", "21121612",
+ /* Column 42 */
+ "12121162", "21221161", "22112161", "11212162", "22121251", "11221252",
+ "12112252", "12121342", "21221341", "22112341", "11212342", "22121431",
+ "11221432", "12112432", "12121522", "21221521", "22112521", "11212522",
+ "22121611", "11221612", "13121161", "11321161", "12212161", "12221251",
+ "13112251", "13121341", "11312251", "11321341", "12212341", "12221431",
+ "13112431", "13121521", "11312431", "11321521", "12212521", "12221611",
+ "11131162", "21122161", "21131251", "11113162"
+ };
+
+ private char[] C49_Set = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+',
+ '%', '!', '&', '*'
+ };
+
+ @Override
+ public boolean encode() {
+ int length = content.length();
+ int i, codeword_count = 0, h, j, M, rows, pad_count = 0;
+ int x_count, y_count, z_count, posn_val, local_value;
+ StringBuilder intermediate = new StringBuilder();
+ StringBuilder localpattern;
+ int[] codewords = new int[170];
+ int[][] c_grid = new int[8][8];
+ int[][] w_grid = new int[8][4];
+
+ if (!content.matches("[\u0000-\u007F]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ if (inputDataType == DataType.GS1) {
+ intermediate.append("*"); // FNC1
+ }
+ for (i = 0; i < length; i++) {
+ if (content.charAt(i) > 127) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+ if ((inputDataType == DataType.GS1) && (content.charAt(i) == '[')) {
+ intermediate.append("*"); // FNC1
+ } else {
+ intermediate.append(c49_table7[content.charAt(i)]);
+ }
+ }
+
+ h = intermediate.length();
+
+ i = 0;
+ do {
+ if ((intermediate.charAt(i) >= '0') && (intermediate.charAt(i) <= '9')) {
+
+ /* Numeric data */
+ int latch = 0;
+ j = 0;
+ do {
+ if ((i + j) >= h) {
+ latch = 1;
+ } else {
+ if ((intermediate.charAt(i + j) >= '0') && (intermediate.charAt(i + j) <= '9')) {
+ j++;
+ } else {
+ latch = 1;
+ }
+ }
+ } while (latch == 0);
+ if (j >= 5) {
+ /* Use Numeric Encodation Method */
+ int block_count, c;
+ int block_remain;
+ int block_value;
+
+ codewords[codeword_count] = 48; /* Numeric Shift */
+ codeword_count++;
+
+ block_count = j / 5;
+ block_remain = j % 5;
+
+ for (c = 0; c < block_count; c++) {
+ if ((c == block_count - 1) && (block_remain == 2)) {
+ /* Rule (d) */
+ block_value = 100000;
+ block_value += (intermediate.charAt(i) - '0') * 1000;
+ block_value += (intermediate.charAt(i + 1) - '0') * 100;
+ block_value += (intermediate.charAt(i + 2) - '0') * 10;
+ block_value += intermediate.charAt(i + 3) - '0';
+
+ codewords[codeword_count] = block_value / (48 * 48);
+ block_value = block_value - (48 * 48) * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value / 48;
+ block_value = block_value - 48 * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value;
+ codeword_count++;
+ i += 4;
+ block_value = (intermediate.charAt(i) - '0') * 100;
+ block_value += (intermediate.charAt(i + 1) - '0') * 10;
+ block_value += intermediate.charAt(i + 2) - '0';
+
+ codewords[codeword_count] = block_value / 48;
+ block_value = block_value - 48 * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value;
+ codeword_count++;
+ i += 3;
+ } else {
+ block_value = (intermediate.charAt(i) - '0') * 10000;
+ block_value += (intermediate.charAt(i + 1) - '0') * 1000;
+ block_value += (intermediate.charAt(i + 2) - '0') * 100;
+ block_value += (intermediate.charAt(i + 3) - '0') * 10;
+ block_value += intermediate.charAt(i + 4) - '0';
+
+ codewords[codeword_count] = block_value / (48 * 48);
+ block_value = block_value - (48 * 48) * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value / 48;
+ block_value = block_value - 48 * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value;
+ codeword_count++;
+ i += 5;
+ }
+ }
+
+ switch (block_remain) {
+ case 1:
+ /* Rule (a) */
+ codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_Set);
+ codeword_count++;
+ i++;
+ break;
+ case 3:
+ /* Rule (b) */
+ block_value = (intermediate.charAt(i) - '0') * 100;
+ block_value += (intermediate.charAt(i + 1) - '0') * 10;
+ block_value += intermediate.charAt(i + 2) - '0';
+
+ codewords[codeword_count] = block_value / 48;
+ block_value = block_value - 48 * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value;
+ codeword_count++;
+ i += 3;
+ break;
+ case 4:
+ /* Rule (c) */
+ block_value = 100000;
+ block_value += (intermediate.charAt(i) - '0') * 1000;
+ block_value += (intermediate.charAt(i + 1) - '0') * 100;
+ block_value += (intermediate.charAt(i + 2) - '0') * 10;
+ block_value += intermediate.charAt(i + 3) - '0';
+
+ codewords[codeword_count] = block_value / (48 * 48);
+ block_value = block_value - (48 * 48) * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value / 48;
+ block_value = block_value - 48 * codewords[codeword_count];
+ codeword_count++;
+ codewords[codeword_count] = block_value;
+ codeword_count++;
+ i += 4;
+ break;
+ }
+ if (i < h) {
+ /* There is more to add */
+ codewords[codeword_count] = 48; /* Numeric Shift */
+ codeword_count++;
+ }
+ } else {
+ codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_Set);
+ codeword_count++;
+ i++;
+ }
+ } else {
+ codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_Set);
+ codeword_count++;
+ i++;
+ }
+ } while (i < h);
+
+ switch (codewords[0]) { /* Set starting mode value */
+ case 48:
+ M = 2;
+ break;
+ case 43:
+ M = 4;
+ break;
+ case 44:
+ M = 5;
+ break;
+ default:
+ M = 0;
+ break;
+ }
+
+ if (M != 0) {
+ for (i = 0; i < codeword_count; i++) {
+ codewords[i] = codewords[i + 1];
+ }
+ codeword_count--;
+ }
+
+ if (codeword_count > 49) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ encodeInfo.append("Starting Mode (M): ").append(M).append("\n");
+
+ /* Place codewords in code character array (c grid) */
+ rows = 0;
+ do {
+ for (i = 0; i < 7; i++) {
+ if (((rows * 7) + i) < codeword_count) {
+ c_grid[rows][i] = codewords[(rows * 7) + i];
+ } else {
+ c_grid[rows][i] = 48; /* Pad */
+ pad_count++;
+ }
+ }
+ rows++;
+ } while ((rows * 7) < codeword_count);
+
+ if ((((rows <= 6) && (pad_count < 5))) || (rows > 6) || (rows == 1)) {
+ /* Add a row */
+ for (i = 0; i < 7; i++) {
+ c_grid[rows][i] = 48; /* Pad */
+ }
+ rows++;
+ }
+
+ /* Add row count and mode character */
+ c_grid[rows - 1][6] = (7 * (rows - 2)) + M;
+
+ /* Add row check character */
+ for (i = 0; i < rows - 1; i++) {
+ int row_sum = 0;
+
+ for (j = 0; j < 7; j++) {
+ row_sum += c_grid[i][j];
+ }
+ c_grid[i][7] = row_sum % 49;
+ }
+
+ /* Calculate Symbol Check Characters */
+ posn_val = 0;
+ x_count = c_grid[rows - 1][6] * 20;
+ y_count = c_grid[rows - 1][6] * 16;
+ z_count = c_grid[rows - 1][6] * 38;
+ for (i = 0; i < rows - 1; i++) {
+ for (j = 0; j < 4; j++) {
+ local_value = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1];
+ x_count += c49_x_weight[posn_val] * local_value;
+ y_count += c49_y_weight[posn_val] * local_value;
+ z_count += c49_z_weight[posn_val] * local_value;
+ posn_val++;
+ }
+ }
+
+ if (rows > 6) {
+ /* Add Z Symbol Check */
+ c_grid[rows - 1][0] = (z_count % 2401) / 49;
+ c_grid[rows - 1][1] = (z_count % 2401) % 49;
+ }
+
+ local_value = (c_grid[rows - 1][0] * 49) + c_grid[rows - 1][1];
+ x_count += c49_x_weight[posn_val] * local_value;
+ y_count += c49_y_weight[posn_val] * local_value;
+ posn_val++;
+
+ /* Add Y Symbol Check */
+ c_grid[rows - 1][2] = (y_count % 2401) / 49;
+ c_grid[rows - 1][3] = (y_count % 2401) % 49;
+
+ local_value = (c_grid[rows - 1][2] * 49) + c_grid[rows - 1][3];
+ x_count += c49_x_weight[posn_val] * local_value;
+
+ /* Add X Symbol Check */
+ c_grid[rows - 1][4] = (x_count % 2401) / 49;
+ c_grid[rows - 1][5] = (x_count % 2401) % 49;
+
+ encodeInfo.append("Check Characters: ").append(Integer.toString(z_count % 2401)).append(" ").append(Integer.toString(y_count % 2401)).append("\n");
+
+ /* Add last row check character */
+ j = 0;
+ for (i = 0; i < 7; i++) {
+ j += c_grid[rows - 1][i];
+ }
+ c_grid[rows - 1][7] = j % 49;
+
+ encodeInfo.append("Codewords: ");
+ /* Transfer data to symbol character array (w grid) */
+ for (i = 0; i < rows; i++) {
+ for (j = 0; j < 4; j++) {
+ w_grid[i][j] = (c_grid[i][2 * j] * 49) + c_grid[i][(2 * j) + 1];
+ encodeInfo.append(Integer.toString(c_grid[i][2 * j])).append(" ").append(Integer.toString(c_grid[i][(2 * j) + 1])).append(" ");
+ }
+ }
+ encodeInfo.append("\n");
+
+ readable = new StringBuilder();
+ pattern = new String[rows];
+ rowCount = rows;
+ rowHeight = new int[rows];
+
+ encodeInfo.append("Symbol Characters: ");
+ for (i = 0; i < rows; i++) {
+ localpattern = new StringBuilder("11"); /* Start character */
+ for (j = 0; j < 4; j++) {
+ encodeInfo.append(Integer.toString(w_grid[i][j])).append(" ");
+ if (i != (rows - 1)) {
+ if (c49_table4[i].charAt(j) == 'E') {
+ /* Even Parity */
+ localpattern.append(c49_appxe_even[w_grid[i][j]]);
+ } else {
+ /* Odd Parity */
+ localpattern.append(c49_appxe_odd[w_grid[i][j]]);
+ }
+ } else {
+ /* Last row uses all even parity */
+ localpattern.append(c49_appxe_even[w_grid[i][j]]);
+ }
+ }
+ localpattern.append("4"); /* Stop character */
+
+ pattern[i] = localpattern.toString();
+ rowHeight[i] = 10;
+
+ }
+ encodeInfo.append("\n");
+ plotSymbol();
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock, yBlock;
+ int x, y, w, h;
+ boolean black;
+ getRectangles().clear();
+ y = 1;
+ h = 1;
+ for (yBlock = 0; yBlock < rowCount; yBlock++) {
+ black = true;
+ x = 15;
+ for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) {
+ if (black) {
+ black = false;
+ w = pattern[yBlock].charAt(xBlock) - '0';
+ if (rowHeight[yBlock] == -1) {
+ h = defaultHeight;
+ } else {
+ h = rowHeight[yBlock];
+ }
+ if (w != 0 && h != 0) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ }
+ if ((x + w) > symbolWidth) {
+ symbolWidth = x + w;
+ }
+ } else {
+ black = true;
+ }
+ x += (double) (pattern[yBlock].charAt(xBlock) - '0');
+ }
+ y += h;
+ if ((y + h) > symbolHeight) {
+ symbolHeight = y + h;
+ }
+ /* Add bars between rows */
+ if (yBlock != (rowCount - 1)) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, (symbolWidth - 15), 2);
+ getRectangles().add(rect);
+ }
+ }
+ Rectangle2D.Double top = new Rectangle2D.Double(0, 0, (symbolWidth + 15), 2);
+ getRectangles().add(top);
+ Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, (symbolWidth + 15), 2);
+ getRectangles().add(bottom);
+ symbolWidth += 30;
+ symbolHeight += 2;
+ mergeVerticalBlocks();
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Code93.java b/barcode/src/main/java/org/xbib/graphics/barcode/Code93.java
new file mode 100755
index 0000000..a286650
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Code93.java
@@ -0,0 +1,192 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Code 93 .
+ * Supports encoding of 7-bit ASCII text. Two check digits are added.
+ */
+public class Code93 extends Symbol {
+
+ /**
+ * Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $,
+ * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters).
+ */
+ private static final String[] CODE_93_CTRL = {
+ "bU", "aA", "aB", "aC", "aD", "aE", "aF", "aG", "aH", "aI",
+ "aJ", "aK", "aL", "aM", "aN", "aO", "aP", "aQ", "aR", "aS",
+ "aT", "aU", "aV", "aW", "aX", "aY", "aZ", "bA", "bB", "bC",
+ "bD", "bE", " ", "cA", "cB", "cC", "$", "%", "cF", "cG",
+ "cH", "cI", "cJ", "+", "cL", "-", ".", "/", "0", "1",
+ "2", "3", "4", "5", "6", "7", "8", "9", "cZ", "bF",
+ "bG", "bH", "bI", "bJ", "bV", "A", "B", "C", "D", "E",
+ "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
+ "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
+ "Z", "bK", "bL", "bM", "bN", "bO", "bW", "dA", "dB", "dC",
+ "dD", "dE", "dF", "dG", "dH", "dI", "dJ", "dK", "dL", "dM",
+ "dN", "dO", "dP", "dQ", "dR", "dS", "dT", "dU", "dV", "dW",
+ "dX", "dY", "dZ", "bP", "bQ", "bR", "bS", "bT"};
+
+ /**
+ * Mapping of control characters to pattern table index (NOTE: a = Ctrl $,
+ * b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters).
+ */
+ private static final char[] CODE_93_LOOKUP = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$',
+ '/', '+', '%', 'a', 'b', 'c', 'd'};
+
+ /**
+ * Code 93 pattern table.
+ */
+ private static final String[] CODE_93_TABLE = {
+ "131112", "111213", "111312", "111411", "121113",
+ "121212", "121311", "111114", "131211", "141111",
+ "211113", "211212", "211311", "221112", "221211",
+ "231111", "112113", "112212", "112311", "122112",
+ "132111", "111123", "111222", "111321", "121122",
+ "131121", "212112", "212211", "211122", "211221",
+ "221121", "222111", "112122", "112221", "122121",
+ "123111", "121131", "311112", "311211", "321111",
+ "112131", "113121", "211131", "121221", "312111",
+ "311121", "122211"};
+
+ /**
+ * Whether or not to show check digits in the human-readable text.
+ */
+ private boolean showCheckDigits = true;
+
+ /**
+ * Optional start/stop delimiter to be shown in the human-readable text.
+ */
+ private Character startStopDelimiter;
+
+ private static char[] toControlChars(String s) {
+ StringBuilder buffer = new StringBuilder();
+ char[] chars = s.toCharArray();
+ for (char asciiCode : chars) {
+ buffer.append(CODE_93_CTRL[asciiCode]);
+ }
+ return buffer.toString().toCharArray();
+ }
+
+ private static int calculateCheckDigitC(int[] values, int length) {
+ int c = 0;
+ int weight = 1;
+ for (int i = length - 1; i >= 0; i--) {
+ c += values[i] * weight;
+ weight++;
+ if (weight == 21) {
+ weight = 1;
+ }
+ }
+ c = c % 47;
+ return c;
+ }
+
+ private static int calculateCheckDigitK(int[] values, int length) {
+ int k = 0;
+ int weight = 1;
+ for (int i = length - 1; i >= 0; i--) {
+ k += values[i] * weight;
+ weight++;
+ if (weight == 16) {
+ weight = 1;
+ }
+ }
+ k = k % 47;
+ return k;
+ }
+
+ private static String toPattern(int[] values) {
+ StringBuilder buffer = new StringBuilder("111141");
+ for (int value : values) {
+ buffer.append(CODE_93_TABLE[value]);
+ }
+ buffer.append("1111411");
+ return buffer.toString();
+ }
+
+ /**
+ * Returns whether or not this symbol shows check digits in the human-readable text.
+ *
+ * @return whether or not this symbol shows check digits in the human-readable text
+ */
+ public boolean getShowCheckDigits() {
+ return showCheckDigits;
+ }
+
+ /**
+ * Sets whether or not to show check digits in the human-readable text (defaults to true
).
+ *
+ * @param showCheckDigits whether or not to show check digits in the human-readable text
+ */
+ public void setShowCheckDigits(boolean showCheckDigits) {
+ this.showCheckDigits = showCheckDigits;
+ }
+
+ /**
+ * Returns the optional start/stop delimiter to be shown in the human-readable text.
+ *
+ * @return the optional start/stop delimiter to be shown in the human-readable text
+ */
+ public Character getStartStopDelimiter() {
+ return startStopDelimiter;
+ }
+
+ /**
+ * Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to null
).
+ *
+ * @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable text
+ */
+ public void setStartStopDelimiter(Character startStopDelimiter) {
+ this.startStopDelimiter = startStopDelimiter;
+ }
+
+ @Override
+ public boolean encode() {
+
+ char[] controlChars = toControlChars(content);
+ int l = controlChars.length;
+
+ if (!content.matches("[\u0000-\u007F]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ int[] values = new int[controlChars.length + 2];
+ for (int i = 0; i < l; i++) {
+ values[i] = positionOf(controlChars[i], CODE_93_LOOKUP);
+ }
+
+ int c = calculateCheckDigitC(values, l);
+ values[l] = c;
+ l++;
+
+ int k = calculateCheckDigitK(values, l);
+ values[l] = k;
+
+ readable = new StringBuilder(content);
+ if (showCheckDigits) {
+ readable.append(CODE_93_LOOKUP[c]).append(CODE_93_LOOKUP[k]);
+ }
+ if (startStopDelimiter != null) {
+ readable = new StringBuilder(startStopDelimiter).append(readable).append(startStopDelimiter);
+ }
+
+ encodeInfo.append("Check Digit C: ").append(c).append("\n");
+ encodeInfo.append("Check Digit K: ").append(k).append("\n");
+ pattern = new String[]{toPattern(values)};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+
+ plotSymbol();
+
+ return true;
+ }
+
+ @Override
+ protected int[] getCodewords() {
+ return getPatternAsCodewords(6);
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java b/barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java
new file mode 100755
index 0000000..78cb569
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java
@@ -0,0 +1,1946 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+/**
+ * Implements Code One.
+ * Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1
+ * data. There are two types of Code One symbol - variable height symbols
+ * which are roughly square (versions A thought to H) and fixed-height
+ * versions (version S and T). Version S symbols can only encode numeric data.
+ * The width of version S and version T symbols is determined by the length
+ * of the input data.
+ */
+public class CodeOne extends Symbol {
+ private final int[] c40_shift = {
+ 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, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2,
+ 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
+ };
+
+ private final int[] c40_value = {
+ 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, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16,
+ 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26,
+ 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, 27, 28, 29, 30, 31
+ };
+
+ private final int[] text_shift = {
+ 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, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 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, 2, 2, 2, 2, 2,
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 3, 3, 3, 3
+ };
+
+ private final int[] text_value = {
+ 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, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16,
+ 17, 18, 19, 20, 21, 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, 22, 23, 24, 25, 26, 0, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31
+ };
+
+ private final int[] c1_height = {
+ 16, 22, 28, 40, 52, 70, 104, 148
+ };
+ private final int[] c1_width = {
+ 18, 22, 32, 42, 54, 76, 98, 134
+ };
+ private final int[] c1_data_length = {
+ 10, 19, 44, 91, 182, 370, 732, 1480
+ };
+ private final int[] c1_ecc_length = {
+ 10, 16, 26, 44, 70, 140, 280, 560
+ };
+ private final int[] c1_blocks = {
+ 1, 1, 1, 1, 1, 2, 4, 8
+ };
+ private final int[] c1_data_blocks = {
+ 10, 19, 44, 91, 182, 185, 183, 185
+ };
+ private final int[] c1_ecc_blocks = {
+ 10, 16, 26, 44, 70, 70, 70, 70
+ };
+ private final int[] c1_grid_width = {
+ 4, 5, 7, 9, 12, 17, 22, 30
+ };
+ private final int[] c1_grid_height = {
+ 5, 7, 10, 15, 21, 30, 46, 68
+ };
+ private int[] data = new int[1500];
+
+ ;
+ private byte[] source;
+ private int[][] datagrid = new int[136][120];
+ private boolean[][] outputGrid = new boolean[148][134];
+ private Version preferredVersion = Version.NONE;
+
+ /**
+ * Set symbol size by "version". Versions A to H are square symbols.
+ * This value may be ignored if the input data does not fit in the
+ * specified version. Version S and T are fixed height symbols.
+ *
+ * @param version Symbol version
+ */
+ public void setPreferredVersion(Version version) {
+ preferredVersion = version;
+ }
+
+ @Override
+ public boolean encode() {
+ int size = 1, i, j, data_blocks;
+ int row, col;
+ int sub_version = 0;
+ int codewords;
+ BigInteger elreg;
+ BigInteger codewordValue;
+ int[] ecc = new int[600];
+ int[] stream = new int[2100];
+ int block_width;
+ int length = content.length();
+ ReedSolomon rs = new ReedSolomon();
+ int data_length;
+ int data_cw, ecc_cw;
+ int[] sub_data = new int[190];
+ StringBuilder bin;
+
+ if (!content.matches("[\u0000-\u00FF]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ if (preferredVersion == Version.S) {
+ /* Version S */
+
+ encodeInfo.append("Version: S");
+
+ if (length > 18) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+?"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ sub_version = 3;
+ codewords = 12;
+ block_width = 6; /* Version S-30 */
+ if (length <= 12) {
+ sub_version = 2;
+ codewords = 8;
+ block_width = 4;
+ } /* Version S-20 */
+ if (length <= 6) {
+ sub_version = 1;
+ codewords = 4;
+ block_width = 2;
+ } /* Version S-10 */
+
+ elreg = new BigInteger(content);
+
+ for (i = 0; i < codewords; i++) {
+ codewordValue = elreg.shiftRight(5 * i);
+ codewordValue = codewordValue.or(BigInteger.valueOf(32));
+ data[codewords - i - 1] = codewordValue.intValue();
+ }
+
+ rs.init_gf(0x25);
+ rs.init_code(codewords, 1);
+ rs.encode(codewords, data);
+
+ for (i = 0; i < codewords; i++) {
+ stream[i] = data[i];
+ stream[i + codewords] = rs.getResult(codewords - i - 1);
+ }
+
+ for (i = 0; i < 136; i++) {
+ for (j = 0; j < 120; j++) {
+ datagrid[i][j] = '0';
+ }
+ }
+
+ i = 0;
+ for (row = 0; row < 2; row++) {
+ for (col = 0; col < block_width; col++) {
+ if ((stream[i] & 0x10) != 0) {
+ datagrid[row * 2][col * 5] = '1';
+ }
+ if ((stream[i] & 0x08) != 0) {
+ datagrid[row * 2][(col * 5) + 1] = '1';
+ }
+ if ((stream[i] & 0x04) != 0) {
+ datagrid[row * 2][(col * 5) + 2] = '1';
+ }
+ if ((stream[i] & 0x02) != 0) {
+ datagrid[(row * 2) + 1][col * 5] = '1';
+ }
+ if ((stream[i] & 0x01) != 0) {
+ datagrid[(row * 2) + 1][(col * 5) + 1] = '1';
+ }
+ if ((stream[i + 1] & 0x10) != 0) {
+ datagrid[row * 2][(col * 5) + 3] = '1';
+ }
+ if ((stream[i + 1] & 0x08) != 0) {
+ datagrid[row * 2][(col * 5) + 4] = '1';
+ }
+ if ((stream[i + 1] & 0x04) != 0) {
+ datagrid[(row * 2) + 1][(col * 5) + 2] = '1';
+ }
+ if ((stream[i + 1] & 0x02) != 0) {
+ datagrid[(row * 2) + 1][(col * 5) + 3] = '1';
+ }
+ if ((stream[i + 1] & 0x01) != 0) {
+ datagrid[(row * 2) + 1][(col * 5) + 4] = '1';
+ }
+ i += 2;
+ }
+ }
+
+ size = 9;
+ rowCount = 8;
+ symbolWidth = 10 * sub_version + 1;
+ }
+
+ if (preferredVersion == Version.T) {
+ /* Version T */
+
+ encodeInfo.append("Version: T\n");
+
+ for (i = 0; i < 40; i++) {
+ data[i] = 0;
+ }
+ data_length = encodeAsCode1Data();
+
+ if (data_length == 0) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ if (data_length > 38) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ size = 10;
+ sub_version = 3;
+ data_cw = 38;
+ ecc_cw = 22;
+ block_width = 12;
+ if (data_length <= 24) {
+ sub_version = 2;
+ data_cw = 24;
+ ecc_cw = 16;
+ block_width = 8;
+ }
+ if (data_length <= 10) {
+ sub_version = 1;
+ data_cw = 10;
+ ecc_cw = 10;
+ block_width = 4;
+ }
+
+ for (i = data_length; i < data_cw; i++) {
+ data[i] = 129; /* Pad */
+ }
+
+ /* Calculate error correction data */
+ rs.init_gf(0x12d);
+ rs.init_code(ecc_cw, 1);
+ rs.encode(data_cw, data);
+
+ /* "Stream" combines data and error correction data */
+ for (i = 0; i < data_cw; i++) {
+ stream[i] = data[i];
+ }
+ for (i = 0; i < ecc_cw; i++) {
+ stream[data_cw + i] = rs.getResult(ecc_cw - i - 1);
+ }
+
+ for (i = 0; i < 136; i++) {
+ for (j = 0; j < 120; j++) {
+ datagrid[i][j] = '0';
+ }
+ }
+
+ i = 0;
+ for (row = 0; row < 5; row++) {
+ for (col = 0; col < block_width; col++) {
+ if ((stream[i] & 0x80) != 0) {
+ datagrid[row * 2][col * 4] = '1';
+ }
+ if ((stream[i] & 0x40) != 0) {
+ datagrid[row * 2][(col * 4) + 1] = '1';
+ }
+ if ((stream[i] & 0x20) != 0) {
+ datagrid[row * 2][(col * 4) + 2] = '1';
+ }
+ if ((stream[i] & 0x10) != 0) {
+ datagrid[row * 2][(col * 4) + 3] = '1';
+ }
+ if ((stream[i] & 0x08) != 0) {
+ datagrid[(row * 2) + 1][col * 4] = '1';
+ }
+ if ((stream[i] & 0x04) != 0) {
+ datagrid[(row * 2) + 1][(col * 4) + 1] = '1';
+ }
+ if ((stream[i] & 0x02) != 0) {
+ datagrid[(row * 2) + 1][(col * 4) + 2] = '1';
+ }
+ if ((stream[i] & 0x01) != 0) {
+ datagrid[(row * 2) + 1][(col * 4) + 3] = '1';
+ }
+ i++;
+ }
+ }
+
+ rowCount = 16;
+ symbolWidth = (sub_version * 16) + 1;
+ }
+
+ if ((preferredVersion != Version.S) && (preferredVersion != Version.T)) {
+ /* Version A to H */
+ for (i = 0; i < 1500; i++) {
+ data[i] = 0;
+ }
+ data_length = encodeAsCode1Data();
+
+ if (data_length == 0) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ for (i = 7; i >= 0; i--) {
+ if (c1_data_length[i] >= data_length) {
+ size = i + 1;
+ }
+ }
+
+ if (getSize(preferredVersion) > size) {
+ size = getSize(preferredVersion);
+ }
+
+ encodeInfo.append("Version: ").append((char) ((size - 1) + 'A')).append("\n");
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < data_length; i++) {
+ encodeInfo.append(Integer.toString(data[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ for (i = data_length; i < c1_data_length[size - 1]; i++) {
+ data[i] = 129; /* Pad */
+ }
+
+ /* Calculate error correction data */
+ data_length = c1_data_length[size - 1];
+
+ data_blocks = c1_blocks[size - 1];
+
+ rs.init_gf(0x12d);
+ rs.init_code(c1_ecc_blocks[size - 1], 0);
+ for (i = 0; i < data_blocks; i++) {
+ for (j = 0; j < c1_data_blocks[size - 1]; j++) {
+
+ sub_data[j] = data[j * data_blocks + i];
+ }
+ rs.encode(c1_data_blocks[size - 1], sub_data);
+ for (j = 0; j < c1_ecc_blocks[size - 1]; j++) {
+ ecc[c1_ecc_length[size - 1] - (j * data_blocks + i) - 1]
+ = rs.getResult(j);
+ }
+ }
+
+ encodeInfo.append("ECC Codeword Count: ").append(c1_ecc_length[size - 1]).append("\n");
+
+ /* "Stream" combines data and error correction data */
+ for (i = 0; i < data_length; i++) {
+ stream[i] = data[i];
+ }
+ for (i = 0; i < c1_ecc_length[size - 1]; i++) {
+ stream[data_length + i] = ecc[i];
+ }
+
+ for (i = 0; i < 136; i++) {
+ for (j = 0; j < 120; j++) {
+ datagrid[i][j] = '0';
+ }
+ }
+
+ i = 0;
+ for (row = 0; row < c1_grid_height[size - 1]; row++) {
+ for (col = 0; col < c1_grid_width[size - 1]; col++) {
+ if ((stream[i] & 0x80) != 0) {
+ datagrid[row * 2][col * 4] = '1';
+ }
+ if ((stream[i] & 0x40) != 0) {
+ datagrid[row * 2][(col * 4) + 1] = '1';
+ }
+ if ((stream[i] & 0x20) != 0) {
+ datagrid[row * 2][(col * 4) + 2] = '1';
+ }
+ if ((stream[i] & 0x10) != 0) {
+ datagrid[row * 2][(col * 4) + 3] = '1';
+ }
+ if ((stream[i] & 0x08) != 0) {
+ datagrid[(row * 2) + 1][col * 4] = '1';
+ }
+ if ((stream[i] & 0x04) != 0) {
+ datagrid[(row * 2) + 1][(col * 4) + 1] = '1';
+ }
+ if ((stream[i] & 0x02) != 0) {
+ datagrid[(row * 2) + 1][(col * 4) + 2] = '1';
+ }
+ if ((stream[i] & 0x01) != 0) {
+ datagrid[(row * 2) + 1][(col * 4) + 3] = '1';
+ }
+ i++;
+ }
+ }
+
+ encodeInfo.append("Grid Size: ").append(c1_grid_width[size - 1]).append(" X ").append(c1_grid_height[size - 1]).append("\n");
+
+ rowCount = c1_height[size - 1];
+ symbolWidth = c1_width[size - 1];
+ }
+
+ for (i = 0; i < 148; i++) {
+ for (j = 0; j < 134; j++) {
+ outputGrid[i][j] = false;
+ }
+ }
+
+ switch (size) {
+ case 1:
+ /* Version A */
+ plotCentralFinder(6, 3, 1);
+ plotVerticalBar(4, 6, 1);
+ plotVerticalBar(12, 5, 0);
+ setGridModule(5, 12);
+ plotSpigot(0);
+ plotSpigot(15);
+ plotDataBlock(0, 0, 5, 4, 0, 0);
+ plotDataBlock(0, 4, 5, 12, 0, 2);
+ plotDataBlock(5, 0, 5, 12, 6, 0);
+ plotDataBlock(5, 12, 5, 4, 6, 2);
+ break;
+ case 2:
+ /* Version B */
+ plotCentralFinder(8, 4, 1);
+ plotVerticalBar(4, 8, 1);
+ plotVerticalBar(16, 7, 0);
+ setGridModule(7, 16);
+ plotSpigot(0);
+ plotSpigot(21);
+ plotDataBlock(0, 0, 7, 4, 0, 0);
+ plotDataBlock(0, 4, 7, 16, 0, 2);
+ plotDataBlock(7, 0, 7, 16, 8, 0);
+ plotDataBlock(7, 16, 7, 4, 8, 2);
+ break;
+ case 3:
+ /* Version C */
+ plotCentralFinder(11, 4, 2);
+ plotVerticalBar(4, 11, 1);
+ plotVerticalBar(26, 13, 1);
+ plotVerticalBar(4, 10, 0);
+ plotVerticalBar(26, 10, 0);
+ plotSpigot(0);
+ plotSpigot(27);
+ plotDataBlock(0, 0, 10, 4, 0, 0);
+ plotDataBlock(0, 4, 10, 20, 0, 2);
+ plotDataBlock(0, 24, 10, 4, 0, 4);
+ plotDataBlock(10, 0, 10, 4, 8, 0);
+ plotDataBlock(10, 4, 10, 20, 8, 2);
+ plotDataBlock(10, 24, 10, 4, 8, 4);
+ break;
+ case 4:
+ /* Version D */
+ plotCentralFinder(16, 5, 1);
+ plotVerticalBar(4, 16, 1);
+ plotVerticalBar(20, 16, 1);
+ plotVerticalBar(36, 16, 1);
+ plotVerticalBar(4, 15, 0);
+ plotVerticalBar(20, 15, 0);
+ plotVerticalBar(36, 15, 0);
+ plotSpigot(0);
+ plotSpigot(12);
+ plotSpigot(27);
+ plotSpigot(39);
+ plotDataBlock(0, 0, 15, 4, 0, 0);
+ plotDataBlock(0, 4, 15, 14, 0, 2);
+ plotDataBlock(0, 18, 15, 14, 0, 4);
+ plotDataBlock(0, 32, 15, 4, 0, 6);
+ plotDataBlock(15, 0, 15, 4, 10, 0);
+ plotDataBlock(15, 4, 15, 14, 10, 2);
+ plotDataBlock(15, 18, 15, 14, 10, 4);
+ plotDataBlock(15, 32, 15, 4, 10, 6);
+ break;
+ case 5:
+ /* Version E */
+ plotCentralFinder(22, 5, 2);
+ plotVerticalBar(4, 22, 1);
+ plotVerticalBar(26, 24, 1);
+ plotVerticalBar(48, 22, 1);
+ plotVerticalBar(4, 21, 0);
+ plotVerticalBar(26, 21, 0);
+ plotVerticalBar(48, 21, 0);
+ plotSpigot(0);
+ plotSpigot(12);
+ plotSpigot(39);
+ plotSpigot(51);
+ plotDataBlock(0, 0, 21, 4, 0, 0);
+ plotDataBlock(0, 4, 21, 20, 0, 2);
+ plotDataBlock(0, 24, 21, 20, 0, 4);
+ plotDataBlock(0, 44, 21, 4, 0, 6);
+ plotDataBlock(21, 0, 21, 4, 10, 0);
+ plotDataBlock(21, 4, 21, 20, 10, 2);
+ plotDataBlock(21, 24, 21, 20, 10, 4);
+ plotDataBlock(21, 44, 21, 4, 10, 6);
+ break;
+ case 6:
+ /* Version F */
+ plotCentralFinder(31, 5, 3);
+ plotVerticalBar(4, 31, 1);
+ plotVerticalBar(26, 35, 1);
+ plotVerticalBar(48, 31, 1);
+ plotVerticalBar(70, 35, 1);
+ plotVerticalBar(4, 30, 0);
+ plotVerticalBar(26, 30, 0);
+ plotVerticalBar(48, 30, 0);
+ plotVerticalBar(70, 30, 0);
+ plotSpigot(0);
+ plotSpigot(12);
+ plotSpigot(24);
+ plotSpigot(45);
+ plotSpigot(57);
+ plotSpigot(69);
+ plotDataBlock(0, 0, 30, 4, 0, 0);
+ plotDataBlock(0, 4, 30, 20, 0, 2);
+ plotDataBlock(0, 24, 30, 20, 0, 4);
+ plotDataBlock(0, 44, 30, 20, 0, 6);
+ plotDataBlock(0, 64, 30, 4, 0, 8);
+ plotDataBlock(30, 0, 30, 4, 10, 0);
+ plotDataBlock(30, 4, 30, 20, 10, 2);
+ plotDataBlock(30, 24, 30, 20, 10, 4);
+ plotDataBlock(30, 44, 30, 20, 10, 6);
+ plotDataBlock(30, 64, 30, 4, 10, 8);
+ break;
+ case 7:
+ /* Version G */
+ plotCentralFinder(47, 6, 2);
+ plotVerticalBar(6, 47, 1);
+ plotVerticalBar(27, 49, 1);
+ plotVerticalBar(48, 47, 1);
+ plotVerticalBar(69, 49, 1);
+ plotVerticalBar(90, 47, 1);
+ plotVerticalBar(6, 46, 0);
+ plotVerticalBar(27, 46, 0);
+ plotVerticalBar(48, 46, 0);
+ plotVerticalBar(69, 46, 0);
+ plotVerticalBar(90, 46, 0);
+ plotSpigot(0);
+ plotSpigot(12);
+ plotSpigot(24);
+ plotSpigot(36);
+ plotSpigot(67);
+ plotSpigot(79);
+ plotSpigot(91);
+ plotSpigot(103);
+ plotDataBlock(0, 0, 46, 6, 0, 0);
+ plotDataBlock(0, 6, 46, 19, 0, 2);
+ plotDataBlock(0, 25, 46, 19, 0, 4);
+ plotDataBlock(0, 44, 46, 19, 0, 6);
+ plotDataBlock(0, 63, 46, 19, 0, 8);
+ plotDataBlock(0, 82, 46, 6, 0, 10);
+ plotDataBlock(46, 0, 46, 6, 12, 0);
+ plotDataBlock(46, 6, 46, 19, 12, 2);
+ plotDataBlock(46, 25, 46, 19, 12, 4);
+ plotDataBlock(46, 44, 46, 19, 12, 6);
+ plotDataBlock(46, 63, 46, 19, 12, 8);
+ plotDataBlock(46, 82, 46, 6, 12, 10);
+ break;
+ case 8:
+ /* Version H */
+ plotCentralFinder(69, 6, 3);
+ plotVerticalBar(6, 69, 1);
+ plotVerticalBar(26, 73, 1);
+ plotVerticalBar(46, 69, 1);
+ plotVerticalBar(66, 73, 1);
+ plotVerticalBar(86, 69, 1);
+ plotVerticalBar(106, 73, 1);
+ plotVerticalBar(126, 69, 1);
+ plotVerticalBar(6, 68, 0);
+ plotVerticalBar(26, 68, 0);
+ plotVerticalBar(46, 68, 0);
+ plotVerticalBar(66, 68, 0);
+ plotVerticalBar(86, 68, 0);
+ plotVerticalBar(106, 68, 0);
+ plotVerticalBar(126, 68, 0);
+ plotSpigot(0);
+ plotSpigot(12);
+ plotSpigot(24);
+ plotSpigot(36);
+ plotSpigot(48);
+ plotSpigot(60);
+ plotSpigot(87);
+ plotSpigot(99);
+ plotSpigot(111);
+ plotSpigot(123);
+ plotSpigot(135);
+ plotSpigot(147);
+ plotDataBlock(0, 0, 68, 6, 0, 0);
+ plotDataBlock(0, 6, 68, 18, 0, 2);
+ plotDataBlock(0, 24, 68, 18, 0, 4);
+ plotDataBlock(0, 42, 68, 18, 0, 6);
+ plotDataBlock(0, 60, 68, 18, 0, 8);
+ plotDataBlock(0, 78, 68, 18, 0, 10);
+ plotDataBlock(0, 96, 68, 18, 0, 12);
+ plotDataBlock(0, 114, 68, 6, 0, 14);
+ plotDataBlock(68, 0, 68, 6, 12, 0);
+ plotDataBlock(68, 6, 68, 18, 12, 2);
+ plotDataBlock(68, 24, 68, 18, 12, 4);
+ plotDataBlock(68, 42, 68, 18, 12, 6);
+ plotDataBlock(68, 60, 68, 18, 12, 8);
+ plotDataBlock(68, 78, 68, 18, 12, 10);
+ plotDataBlock(68, 96, 68, 18, 12, 12);
+ plotDataBlock(68, 114, 68, 6, 12, 14);
+ break;
+ case 9:
+ /* Version S */
+ plotHorizontalBar(5, 1);
+ plotHorizontalBar(7, 1);
+ setGridModule(6, 0);
+ setGridModule(6, symbolWidth - 1);
+ resetGridModule(7, 1);
+ resetGridModule(7, symbolWidth - 2);
+ switch (sub_version) {
+ case 1:
+ /* Version S-10 */
+ setGridModule(0, 5);
+ plotDataBlock(0, 0, 4, 5, 0, 0);
+ plotDataBlock(0, 5, 4, 5, 0, 1);
+ break;
+ case 2:
+ /* Version S-20 */
+ setGridModule(0, 10);
+ setGridModule(4, 10);
+ plotDataBlock(0, 0, 4, 10, 0, 0);
+ plotDataBlock(0, 10, 4, 10, 0, 1);
+ break;
+ case 3:
+ /* Version S-30 */
+ setGridModule(0, 15);
+ setGridModule(4, 15);
+ setGridModule(6, 15);
+ plotDataBlock(0, 0, 4, 15, 0, 0);
+ plotDataBlock(0, 15, 4, 15, 0, 1);
+ break;
+ }
+ break;
+ case 10:
+ /* Version T */
+ plotHorizontalBar(11, 1);
+ plotHorizontalBar(13, 1);
+ plotHorizontalBar(15, 1);
+ setGridModule(12, 0);
+ setGridModule(12, symbolWidth - 1);
+ setGridModule(14, 0);
+ setGridModule(14, symbolWidth - 1);
+ resetGridModule(13, 1);
+ resetGridModule(13, symbolWidth - 2);
+ resetGridModule(15, 1);
+ resetGridModule(15, symbolWidth - 2);
+ switch (sub_version) {
+ case 1:
+ /* Version T-16 */
+ setGridModule(0, 8);
+ setGridModule(10, 8);
+ plotDataBlock(0, 0, 10, 8, 0, 0);
+ plotDataBlock(0, 8, 10, 8, 0, 1);
+ break;
+ case 2:
+ /* Version T-32 */
+ setGridModule(0, 16);
+ setGridModule(10, 16);
+ setGridModule(12, 16);
+ plotDataBlock(0, 0, 10, 16, 0, 0);
+ plotDataBlock(0, 16, 10, 16, 0, 1);
+ break;
+ case 3:
+ /* Verion T-48 */
+ setGridModule(0, 24);
+ setGridModule(10, 24);
+ setGridModule(12, 24);
+ setGridModule(14, 24);
+ plotDataBlock(0, 0, 10, 24, 0, 0);
+ plotDataBlock(0, 24, 10, 24, 0, 1);
+ break;
+ }
+ break;
+ }
+
+ readable = new StringBuilder();
+ pattern = new String[rowCount];
+ rowHeight = new int[rowCount];
+ for (i = 0; i < rowCount; i++) {
+ bin = new StringBuilder();
+ for (j = 0; j < symbolWidth; j++) {
+ if (outputGrid[i][j]) {
+ bin.append("1");
+ } else {
+ bin.append("0");
+ }
+ }
+ pattern[i] = bin2pat(bin.toString());
+ rowHeight[i] = 1;
+ }
+ plotSymbol();
+ return true;
+ }
+
+ private int encodeAsCode1Data() {
+ c1Mode current_mode, next_mode;
+ boolean latch;
+ boolean done;
+ int sourcePoint, targetPoint, i, j;
+ int c40_p;
+ int text_p;
+ int edi_p;
+ int byte_start = 0;
+ int[] c40_buffer = new int[6];
+ int[] text_buffer = new int[6];
+ int[] edi_buffer = new int[6];
+ StringBuilder decimal_binary = new StringBuilder();
+ int length = content.length();
+ int shift_set, value;
+ int data_left, decimal_count;
+ int sub_value;
+ int bits_left_in_byte, target_count;
+ boolean isTwoDigits;
+
+ try {
+ source = content.getBytes("ISO8859_1");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Invalid character in input data");
+ return 0;
+ }
+
+ sourcePoint = 0;
+ targetPoint = 0;
+ c40_p = 0;
+ text_p = 0;
+ edi_p = 0;
+
+ if (inputDataType == DataType.GS1) {
+ data[targetPoint] = 232;
+ targetPoint++;
+ } /* FNC1 */
+
+ /* Step A */
+ current_mode = c1Mode.C1_ASCII;
+ next_mode = c1Mode.C1_ASCII;
+
+ do {
+ if (current_mode != next_mode) {
+ /* Change mode */
+ switch (next_mode) {
+ case C1_C40:
+ data[targetPoint] = 230;
+ targetPoint++;
+ break;
+ case C1_TEXT:
+ data[targetPoint] = 239;
+ targetPoint++;
+ break;
+ case C1_EDI:
+ data[targetPoint] = 238;
+ targetPoint++;
+ break;
+ case C1_BYTE:
+ data[targetPoint] = 231;
+ targetPoint++;
+ break;
+ }
+ }
+
+ if ((current_mode != c1Mode.C1_BYTE) && (next_mode == c1Mode.C1_BYTE)) {
+ byte_start = targetPoint;
+ }
+ current_mode = next_mode;
+
+ if (current_mode == c1Mode.C1_ASCII) { /* Step B - ASCII encodation */
+ next_mode = c1Mode.C1_ASCII;
+
+ if ((length - sourcePoint) >= 21) { /* Step B1 */
+ j = 0;
+
+ for (i = 0; i < 21; i++) {
+ if ((source[sourcePoint + i] >= '0') && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 21) {
+ next_mode = c1Mode.C1_DECIMAL;
+ decimal_binary.append("1111");
+ }
+ }
+
+ if ((next_mode == c1Mode.C1_ASCII) && ((length - sourcePoint) >= 13)) { /* Step B2 */
+ j = 0;
+
+ for (i = 0; i < 13; i++) {
+ if ((source[sourcePoint + i] >= '0') && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 13) {
+ latch = false;
+ for (i = sourcePoint + 13; i < length; i++) {
+ if (!((source[i] >= '0') &&
+ (source[i] <= '9'))) {
+ latch = true;
+ }
+ }
+
+ if (!(latch)) {
+ next_mode = c1Mode.C1_DECIMAL;
+ decimal_binary.append("1111");
+ }
+ }
+ }
+
+ if (next_mode == c1Mode.C1_ASCII) { /* Step B3 */
+ isTwoDigits = false;
+ if ((sourcePoint + 1) != length) {
+ if ((source[sourcePoint] >= '0') && (source[sourcePoint] <= '9')) {
+ if ((source[sourcePoint + 1] >= '0') && (source[sourcePoint + 1] <= '9')) {
+ // remaining data consists of two numeric digits
+ data[targetPoint] = (10 * (source[sourcePoint] - '0'))
+ + (source[sourcePoint + 1] - '0') + 130;
+ targetPoint++;
+ sourcePoint += 2;
+ isTwoDigits = true;
+ }
+ }
+ }
+
+ if (!(isTwoDigits)) {
+ if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) {
+ if ((length - sourcePoint) >= 15) { /* Step B4 */
+ j = 0;
+
+ for (i = 0; i < 15; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 15) {
+ data[targetPoint] = 236; /* FNC1 and change to Decimal */
+ targetPoint++;
+ sourcePoint++;
+ next_mode = c1Mode.C1_DECIMAL;
+ }
+ }
+
+ if ((length - sourcePoint) >= 7) { /* Step B5 */
+ j = 0;
+
+ for (i = 0; i < 7; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 7) {
+ latch = false;
+ for (i = sourcePoint + 7; i < length; i++) {
+ if (!((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9'))) {
+ latch = true;
+ }
+ }
+
+ if (!(latch)) {
+ data[targetPoint] = 236; /* FNC1 and change to Decimal */
+ targetPoint++;
+ sourcePoint++;
+ next_mode = c1Mode.C1_DECIMAL;
+ }
+ }
+ }
+ }
+
+ if (next_mode == c1Mode.C1_ASCII) {
+
+ /* Step B6 */
+ next_mode = lookAheadTest(length, sourcePoint, current_mode);
+
+ if (next_mode == c1Mode.C1_ASCII) {
+ if (source[sourcePoint] > 127) {
+ /* Step B7 */
+ data[targetPoint] = 235;
+ targetPoint++; /* FNC4 */
+ data[targetPoint] = (source[sourcePoint] - 128) + 1;
+ targetPoint++;
+ sourcePoint++;
+ } else {
+ /* Step B8 */
+ if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) {
+ data[targetPoint] = 232;
+ targetPoint++;
+ sourcePoint++; /* FNC1 */
+ } else {
+ data[targetPoint] = source[sourcePoint] + 1;
+ targetPoint++;
+ sourcePoint++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (current_mode == c1Mode.C1_C40) { /* Step C - C40 encodation */
+ done = false;
+ next_mode = c1Mode.C1_C40;
+ if (c40_p == 0) {
+ if ((length - sourcePoint) >= 12) {
+ j = 0;
+
+ for (i = 0; i < 12; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 12) {
+ next_mode = c1Mode.C1_ASCII;
+ done = true;
+ }
+ }
+
+ if ((length - sourcePoint) >= 8) {
+ j = 0;
+
+ for (i = 0; i < 8; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if ((length - sourcePoint) == 8) {
+ latch = true;
+ } else {
+ latch = true;
+ for (j = sourcePoint + 8; j < length; j++) {
+ if ((source[j] <= '0') || (source[j] >= '9')) {
+ latch = false;
+ }
+ }
+ }
+
+ if ((j == 8) && latch) {
+ next_mode = c1Mode.C1_ASCII;
+ done = true;
+ }
+ }
+
+ if (!(done)) {
+ next_mode = lookAheadTest(length, sourcePoint, current_mode);
+ }
+ }
+
+ if (next_mode != c1Mode.C1_C40) {
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ } else {
+ if (source[sourcePoint] > 127) {
+ c40_buffer[c40_p] = 1;
+ c40_p++;
+ c40_buffer[c40_p] = 30;
+ c40_p++; /* Upper Shift */
+ shift_set = c40_shift[source[sourcePoint] - 128];
+ value = c40_value[source[sourcePoint] - 128];
+ } else {
+ shift_set = c40_shift[source[sourcePoint]];
+ value = c40_value[source[sourcePoint]];
+ }
+
+ if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) {
+ shift_set = 2;
+ value = 27; /* FNC1 */
+ }
+
+ if (shift_set != 0) {
+ c40_buffer[c40_p] = shift_set - 1;
+ c40_p++;
+ }
+ c40_buffer[c40_p] = value;
+ c40_p++;
+
+ if (c40_p >= 3) {
+ int iv;
+
+ iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1])
+ + (c40_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+
+ c40_buffer[0] = c40_buffer[3];
+ c40_buffer[1] = c40_buffer[4];
+ c40_buffer[2] = c40_buffer[5];
+ c40_buffer[3] = 0;
+ c40_buffer[4] = 0;
+ c40_buffer[5] = 0;
+ c40_p -= 3;
+ }
+ sourcePoint++;
+ }
+ }
+
+ if (current_mode == c1Mode.C1_TEXT) { /* Step D - Text encodation */
+ done = false;
+ next_mode = c1Mode.C1_TEXT;
+ if (text_p == 0) {
+ if ((length - sourcePoint) >= 12) {
+ j = 0;
+
+ for (i = 0; i < 12; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 12) {
+ next_mode = c1Mode.C1_ASCII;
+ done = true;
+ }
+ }
+
+ if ((length - sourcePoint) >= 8) {
+ j = 0;
+
+ for (i = 0; i < 8; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if ((length - sourcePoint) == 8) {
+ latch = true;
+ } else {
+ latch = true;
+ for (j = sourcePoint + 8; j < length; j++) {
+ if ((source[j] <= '0') || (source[j] >= '9')) {
+ latch = false;
+ }
+ }
+ }
+
+ if ((j == 8) && latch) {
+ next_mode = c1Mode.C1_ASCII;
+ done = true;
+ }
+ }
+
+ if (!(done)) {
+ next_mode = lookAheadTest(length, sourcePoint, current_mode);
+ }
+ }
+
+ if (next_mode != c1Mode.C1_TEXT) {
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ } else {
+ if (source[sourcePoint] > 127) {
+ text_buffer[text_p] = 1;
+ text_p++;
+ text_buffer[text_p] = 30;
+ text_p++; /* Upper Shift */
+ shift_set = text_shift[source[sourcePoint] - 128];
+ value = text_value[source[sourcePoint] - 128];
+ } else {
+ shift_set = text_shift[source[sourcePoint]];
+ value = text_value[source[sourcePoint]];
+ }
+
+ if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) {
+ shift_set = 2;
+ value = 27; /* FNC1 */
+ }
+
+ if (shift_set != 0) {
+ text_buffer[text_p] = shift_set - 1;
+ text_p++;
+ }
+ text_buffer[text_p] = value;
+ text_p++;
+
+ if (text_p >= 3) {
+ int iv;
+
+ iv = (1600 * text_buffer[0]) + (40 * text_buffer[1])
+ + (text_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+
+ text_buffer[0] = text_buffer[3];
+ text_buffer[1] = text_buffer[4];
+ text_buffer[2] = text_buffer[5];
+ text_buffer[3] = 0;
+ text_buffer[4] = 0;
+ text_buffer[5] = 0;
+ text_p -= 3;
+ }
+ sourcePoint++;
+ }
+ }
+
+ if (current_mode == c1Mode.C1_EDI) { /* Step E - EDI Encodation */
+
+ value = 0;
+ next_mode = c1Mode.C1_EDI;
+ if (edi_p == 0) {
+ if ((length - sourcePoint) >= 12) {
+ j = 0;
+
+ for (i = 0; i < 12; i++) {
+ if ((source[sourcePoint + i] >= '0')
+ && (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if (j == 12) {
+ next_mode = c1Mode.C1_ASCII;
+ }
+ }
+
+ if ((length - sourcePoint) >= 8) {
+ j = 0;
+
+ for (i = 0; i < 8; i++) {
+ if ((source[sourcePoint + i] >= '0') &&
+ (source[sourcePoint + i] <= '9')) {
+ j++;
+ }
+ }
+
+ if ((length - sourcePoint) == 8) {
+ latch = true;
+ } else {
+ latch = true;
+ for (j = sourcePoint + 8; j < length; j++) {
+ if ((source[j] <= '0') || (source[j] >= '9')) {
+ latch = false;
+ }
+ }
+ }
+
+ if ((j == 8) && latch) {
+ next_mode = c1Mode.C1_ASCII;
+ }
+ }
+
+ if (!((isEdiEncodable(source[sourcePoint])
+ && isEdiEncodable(source[sourcePoint + 1]))
+ && isEdiEncodable(source[sourcePoint + 2]))) {
+ next_mode = c1Mode.C1_ASCII;
+ }
+ }
+
+ if (next_mode != c1Mode.C1_EDI) {
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ } else {
+ if (source[sourcePoint] == 13) {
+ value = 0;
+ }
+ if (source[sourcePoint] == '*') {
+ value = 1;
+ }
+ if (source[sourcePoint] == '>') {
+ value = 2;
+ }
+ if (source[sourcePoint] == ' ') {
+ value = 3;
+ }
+ if ((source[sourcePoint] >= '0') && (source[sourcePoint] <= '9')) {
+ value = source[sourcePoint] - '0' + 4;
+ }
+ if ((source[sourcePoint] >= 'A') && (source[sourcePoint] <= 'Z')) {
+ value = source[sourcePoint] - 'A' + 14;
+ }
+
+ edi_buffer[edi_p] = value;
+ edi_p++;
+
+ if (edi_p >= 3) {
+ int iv;
+
+ iv = (1600 * edi_buffer[0]) + (40 * edi_buffer[1])
+ + (edi_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+
+ edi_buffer[0] = edi_buffer[3];
+ edi_buffer[1] = edi_buffer[4];
+ edi_buffer[2] = edi_buffer[5];
+ edi_buffer[3] = 0;
+ edi_buffer[4] = 0;
+ edi_buffer[5] = 0;
+ edi_p -= 3;
+ }
+ sourcePoint++;
+ }
+ }
+
+ if (current_mode == c1Mode.C1_DECIMAL) { /* Step F - Decimal encodation */
+
+ next_mode = c1Mode.C1_DECIMAL;
+
+ data_left = length - sourcePoint;
+ decimal_count = 0;
+
+ if (data_left >= 1) {
+ if ((source[sourcePoint] >= '0') && (source[sourcePoint] <= '9')) {
+ decimal_count = 1;
+ }
+ }
+ if (data_left >= 2) {
+ if ((decimal_count == 1) && ((source[sourcePoint + 1] >= '0')
+ && (source[sourcePoint + 1] <= '9'))) {
+ decimal_count = 2;
+ }
+ }
+ if (data_left >= 3) {
+ if ((decimal_count == 2) && ((source[sourcePoint + 2] >= '0')
+ && (source[sourcePoint + 2] <= '9'))) {
+ decimal_count = 3;
+ }
+ }
+
+ if (decimal_count != 3) {
+
+ /* Finish Decimal mode and go back to ASCII */
+
+ decimal_binary.append("111111"); /* Unlatch */
+
+ target_count = 3;
+ if (decimal_binary.length() <= 16) {
+ target_count = 2;
+ }
+ if (decimal_binary.length() <= 8) {
+ target_count = 1;
+ }
+ bits_left_in_byte = (8 * target_count) - decimal_binary.length();
+ if (bits_left_in_byte == 8) {
+ bits_left_in_byte = 0;
+ }
+
+ if (bits_left_in_byte == 2) {
+ decimal_binary.append("01");
+ }
+
+ if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) {
+ if (decimal_count >= 1) {
+ sub_value = source[sourcePoint] - '0' + 1;
+
+ for (i = 0x08; i > 0; i = i >> 1) {
+ if ((sub_value & i) != 0) {
+ decimal_binary.append("1");
+ } else {
+ decimal_binary.append("0");
+ }
+ }
+ sourcePoint++;
+ } else {
+ decimal_binary.append("1111");
+ }
+ }
+
+ if (bits_left_in_byte == 6) {
+ decimal_binary.append("01");
+ }
+
+ /* Binary buffer is full - transfer to data */
+ if (target_count >= 1) {
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+ }
+ targetPoint++;
+ }
+ if (target_count >= 2) {
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(8 + i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+
+ }
+ targetPoint++;
+ }
+ if (target_count == 3) {
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(16 + i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+
+ }
+ targetPoint++;
+ }
+
+ next_mode = c1Mode.C1_ASCII;
+ } else {
+ /* There are three digits - convert the value to binary */
+ value = (100 * (source[sourcePoint] - '0'))
+ + (10 * (source[sourcePoint + 1] - '0'))
+ + (source[sourcePoint + 2] - '0') + 1;
+
+ for (i = 0x200; i > 0; i = i >> 1) {
+ if ((value & i) != 0) {
+ decimal_binary.append("1");
+ } else {
+ decimal_binary.append("0");
+ }
+ }
+ sourcePoint += 3;
+ }
+
+ if (decimal_binary.length() >= 24) {
+ /* Binary buffer is full - transfer to data */
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+
+ if (decimal_binary.charAt(8 + i) == '1') {
+ data[targetPoint + 1] += 128 >> i;
+ }
+
+ if (decimal_binary.charAt(16 + i) == '1') {
+ data[targetPoint + 2] += 128 >> i;
+ }
+
+ }
+ targetPoint += 3;
+
+ if (decimal_binary.length() > 24) {
+ decimal_binary = new StringBuilder(decimal_binary.substring(24));
+ }
+ }
+ }
+
+ if (current_mode == c1Mode.C1_BYTE) {
+ next_mode = c1Mode.C1_BYTE;
+
+ if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) {
+ next_mode = c1Mode.C1_ASCII;
+ } else {
+ if (source[sourcePoint] <= 127) {
+ next_mode = lookAheadTest(length, sourcePoint, current_mode);
+ }
+ }
+
+ if (next_mode != c1Mode.C1_BYTE) {
+ /* Insert byte field length */
+ if ((targetPoint - byte_start) <= 249) {
+ for (i = targetPoint; i >= byte_start; i--) {
+ data[i + 1] = data[i];
+ }
+ data[byte_start] = (targetPoint - byte_start);
+ targetPoint++;
+ } else {
+ for (i = targetPoint; i >= byte_start; i--) {
+ data[i + 2] = data[i];
+ }
+ data[byte_start] = 249 + ((targetPoint - byte_start) / 250);
+ data[byte_start + 1] = ((targetPoint - byte_start) % 250);
+ targetPoint += 2;
+ }
+ } else {
+ data[targetPoint] = source[sourcePoint];
+ targetPoint++;
+ sourcePoint++;
+ }
+ }
+
+ if (targetPoint > 1480) {
+ /* Data is too large for symbol */
+ errorMsg.append("Input data too long");
+ return 0;
+ }
+ } while (sourcePoint < length);
+
+ /* Empty buffers */
+ if (c40_p == 2) {
+ int iv;
+
+ c40_buffer[2] = 1;
+ iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1])
+ + (c40_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ }
+ if (c40_p == 1) {
+ int iv;
+
+ c40_buffer[1] = 1;
+ c40_buffer[2] = 31; /* Pad */
+ iv = (1600 * c40_buffer[0]) + (40 * c40_buffer[1])
+ + (c40_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ }
+ if (text_p == 2) {
+ int iv;
+
+ text_buffer[2] = 1;
+ iv = (1600 * text_buffer[0]) + (40 * text_buffer[1])
+ + (text_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ }
+ if (text_p == 1) {
+ int iv;
+
+ text_buffer[1] = 1;
+ text_buffer[2] = 31; /* Pad */
+ iv = (1600 * text_buffer[0]) + (40 * text_buffer[1])
+ + (text_buffer[2]) + 1;
+ data[targetPoint] = iv / 256;
+ targetPoint++;
+ data[targetPoint] = iv % 256;
+ targetPoint++;
+ data[targetPoint] = 255;
+ targetPoint++; /* Unlatch */
+ }
+
+ if (current_mode == c1Mode.C1_DECIMAL) {
+ /* Finish Decimal mode and go back to ASCII */
+
+ decimal_binary.append("111111"); /* Unlatch */
+
+ target_count = 3;
+ if (decimal_binary.length() <= 16) {
+ target_count = 2;
+ }
+ if (decimal_binary.length() <= 8) {
+ target_count = 1;
+ }
+ bits_left_in_byte = (8 * target_count) - decimal_binary.length();
+ if (bits_left_in_byte == 8) {
+ bits_left_in_byte = 0;
+ }
+
+ if (bits_left_in_byte == 2) {
+ decimal_binary.append("01");
+ }
+
+ if ((bits_left_in_byte == 4) || (bits_left_in_byte == 6)) {
+ decimal_binary.append("1111");
+ }
+
+ if (bits_left_in_byte == 6) {
+ decimal_binary.append("01");
+ }
+
+ /* Binary buffer is full - transfer to data */
+ if (target_count >= 1) {
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+ }
+ targetPoint++;
+ }
+ if (target_count >= 2) {
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(8 + i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+ }
+ targetPoint++;
+ }
+ if (target_count == 3) {
+ for (i = 0; i < 8; i++) {
+ if (decimal_binary.charAt(16 + i) == '1') {
+ data[targetPoint] += 128 >> i;
+ }
+ }
+ targetPoint++;
+ }
+ }
+
+ if (current_mode == c1Mode.C1_BYTE) {
+ /* Insert byte field length */
+ if ((targetPoint - byte_start) <= 249) {
+ for (i = targetPoint; i >= byte_start; i--) {
+ data[i + 1] = data[i];
+ }
+ data[byte_start] = (targetPoint - byte_start);
+ targetPoint++;
+ } else {
+ for (i = targetPoint; i >= byte_start; i--) {
+ data[i + 2] = data[i];
+ }
+ data[byte_start] = 249 + ((targetPoint - byte_start) / 250);
+ data[byte_start + 1] = ((targetPoint - byte_start) % 250);
+ targetPoint += 2;
+ }
+ }
+
+ /* Re-check length of data */
+ if (targetPoint > 1480) {
+ /* Data is too large for symbol */
+ errorMsg.append("Input data too long");
+ return 0;
+ }
+
+ return targetPoint;
+ }
+
+ private c1Mode lookAheadTest(int sourcelen, int position,
+ c1Mode current_mode) {
+ double ascii_count, c40_count, text_count, edi_count, byte_count;
+ int reduced_char;
+ int done, best_count, sp;
+ c1Mode best_scheme;
+
+ /* Step J */
+ if (current_mode == c1Mode.C1_ASCII) {
+ ascii_count = 0.0;
+ c40_count = 1.0;
+ text_count = 1.0;
+ edi_count = 1.0;
+ byte_count = 2.0;
+ } else {
+ ascii_count = 1.0;
+ c40_count = 2.0;
+ text_count = 2.0;
+ edi_count = 2.0;
+ byte_count = 3.0;
+ }
+
+ switch (current_mode) {
+ case C1_C40:
+ c40_count = 0.0;
+ break;
+ case C1_TEXT:
+ text_count = 0.0;
+ break;
+ case C1_BYTE:
+ byte_count = 0.0;
+ break;
+ case C1_EDI:
+ edi_count = 0.0;
+ break;
+ }
+
+ for (sp = position;
+ (sp < sourcelen) && (sp <= (position + 8)); sp++) {
+
+ if (source[sp] <= 127) {
+ reduced_char = source[sp];
+ } else {
+ reduced_char = source[sp] - 127;
+ }
+
+ /* Step L */
+ if ((source[sp] >= '0') && (source[sp] <= '9')) {
+ ascii_count += 0.5;
+ } else {
+ ascii_count = roundUpToNextInteger(ascii_count);
+ if (source[sp] > 127) {
+ ascii_count += 2.0;
+ } else {
+ ascii_count += 1.0;
+ }
+ }
+
+ /* Step M */
+ done = 0;
+ if (reduced_char == ' ') {
+ c40_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if ((reduced_char >= '0') && (reduced_char <= '9')) {
+ c40_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if ((reduced_char >= 'A') && (reduced_char <= 'Z')) {
+ c40_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if (source[sp] > 127) {
+ c40_count += (4.0 / 3.0);
+ }
+ if (done == 0) {
+ c40_count += (4.0 / 3.0);
+ }
+
+ /* Step N */
+ done = 0;
+ if (reduced_char == ' ') {
+ text_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if ((reduced_char >= '0') && (reduced_char <= '9')) {
+ text_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if ((reduced_char >= 'a') && (reduced_char <= 'z')) {
+ text_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if (source[sp] > 127) {
+ text_count += (4.0 / 3.0);
+ }
+ if (done == 0) {
+ text_count += (4.0 / 3.0);
+ }
+
+ /* Step O */
+ done = 0;
+ if (source[sp] == 13) {
+ edi_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if (source[sp] == '*') {
+ edi_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if (source[sp] == '>') {
+ edi_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if (source[sp] == ' ') {
+ edi_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if ((source[sp] >= '0') && (source[sp] <= '9')) {
+ edi_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if ((source[sp] >= 'A') && (source[sp] <= 'Z')) {
+ edi_count += (2.0 / 3.0);
+ done = 1;
+ }
+ if (source[sp] > 127) {
+ edi_count += (13.0 / 3.0);
+ } else {
+ if (done == 0) {
+ edi_count += (10.0 / 3.0);
+ }
+ }
+
+ /* Step P */
+ if ((inputDataType == DataType.GS1) && (source[sp] == '[')) {
+ byte_count += 3.0;
+ } else {
+ byte_count += 1.0;
+ }
+
+ }
+
+ ascii_count = roundUpToNextInteger(ascii_count);
+ c40_count = roundUpToNextInteger(c40_count);
+ text_count = roundUpToNextInteger(text_count);
+ edi_count = roundUpToNextInteger(edi_count);
+ byte_count = roundUpToNextInteger(byte_count);
+ best_scheme = c1Mode.C1_ASCII;
+
+ if (sp == sourcelen) {
+ /* Step K */
+ best_count = (int) edi_count;
+
+ if (text_count <= best_count) {
+ best_count = (int) text_count;
+ best_scheme = c1Mode.C1_TEXT;
+ }
+
+ if (c40_count <= best_count) {
+ best_count = (int) c40_count;
+ best_scheme = c1Mode.C1_C40;
+ }
+
+ if (ascii_count <= best_count) {
+ best_count = (int) ascii_count;
+ best_scheme = c1Mode.C1_ASCII;
+ }
+
+ if (byte_count <= best_count) {
+ best_scheme = c1Mode.C1_BYTE;
+ }
+ } else {
+ /* Step Q */
+
+ if (((edi_count + 1.0 <= ascii_count)
+ && (edi_count + 1.0 <= c40_count))
+ && ((edi_count + 1.0 <= byte_count)
+ && (edi_count + 1.0 <= text_count))) {
+ best_scheme = c1Mode.C1_EDI;
+ }
+
+ if ((c40_count + 1.0 <= ascii_count)
+ && (c40_count + 1.0 <= text_count)) {
+
+ if (c40_count < edi_count) {
+ best_scheme = c1Mode.C1_C40;
+ } else {
+ if (c40_count == edi_count) {
+ if (preferEdi(sourcelen, position)) {
+ best_scheme = c1Mode.C1_EDI;
+ } else {
+ best_scheme = c1Mode.C1_C40;
+ }
+ }
+ }
+ }
+
+ if (((text_count + 1.0 <= ascii_count)
+ && (text_count + 1.0 <= c40_count))
+ && ((text_count + 1.0 <= byte_count)
+ && (text_count + 1.0 <= edi_count))) {
+ best_scheme = c1Mode.C1_TEXT;
+ }
+
+ if (((ascii_count + 1.0 <= byte_count)
+ && (ascii_count + 1.0 <= c40_count))
+ && ((ascii_count + 1.0 <= text_count)
+ && (ascii_count + 1.0 <= edi_count))) {
+ best_scheme = c1Mode.C1_ASCII;
+ }
+
+ if (((byte_count + 1.0 <= ascii_count)
+ && (byte_count + 1.0 <= c40_count))
+ && ((byte_count + 1.0 <= text_count)
+ && (byte_count + 1.0 <= edi_count))) {
+ best_scheme = c1Mode.C1_BYTE;
+ }
+ }
+ return best_scheme;
+ }
+
+ private double roundUpToNextInteger(double input) {
+ double fraction, output;
+
+ fraction = input - (int) input;
+ if (fraction > 0.01) {
+ output = (input - fraction) + 1.0;
+ } else {
+ output = input;
+ }
+
+ return output;
+ }
+
+ private boolean preferEdi(int sourcelen, int position) {
+ int i;
+
+ for (i = position; isEdiEncodable(source[position + i])
+ && ((position + i) < sourcelen); i++) {
+ ;
+ }
+
+ if ((position + i) == sourcelen) {
+ /* Reached end of input */
+ return false;
+ }
+
+ if (source[position + i - 1] == 13) {
+ return true;
+ }
+ if (source[position + i - 1] == '*') {
+ return true;
+ }
+ if (source[position + i - 1] == '>') {
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isEdiEncodable(int input) {
+ boolean result = false;
+
+ if (input == 13) {
+ result = true;
+ }
+ if (input == '*') {
+ result = true;
+ }
+ if (input == '>') {
+ result = true;
+ }
+ if (input == ' ') {
+ result = true;
+ }
+ if ((input >= '0') && (input <= '9')) {
+ result = true;
+ }
+ if ((input >= 'A') && (input <= 'Z')) {
+ result = true;
+ }
+
+ return result;
+ }
+
+ private void plotCentralFinder(int start_row, int row_count, int full_rows) {
+ int i;
+
+ for (i = 0; i < row_count; i++) {
+ if (i < full_rows) {
+ plotHorizontalBar(start_row + (i * 2), 1);
+ } else {
+ plotHorizontalBar(start_row + (i * 2), 0);
+ if (i != row_count - 1) {
+ setGridModule(start_row + (i * 2) + 1, 1);
+ setGridModule(start_row + (i * 2) + 1, symbolWidth - 2);
+ }
+ }
+ }
+ }
+
+ private void plotHorizontalBar(int row_no, int full) {
+ int i;
+
+ if (full != 0) {
+ for (i = 0; i < symbolWidth; i++) {
+ setGridModule(row_no, i);
+ }
+ } else {
+ for (i = 1; i < symbolWidth - 1; i++) {
+ setGridModule(row_no, i);
+ }
+ }
+ }
+
+ private void plotVerticalBar(int column, int height, int top) {
+ int i;
+
+ if (top != 0) {
+ for (i = 0; i < height; i++) {
+ setGridModule(i, column);
+ }
+ } else {
+ for (i = 0; i < height; i++) {
+ setGridModule(rowCount - i - 1, column);
+ }
+ }
+ }
+
+ private void plotSpigot(int row_no) {
+ int i;
+
+ for (i = symbolWidth - 1; i > 0; i--) {
+ if (outputGrid[row_no][i - 1]) {
+ setGridModule(row_no, i);
+ }
+ }
+ }
+
+ private void plotDataBlock(int start_row, int start_col, int height,
+ int width, int row_offset, int col_offset) {
+ int i, j;
+
+ for (i = start_row; i < (start_row + height); i++) {
+ for (j = start_col; j < (start_col + width); j++) {
+ if (datagrid[i][j] == '1') {
+ setGridModule(i + row_offset, j + col_offset);
+ }
+ }
+ }
+ }
+
+ private void setGridModule(int row, int column) {
+ outputGrid[row][column] = true;
+ }
+
+ private void resetGridModule(int row, int column) {
+ outputGrid[row][column] = false;
+ }
+
+ private int getSize(Version version) {
+ int size = 0;
+
+ switch (version) {
+ case A:
+ size = 1;
+ break;
+ case B:
+ size = 2;
+ break;
+ case C:
+ size = 3;
+ break;
+ case D:
+ size = 4;
+ break;
+ case E:
+ size = 5;
+ break;
+ case F:
+ size = 6;
+ break;
+ case G:
+ size = 7;
+ break;
+ case H:
+ size = 8;
+ break;
+ }
+
+ return size;
+ }
+
+ private enum c1Mode {
+ C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE
+ }
+
+ public enum Version {
+ NONE, A, B, C, D, E, F, G, H, S, T
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Composite.java b/barcode/src/main/java/org/xbib/graphics/barcode/Composite.java
new file mode 100755
index 0000000..fd0ef27
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Composite.java
@@ -0,0 +1,2846 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements GS1 Composite symbology according to ISO/IEC 24723:2010.
+ * Composite symbols comprise a 2D element which encodes GS1 data
+ * and a "linear" element which can be UPC, EAN, Code 128 or
+ * GS1 DataBar symbol.
+ */
+public class Composite extends Symbol {
+ /* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */
+ private int[] ccaCoeffs = {
+ /* k = 4 */
+ 522, 568, 723, 809,
+ /* k = 5 */
+ 427, 919, 460, 155, 566,
+ /* k = 6 */
+ 861, 285, 19, 803, 17, 766,
+ /* k = 7 */
+ 76, 925, 537, 597, 784, 691, 437,
+ /* k = 8 */
+ 237, 308, 436, 284, 646, 653, 428, 379
+ };
+ private int[] coefrs = {
+ /* k = 2 */
+ 27, 917,
+ /* k = 4 */
+ 522, 568, 723, 809,
+ /* k = 8 */
+ 237, 308, 436, 284, 646, 653, 428, 379,
+ /* k = 16 */
+ 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
+ /* k = 32 */
+ 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
+ 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
+ /* k = 64 */
+ 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612,
+ 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184,
+ 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502,
+ 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543,
+ /* k = 128 */
+ 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415,
+ 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704,
+ 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569,
+ 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776,
+ 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898,
+ 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616,
+ 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34,
+ 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539,
+ /* k = 256 */
+ 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720,
+ 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757,
+ 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137,
+ 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884,
+ 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521,
+ 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470,
+ 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90,
+ 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134,
+ 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234,
+ 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621,
+ 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528,
+ 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550,
+ 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754,
+ 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532,
+ 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173,
+ 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10,
+ /* k = 512 */
+ 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492,
+ 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781,
+ 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534,
+ 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41,
+ 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741,
+ 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142,
+ 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258,
+ 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303,
+ 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402,
+ 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785,
+ 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543,
+ 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820,
+ 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578,
+ 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911,
+ 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408,
+ 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729,
+ 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772,
+ 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777,
+ 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45,
+ 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905,
+ 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341,
+ 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808,
+ 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249,
+ 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791,
+ 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437,
+ 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842,
+ 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316,
+ 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656,
+ 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433,
+ 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780,
+ 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647,
+ 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263
+ };
+
+ /* rows, error codewords, k-offset of valid CC-A sizes from ISO/IEC 24723:2006 Table 9 */
+ private int[] ccaVariants = {
+ 5, 6, 7, 8, 9, 10, 12, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 4, 4, 5, 5, 6, 6, 7, 4, 5, 6, 7, 7, 4, 5, 6, 7, 8, 0, 0, 4, 4, 9, 9, 15, 0, 4, 9, 15, 15, 0, 4, 9, 15, 22
+ };
+
+ /* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24723:2006 tables 10 and 11 */
+ private int[] aRAPTable = {
+ 39, 1, 32, 8, 14, 43, 20, 11, 1, 5, 15, 21, 40, 43, 46, 34, 29, 0, 0, 0, 0, 0, 0, 0, 43, 33, 37, 47, 1, 20, 23, 26, 14, 9, 19, 33, 12, 40, 46, 23, 52, 23, 13, 17, 27, 33, 52, 3, 6, 46, 41, 6, 0, 3, 3, 3, 0, 3, 3, 0, 3, 6, 6, 0, 0, 0, 0, 3
+ };
+ private String[] codagemc = {
+ "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA",
+ "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs",
+ "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk",
+ "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc",
+ "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw",
+ "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk",
+ "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk",
+ "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk",
+ "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk",
+ "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi",
+ "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr",
+ "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA",
+ "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk",
+ "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy",
+ "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs",
+ "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk",
+ "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss",
+ "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj",
+ "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE",
+ "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc",
+ "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii",
+ "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA",
+ "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs",
+ "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD",
+ "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb",
+ "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak",
+ "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD",
+ "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow",
+ "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj",
+ "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw",
+ "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw",
+ "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE",
+ "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa",
+ "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug",
+ "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb",
+ "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA",
+ "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE",
+ "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb",
+ "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE",
+ "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE",
+ "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa",
+ "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg",
+ "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc",
+ "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE",
+ "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn",
+ "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos",
+ "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg",
+ "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug",
+ "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk",
+ "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb",
+ "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa",
+ "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE",
+ "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD",
+ "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba",
+ "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc",
+ "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE",
+ "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc",
+ "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC",
+ "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso",
+ "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl",
+ "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl",
+ "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu",
+ "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari",
+ "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda",
+ "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD",
+ "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn",
+ "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB",
+ "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo",
+ "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl",
+ "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl",
+ "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd",
+ "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos",
+ "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA",
+ "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj",
+ "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw",
+ "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw",
+ "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi",
+ "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk",
+ "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig",
+ "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb",
+ "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb",
+ "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac",
+ "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC",
+ "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza",
+ "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC",
+ "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB",
+ "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm",
+ "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes",
+ "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi",
+ "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk",
+ "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg",
+ "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi",
+ "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk",
+ "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB",
+ "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB",
+ "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC",
+ "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC",
+ "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw",
+ "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn",
+ "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB",
+ "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc",
+ "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf",
+ "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv",
+ "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo",
+ "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku",
+ "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt",
+ "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa",
+ "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB",
+ "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc",
+ "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw",
+ "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn",
+ "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB",
+ "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc",
+ "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs",
+ "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy",
+ "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm",
+ "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo",
+ "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm",
+ "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy",
+ "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt",
+ "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu",
+ "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx",
+ "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE",
+ "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn",
+ "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy",
+ "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo",
+ "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv",
+ "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz",
+ "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu",
+ "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu",
+ "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh",
+ "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx",
+ "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz",
+ "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv",
+ "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd",
+ "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt",
+ "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx",
+ "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow",
+ "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj",
+ "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx",
+ "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus",
+ "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD",
+ "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr",
+ "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw",
+ "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq",
+ "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB",
+ "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors",
+ "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm",
+ "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv",
+ "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz",
+ "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu",
+ "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy",
+ "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy",
+ "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo",
+ "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz",
+ "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl",
+ "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj",
+ "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky",
+ "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz",
+ "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt",
+ "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj",
+ "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh",
+ "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk",
+ "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy",
+ "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj",
+ "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns",
+ "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz",
+ "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz",
+ "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz",
+ "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx",
+ "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks",
+ "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj",
+ "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy",
+ "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi",
+ "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb",
+ "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg",
+ "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj",
+ "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa",
+ "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz",
+ "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb",
+ "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk",
+ "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj",
+ "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw",
+ "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk",
+ "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD",
+ "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj",
+ "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB",
+ "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb",
+ "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg",
+ "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn",
+ "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw",
+ "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj",
+ "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw",
+ "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb",
+ "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb",
+ "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc",
+ "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva",
+ "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD",
+ "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB",
+ "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn",
+ "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl",
+ "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw",
+ "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi",
+ "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr",
+ "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC",
+ "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq",
+ "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm",
+ "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf",
+ "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu",
+ "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa",
+ "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn",
+ "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv",
+ "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt",
+ "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif",
+ "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp"
+ };
+ private char[] brSet = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '*', '+', '-'
+ };
+ private String[] PDFttf = {
+ "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111",
+ "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001",
+ "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010",
+ "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001"
+ };
+ /* Left and Right Row Address Pattern from Table 2 */
+ private String[] RAPLR = {"", "221311", "311311", "312211", "222211", "213211", "214111", "223111",
+ "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211",
+ "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122",
+ "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121",
+ "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213",
+ "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311"};
+
+ /* Centre Row Address Pattern from Table 2 */
+ private String[] RAPC = {"", "112231", "121231", "122131", "131131", "131221", "132121", "141121",
+ "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111",
+ "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411",
+ "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113",
+ "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223",
+ "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141"};
+ private int[] MicroVariants = {1, 1, 1, 1, 1, 1, 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,
+ 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44,
+ 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50,
+ 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294};
+ /* rows, columns, error codewords, k-offset */
+ /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
+ private int[] Microcoeffs = {
+ /* k = 7 */
+ 76, 925, 537, 597, 784, 691, 437,
+ /* k = 8 */
+ 237, 308, 436, 284, 646, 653, 428, 379,
+ /* k = 9 */
+ 567, 527, 622, 257, 289, 362, 501, 441, 205,
+ /* k = 10 */
+ 377, 457, 64, 244, 826, 841, 818, 691, 266, 612,
+ /* k = 11 */
+ 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904,
+ /* k = 12 */
+ 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851,
+ /* k = 13 */
+ 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692,
+ /* k = 14 */
+ 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215,
+ /* k = 15 */
+ 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642,
+ /* k = 16 */
+ 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
+ /* k = 18 */
+ 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756,
+ 760, 573,
+ /* k = 21 */
+ 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691,
+ 347, 165, 193, 259, 568,
+ /* k = 26 */
+ 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893,
+ 699, 245, 441, 454, 325, 858, 131, 847, 764, 169,
+ /* k = 32 */
+ 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
+ 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
+ /* k = 38 */
+ 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684,
+ 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771,
+ 554, 289, 231, 125, 117, 518,
+ /* k = 44 */
+ 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405,
+ 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213,
+ 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285,
+ /* k = 50 */
+ 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303,
+ 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26,
+ 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820,
+ 718, 435};
+
+ /* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */
+ private int[] RAPTable = {1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25,
+ 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49,
+ 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0};
+
+ private StringBuilder binaryString;
+ private int ecc;
+ private LinearEncoding symbology = LinearEncoding.CODE_128;
+ private String generalField;
+ private gfMode[] generalFieldType;
+
+ ;
+ private int ccWidth;
+ private int[][] pwr928 = new int[69][7];
+ private int[] codeWords = new int[180];
+ private int codeWordCount;
+ private int[] bitStr = new int[13];
+ private int[] inputData;
+ private CompositeMode ccMode;
+ private String linearContent;
+ private CompositeMode userPreferredMode = CompositeMode.CC_A;
+ private int targetBitsize;
+ private int remainder;
+ private int linearWidth; // Width of Code 128 linear
+
+ public Composite() {
+ inputDataType = DataType.GS1;
+ }
+
+ @Override
+ public void setDataType(DataType dummy) {
+ // Do nothing!
+ }
+
+ /**
+ * Set the type of linear component included in the composite symbol,
+ * this will determine how the lower part of the symbol is encoded.
+ *
+ * @param linearSymbology The symbology of the linear component
+ */
+ public void setSymbology(LinearEncoding linearSymbology) {
+ symbology = linearSymbology;
+ }
+
+ /**
+ * Set the data to be encoded in the linear component of the composite
+ * symbol.
+ *
+ * @param input The linear data in GS1 format
+ */
+ public void setLinear(String input) {
+ linearContent = input;
+ }
+
+ /**
+ * Set the preferred encoding method for the 2D component of the
+ * composite symbol. This value may be ignored if the amount of data
+ * supplied is too big for the selected encoding. Mode CC-C can only be
+ * used with a Code 128 linear component.
+ *
+ * @param userMode Preferred mode
+ */
+ public void setPreferredMode(CompositeMode userMode) {
+ userPreferredMode = userMode;
+ }
+
+ @Override
+ public boolean encode() {
+ List linearRect = new ArrayList<>();
+ List linearTxt = new ArrayList<>();
+ List combineRect = new ArrayList<>();
+ List combineTxt = new ArrayList<>();
+ StringBuilder linearEncodeInfo = null;
+ String linearErrorMsg = "";
+ int linearHeight = 0;
+ int topShift = 0;
+ int bottomShift = 0;
+ int maxX = 0;
+ int i;
+ linearWidth = 0;
+
+ if (linearContent.isEmpty()) {
+ errorMsg.append("No linear data set");
+ return false;
+ }
+
+ // Manage composite component encoding first
+ if (!(encodeComposite())) {
+ return false;
+ }
+
+ // Then encode linear component
+ try {
+ switch (symbology) {
+ case UPCA:
+ Upc upca = new Upc();
+ upca.setMode(Upc.Mode.UPCA);
+ upca.setLinkageFlag();
+ upca.setContent(linearContent);
+ linearRect = upca.getRectangles();
+ linearTxt = upca.getTexts();
+ linearHeight = upca.symbolHeight;
+ linearEncodeInfo = upca.encodeInfo;
+ topShift = 3;
+ break;
+ case UPCE:
+ Upc upce = new Upc();
+ upce.setMode(Upc.Mode.UPCE);
+ upce.setLinkageFlag();
+ upce.setContent(linearContent);
+ linearRect = upce.getRectangles();
+ linearTxt = upce.getTexts();
+ linearHeight = upce.symbolHeight;
+ linearEncodeInfo = upce.encodeInfo;
+ topShift = 3;
+ break;
+ case EAN:
+ Ean ean = new Ean();
+ if (eanCalculateVersion() == 8) {
+ ean.setMode(Ean.Mode.EAN8);
+ bottomShift = 8;
+ } else {
+ ean.setMode(Ean.Mode.EAN13);
+ topShift = 3;
+ }
+ ean.setLinkageFlag();
+ ean.setContent(linearContent);
+ linearRect = ean.getRectangles();
+ linearTxt = ean.getTexts();
+ linearHeight = ean.symbolHeight;
+ linearEncodeInfo = ean.encodeInfo;
+ break;
+ case CODE_128:
+ Code128 code128 = new Code128();
+ switch (ccMode) {
+ case CC_A:
+ code128.setCca();
+ break;
+ case CC_B:
+ code128.setCcb();
+ break;
+ case CC_C:
+ code128.setCcc();
+ bottomShift = 7;
+ break;
+ }
+ code128.setDataType(DataType.GS1);
+ code128.setContent(linearContent);
+ linearWidth = code128.symbolWidth;
+ linearRect = code128.getRectangles();
+ linearTxt = code128.getTexts();
+ linearHeight = code128.symbolHeight;
+ linearEncodeInfo = code128.encodeInfo;
+ break;
+ case DATABAR_14:
+ DataBar14 dataBar14 = new DataBar14();
+ dataBar14.setLinkageFlag();
+ dataBar14.setLinearMode();
+ dataBar14.setContent(linearContent);
+ linearRect = dataBar14.getRectangles();
+ linearTxt = dataBar14.getTexts();
+ linearHeight = dataBar14.symbolHeight;
+ linearEncodeInfo = dataBar14.encodeInfo;
+ bottomShift = 4;
+ break;
+ case DATABAR_14_STACK_OMNI:
+ DataBar14 dataBar14SO = new DataBar14();
+ dataBar14SO.setLinkageFlag();
+ dataBar14SO.setOmnidirectionalMode();
+ dataBar14SO.setContent(linearContent);
+ linearRect = dataBar14SO.getRectangles();
+ linearTxt = dataBar14SO.getTexts();
+ linearHeight = dataBar14SO.symbolHeight;
+ linearEncodeInfo = dataBar14SO.encodeInfo;
+ topShift = 1;
+ break;
+ case DATABAR_14_STACK:
+ DataBar14 dataBar14S = new DataBar14();
+ dataBar14S.setLinkageFlag();
+ dataBar14S.setStackedMode();
+ dataBar14S.setContent(linearContent);
+ linearRect = dataBar14S.getRectangles();
+ linearTxt = dataBar14S.getTexts();
+ linearHeight = dataBar14S.symbolHeight;
+ linearEncodeInfo = dataBar14S.encodeInfo;
+ topShift = 1;
+ break;
+ case DATABAR_LIMITED:
+ DataBarLimited dataBarLimited = new DataBarLimited();
+ dataBarLimited.setLinkageFlag();
+ dataBarLimited.setContent(linearContent);
+ linearRect = dataBarLimited.getRectangles();
+ linearTxt = dataBarLimited.getTexts();
+ linearHeight = dataBarLimited.symbolHeight;
+ linearEncodeInfo = dataBarLimited.encodeInfo;
+ topShift = 1;
+ break;
+ case DATABAR_EXPANDED:
+ DataBarExpanded dataBarExpanded = new DataBarExpanded();
+ dataBarExpanded.setLinkageFlag();
+ dataBarExpanded.setNotStacked();
+ dataBarExpanded.setContent(linearContent);
+ linearRect = dataBarExpanded.getRectangles();
+ linearTxt = dataBarExpanded.getTexts();
+ linearHeight = dataBarExpanded.symbolHeight;
+ linearEncodeInfo = dataBarExpanded.encodeInfo;
+ topShift = 2;
+ break;
+ case DATABAR_EXPANDED_STACK:
+ DataBarExpanded dataBarExpandedS = new DataBarExpanded();
+ dataBarExpandedS.setLinkageFlag();
+ dataBarExpandedS.setStacked();
+ dataBarExpandedS.setContent(linearContent);
+ linearRect = dataBarExpandedS.getRectangles();
+ linearTxt = dataBarExpandedS.getTexts();
+ linearHeight = dataBarExpandedS.symbolHeight;
+ linearEncodeInfo = dataBarExpandedS.encodeInfo;
+ topShift = 2;
+ break;
+ default:
+ linearErrorMsg = "Linear symbol not recognised";
+ break;
+ }
+ } catch (Exception e) {
+ linearErrorMsg = e.getMessage();
+ }
+
+ if (!linearErrorMsg.isEmpty()) {
+ errorMsg.append(linearErrorMsg);
+ return false;
+ }
+
+ if ((ccMode == CompositeMode.CC_C) && (symbology == LinearEncoding.CODE_128)) {
+ /* Width of composite component depends on width of linear component,
+ so recalculate. */
+ rowCount = 0;
+ getRectangles().clear();
+ symbolHeight = 0;
+ symbolWidth = 0;
+ encodeInfo = new StringBuilder();
+ if (!(encodeComposite())) {
+ return false;
+ }
+ }
+
+ if ((ccMode != CompositeMode.CC_C) && (symbology == LinearEncoding.CODE_128)) {
+ if (linearWidth > symbolWidth) {
+ topShift = (linearWidth - symbolWidth) / 2;
+ }
+ }
+
+ for (i = 0; i < getRectangles().size(); i++) {
+ Rectangle2D.Double comprect = new Rectangle2D.Double(getRectangles().get(i).x + topShift,
+ getRectangles().get(i).y,
+ getRectangles().get(i).width,
+ getRectangles().get(i).height);
+ if ((getRectangles().get(i).x + topShift + getRectangles().get(i).width) > maxX) {
+ maxX = (int) (getRectangles().get(i).x + topShift + getRectangles().get(i).width);
+ }
+ combineRect.add(comprect);
+ }
+
+ for (i = 0; i < linearRect.size(); i++) {
+ Rectangle2D.Double linrect = new Rectangle2D.Double(linearRect.get(i).x + bottomShift, linearRect.get(i).y, linearRect.get(i).width, linearRect.get(i).height);
+ linrect.y += symbolHeight;
+ if ((linearRect.get(i).x + bottomShift + linearRect.get(i).width) > maxX) {
+ maxX = (int) (linearRect.get(i).x + bottomShift + linearRect.get(i).width);
+ }
+ combineRect.add(linrect);
+ }
+
+ for (i = 0; i < linearTxt.size(); i++) {
+ double x = linearTxt.get(i).x + bottomShift;
+ double y = linearTxt.get(i).y + symbolHeight;
+ String text = linearTxt.get(i).text;
+ TextBox lintxt = new TextBox(x, y, text);
+ combineTxt.add(lintxt);
+ }
+ getRectangles().clear();
+ getRectangles().addAll(combineRect);
+ getTexts().clear();
+ getTexts().addAll(combineTxt);
+ symbolHeight += linearHeight;
+ symbolWidth = maxX;
+
+ encodeInfo.append(linearEncodeInfo);
+
+ return true;
+ }
+
+ private boolean encodeComposite() {
+
+ if (content.length() > 2990) {
+ errorMsg.append("2D component input data too long");
+ return false;
+ }
+
+ ccMode = userPreferredMode;
+
+ if ((ccMode == CompositeMode.CC_C) && (symbology != LinearEncoding.CODE_128)) {
+ /* CC-C can only be used with a GS1-128 linear part */
+ errorMsg.append("Invalid mode (CC-C only valid with GS1-128 linear component)");
+ return false;
+ }
+
+ switch (symbology) {
+ /* Determine width of 2D component according to ISO/IEC 24723 Table 1 */
+ case EAN:
+ if (eanCalculateVersion() == 8) {
+ ccWidth = 3;
+ } else {
+ ccWidth = 4;
+ }
+ break;
+ case UPCE:
+ case DATABAR_14_STACK_OMNI:
+ case DATABAR_14_STACK:
+ ccWidth = 2;
+ break;
+ case DATABAR_LIMITED:
+ ccWidth = 3;
+ break;
+ case CODE_128:
+ case DATABAR_14:
+ case DATABAR_EXPANDED:
+ case UPCA:
+ case DATABAR_EXPANDED_STACK:
+ ccWidth = 4;
+ break;
+ }
+
+ encodeInfo.append("Composite width: ").append(Integer.toString(ccWidth)).append("\n");
+
+ if (ccMode == CompositeMode.CC_A && !ccBinaryString()) {
+ ccMode = CompositeMode.CC_B;
+ }
+
+ if (ccMode == CompositeMode.CC_B) { /* If the data didn't fit into CC-A it is recalculated for CC-B */
+ if (!(ccBinaryString())) {
+ if (symbology != LinearEncoding.CODE_128) {
+ errorMsg.append("Input too long");
+ return false;
+ } else {
+ ccMode = CompositeMode.CC_C;
+ }
+ }
+ }
+
+ if (ccMode == CompositeMode.CC_C) {
+ /* If the data didn't fit in CC-B (and linear
+ * part is GS1-128) it is recalculated for CC-C */
+ if (!(ccBinaryString())) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+ }
+
+ switch (ccMode) { /* Note that ecc_level is only relevant to CC-C */
+ case CC_A:
+ ccA();
+ encodeInfo.append("Composite type: CC-A\n");
+ break;
+ case CC_B:
+ ccB();
+ encodeInfo.append("Composite type: CC-B\n");
+ break;
+ case CC_C:
+ ccC();
+ encodeInfo.append("Composite type: CC-C\n");
+ break;
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private int eanCalculateVersion() {
+ /* Determine if EAN-8 or EAN-13 is being used */
+
+ int length = 0;
+ int i;
+ boolean latch;
+
+ latch = true;
+ for (i = 0; i < linearContent.length(); i++) {
+ if ((linearContent.charAt(i) >= '0') && (linearContent.charAt(i) <= '9')) {
+ if (latch) {
+ length++;
+ }
+ } else {
+ latch = false;
+ }
+ }
+
+ if (length <= 7) {
+ // EAN-8
+ return 8;
+ } else {
+ // EAN-13
+ return 13;
+ }
+ }
+
+ private boolean calculateSymbolSize() {
+ int i;
+ int binaryLength = binaryString.length();
+ if (ccMode == CompositeMode.CC_A) {
+ /* CC-A 2D component - calculate remaining space */
+ switch (ccWidth) {
+ case 2:
+ if (binaryLength > 167) {
+ return true;
+ }
+ targetBitsize = 167;
+ if (binaryLength <= 138) {
+ targetBitsize = 138;
+ }
+ if (binaryLength <= 118) {
+ targetBitsize = 118;
+ }
+ if (binaryLength <= 108) {
+ targetBitsize = 108;
+ }
+ if (binaryLength <= 88) {
+ targetBitsize = 88;
+ }
+ if (binaryLength <= 78) {
+ targetBitsize = 78;
+ }
+ if (binaryLength <= 59) {
+ targetBitsize = 59;
+ }
+ break;
+ case 3:
+ if (binaryLength > 167) {
+ return true;
+ }
+ targetBitsize = 167;
+ if (binaryLength <= 138) {
+ targetBitsize = 138;
+ }
+ if (binaryLength <= 118) {
+ targetBitsize = 118;
+ }
+ if (binaryLength <= 98) {
+ targetBitsize = 98;
+ }
+ if (binaryLength <= 78) {
+ targetBitsize = 78;
+ }
+ break;
+ case 4:
+ if (binaryLength > 197) {
+ return true;
+ }
+ targetBitsize = 197;
+ if (binaryLength <= 167) {
+ targetBitsize = 167;
+ }
+ if (binaryLength <= 138) {
+ targetBitsize = 138;
+ }
+ if (binaryLength <= 108) {
+ targetBitsize = 108;
+ }
+ if (binaryLength <= 78) {
+ targetBitsize = 78;
+ }
+ break;
+ }
+ }
+
+ if (ccMode == CompositeMode.CC_B) {
+ /* CC-B 2D component - calculated from ISO/IEC 24728 Table 1 */
+ switch (ccWidth) {
+ case 2:
+ if (binaryLength > 336) {
+ return true;
+ }
+ targetBitsize = 336;
+ if (binaryLength <= 296) {
+ targetBitsize = 296;
+ }
+ if (binaryLength <= 256) {
+ targetBitsize = 256;
+ }
+ if (binaryLength <= 208) {
+ targetBitsize = 208;
+ }
+ if (binaryLength <= 160) {
+ targetBitsize = 160;
+ }
+ if (binaryLength <= 104) {
+ targetBitsize = 104;
+ }
+ if (binaryLength <= 56) {
+ targetBitsize = 56;
+ }
+ break;
+ case 3:
+ if (binaryLength > 768) {
+ return true;
+ }
+ targetBitsize = 768;
+ if (binaryLength <= 648) {
+ targetBitsize = 648;
+ }
+ if (binaryLength <= 536) {
+ targetBitsize = 536;
+ }
+ if (binaryLength <= 416) {
+ targetBitsize = 416;
+ }
+ if (binaryLength <= 304) {
+ targetBitsize = 304;
+ }
+ if (binaryLength <= 208) {
+ targetBitsize = 208;
+ }
+ if (binaryLength <= 152) {
+ targetBitsize = 152;
+ }
+ if (binaryLength <= 112) {
+ targetBitsize = 112;
+ }
+ if (binaryLength <= 72) {
+ targetBitsize = 72;
+ }
+ if (binaryLength <= 32) {
+ targetBitsize = 32;
+ }
+ break;
+ case 4:
+ if (binaryLength > 1184) {
+ return true;
+ }
+ targetBitsize = 1184;
+ if (binaryLength <= 1016) {
+ targetBitsize = 1016;
+ }
+ if (binaryLength <= 840) {
+ targetBitsize = 840;
+ }
+ if (binaryLength <= 672) {
+ targetBitsize = 672;
+ }
+ if (binaryLength <= 496) {
+ targetBitsize = 496;
+ }
+ if (binaryLength <= 352) {
+ targetBitsize = 352;
+ }
+ if (binaryLength <= 264) {
+ targetBitsize = 264;
+ }
+ if (binaryLength <= 208) {
+ targetBitsize = 208;
+ }
+ if (binaryLength <= 152) {
+ targetBitsize = 152;
+ }
+ if (binaryLength <= 96) {
+ targetBitsize = 96;
+ }
+ if (binaryLength <= 56) {
+ targetBitsize = 56;
+ }
+ break;
+ }
+ }
+
+ if (ccMode == CompositeMode.CC_C) {
+ /* CC-C 2D Component is a bit more complex! */
+ int byteLength, codewordsUsed, eccLevel, eccCodewords, rows;
+ int codewordsTotal, targetCodewords, targetBytesize;
+
+ byteLength = binaryLength / 8;
+ if (binaryLength % 8 != 0) {
+ byteLength++;
+ }
+
+ codewordsUsed = (byteLength / 6) * 5;
+ codewordsUsed += byteLength % 6;
+
+ eccLevel = 7;
+ if (codewordsUsed <= 1280) {
+ eccLevel = 6;
+ }
+ if (codewordsUsed <= 640) {
+ eccLevel = 5;
+ }
+ if (codewordsUsed <= 320) {
+ eccLevel = 4;
+ }
+ if (codewordsUsed <= 160) {
+ eccLevel = 3;
+ }
+ if (codewordsUsed <= 40) {
+ eccLevel = 2;
+ }
+ ecc = eccLevel;
+ eccCodewords = 1;
+ for (i = 1; i <= (eccLevel + 1); i++) {
+ eccCodewords *= 2;
+ }
+
+ codewordsUsed += eccCodewords;
+ codewordsUsed += 3;
+
+ if (linearWidth == 0) {
+ /* Linear component not yet calculated */
+ ccWidth = (int) (0.5 + Math.sqrt((codewordsUsed) / 3.0));
+ } else {
+ ccWidth = (linearWidth - 53) / 17;
+ }
+
+ if ((codewordsUsed / ccWidth) > 90) {
+ /* stop the symbol from becoming too high */
+ ccWidth = ccWidth + 1;
+ }
+
+ rows = codewordsUsed / ccWidth;
+ if (codewordsUsed % ccWidth != 0) {
+ rows++;
+ }
+
+ while (ccWidth > (3 * rows)) {
+ /* stop the symbol from becoming too wide (section 10) */
+ ccWidth--;
+
+ rows = codewordsUsed / ccWidth;
+ if (codewordsUsed % ccWidth != 0) {
+ rows++;
+ }
+ }
+ ;
+
+ codewordsTotal = ccWidth * rows;
+
+ targetCodewords = codewordsTotal - eccCodewords;
+ targetCodewords -= 3;
+
+ targetBytesize = 6 * (targetCodewords / 5);
+ targetBytesize += targetCodewords % 5;
+
+ targetBitsize = 8 * targetBytesize;
+ }
+
+ remainder = targetBitsize - binaryLength;
+ return false;
+ }
+
+ private boolean ccBinaryString() {
+ /* Handles all data encoding from section 5 of ISO/IEC 24723 */
+ int encodingMethod, readPosn, d1, d2, value, alphaPad;
+ int i, j, aiCrop, fnc1Latch;
+ int groupVal;
+ int ai90Mode;
+ boolean latch;
+ int alpha, alphanum, numeric, test1, test2, test3, nextAiPosn;
+ int numericValue, table3Letter;
+ String numericPart;
+ String ninety;
+ int latchOffset;
+
+ encodingMethod = 1;
+ readPosn = 0;
+ aiCrop = 0;
+ fnc1Latch = 0;
+ alphaPad = 0;
+ ai90Mode = 0;
+ ecc = 0;
+ value = 0;
+ targetBitsize = 0;
+
+ if ((content.charAt(0) == '1') && ((content.charAt(1) == '0') || (content.charAt(1) == '1') || (content.charAt(1) == '7')) && (content.length() >= 8)) {
+ /* Source starts (10), (11) or (17) */
+ encodingMethod = 2;
+ }
+
+ if ((content.charAt(0) == '9') && (content.charAt(1) == '0')) {
+ /* Source starts (90) */
+ encodingMethod = 3;
+ }
+
+ encodeInfo.append("Composite Encodation: ");
+ switch (encodingMethod) {
+ case 1:
+ encodeInfo.append("0\n");
+ break;
+ case 2:
+ encodeInfo.append("10\n");
+ break;
+ case 3:
+ encodeInfo.append("11\n");
+ break;
+ }
+
+ binaryString = new StringBuilder();
+
+ if (encodingMethod == 1) {
+ binaryString.append("0");
+ }
+
+ if (encodingMethod == 2) {
+ /* Encoding Method field "10" - date and lot number */
+
+ binaryString.append("10");
+
+ if (content.charAt(1) == '0') {
+ /* No date data */
+ binaryString.append("11");
+ readPosn = 2;
+ } else {
+ /* Production Date (11) or Expiration Date (17) */
+ groupVal = ((10 * (content.charAt(2) - '0')) + (content.charAt(3) - '0')) * 384;
+ groupVal += (((10 * (content.charAt(4) - '0')) + (content.charAt(5) - '0')) - 1) * 32;
+ groupVal += (10 * (content.charAt(6) - '0')) + (content.charAt(7) - '0');
+
+ for (j = 0; j < 16; j++) {
+ if ((groupVal & (0x8000 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+
+ if (content.charAt(1) == '1') {
+ /* Production Date AI 11 */
+ binaryString.append("0");
+ } else {
+ /* Expiration Date AI 17 */
+ binaryString.append("1");
+ }
+ readPosn = 8;
+ }
+
+ if ((readPosn + 2) < content.length()) {
+ if ((content.charAt(readPosn) == '1') && (content.charAt(readPosn + 1) == '0')) {
+ /* Followed by AI 10 - strip this from general field */
+ readPosn += 2;
+ } else {
+ /* An FNC1 character needs to be inserted in the general field */
+ fnc1Latch = 1;
+ }
+ } else {
+ fnc1Latch = 1;
+ }
+ }
+
+ if (encodingMethod == 3) {
+ /* Encodation Method field of "11" - AI 90 */
+ /* "This encodation method may be used if an element string with an AI
+ 90 occurs at the start of the data message, and if the data field
+ following the two-digit AI 90 starts with an alphanumeric string which
+ complies with a specific format." (para 5.2.2) */
+
+ j = content.length();
+ for (i = content.length(); i > 2; i--) {
+ if (content.charAt(i - 1) == '[') {
+ j = i;
+ }
+ }
+
+ ninety = content.substring(2, j - 1);
+
+
+ /* Find out if the AI 90 data is alphabetic or numeric or both */
+
+ alpha = 0;
+ alphanum = 0;
+ numeric = 0;
+
+ for (i = 0; i < ninety.length(); i++) {
+
+ if ((ninety.charAt(i) >= 'A') && (ninety.charAt(i) <= 'Z')) {
+ /* Character is alphabetic */
+ alpha += 1;
+ }
+
+ if ((ninety.charAt(i) >= '0') && (ninety.charAt(i) <= '9')) {
+ /* Character is numeric */
+ numeric += 1;
+ }
+
+ switch (ninety.charAt(i)) {
+ case '*':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ alphanum += 1;
+ break;
+ }
+
+ if (!(((ninety.charAt(i) >= '0') && (ninety.charAt(i) <= '9')) || ((ninety.charAt(i) >= 'A') && (ninety.charAt(i) <= 'Z')))) {
+ if ((ninety.charAt(i) != '*') && (ninety.charAt(i) != ',') && (ninety.charAt(i) != '-') && (ninety.charAt(i) != '.') && (ninety.charAt(i) != '/')) {
+ /* An Invalid AI 90 character */
+ errorMsg.append("Invalid AI 90 data");
+ return false;
+ }
+ }
+ }
+
+ /* must start with 0, 1, 2 or 3 digits followed by an uppercase character */
+ test1 = -1;
+ for (i = 3; i >= 0; i--) {
+ if ((ninety.charAt(i) >= 'A') && (ninety.charAt(i) <= 'Z')) {
+ test1 = i;
+ }
+ }
+
+ test2 = 0;
+ for (i = 0; i < test1; i++) {
+ if (!((ninety.charAt(i) >= '0') && (ninety.charAt(i) <= '9'))) {
+ test2 = 1;
+ }
+ }
+
+ /* leading zeros are not permitted */
+ test3 = 0;
+ if ((test1 >= 1) && (ninety.charAt(0) == '0')) {
+ test3 = 1;
+ }
+
+ if ((test1 != -1) && (test2 != 1) && (test3 == 0)) {
+ /* Encodation method "11" can be used */
+ binaryString.append("11");
+
+ numeric -= test1;
+ alpha--;
+
+ /* Decide on numeric, alpha or alphanumeric mode */
+ /* Alpha mode is a special mode for AI 90 */
+
+ if (alphanum > 0) {
+ /* Alphanumeric mode */
+ binaryString.append("0");
+ ai90Mode = 1;
+ } else {
+ if (alpha > numeric) {
+ /* Alphabetic mode */
+ binaryString.append("11");
+ ai90Mode = 2;
+ } else {
+ /* Numeric mode */
+ binaryString.append("10");
+ ai90Mode = 3;
+ }
+ }
+
+ nextAiPosn = 2 + ninety.length();
+
+ if (content.charAt(nextAiPosn) == '[') {
+ /* There are more AIs afterwords */
+ if ((content.charAt(nextAiPosn + 1) == '2') && (content.charAt(nextAiPosn + 2) == '1')) {
+ /* AI 21 follows */
+ aiCrop = 1;
+ }
+
+ if ((content.charAt(nextAiPosn + 1) == '8') && (content.charAt(nextAiPosn + 2) == '0') && (content.charAt(nextAiPosn + 3) == '0') && (content.charAt(nextAiPosn + 4) == '4')) {
+ /* AI 8004 follows */
+ aiCrop = 2;
+ }
+ }
+
+ switch (aiCrop) {
+ case 0:
+ binaryString.append("0");
+ break;
+ case 1:
+ binaryString.append("10");
+ break;
+ case 2:
+ binaryString.append("11");
+ break;
+ }
+
+ if (test1 == 0) {
+ numericPart = "0";
+ } else {
+ numericPart = ninety.substring(0, test1);
+ }
+
+ numericValue = 0;
+ for (i = 0; i < numericPart.length(); i++) {
+ numericValue *= 10;
+ numericValue += numericPart.charAt(i) - '0';
+ }
+
+ table3Letter = -1;
+ if (numericValue < 31) {
+ switch (ninety.charAt(test1)) {
+ case 'B':
+ table3Letter = 0;
+ break;
+ case 'D':
+ table3Letter = 1;
+ break;
+ case 'H':
+ table3Letter = 2;
+ break;
+ case 'I':
+ table3Letter = 3;
+ break;
+ case 'J':
+ table3Letter = 4;
+ break;
+ case 'K':
+ table3Letter = 5;
+ break;
+ case 'L':
+ table3Letter = 6;
+ break;
+ case 'N':
+ table3Letter = 7;
+ break;
+ case 'P':
+ table3Letter = 8;
+ break;
+ case 'Q':
+ table3Letter = 9;
+ break;
+ case 'R':
+ table3Letter = 10;
+ break;
+ case 'S':
+ table3Letter = 11;
+ break;
+ case 'T':
+ table3Letter = 12;
+ break;
+ case 'V':
+ table3Letter = 13;
+ break;
+ case 'W':
+ table3Letter = 14;
+ break;
+ case 'Z':
+ table3Letter = 15;
+ break;
+ }
+ }
+
+ if (table3Letter != -1) {
+ /* Encoding can be done according to 5.2.2 c) 2) */
+ /* five bit binary string representing value before letter */
+ for (j = 0; j < 5; j++) {
+ if ((numericValue & (0x10 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+
+ /* followed by four bit representation of letter from Table 3 */
+ for (j = 0; j < 4; j++) {
+ if ((table3Letter & (0x08 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ } else {
+ /* Encoding is done according to 5.2.2 c) 3) */
+ binaryString.append("11111");
+ /* ten bit representation of number */
+ for (j = 0; j < 10; j++) {
+ if ((numericValue & (0x200 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+
+ /* five bit representation of ASCII character */
+ for (j = 0; j < 5; j++) {
+ if (((ninety.charAt(test1) - 65) & (0x10 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ readPosn = test1 + 3;
+ } else {
+ /* Use general field encodation instead */
+ binaryString.append("0");
+ readPosn = 0;
+ }
+
+
+ /* Now encode the rest of the AI 90 data field */
+ if (ai90Mode == 2) {
+ /* Alpha encodation (section 5.2.3) */
+ do {
+ if ((content.charAt(readPosn) >= '0') && (content.charAt(readPosn) <= '9')) {
+ for (j = 0; j < 5; j++) {
+ if (((content.charAt(readPosn) + 4) & (0x10 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if ((content.charAt(readPosn) >= 'A') && (content.charAt(readPosn) <= 'Z')) {
+ for (j = 0; j < 6; j++) {
+ if (((content.charAt(readPosn) - 65) & (0x20 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if (content.charAt(readPosn) == '[') {
+ binaryString.append("11111");
+ }
+
+ readPosn++;
+ } while ((content.charAt(readPosn - 1) != '[') && (readPosn < content.length()));
+ alphaPad = 1; /* This is overwritten if a general field is encoded */
+ }
+
+ if (ai90Mode == 1) {
+ /* Alphanumeric mode */
+ do {
+ if ((content.charAt(readPosn) >= '0') && (content.charAt(readPosn) <= '9')) {
+ for (j = 0; j < 5; j++) {
+ if (((content.charAt(readPosn) - 43) & (0x10 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if ((content.charAt(readPosn) >= 'A') && (content.charAt(readPosn) <= 'Z')) {
+ for (j = 0; j < 6; j++) {
+ if (((content.charAt(readPosn) - 33) & (0x20 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ switch (content.charAt(readPosn)) {
+ case '[':
+ binaryString.append("01111");
+ break;
+ case '*':
+ binaryString.append("111010");
+ break;
+ case ',':
+ binaryString.append("111011");
+ break;
+ case '-':
+ binaryString.append("111100");
+ break;
+ case '.':
+ binaryString.append("111101");
+ break;
+ case '/':
+ binaryString.append("111110");
+ break;
+ }
+
+ readPosn++;
+ } while ((content.charAt(readPosn - 1) != '[') && (content.charAt(readPosn - 1) != '\0'));
+ }
+
+ readPosn += (2 * aiCrop);
+ }
+
+
+ /* The compressed data field has been processed if appropriate - the
+ rest of the data (if any) goes into a general-purpose data compaction field */
+
+ j = 0;
+ generalField = "";
+ if (fnc1Latch == 1) {
+ /* Encodation method "10" has been used but it is not followed by
+ AI 10, so a FNC1 character needs to be added */
+ generalField += "[";
+ }
+
+ generalField += content.substring(readPosn);
+
+
+ latch = false;
+ if (generalField.length() != 0) {
+ alphaPad = 0;
+
+
+ generalFieldType = new gfMode[generalField.length()];
+
+ for (i = 0; i < generalField.length(); i++) {
+ /* Table 13 - ISO/IEC 646 encodation */
+ if ((generalField.charAt(i) < ' ') || (generalField.charAt(i) > 'z')) {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ } else {
+ generalFieldType[i] = gfMode.ISOIEC;
+ }
+
+ if (generalField.charAt(i) == '#') {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == '$') {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == '@') {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == 92) {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == '^') {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == 96) {
+ generalFieldType[i] = gfMode.INVALID_CHAR;
+ latch = true;
+ }
+
+ /* Table 12 - Alphanumeric encodation */
+ if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) {
+ generalFieldType[i] = gfMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '*') {
+ generalFieldType[i] = gfMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == ',') {
+ generalFieldType[i] = gfMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '-') {
+ generalFieldType[i] = gfMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '.') {
+ generalFieldType[i] = gfMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '/') {
+ generalFieldType[i] = gfMode.ALPHA_OR_ISO;
+ }
+
+ /* Numeric encodation */
+ if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) {
+ generalFieldType[i] = gfMode.ANY_ENC;
+ }
+ if (generalField.charAt(i) == '[') {
+ /* FNC1 can be encoded in any system */
+ generalFieldType[i] = gfMode.ANY_ENC;
+ }
+
+ }
+
+ if (latch) {
+ /* Invalid characters in input data */
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ for (i = 0; i < generalField.length() - 1; i++) {
+ if ((generalFieldType[i] == gfMode.ISOIEC) && (generalField.charAt(i + 1) == '[')) {
+ generalFieldType[i + 1] = gfMode.ISOIEC;
+ }
+ }
+
+ for (i = 0; i < generalField.length() - 1; i++) {
+ if ((generalFieldType[i] == gfMode.ALPHA_OR_ISO) && (generalField.charAt(i + 1) == '[')) {
+ generalFieldType[i + 1] = gfMode.ALPHA_OR_ISO;
+ }
+ }
+
+ latch = applyGeneralFieldRules();
+
+ i = 0;
+ do {
+ switch (generalFieldType[i]) {
+ case NUMERIC:
+ if (i != 0) {
+ if ((generalFieldType[i - 1] != gfMode.NUMERIC) && (generalField.charAt(i - 1) != '[')) {
+ binaryString.append("000"); /* Numeric latch */
+ }
+ }
+
+ if (generalField.charAt(i) != '[') {
+ d1 = generalField.charAt(i) - '0';
+ } else {
+ d1 = 10;
+ }
+
+ if (i < generalField.length() - 1) {
+ if (generalField.charAt(i + 1) != '[') {
+ d2 = generalField.charAt(i + 1) - '0';
+ } else {
+ d2 = 10;
+ }
+ } else {
+ d2 = 10;
+ }
+
+ if ((d1 != 10) || (d2 != 10)) {
+ /* If (d1==10)&&(d2==10) then input is either FNC1,FNC1 or FNC1,EOL */
+ value = (11 * d1) + d2 + 8;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & 0x40 >> j) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+
+ i += 2;
+ }
+ break;
+
+ case ALPHA:
+ if (i != 0) {
+ if ((generalFieldType[i - 1] == gfMode.NUMERIC) || (generalField.charAt(i - 1) == '[')) {
+ binaryString.append("0000"); /* Alphanumeric latch */
+ }
+ if (generalFieldType[i - 1] == gfMode.ISOIEC) {
+ binaryString.append("00100"); /* ISO/IEC 646 latch */
+ }
+ }
+
+ if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) {
+
+ value = generalField.charAt(i) - 43;
+
+ for (j = 0; j < 5; j++) {
+ if ((value & (0x10 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) {
+
+ value = generalField.charAt(i) - 33;
+
+ for (j = 0; j < 6; j++) {
+ if ((value & (0x20 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if (generalField.charAt(i) == '[') {
+ binaryString.append("01111"); /* FNC1/Numeric latch */
+ }
+ if (generalField.charAt(i) == '*') {
+ binaryString.append("111010"); /* asterisk */
+ }
+ if (generalField.charAt(i) == ',') {
+ binaryString.append("111011"); /* comma */
+ }
+ if (generalField.charAt(i) == '-') {
+ binaryString.append("111100"); /* minus or hyphen */
+ }
+ if (generalField.charAt(i) == '.') {
+ binaryString.append("111101"); /* period or full stop */
+ }
+ if (generalField.charAt(i) == '/') {
+ binaryString.append("111110"); /* slash or solidus */
+ }
+
+ i++;
+ break;
+
+ case ISOIEC:
+ if (i != 0) {
+ if ((generalFieldType[i - 1] == gfMode.NUMERIC) || (generalField.charAt(i - 1) == '[')) {
+ binaryString.append("0000"); /* Alphanumeric latch */
+ binaryString.append("00100"); /* ISO/IEC 646 latch */
+ }
+ if (generalFieldType[i - 1] == gfMode.ALPHA) {
+ binaryString.append("00100"); /* ISO/IEC 646 latch */
+ }
+ }
+
+ if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) {
+
+ value = generalField.charAt(i) - 43;
+
+ for (j = 0; j < 5; j++) {
+ if ((value & (0x10 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) {
+
+ value = generalField.charAt(i) - 1;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if ((generalField.charAt(i) >= 'a') && (generalField.charAt(i) <= 'z')) {
+
+ value = generalField.charAt(i) - 7;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if (generalField.charAt(i) == '[') {
+ binaryString.append("01111"); /* FNC1/Numeric latch */
+ }
+ if (generalField.charAt(i) == '!') {
+ binaryString.append("11101000"); /* exclamation mark */
+ }
+ if (generalField.charAt(i) == 34) {
+ binaryString.append("11101001"); /* quotation mark */
+ }
+ if (generalField.charAt(i) == 37) {
+ binaryString.append("11101010"); /* percent sign */
+ }
+ if (generalField.charAt(i) == '&') {
+ binaryString.append("11101011"); /* ampersand */
+ }
+ if (generalField.charAt(i) == 39) {
+ binaryString.append("11101100"); /* apostrophe */
+ }
+ if (generalField.charAt(i) == '(') {
+ binaryString.append("11101101"); /* left parenthesis */
+ }
+ if (generalField.charAt(i) == ')') {
+ binaryString.append("11101110"); /* right parenthesis */
+ }
+ if (generalField.charAt(i) == '*') {
+ binaryString.append("11101111"); /* asterisk */
+ }
+ if (generalField.charAt(i) == '+') {
+ binaryString.append("11110000"); /* plus sign */
+ }
+ if (generalField.charAt(i) == ',') {
+ binaryString.append("11110001"); /* comma */
+ }
+ if (generalField.charAt(i) == '-') {
+ binaryString.append("11110010"); /* minus or hyphen */
+ }
+ if (generalField.charAt(i) == '.') {
+ binaryString.append("11110011"); /* period or full stop */
+ }
+ if (generalField.charAt(i) == '/') {
+ binaryString.append("11110100"); /* slash or solidus */
+ }
+ if (generalField.charAt(i) == ':') {
+ binaryString.append("11110101"); /* colon */
+ }
+ if (generalField.charAt(i) == ';') {
+ binaryString.append("11110110"); /* semicolon */
+ }
+ if (generalField.charAt(i) == '<') {
+ binaryString.append("11110111"); /* less-than sign */
+ }
+ if (generalField.charAt(i) == '=') {
+ binaryString.append("11111000"); /* equals sign */
+ }
+ if (generalField.charAt(i) == '>') {
+ binaryString.append("11111001"); /* greater-than sign */
+ }
+ if (generalField.charAt(i) == '?') {
+ binaryString.append("11111010"); /* question mark */
+ }
+ if (generalField.charAt(i) == '_') {
+ binaryString.append("11111011"); /* underline or low line */
+ }
+ if (generalField.charAt(i) == ' ') {
+ binaryString.append("11111100"); /* space */
+ }
+ i++;
+ break;
+ }
+
+
+ latchOffset = 0;
+ if (latch) {
+ latchOffset = 1;
+ }
+ } while ((i + latchOffset) < generalField.length());
+ }
+
+ if (calculateSymbolSize()) {
+ return false;
+ }
+
+ if (latch) {
+ i = generalField.length() - 1;
+ /* There is still one more numeric digit to encode */
+
+ if (generalField.charAt(i) == '[') {
+ binaryString.append("000001111");
+ } else {
+ if ((remainder >= 4) && (remainder <= 6)) {
+ d1 = generalField.charAt(i) - '0';
+ for (j = 0; j < 4; j++) {
+ if ((value & (0x08 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ } else {
+ d1 = generalField.charAt(i) - '0';
+ d2 = 10;
+
+ value = (11 * d1) + d2 + 8;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) == 0x00) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ /* This may push the symbol up to the next size */
+ }
+ }
+ }
+
+ if (binaryString.length() > 11805) { /* (2361 * 5) */
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ /* size of the symbol may have changed when adding data in the above sequence */
+ if (calculateSymbolSize()) {
+ return false;
+ }
+
+ encodeInfo.append("Composite Binary Length: ").append(Integer.toString(binaryString.length())).append("\n");
+ displayBinaryString();
+
+ if (binaryString.length() < targetBitsize) {
+ /* Now add padding to binary string */
+ if (alphaPad == 1) {
+ binaryString.append("11111");
+ /* Extra FNC1 character required after Alpha encodation (section 5.2.3) */
+ }
+
+ if ((generalField.length() != 0) && (generalFieldType[generalField.length() - 1] == gfMode.NUMERIC)) {
+ binaryString.append("0000");
+ }
+
+ while (binaryString.length() < targetBitsize) {
+ binaryString.append("00100");
+ }
+
+ binaryString = new StringBuilder(binaryString.substring(0, targetBitsize));
+ }
+
+ return true;
+ }
+
+ private void displayBinaryString() {
+ int i, nibble;
+ /* Display binary string as hexadecimal */
+
+ encodeInfo.append("Composite Binary String: ");
+ nibble = 0;
+ for (i = 0; i < binaryString.length(); i++) {
+ switch (i % 4) {
+ case 0:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 8;
+ }
+ break;
+ case 1:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 4;
+ }
+ break;
+ case 2:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 2;
+ }
+ break;
+ case 3:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 1;
+ }
+ encodeInfo.append(Integer.toHexString(nibble));
+ nibble = 0;
+ break;
+ }
+ }
+
+ if ((binaryString.length() % 4) != 0) {
+ encodeInfo.append(Integer.toHexString(nibble));
+ }
+ encodeInfo.append("\n");
+ }
+
+ private boolean applyGeneralFieldRules() {
+ /* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3
+ of ISO/IEC 24724:2006 */
+
+ int blockCount, i, j, k;
+ gfMode current, next, last;
+ int[] blockLength = new int[200];
+ gfMode[] blockType = new gfMode[200];
+
+ blockCount = 0;
+
+ blockLength[blockCount] = 1;
+ blockType[blockCount] = generalFieldType[0];
+
+ for (i = 1; i < generalField.length(); i++) {
+ current = generalFieldType[i];
+ last = generalFieldType[i - 1];
+
+ if (current == last) {
+ blockLength[blockCount] = blockLength[blockCount] + 1;
+ } else {
+ blockCount++;
+ blockLength[blockCount] = 1;
+ blockType[blockCount] = generalFieldType[i];
+ }
+ }
+
+ blockCount++;
+
+ for (i = 0; i < blockCount; i++) {
+ current = blockType[i];
+ next = blockType[i + 1];
+
+ if ((current == gfMode.ISOIEC) && (i != (blockCount - 1))) {
+ if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] >= 4)) {
+ blockType[i + 1] = gfMode.NUMERIC;
+ }
+ if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] < 4)) {
+ blockType[i + 1] = gfMode.ISOIEC;
+ }
+ if ((next == gfMode.ALPHA_OR_ISO) && (blockLength[i + 1] >= 5)) {
+ blockType[i + 1] = gfMode.ALPHA;
+ }
+ if ((next == gfMode.ALPHA_OR_ISO) && (blockLength[i + 1] < 5)) {
+ blockType[i + 1] = gfMode.ISOIEC;
+ }
+ }
+
+ if (current == gfMode.ALPHA_OR_ISO) {
+ blockType[i] = gfMode.ALPHA;
+ }
+
+ if ((current == gfMode.ALPHA) && (i != (blockCount - 1))) {
+ if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] >= 6)) {
+ blockType[i + 1] = gfMode.NUMERIC;
+ }
+ if ((next == gfMode.ANY_ENC) && (blockLength[i + 1] < 6)) {
+ if ((i == blockCount - 2) && (blockLength[i + 1] >= 4)) {
+ blockType[i + 1] = gfMode.NUMERIC;
+ } else {
+ blockType[i + 1] = gfMode.ALPHA;
+ }
+ }
+ }
+
+ if (current == gfMode.ANY_ENC) {
+ blockType[i] = gfMode.NUMERIC;
+ }
+ }
+
+ if (blockCount > 1) {
+ i = 1;
+ while (i < blockCount) {
+ if (blockType[i - 1] == blockType[i]) {
+ /* bring together */
+ blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
+ j = i + 1;
+
+ /* decreace the list */
+ while (j < blockCount) {
+ blockLength[j - 1] = blockLength[j];
+ blockType[j - 1] = blockType[j];
+ j++;
+ }
+ blockCount--;
+ i--;
+ }
+ i++;
+ }
+ }
+
+ for (i = 0; i < blockCount - 1; i++) {
+ if ((blockType[i] == gfMode.NUMERIC) && ((blockLength[i] & 1) != 0)) {
+ /* Odd size numeric block */
+ blockLength[i] = blockLength[i] - 1;
+ blockLength[i + 1] = blockLength[i + 1] + 1;
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < blockCount; i++) {
+ for (k = 0; k < blockLength[i]; k++) {
+ generalFieldType[j] = blockType[i];
+ j++;
+ }
+ }
+
+ /* If the last block is numeric and an odd size, further
+ processing needs to be done outside this procedure */
+ return (blockType[blockCount - 1] == gfMode.NUMERIC) && ((blockLength[blockCount - 1] & 1) != 0);
+ }
+
+ private void ccA() {
+ /* CC-A 2D component */
+ int i, strpos, segment, cwCnt, variant, rows;
+ int k, offset, j, total;
+ int[] rsCodeWords = new int[8];
+ int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster;
+ int LeftRAP, RightRAP, CentreRAP, Cluster;
+ int[] dummy = new int[5];
+ int flip, loop;
+ String codebarre;
+ StringBuilder bin;
+ StringBuilder localSource; /* A copy of source but with padding zeroes to make 208 bits */
+
+ variant = 0;
+
+ for (i = 0; i < 13; i++) {
+ bitStr[i] = 0;
+ }
+ for (i = 0; i < 28; i++) {
+ codeWords[i] = 0;
+ }
+
+ localSource = binaryString;
+ for (i = binaryString.length(); i < 208; i++) {
+ localSource.append("0");
+ }
+
+ for (segment = 0; segment < 13; segment++) {
+ strpos = segment * 16;
+ bitStr[segment] = 0;
+ for (i = 0; i < 16; i++) {
+ if (localSource.charAt(strpos + i) == '1') {
+ bitStr[segment] += 0x8000 >> i;
+ }
+ }
+ }
+
+ init928();
+ /* encode codeWords from bitStr */
+ cwCnt = encode928(binaryString.length());
+
+ switch (ccWidth) {
+ case 2:
+ switch (cwCnt) {
+ case 6:
+ variant = 0;
+ break;
+ case 8:
+ variant = 1;
+ break;
+ case 9:
+ variant = 2;
+ break;
+ case 11:
+ variant = 3;
+ break;
+ case 12:
+ variant = 4;
+ break;
+ case 14:
+ variant = 5;
+ break;
+ case 17:
+ variant = 6;
+ break;
+ }
+ break;
+ case 3:
+ switch (cwCnt) {
+ case 8:
+ variant = 7;
+ break;
+ case 10:
+ variant = 8;
+ break;
+ case 12:
+ variant = 9;
+ break;
+ case 14:
+ variant = 10;
+ break;
+ case 17:
+ variant = 11;
+ break;
+ }
+ break;
+ case 4:
+ switch (cwCnt) {
+ case 8:
+ variant = 12;
+ break;
+ case 11:
+ variant = 13;
+ break;
+ case 14:
+ variant = 14;
+ break;
+ case 17:
+ variant = 15;
+ break;
+ case 20:
+ variant = 16;
+ break;
+ }
+ break;
+ }
+
+ rows = ccaVariants[variant];
+ k = ccaVariants[17 + variant];
+ offset = ccaVariants[34 + variant];
+
+ /* Reed-Solomon error correction */
+
+ for (i = 0; i < 8; i++) {
+ rsCodeWords[i] = 0;
+ }
+ total = 0;
+ encodeInfo.append("Composite Codewords: ");
+ for (i = 0; i < cwCnt; i++) {
+ total = (codeWords[i] + rsCodeWords[k - 1]) % 929;
+ for (j = k - 1; j >= 0; j--) {
+ if (j == 0) {
+ rsCodeWords[j] = (929 - (total * ccaCoeffs[offset + j]) % 929) % 929;
+ } else {
+ rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - (total * ccaCoeffs[offset + j]) % 929) % 929;
+ }
+ }
+ encodeInfo.append(Integer.toString(codeWords[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ for (j = 0; j < k; j++) {
+ if (rsCodeWords[j] != 0) {
+ rsCodeWords[j] = 929 - rsCodeWords[j];
+ }
+ }
+
+ for (i = k - 1; i >= 0; i--) {
+ codeWords[cwCnt] = rsCodeWords[i];
+ cwCnt++;
+ }
+
+ /* Place data into table */
+ LeftRAPStart = aRAPTable[variant];
+ CentreRAPStart = aRAPTable[variant + 17];
+ RightRAPStart = aRAPTable[variant + 34];
+ StartCluster = aRAPTable[variant + 51] / 3;
+
+ LeftRAP = LeftRAPStart;
+ CentreRAP = CentreRAPStart;
+ RightRAP = RightRAPStart;
+ Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
+
+ readable = new StringBuilder();
+ rowCount = rows;
+ pattern = new String[rowCount];
+ rowHeight = new int[rowCount];
+
+ for (i = 0; i < rows; i++) {
+ codebarre = "";
+ offset = 929 * Cluster;
+ for (j = 0; j < 5; j++) {
+ dummy[j] = 0;
+ }
+ for (j = 0; j < ccWidth; j++) {
+ dummy[j + 1] = codeWords[i * ccWidth + j];
+ }
+ /* Copy the data into codebarre */
+ codebarre += RAPLR[LeftRAP];
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[1]];
+ codebarre += "1";
+ if (ccWidth == 3) {
+ codebarre += RAPC[CentreRAP];
+ }
+ if (ccWidth >= 2) {
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[2]];
+ codebarre += "1";
+ }
+ if (ccWidth == 4) {
+ codebarre += RAPC[CentreRAP];
+ }
+ if (ccWidth >= 3) {
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[3]];
+ codebarre += "1";
+ }
+ if (ccWidth == 4) {
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[4]];
+ codebarre += "1";
+ }
+ codebarre += RAPLR[RightRAP];
+ codebarre += "1"; /* stop */
+
+ /* Now codebarre is a mixture of letters and numbers */
+
+ flip = 1;
+ bin = new StringBuilder();
+ for (loop = 0; loop < codebarre.length(); loop++) {
+ if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) {
+ for (k = 0; k < codebarre.charAt(loop) - '0'; k++) {
+ if (flip == 0) {
+ bin.append('0');
+ } else {
+ bin.append('1');
+ }
+ }
+ if (flip == 0) {
+ flip = 1;
+ } else {
+ flip = 0;
+ }
+ } else {
+ bin.append(PDFttf[positionOf(codebarre.charAt(loop), brSet)]);
+ }
+ }
+
+ rowHeight[i] = 2;
+ pattern[i] = bin2pat(bin.toString());
+
+ /* Set up RAPs and Cluster for next row */
+ LeftRAP++;
+ CentreRAP++;
+ RightRAP++;
+ Cluster++;
+
+ if (LeftRAP == 53) {
+ LeftRAP = 1;
+ }
+ if (CentreRAP == 53) {
+ CentreRAP = 1;
+ }
+ if (RightRAP == 53) {
+ RightRAP = 1;
+ }
+ if (Cluster == 3) {
+ Cluster = 0;
+ }
+ }
+ }
+
+ /* initialize pwr928 encoding table */
+ private void init928() {
+ int i, j, v;
+ int[] cw = new int[7];
+ cw[6] = 1;
+ for (i = 5; i >= 0; i--) {
+ cw[i] = 0;
+ }
+
+ for (i = 0; i < 7; i++) {
+ pwr928[0][i] = cw[i];
+ }
+ for (j = 1; j < 69; j++) {
+ for (v = 0, i = 6; i >= 1; i--) {
+ v = (2 * cw[i]) + (v / 928);
+ pwr928[j][i] = cw[i] = v % 928;
+ }
+ pwr928[j][0] = cw[0] = (2 * cw[0]) + (v / 928);
+ }
+ }
+
+ /* converts bit string to base 928 values, codeWords[0] is highest order */
+ int encode928(int bitLng) {
+ int i, j, b, bitCnt, cwNdx, cwCnt, cwLng;
+ for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) {
+ bitCnt = min(bitLng - b, 69);
+ cwLng += cwCnt = bitCnt / 10 + 1;
+ for (i = 0; i < cwCnt; i++) {
+ codeWords[cwNdx + i] = 0; /* init 0 */
+ }
+ for (i = 0; i < bitCnt; i++) {
+ if (getBit(b + bitCnt - i - 1)) {
+ for (j = 0; j < cwCnt; j++) {
+ codeWords[cwNdx + j] += pwr928[i][j + 7 - cwCnt];
+ }
+ }
+ }
+ for (i = cwCnt - 1; i > 0; i--) {
+ /* add "carries" */
+ codeWords[cwNdx + i - 1] += codeWords[cwNdx + i] / 928L;
+ codeWords[cwNdx + i] %= 928L;
+ }
+ }
+ return (cwLng);
+ }
+
+ private int min(int first, int second) {
+ if (first <= second) {
+ return first;
+ } else {
+ return second;
+ }
+ }
+
+ /* gets bit in bitString at bitPos */
+ private boolean getBit(int arg) {
+ return (bitStr[arg >> 4] & (0x8000 >> (arg & 15))) != 0;
+ }
+
+ private void ccB() {
+ /* CC-B 2D component */
+ int length, i, binloc;
+ int k, j, longueur, offset;
+ int[] mccorrection = new int[50];
+ int total;
+ int[] dummy = new int[5];
+ String codebarre;
+ String bin;
+ int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
+ int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop;
+ int option2, rows;
+ inputData = new int[(binaryString.length() / 8) + 3];
+
+ length = binaryString.length() / 8;
+
+ for (i = 0; i < length; i++) {
+ binloc = i * 8;
+
+ inputData[i] = 0;
+ for (j = 0; j < 8; j++) {
+ if (binaryString.charAt(binloc + j) == '1') {
+ inputData[i] += 0x80 >> j;
+ }
+ }
+ }
+
+ codeWordCount = 0;
+
+ /* "the CC-B component shall have codeword 920 in the first symbol character position" (section 9a) */
+ codeWords[codeWordCount] = 920;
+ codeWordCount++;
+
+ byteprocess(0, length);
+
+ /* Now figure out which variant of the symbol to use and load values accordingly */
+
+ variant = 0;
+
+ if (ccWidth == 2) {
+ variant = 13;
+ if (codeWordCount <= 33) {
+ variant = 12;
+ }
+ if (codeWordCount <= 29) {
+ variant = 11;
+ }
+ if (codeWordCount <= 24) {
+ variant = 10;
+ }
+ if (codeWordCount <= 19) {
+ variant = 9;
+ }
+ if (codeWordCount <= 13) {
+ variant = 8;
+ }
+ if (codeWordCount <= 8) {
+ variant = 7;
+ }
+ }
+
+ if (ccWidth == 3) {
+ variant = 23;
+ if (codeWordCount <= 70) {
+ variant = 22;
+ }
+ if (codeWordCount <= 58) {
+ variant = 21;
+ }
+ if (codeWordCount <= 46) {
+ variant = 20;
+ }
+ if (codeWordCount <= 34) {
+ variant = 19;
+ }
+ if (codeWordCount <= 24) {
+ variant = 18;
+ }
+ if (codeWordCount <= 18) {
+ variant = 17;
+ }
+ if (codeWordCount <= 14) {
+ variant = 16;
+ }
+ if (codeWordCount <= 10) {
+ variant = 15;
+ }
+ if (codeWordCount <= 6) {
+ variant = 14;
+ }
+ }
+
+ if (ccWidth == 4) {
+ variant = 34;
+ if (codeWordCount <= 108) {
+ variant = 33;
+ }
+ if (codeWordCount <= 90) {
+ variant = 32;
+ }
+ if (codeWordCount <= 72) {
+ variant = 31;
+ }
+ if (codeWordCount <= 54) {
+ variant = 30;
+ }
+ if (codeWordCount <= 39) {
+ variant = 29;
+ }
+ if (codeWordCount <= 30) {
+ variant = 28;
+ }
+ if (codeWordCount <= 24) {
+ variant = 27;
+ }
+ if (codeWordCount <= 18) {
+ variant = 26;
+ }
+ if (codeWordCount <= 12) {
+ variant = 25;
+ }
+ if (codeWordCount <= 8) {
+ variant = 24;
+ }
+ }
+
+ /* Now we have the variant we can load the data - from here on the same as MicroPDF417 code */
+ variant--;
+ option2 = MicroVariants[variant]; /* columns */
+ rows = MicroVariants[variant + 34]; /* rows */
+ k = MicroVariants[variant + 68]; /* number of EC CWs */
+ longueur = (option2 * rows) - k; /* number of non-EC CWs */
+ i = longueur - codeWordCount; /* amount of padding required */
+ offset = MicroVariants[variant + 102]; /* coefficient offset */
+
+ /* We add the padding */
+ while (i > 0) {
+ codeWords[codeWordCount] = 900;
+ codeWordCount++;
+ i--;
+ }
+
+ /* Reed-Solomon error correction */
+ longueur = codeWordCount;
+ for (loop = 0; loop < 50; loop++) {
+ mccorrection[loop] = 0;
+ }
+ encodeInfo.append("Composite Codewords: ");
+ for (i = 0; i < longueur; i++) {
+ total = (codeWords[i] + mccorrection[k - 1]) % 929;
+ for (j = k - 1; j >= 0; j--) {
+ if (j == 0) {
+ mccorrection[j] = (929 - (total * Microcoeffs[offset + j]) % 929) % 929;
+ } else {
+ mccorrection[j] = (mccorrection[j - 1] + 929 - (total * Microcoeffs[offset + j]) % 929) % 929;
+ }
+ }
+ encodeInfo.append(Integer.toString(codeWords[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ for (j = 0; j < k; j++) {
+ if (mccorrection[j] != 0) {
+ mccorrection[j] = 929 - mccorrection[j];
+ }
+ }
+ /* we add these codes to the string */
+ for (i = k - 1; i >= 0; i--) {
+ codeWords[codeWordCount] = mccorrection[i];
+ codeWordCount++;
+ }
+
+ /* Now get the RAP (Row Address Pattern) start values */
+ LeftRAPStart = RAPTable[variant];
+ CentreRAPStart = RAPTable[variant + 34];
+ RightRAPStart = RAPTable[variant + 68];
+ StartCluster = RAPTable[variant + 102] / 3;
+
+ /* That's all values loaded, get on with the encoding */
+
+ LeftRAP = LeftRAPStart;
+ CentreRAP = CentreRAPStart;
+ RightRAP = RightRAPStart;
+ Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
+
+ readable = new StringBuilder();
+ rowCount = rows;
+ pattern = new String[rowCount];
+ rowHeight = new int[rowCount];
+
+ for (i = 0; i < rows; i++) {
+ codebarre = "";
+ offset = 929 * Cluster;
+ for (j = 0; j < 5; j++) {
+ dummy[j] = 0;
+ }
+ for (j = 0; j < option2; j++) {
+ dummy[j + 1] = codeWords[i * option2 + j];
+ }
+ /* Copy the data into codebarre */
+ codebarre += RAPLR[LeftRAP];
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[1]];
+ codebarre += "1";
+ if (ccWidth == 3) {
+ codebarre += RAPC[CentreRAP];
+ }
+ if (ccWidth >= 2) {
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[2]];
+ codebarre += "1";
+ }
+ if (ccWidth == 4) {
+ codebarre += RAPC[CentreRAP];
+ }
+ if (ccWidth >= 3) {
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[3]];
+ codebarre += "1";
+ }
+ if (ccWidth == 4) {
+ codebarre += "1";
+ codebarre += codagemc[offset + dummy[4]];
+ codebarre += "1";
+ }
+ codebarre += RAPLR[RightRAP];
+ codebarre += "1"; /* stop */
+
+ /* Now codebarre is a mixture of letters and numbers */
+
+ flip = 1;
+ bin = "";
+ for (loop = 0; loop < codebarre.length(); loop++) {
+ if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) {
+ for (k = 0; k < codebarre.charAt(loop) - '0'; k++) {
+ if (flip == 0) {
+ bin += '0';
+ } else {
+ bin += '1';
+ }
+ }
+ if (flip == 0) {
+ flip = 1;
+ } else {
+ flip = 0;
+ }
+ } else {
+ bin += PDFttf[positionOf(codebarre.charAt(loop), brSet)];
+ }
+ }
+
+ pattern[i] = bin2pat(bin);
+ rowHeight[i] = 2;
+
+ /* Set up RAPs and Cluster for next row */
+ LeftRAP++;
+ CentreRAP++;
+ RightRAP++;
+ Cluster++;
+
+ if (LeftRAP == 53) {
+ LeftRAP = 1;
+ }
+ if (CentreRAP == 53) {
+ CentreRAP = 1;
+ }
+ if (RightRAP == 53) {
+ RightRAP = 1;
+ }
+ if (Cluster == 3) {
+ Cluster = 0;
+ }
+
+ }
+ }
+
+ private void ccC() {
+ /* CC-C 2D component - byte compressed PDF417 */
+ int length, i, binloc, k;
+ int offset, longueur, loop, total, j;
+ int[] mccorrection = new int[520];
+ int c1, c2, c3;
+ int[] dummy = new int[35];
+ String codebarre;
+ String bin;
+ inputData = new int[(binaryString.length() / 8) + 4];
+
+ length = binaryString.length() / 8;
+
+ for (i = 0; i < length; i++) {
+ binloc = i * 8;
+
+ inputData[i] = 0;
+ for (j = 0; j < 8; j++) {
+ if (binaryString.charAt(binloc + j) == '1') {
+ inputData[i] += 0x80 >> j;
+ }
+ }
+ }
+
+ codeWordCount = 0;
+
+ codeWords[codeWordCount] = 0; /* space for length descriptor */
+ codeWordCount++;
+ codeWords[codeWordCount] = 920; /* CC-C identifier */
+ codeWordCount++;
+
+ byteprocess(0, length);
+
+ codeWords[0] = codeWordCount;
+
+ k = 1;
+ for (i = 1; i <= (ecc + 1); i++) {
+ k *= 2;
+ }
+
+ /* 796 - we now take care of the Reed Solomon codes */
+ switch (ecc) {
+ case 1:
+ offset = 2;
+ break;
+ case 2:
+ offset = 6;
+ break;
+ case 3:
+ offset = 14;
+ break;
+ case 4:
+ offset = 30;
+ break;
+ case 5:
+ offset = 62;
+ break;
+ case 6:
+ offset = 126;
+ break;
+ case 7:
+ offset = 254;
+ break;
+ case 8:
+ offset = 510;
+ break;
+ default:
+ offset = 0;
+ break;
+ }
+
+ longueur = codeWordCount;
+ for (loop = 0; loop < 520; loop++) {
+ mccorrection[loop] = 0;
+ }
+ encodeInfo.append("Composite Codewords: ");
+ for (i = 0; i < longueur; i++) {
+ total = (codeWords[i] + mccorrection[k - 1]) % 929;
+ for (j = k - 1; j >= 0; j--) {
+ if (j == 0) {
+ mccorrection[j] = (929 - (total * coefrs[offset + j]) % 929) % 929;
+ } else {
+ mccorrection[j] = (mccorrection[j - 1] + 929 - (total * coefrs[offset + j]) % 929) % 929;
+ }
+ }
+ encodeInfo.append(Integer.toString(codeWords[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ for (j = 0; j < k; j++) {
+ if (mccorrection[j] != 0) {
+ mccorrection[j] = 929 - mccorrection[j];
+ }
+ }
+ /* we add these codes to the string */
+ for (i = k - 1; i >= 0; i--) {
+ codeWords[codeWordCount] = mccorrection[i];
+ codeWordCount++;
+ }
+
+ /* 818 - The CW string is finished */
+ c1 = (codeWordCount / ccWidth - 1) / 3;
+ c2 = ecc * 3 + (codeWordCount / ccWidth - 1) % 3;
+ c3 = ccWidth - 1;
+
+ readable = new StringBuilder();
+ rowCount = codeWordCount / ccWidth;
+ pattern = new String[rowCount];
+ rowHeight = new int[rowCount];
+
+ /* we now encode each row */
+ for (i = 0; i <= (codeWordCount / ccWidth) - 1; i++) {
+ for (j = 0; j < ccWidth; j++) {
+ dummy[j + 1] = codeWords[i * ccWidth + j];
+ }
+ k = (i / 3) * 30;
+ switch (i % 3) {
+ /* follows this pattern from US Patent 5,243,655:
+ Row 0: L0 (row #, # of rows) R0 (row #, # of columns)
+ Row 1: L1 (row #, security level) R1 (row #, # of rows)
+ Row 2: L2 (row #, # of columns) R2 (row #, security level)
+ Row 3: L3 (row #, # of rows) R3 (row #, # of columns)
+ etc. */
+ case 0:
+ dummy[0] = k + c1;
+ dummy[ccWidth + 1] = k + c3;
+ break;
+ case 1:
+ dummy[0] = k + c2;
+ dummy[ccWidth + 1] = k + c1;
+ break;
+ case 2:
+ dummy[0] = k + c3;
+ dummy[ccWidth + 1] = k + c2;
+ break;
+ }
+ codebarre = "+*"; /* Start with a start char and a separator */
+
+ for (j = 0; j <= ccWidth + 1; j++) {
+ switch (i % 3) {
+ case 1:
+ offset = 929; /* cluster(3) */
+ break;
+ case 2:
+ offset = 1858; /* cluster(6) */
+ break;
+ default:
+ offset = 0; /* cluster(0) */
+ break;
+ }
+ codebarre += codagemc[offset + dummy[j]];
+ codebarre += "*";
+ }
+ codebarre += "-";
+
+ bin = "";
+ for (loop = 0; loop < codebarre.length(); loop++) {
+ bin += PDFttf[positionOf(codebarre.charAt(loop), brSet)];
+ }
+ pattern[i] = bin2pat(bin);
+ rowHeight[i] = 3;
+ }
+ }
+
+ private void byteprocess(int start, int length) {
+ int len = 0;
+ int chunkLen = 0;
+ BigInteger mantisa;
+ BigInteger total;
+ BigInteger word;
+
+ /* select the switch for multiple of 6 bytes */
+ if ((binaryString.length() % 6) == 0) {
+ codeWords[codeWordCount++] = 924;
+ } else {
+ codeWords[codeWordCount++] = 901;
+ }
+
+ while (len < length) {
+ chunkLen = length - len;
+ if (6 <= chunkLen) /* Take groups of 6 */ {
+ chunkLen = 6;
+ len += chunkLen;
+ total = BigInteger.valueOf(0);
+
+ while ((chunkLen--) != 0) {
+ mantisa = BigInteger.valueOf(inputData[start++]);
+ total = total.or(mantisa.shiftLeft(chunkLen * 8));
+ }
+
+ chunkLen = 5;
+
+ while ((chunkLen--) != 0) {
+
+ word = total.mod(BigInteger.valueOf(900));
+ codeWords[codeWordCount + chunkLen] = word.intValue();
+ total = total.divide(BigInteger.valueOf(900));
+ }
+ codeWordCount += 5;
+ } else /* If it remain a group of less than 6 bytes */ {
+ len += chunkLen;
+ while ((chunkLen--) != 0) {
+ codeWords[codeWordCount++] = inputData[start++];
+ }
+ }
+ }
+ }
+
+ public enum LinearEncoding {
+ UPCA, UPCE, EAN, CODE_128, DATABAR_14, DATABAR_14_STACK,
+ DATABAR_14_STACK_OMNI, DATABAR_LIMITED, DATABAR_EXPANDED,
+ DATABAR_EXPANDED_STACK
+ }
+
+ private enum gfMode {
+ NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
+ }
+
+ public enum CompositeMode {
+ CC_A, CC_B, CC_C
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java
new file mode 100755
index 0000000..c553eb7
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java
@@ -0,0 +1,698 @@
+package org.xbib.graphics.barcode;
+
+import java.math.BigInteger;
+
+/**
+ * Implements GS1 DataBar Omnidirectional and GS1 DataBar Truncated according to ISO/IEC 24724:2011.
+ * Input data should be a 13 digit Global Trade Identification Number
+ * without check digit or Application Identifier [01].
+ */
+public class DataBar14 extends Symbol {
+
+ private int[] g_sum_table = {
+ 0, 161, 961, 2015, 2715, 0, 336, 1036, 1516
+ };
+ private int[] t_table = {
+ 1, 10, 34, 70, 126, 4, 20, 48, 81
+ };
+ private int[] widths = new int[8];
+ private int[] modules_odd = {
+ 12, 10, 8, 6, 4, 5, 7, 9, 11
+ };
+ private int[] modules_even = {
+ 4, 6, 8, 10, 12, 10, 8, 6, 4
+ };
+ private int[] widest_odd = {
+ 8, 6, 4, 3, 1, 2, 4, 6, 8
+ };
+ private int[] widest_even = {
+ 1, 3, 5, 6, 8, 7, 5, 3, 1
+ };
+ private int[] checksum_weight = { /* Table 5 */
+ 1, 3, 9, 27, 2, 6, 18, 54, 4, 12, 36, 29, 8, 24, 72, 58, 16, 48, 65,
+ 37, 32, 17, 51, 74, 64, 34, 23, 69, 49, 68, 46, 59
+ };
+ private int[] finder_pattern = {
+ 3, 8, 2, 1, 1, 3, 5, 5, 1, 1, 3, 3, 7, 1, 1, 3, 1, 9, 1, 1, 2, 7, 4,
+ 1, 1, 2, 5, 6, 1, 1, 2, 3, 8, 1, 1, 1, 5, 7, 1, 1, 1, 3, 9, 1, 1
+ };
+
+ private boolean linkageFlag;
+ private gb14Mode symbolType;
+
+ private boolean[][] grid = new boolean[5][100];
+ private boolean[] seperator = new boolean[100];
+
+ public DataBar14() {
+ linkageFlag = false;
+ symbolType = gb14Mode.LINEAR;
+ }
+
+ @Override
+ public void setDataType(DataType dummy) {
+ // Do nothing!
+ }
+
+ protected void setLinkageFlag() {
+ linkageFlag = true;
+ }
+
+ protected void unsetLinkageFlag() {
+ linkageFlag = false;
+ }
+
+ /**
+ * Set symbol type to DataBar-14
+ */
+ public void setLinearMode() {
+ symbolType = gb14Mode.LINEAR;
+ }
+
+ /**
+ * Set symbol type to DataBar-14 Omnidirectional
+ */
+ public void setOmnidirectionalMode() {
+ symbolType = gb14Mode.OMNI;
+ }
+
+ /**
+ * Set symbol type to DataBar-14 Omnidirectional Stacked
+ */
+ public void setStackedMode() {
+ symbolType = gb14Mode.STACKED;
+ }
+
+ @Override
+ public boolean encode() {
+ BigInteger accum;
+ BigInteger left_reg;
+ BigInteger right_reg;
+ int[] data_character = new int[4];
+ int[] data_group = new int[4];
+ int[] v_odd = new int[4];
+ int[] v_even = new int[4];
+ int i;
+ int[][] data_widths = new int[8][4];
+ int checksum;
+ int c_left;
+ int c_right;
+ int[] total_widths = new int[46];
+ int writer;
+ char latch;
+ int j;
+ int count;
+ int check_digit;
+ String hrt;
+ String bin;
+ int compositeOffset = 0;
+
+ if (content.length() > 13) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+?"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ accum = new BigInteger(content);
+ if (linkageFlag) {
+ accum = accum.add(new BigInteger("10000000000000"));
+ compositeOffset = 1;
+ }
+
+ /* Calculate left and right pair values */
+ left_reg = accum.divide(new BigInteger("4537077"));
+ right_reg = accum.mod(new BigInteger("4537077"));
+
+ /* Calculate four data characters */
+ accum = left_reg.divide(new BigInteger("1597"));
+ data_character[0] = accum.intValue();
+ accum = left_reg.mod(new BigInteger("1597"));
+ data_character[1] = accum.intValue();
+ accum = right_reg.divide(new BigInteger("1597"));
+ data_character[2] = accum.intValue();
+ accum = right_reg.mod(new BigInteger("1597"));
+ data_character[3] = accum.intValue();
+
+ encodeInfo.append("Data characters: ");
+ for (i = 0; i < 4; i++) {
+ encodeInfo.append(Integer.toString(data_character[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Calculate odd and even subset values */
+ if ((data_character[0] >= 0) && (data_character[0] <= 160)) {
+ data_group[0] = 0;
+ }
+ if ((data_character[0] >= 161) && (data_character[0] <= 960)) {
+ data_group[0] = 1;
+ }
+ if ((data_character[0] >= 961) && (data_character[0] <= 2014)) {
+ data_group[0] = 2;
+ }
+ if ((data_character[0] >= 2015) && (data_character[0] <= 2714)) {
+ data_group[0] = 3;
+ }
+ if ((data_character[0] >= 2715) && (data_character[0] <= 2840)) {
+ data_group[0] = 4;
+ }
+ if ((data_character[1] >= 0) && (data_character[1] <= 335)) {
+ data_group[1] = 5;
+ }
+ if ((data_character[1] >= 336) && (data_character[1] <= 1035)) {
+ data_group[1] = 6;
+ }
+ if ((data_character[1] >= 1036) && (data_character[1] <= 1515)) {
+ data_group[1] = 7;
+ }
+ if ((data_character[1] >= 1516) && (data_character[1] <= 1596)) {
+ data_group[1] = 8;
+ }
+ if ((data_character[3] >= 0) && (data_character[3] <= 335)) {
+ data_group[3] = 5;
+ }
+ if ((data_character[3] >= 336) && (data_character[3] <= 1035)) {
+ data_group[3] = 6;
+ }
+ if ((data_character[3] >= 1036) && (data_character[3] <= 1515)) {
+ data_group[3] = 7;
+ }
+ if ((data_character[3] >= 1516) && (data_character[3] <= 1596)) {
+ data_group[3] = 8;
+ }
+ if ((data_character[2] >= 0) && (data_character[2] <= 160)) {
+ data_group[2] = 0;
+ }
+ if ((data_character[2] >= 161) && (data_character[2] <= 960)) {
+ data_group[2] = 1;
+ }
+ if ((data_character[2] >= 961) && (data_character[2] <= 2014)) {
+ data_group[2] = 2;
+ }
+ if ((data_character[2] >= 2015) && (data_character[2] <= 2714)) {
+ data_group[2] = 3;
+ }
+ if ((data_character[2] >= 2715) && (data_character[2] <= 2840)) {
+ data_group[2] = 4;
+ }
+
+ v_odd[0] = (data_character[0] - g_sum_table[data_group[0]]) / t_table[data_group[0]];
+ v_even[0] = (data_character[0] - g_sum_table[data_group[0]]) % t_table[data_group[0]];
+ v_odd[1] = (data_character[1] - g_sum_table[data_group[1]]) % t_table[data_group[1]];
+ v_even[1] = (data_character[1] - g_sum_table[data_group[1]]) / t_table[data_group[1]];
+ v_odd[3] = (data_character[3] - g_sum_table[data_group[3]]) % t_table[data_group[3]];
+ v_even[3] = (data_character[3] - g_sum_table[data_group[3]]) / t_table[data_group[3]];
+ v_odd[2] = (data_character[2] - g_sum_table[data_group[2]]) / t_table[data_group[2]];
+ v_even[2] = (data_character[2] - g_sum_table[data_group[2]]) % t_table[data_group[2]];
+
+ /* Use RSS subset width algorithm */
+ for (i = 0; i < 4; i++) {
+ if ((i == 0) || (i == 2)) {
+ getWidths(v_odd[i], modules_odd[data_group[i]], 4, widest_odd[data_group[i]], 1);
+ data_widths[0][i] = widths[0];
+ data_widths[2][i] = widths[1];
+ data_widths[4][i] = widths[2];
+ data_widths[6][i] = widths[3];
+ getWidths(v_even[i], modules_even[data_group[i]], 4, widest_even[data_group[i]], 0);
+ data_widths[1][i] = widths[0];
+ data_widths[3][i] = widths[1];
+ data_widths[5][i] = widths[2];
+ data_widths[7][i] = widths[3];
+ } else {
+ getWidths(v_odd[i], modules_odd[data_group[i]], 4, widest_odd[data_group[i]], 0);
+ data_widths[0][i] = widths[0];
+ data_widths[2][i] = widths[1];
+ data_widths[4][i] = widths[2];
+ data_widths[6][i] = widths[3];
+ getWidths(v_even[i], modules_even[data_group[i]], 4, widest_even[data_group[i]], 1);
+ data_widths[1][i] = widths[0];
+ data_widths[3][i] = widths[1];
+ data_widths[5][i] = widths[2];
+ data_widths[7][i] = widths[3];
+ }
+ }
+
+ checksum = 0;
+ /* Calculate the checksum */
+ for (i = 0; i < 8; i++) {
+ checksum += checksum_weight[i] * data_widths[i][0];
+ checksum += checksum_weight[i + 8] * data_widths[i][1];
+ checksum += checksum_weight[i + 16] * data_widths[i][2];
+ checksum += checksum_weight[i + 24] * data_widths[i][3];
+ }
+ checksum %= 79;
+
+ /* Calculate the two check characters */
+ if (checksum >= 8) {
+ checksum++;
+ }
+ if (checksum >= 72) {
+ checksum++;
+ }
+ c_left = checksum / 9;
+ c_right = checksum % 9;
+
+ encodeInfo.append("Checksum: ").append(Integer.toString(checksum)).append("\n");
+
+ /* Put element widths together */
+ total_widths[0] = 1;
+ total_widths[1] = 1;
+ total_widths[44] = 1;
+ total_widths[45] = 1;
+ for (i = 0; i < 8; i++) {
+ total_widths[i + 2] = data_widths[i][0];
+ total_widths[i + 15] = data_widths[7 - i][1];
+ total_widths[i + 23] = data_widths[i][3];
+ total_widths[i + 36] = data_widths[7 - i][2];
+ }
+ for (i = 0; i < 5; i++) {
+ total_widths[i + 10] = finder_pattern[i + (5 * c_left)];
+ total_widths[i + 31] = finder_pattern[(4 - i) + (5 * c_right)];
+ }
+
+ rowCount = 0;
+ for (i = 0; i < 100; i++) {
+ seperator[i] = false;
+ }
+ /* Put this data into the symbol */
+ if (symbolType == gb14Mode.LINEAR) {
+ writer = 0;
+ latch = '0';
+ for (i = 0; i < 46; i++) {
+ for (j = 0; j < total_widths[i]; j++) {
+ if (latch == '1') {
+ setGridModule(rowCount, writer);
+ }
+ writer++;
+ }
+ if (latch == '1') {
+ latch = '0';
+ } else {
+ latch = '1';
+ }
+ }
+ if (symbolWidth < writer) {
+ symbolWidth = writer;
+ }
+
+ if (linkageFlag) {
+ /* separator pattern for composite symbol */
+ for (i = 4; i < 92; i++) {
+ seperator[i] = (!(grid[0][i]));
+ }
+ latch = '1';
+ for (i = 16; i < 32; i++) {
+ if (!(grid[0][i])) {
+ if (latch == '1') {
+ seperator[i] = true;
+ latch = '0';
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ }
+ latch = '1';
+ for (i = 63; i < 78; i++) {
+ if (!(grid[0][i])) {
+ if (latch == '1') {
+ seperator[i] = true;
+ latch = '0';
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ }
+ }
+ rowCount = rowCount + 1;
+
+ count = 0;
+ check_digit = 0;
+
+ /* Calculate check digit from Annex A and place human readable text */
+ readable = new StringBuilder("(01)");
+ hrt = "";
+ for (i = content.length(); i < 13; i++) {
+ hrt += "0";
+ }
+ hrt += content;
+
+ for (i = 0; i < 13; i++) {
+ count += hrt.charAt(i) - '0';
+
+ if ((i & 1) == 0) {
+ count += 2 * (hrt.charAt(i) - '0');
+ }
+ }
+
+ check_digit = 10 - (count % 10);
+ if (check_digit == 10) {
+ check_digit = 0;
+ }
+ hrt += (char) (check_digit + '0');
+
+ readable.append(hrt);
+ }
+
+ if (symbolType == gb14Mode.STACKED) {
+ /* top row */
+ writer = 0;
+ latch = '0';
+ for (i = 0; i < 23; i++) {
+ for (j = 0; j < total_widths[i]; j++) {
+ if (latch == '1') {
+ setGridModule(rowCount, writer);
+ } else {
+ unsetGridModule(rowCount, writer);
+ }
+ writer++;
+ }
+ if (latch == '1') {
+ latch = '0';
+ } else {
+ latch = '1';
+ }
+ }
+ setGridModule(rowCount, writer);
+ unsetGridModule(rowCount, writer + 1);
+
+ /* bottom row */
+ rowCount = rowCount + 2;
+ setGridModule(rowCount, 0);
+ unsetGridModule(rowCount, 1);
+ writer = 0;
+ latch = '1';
+ for (i = 23; i < 46; i++) {
+ for (j = 0; j < total_widths[i]; j++) {
+ if (latch == '1') {
+ setGridModule(rowCount, writer + 2);
+ } else {
+ unsetGridModule(rowCount, writer + 2);
+ }
+ writer++;
+ }
+ if (latch == '1') {
+ latch = '0';
+ } else {
+ latch = '1';
+ }
+ }
+
+ /* separator pattern */
+ for (i = 4; i < 46; i++) {
+ if (gridModuleIsSet(rowCount - 2, i) == gridModuleIsSet(rowCount, i)) {
+ if (!(gridModuleIsSet(rowCount - 2, i))) {
+ setGridModule(rowCount - 1, i);
+ }
+ } else {
+ if (!(gridModuleIsSet(rowCount - 1, i - 1))) {
+ setGridModule(rowCount - 1, i);
+ }
+ }
+ }
+
+ if (linkageFlag) {
+ /* separator pattern for composite symbol */
+ for (i = 4; i < 46; i++) {
+ seperator[i] = (!(grid[0][i]));
+ }
+ latch = '1';
+ for (i = 16; i < 32; i++) {
+ if (!(grid[0][i])) {
+ if (latch == '1') {
+ seperator[i] = true;
+ latch = '0';
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ }
+ }
+ rowCount = rowCount + 1;
+ if (symbolWidth < 50) {
+ symbolWidth = 50;
+ }
+ }
+
+ if (symbolType == gb14Mode.OMNI) {
+ /* top row */
+ writer = 0;
+ latch = '0';
+ for (i = 0; i < 23; i++) {
+ for (j = 0; j < total_widths[i]; j++) {
+ if (latch == '1') {
+ setGridModule(rowCount, writer);
+ } else {
+ unsetGridModule(rowCount, writer);
+ }
+ writer++;
+ }
+ latch = (latch == '1' ? '0' : '1');
+ }
+ setGridModule(rowCount, writer);
+ unsetGridModule(rowCount, writer + 1);
+
+ /* bottom row */
+ rowCount = rowCount + 4;
+ setGridModule(rowCount, 0);
+ unsetGridModule(rowCount, 1);
+ writer = 0;
+ latch = '1';
+ for (i = 23; i < 46; i++) {
+ for (j = 0; j < total_widths[i]; j++) {
+ if (latch == '1') {
+ setGridModule(rowCount, writer + 2);
+ } else {
+ unsetGridModule(rowCount, writer + 2);
+ }
+ writer++;
+ }
+ if (latch == '1') {
+ latch = '0';
+ } else {
+ latch = '1';
+ }
+ }
+
+ /* middle separator */
+ for (i = 5; i < 46; i += 2) {
+ setGridModule(rowCount - 2, i);
+ }
+
+ /* top separator */
+ for (i = 4; i < 46; i++) {
+ if (!(gridModuleIsSet(rowCount - 4, i))) {
+ setGridModule(rowCount - 3, i);
+ }
+ }
+ latch = '1';
+ for (i = 17; i < 33; i++) {
+ if (!(gridModuleIsSet(rowCount - 4, i))) {
+ if (latch == '1') {
+ setGridModule(rowCount - 3, i);
+ latch = '0';
+ } else {
+ unsetGridModule(rowCount - 3, i);
+ latch = '1';
+ }
+ } else {
+ unsetGridModule(rowCount - 3, i);
+ latch = '1';
+ }
+ }
+
+ /* bottom separator */
+ for (i = 4; i < 46; i++) {
+ if (!(gridModuleIsSet(rowCount, i))) {
+ setGridModule(rowCount - 1, i);
+ }
+ }
+ latch = '1';
+ for (i = 16; i < 32; i++) {
+ if (!(gridModuleIsSet(rowCount, i))) {
+ if (latch == '1') {
+ setGridModule(rowCount - 1, i);
+ latch = '0';
+ } else {
+ unsetGridModule(rowCount - 1, i);
+ latch = '1';
+ }
+ } else {
+ unsetGridModule(rowCount - 1, i);
+ latch = '1';
+ }
+ }
+
+ if (symbolWidth < 50) {
+ symbolWidth = 50;
+ }
+ if (linkageFlag) {
+ /* separator pattern for composite symbol */
+ for (i = 4; i < 46; i++) {
+ seperator[i] = (!(grid[0][i]));
+ }
+ latch = '1';
+ for (i = 16; i < 32; i++) {
+ if (!(grid[0][i])) {
+ if (latch == '1') {
+ seperator[i] = true;
+ latch = '0';
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ } else {
+ seperator[i] = false;
+ latch = '1';
+ }
+ }
+ }
+ rowCount = rowCount + 1;
+ }
+
+ pattern = new String[rowCount + compositeOffset];
+ rowHeight = new int[rowCount + compositeOffset];
+
+ if (linkageFlag) {
+ bin = "";
+ for (j = 0; j < symbolWidth; j++) {
+ if (seperator[j]) {
+ bin += "1";
+ } else {
+ bin += "0";
+ }
+ }
+ pattern[0] = bin2pat(bin);
+ rowHeight[0] = 1;
+ }
+
+ for (i = 0; i < rowCount; i++) {
+ bin = "";
+ for (j = 0; j < symbolWidth; j++) {
+ if (grid[i][j]) {
+ bin += "1";
+ } else {
+ bin += "0";
+ }
+ }
+ pattern[i + compositeOffset] = bin2pat(bin);
+ }
+
+ if (symbolType == gb14Mode.LINEAR) {
+ rowHeight[0 + compositeOffset] = -1;
+ }
+ if (symbolType == gb14Mode.STACKED) {
+ rowHeight[0 + compositeOffset] = 5;
+ rowHeight[1 + compositeOffset] = 1;
+ rowHeight[2 + compositeOffset] = 7;
+ }
+ if (symbolType == gb14Mode.OMNI) {
+ rowHeight[0 + compositeOffset] = -1;
+ rowHeight[1 + compositeOffset] = 1;
+ rowHeight[2 + compositeOffset] = 1;
+ rowHeight[3 + compositeOffset] = 1;
+ rowHeight[4 + compositeOffset] = -1;
+ }
+
+ if (linkageFlag) {
+ rowCount++;
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private int getCombinations(int n, int r) {
+ int i, j;
+ int maxDenom, minDenom;
+ int val;
+
+ if (n - r > r) {
+ minDenom = r;
+ maxDenom = n - r;
+ } else {
+ minDenom = n - r;
+ maxDenom = r;
+ }
+ val = 1;
+ j = 1;
+ for (i = n; i > maxDenom; i--) {
+ val *= i;
+ if (j <= minDenom) {
+ val /= j;
+ j++;
+ }
+ }
+ for (; j <= minDenom; j++) {
+ val /= j;
+ }
+ return (val);
+ }
+
+ private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) {
+ int bar;
+ int elmWidth;
+ int mxwElement;
+ int subVal, lessVal;
+ int narrowMask = 0;
+ for (bar = 0; bar < elements - 1; bar++) {
+ for (elmWidth = 1, narrowMask |= (1 << bar); ;
+ elmWidth++, narrowMask &= ~(1 << bar)) {
+ /* get all combinations */
+ subVal = getCombinations(n - elmWidth - 1, elements - bar - 2);
+ /* less combinations with no single-module element */
+ if ((noNarrow == 0) && (narrowMask == 0)
+ && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
+ subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2);
+ }
+ /* less combinations with elements > maxVal */
+ if (elements - bar - 1 > 1) {
+ lessVal = 0;
+ for (mxwElement = n - elmWidth - (elements - bar - 2);
+ mxwElement > maxWidth;
+ mxwElement--) {
+ lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3);
+ }
+ subVal -= lessVal * (elements - 1 - bar);
+ } else if (n - elmWidth > maxWidth) {
+ subVal--;
+ }
+ val -= subVal;
+ if (val < 0) break;
+ }
+ val += subVal;
+ n -= elmWidth;
+ widths[bar] = elmWidth;
+ }
+ widths[bar] = n;
+ }
+
+ private void setGridModule(int row, int column) {
+ grid[row][column] = true;
+ }
+
+ private void unsetGridModule(int row, int column) {
+ grid[row][column] = false;
+ }
+
+ private boolean gridModuleIsSet(int row, int column) {
+ return grid[row][column];
+ }
+
+ private enum gb14Mode {
+ LINEAR, OMNI, STACKED
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java
new file mode 100755
index 0000000..fe0da9d
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java
@@ -0,0 +1,1652 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements GS1 DataBar Expanded Omnidirectional and GS1 Expanded Stacked
+ * Omnidirectional according to ISO/IEC 24724:2011.
+ * DataBar expanded encodes GS1 data in either a linear or stacked
+ * format.
+ */
+public class DataBarExpanded extends Symbol {
+
+ private static final int[] G_SUM_EXP = {
+ 0, 348, 1388, 2948, 3988
+ };
+ private static final int[] T_EVEN_EXP = {
+ 4, 20, 52, 104, 204
+ };
+ private static final int[] MODULES_ODD_EXP = {
+ 12, 10, 8, 6, 4
+ };
+ private static final int[] MODULES_EVEN_EXP = {
+ 5, 7, 9, 11, 13
+ };
+ private static final int[] WIDEST_ODD_EXP = {
+ 7, 5, 4, 3, 1
+ };
+ private static final int[] WIDEST_EVEN_EXP = {
+ 2, 4, 5, 6, 8
+ };
+ private static final int[] CHECKSUM_WEIGHT_EXP = { /* Table 14 */
+ 1, 3, 9, 27, 81, 32, 96, 77, 20, 60, 180, 118, 143, 7, 21, 63, 189,
+ 145, 13, 39, 117, 140, 209, 205, 193, 157, 49, 147, 19, 57, 171, 91,
+ 62, 186, 136, 197, 169, 85, 44, 132, 185, 133, 188, 142, 4, 12, 36,
+ 108, 113, 128, 173, 97, 80, 29, 87, 50, 150, 28, 84, 41, 123, 158, 52,
+ 156, 46, 138, 203, 187, 139, 206, 196, 166, 76, 17, 51, 153, 37, 111,
+ 122, 155, 43, 129, 176, 106, 107, 110, 119, 146, 16, 48, 144, 10, 30,
+ 90, 59, 177, 109, 116, 137, 200, 178, 112, 125, 164, 70, 210, 208, 202,
+ 184, 130, 179, 115, 134, 191, 151, 31, 93, 68, 204, 190, 148, 22, 66,
+ 198, 172, 94, 71, 2, 6, 18, 54, 162, 64, 192, 154, 40, 120, 149, 25,
+ 75, 14, 42, 126, 167, 79, 26, 78, 23, 69, 207, 199, 175, 103, 98, 83,
+ 38, 114, 131, 182, 124, 161, 61, 183, 127, 170, 88, 53, 159, 55, 165,
+ 73, 8, 24, 72, 5, 15, 45, 135, 194, 160, 58, 174, 100, 89
+ };
+ private static final int[] FINDER_PATTERN_EXP = { /* Table 15 */
+ 1, 8, 4, 1, 1, 1, 1, 4, 8, 1, 3, 6, 4, 1, 1, 1, 1, 4, 6, 3, 3, 4, 6, 1,
+ 1, 1, 1, 6, 4, 3, 3, 2, 8, 1, 1, 1, 1, 8, 2, 3, 2, 6, 5, 1, 1, 1, 1, 5,
+ 6, 2, 2, 2, 9, 1, 1, 1, 1, 9, 2, 2
+ };
+ private static final int[] FINDER_SEQUENCE = { /* Table 16 */
+ 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6,
+ 3, 8, 0, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 5, 0, 0, 0, 0, 0, 0, 1, 10, 3,
+ 8, 7, 12, 0, 0, 0, 0, 0, 1, 10, 3, 8, 9, 12, 11, 0, 0, 0, 0, 1, 2, 3,
+ 4, 5, 6, 7, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 9, 0, 0, 1, 2, 3, 4,
+ 5, 6, 7, 10, 11, 12, 0, 1, 2, 3, 4, 5, 8, 7, 10, 9, 12, 11
+ };
+ private static final int[] WEIGHT_ROWS = {
+ 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6,
+ 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 3, 4,
+ 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13,
+ 14, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14,
+ 11, 12, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14,
+ 15, 16, 21, 22, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 17, 18, 15, 16, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 17, 18, 19, 20, 21, 22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 13, 14, 11, 12, 17, 18, 15, 16, 21, 22, 19, 20
+ };
+
+ private String source;
+ private StringBuilder binaryString = new StringBuilder();
+ private String generalField;
+ private EncodeMode[] generalFieldType;
+ private int[] widths = new int[8];
+ private boolean linkageFlag;
+
+ private int preferredNoOfColumns = 0;
+ private dbeMode symbolType;
+
+ public DataBarExpanded() {
+ linkageFlag = false;
+ inputDataType = DataType.GS1;
+ }
+
+ private static int calculateRemainder(int binaryStringLength) {
+ int remainder = 12 - (binaryStringLength % 12);
+ if (remainder == 12) {
+ remainder = 0;
+ }
+ if (binaryStringLength < 36) {
+ remainder = 36 - binaryStringLength;
+ }
+ return remainder;
+ }
+
+ /**
+ * Set the width of a stacked symbol by selecting the number
+ * of "columns" or symbol segments in each row of data.
+ *
+ * @param columns Number of segments in each row
+ */
+ public void setNoOfColumns(int columns) {
+ preferredNoOfColumns = columns;
+ }
+
+ ;
+
+ @Override
+ public void setDataType(DataType dummy) {
+ // Do nothing!
+ }
+
+ /**
+ * Set symbology to DataBar Expanded Stacked
+ */
+ public void setStacked() {
+ symbolType = dbeMode.STACKED;
+ }
+
+ /**
+ * Set symbology to DataBar Expanded
+ */
+ public void setNotStacked() {
+ symbolType = dbeMode.UNSTACKED;
+ }
+
+ protected void setLinkageFlag() {
+ linkageFlag = true;
+ }
+
+ protected void unsetLinkageFlag() {
+ linkageFlag = false;
+ }
+
+ @Override
+ public boolean encode() {
+ int i;
+ int j;
+ int k;
+ int dataChars;
+ int[] vs = new int[21];
+ int[] group = new int[21];
+ int[] vOdd = new int[21];
+ int[] vEven = new int[21];
+ int[][] charWidths = new int[21][8];
+ int checksum;
+ int row;
+ int checkChar;
+ int cGroup;
+ int cOdd;
+ int cEven;
+ int[] checkWidths = new int[8];
+ int patternWidth;
+ int[] elements = new int[235];
+ int codeblocks;
+ int stackRows;
+ int blocksPerRow;
+ int currentBlock;
+ int currentRow;
+ boolean specialCaseRow;
+ int elementsInSub;
+ int reader;
+ int writer;
+ int[] subElements = new int[235];
+ int l;
+ int symbolRow;
+ String seperatorBinary;
+ String seperatorPattern;
+ boolean black;
+ boolean leftToRight;
+ int compositeOffset;
+
+ source = content;
+
+ if (linkageFlag) {
+ binaryString = new StringBuilder("1");
+ compositeOffset = 1;
+ } else {
+ binaryString = new StringBuilder("0");
+ compositeOffset = 0;
+ }
+ if (!calculateBinaryString()) {
+ return false;
+ }
+
+ dataChars = binaryString.length() / 12;
+
+ encodeInfo.append("Data characters: ");
+ for (i = 0; i < dataChars; i++) {
+ vs[i] = 0;
+ for (j = 0; j < 12; j++) {
+ if (binaryString.charAt((i * 12) + j) == '1') {
+ vs[i] += 2048 >> j;
+ }
+ }
+ encodeInfo.append(Integer.toString(vs[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ for (i = 0; i < dataChars; i++) {
+ if (vs[i] <= 347) {
+ group[i] = 1;
+ }
+ if ((vs[i] >= 348) && (vs[i] <= 1387)) {
+ group[i] = 2;
+ }
+ if ((vs[i] >= 1388) && (vs[i] <= 2947)) {
+ group[i] = 3;
+ }
+ if ((vs[i] >= 2948) && (vs[i] <= 3987)) {
+ group[i] = 4;
+ }
+ if (vs[i] >= 3988) {
+ group[i] = 5;
+ }
+ vOdd[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) / T_EVEN_EXP[group[i] - 1];
+ vEven[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) % T_EVEN_EXP[group[i] - 1];
+
+ getWidths(vOdd[i], MODULES_ODD_EXP[group[i] - 1], 4, WIDEST_ODD_EXP[group[i] - 1], 0);
+ charWidths[i][0] = widths[0];
+ charWidths[i][2] = widths[1];
+ charWidths[i][4] = widths[2];
+ charWidths[i][6] = widths[3];
+ getWidths(vEven[i], MODULES_EVEN_EXP[group[i] - 1], 4, WIDEST_EVEN_EXP[group[i] - 1], 1);
+ charWidths[i][1] = widths[0];
+ charWidths[i][3] = widths[1];
+ charWidths[i][5] = widths[2];
+ charWidths[i][7] = widths[3];
+ }
+
+ /* 7.2.6 Check character */
+ /* The checksum value is equal to the mod 211 residue of the weighted sum of the widths of the
+ elements in the data characters. */
+ checksum = 0;
+ for (i = 0; i < dataChars; i++) {
+ row = WEIGHT_ROWS[(((dataChars - 2) / 2) * 21) + i];
+ for (j = 0; j < 8; j++) {
+ checksum += (charWidths[i][j] * CHECKSUM_WEIGHT_EXP[(row * 8) + j]);
+
+ }
+ }
+
+ checkChar = (211 * ((dataChars + 1) - 4)) + (checksum % 211);
+
+ encodeInfo.append("Check Character: ").append(Integer.toString(checkChar)).append("\n");
+
+ cGroup = 1;
+ if ((checkChar >= 348) && (checkChar <= 1387)) {
+ cGroup = 2;
+ }
+ if ((checkChar >= 1388) && (checkChar <= 2947)) {
+ cGroup = 3;
+ }
+ if ((checkChar >= 2948) && (checkChar <= 3987)) {
+ cGroup = 4;
+ }
+ if (checkChar >= 3988) {
+ cGroup = 5;
+ }
+
+ cOdd = (checkChar - G_SUM_EXP[cGroup - 1]) / T_EVEN_EXP[cGroup - 1];
+ cEven = (checkChar - G_SUM_EXP[cGroup - 1]) % T_EVEN_EXP[cGroup - 1];
+
+ getWidths(cOdd, MODULES_ODD_EXP[cGroup - 1], 4, WIDEST_ODD_EXP[cGroup - 1], 0);
+ checkWidths[0] = widths[0];
+ checkWidths[2] = widths[1];
+ checkWidths[4] = widths[2];
+ checkWidths[6] = widths[3];
+ getWidths(cEven, MODULES_EVEN_EXP[cGroup - 1], 4, WIDEST_EVEN_EXP[cGroup - 1], 1);
+ checkWidths[1] = widths[0];
+ checkWidths[3] = widths[1];
+ checkWidths[5] = widths[2];
+ checkWidths[7] = widths[3];
+
+ /* Initialise element array */
+ patternWidth = ((((dataChars + 1) / 2) + ((dataChars + 1) & 1)) * 5) + ((dataChars + 1) * 8) + 4;
+ for (i = 0; i < patternWidth; i++) {
+ elements[i] = 0;
+ }
+
+ elements[0] = 1;
+ elements[1] = 1;
+ elements[patternWidth - 2] = 1;
+ elements[patternWidth - 1] = 1;
+
+ /* Put finder patterns in element array */
+ for (i = 0; i < (((dataChars + 1) / 2) + ((dataChars + 1) & 1)); i++) {
+ k = ((((((dataChars + 1) - 2) / 2) + ((dataChars + 1) & 1)) - 1) * 11) + i;
+ for (j = 0; j < 5; j++) {
+ elements[(21 * i) + j + 10] = FINDER_PATTERN_EXP[((FINDER_SEQUENCE[k] - 1) * 5) + j];
+ }
+ }
+
+ /* Put check character in element array */
+ for (i = 0; i < 8; i++) {
+ elements[i + 2] = checkWidths[i];
+ }
+
+ /* Put forward reading data characters in element array */
+ for (i = 1; i < dataChars; i += 2) {
+ for (j = 0; j < 8; j++) {
+ elements[(((i - 1) / 2) * 21) + 23 + j] = charWidths[i][j];
+ }
+ }
+
+ /* Put reversed data characters in element array */
+ for (i = 0; i < dataChars; i += 2) {
+ for (j = 0; j < 8; j++) {
+ elements[((i / 2) * 21) + 15 + j] = charWidths[i][7 - j];
+ }
+ }
+
+
+ if (symbolType == dbeMode.UNSTACKED) {
+ /* Copy elements into symbol */
+ rowCount = 1 + compositeOffset;
+ rowHeight = new int[1 + compositeOffset];
+ rowHeight[0 + compositeOffset] = -1;
+ pattern = new String[1 + compositeOffset];
+
+ pattern[0 + compositeOffset] = "0";
+ writer = 0;
+ black = false;
+ seperatorBinary = "";
+ for (i = 0; i < patternWidth; i++) {
+ pattern[0 + compositeOffset] += (char) (elements[i] + '0');
+ for (j = 0; j < elements[i]; j++) {
+ if (black) {
+ seperatorBinary += "0";
+ } else {
+ seperatorBinary += "1";
+ }
+ }
+
+ black = !(black);
+ writer += elements[i];
+ }
+ seperatorBinary = "0000" + seperatorBinary.substring(4, writer - 4);
+ for (j = 0; j < (writer / 49); j++) {
+ k = (49 * j) + 18;
+ for (i = 0; i < 15; i++) {
+ if ((seperatorBinary.charAt(i + k - 1) == '1')
+ && (seperatorBinary.charAt(i + k) == '1')) {
+ seperatorBinary = seperatorBinary.substring(0, (i + k))
+ + "0" + seperatorBinary.substring(i + k + 1);
+ }
+ }
+ }
+ if (linkageFlag) {
+ // Add composite code seperator
+ pattern[0] = bin2pat(seperatorBinary);
+ rowHeight[0] = 1;
+ }
+
+ } else {
+ /* RSS Expanded Stacked */
+ codeblocks = (dataChars + 1) / 2 + ((dataChars + 1) % 2);
+
+ blocksPerRow = preferredNoOfColumns;
+ if ((blocksPerRow < 1) || (blocksPerRow > 10)) {
+ blocksPerRow = 2;
+ }
+
+ if (linkageFlag && (blocksPerRow == 1)) {
+ /* "There shall be a minimum of four symbol characters in the
+ first row of an RSS Expanded Stacked symbol when it is the linear
+ component of an EAN.UCC Composite symbol." */
+ blocksPerRow = 2;
+ }
+
+ stackRows = codeblocks / blocksPerRow;
+ if (codeblocks % blocksPerRow > 0) {
+ stackRows++;
+ }
+
+ rowCount = (stackRows * 4) - 3;
+ rowHeight = new int[rowCount + compositeOffset];
+ pattern = new String[rowCount + compositeOffset];
+ symbolRow = 0;
+
+ currentBlock = 0;
+ for (currentRow = 1; currentRow <= stackRows; currentRow++) {
+ for (i = 0; i < 235; i++) {
+ subElements[i] = 0;
+ }
+ specialCaseRow = false;
+
+ /* Row Start */
+ subElements[0] = 1;
+ subElements[1] = 1;
+ elementsInSub = 2;
+
+ /* Row Data */
+ reader = 0;
+ do {
+ if ((((blocksPerRow & 1) != 0) || ((currentRow & 1) != 0))
+ || ((currentRow == stackRows)
+ && (codeblocks != (currentRow * blocksPerRow))
+ && ((((currentRow * blocksPerRow) - codeblocks) & 1)) != 0)) {
+ /* left to right */
+ leftToRight = true;
+ i = 2 + (currentBlock * 21);
+ for (j = 0; j < 21; j++) {
+ if ((i + j) < patternWidth) {
+ subElements[j + (reader * 21) + 2] = elements[i + j];
+ elementsInSub++;
+ }
+ }
+ } else {
+ /* right to left */
+ leftToRight = false;
+ if ((currentRow * blocksPerRow) < codeblocks) {
+ /* a full row */
+ i = 2 + (((currentRow * blocksPerRow) - reader - 1) * 21);
+ for (j = 0; j < 21; j++) {
+ if ((i + j) < patternWidth) {
+ subElements[(20 - j) + (reader * 21) + 2] = elements[i + j];
+ elementsInSub++;
+ }
+ }
+ } else {
+ /* a partial row */
+ k = ((currentRow * blocksPerRow) - codeblocks);
+ l = (currentRow * blocksPerRow) - reader - 1;
+ i = 2 + ((l - k) * 21);
+ for (j = 0; j < 21; j++) {
+ if ((i + j) < patternWidth) {
+ subElements[(20 - j) + (reader * 21) + 2] = elements[i + j];
+ elementsInSub++;
+ }
+ }
+ }
+ }
+ reader++;
+ currentBlock++;
+ } while ((reader < blocksPerRow) && (currentBlock < codeblocks));
+
+ /* Row Stop */
+ subElements[elementsInSub] = 1;
+ subElements[elementsInSub + 1] = 1;
+ elementsInSub += 2;
+
+ pattern[symbolRow + compositeOffset] = "";
+ black = true;
+ rowHeight[symbolRow + compositeOffset] = -1;
+
+ if ((currentRow & 1) != 0) {
+ pattern[symbolRow + compositeOffset] = "0";
+ black = false;
+ } else {
+ if ((currentRow == stackRows)
+ && (codeblocks != (currentRow * blocksPerRow))
+ && ((((currentRow * blocksPerRow) - codeblocks) & 1) != 0)) {
+ /* Special case bottom row */
+ specialCaseRow = true;
+ subElements[0] = 2;
+ pattern[symbolRow + compositeOffset] = "0";
+ black = false;
+ }
+ }
+
+ writer = 0;
+
+ seperatorBinary = "";
+ for (i = 0; i < elementsInSub; i++) {
+ pattern[symbolRow + compositeOffset] += (char) (subElements[i] + '0');
+ for (j = 0; j < subElements[i]; j++) {
+ if (black) {
+ seperatorBinary += "0";
+ } else {
+ seperatorBinary += "1";
+ }
+ }
+
+ black = !(black);
+ writer += subElements[i];
+ }
+ seperatorBinary = "0000" + seperatorBinary.substring(4, writer - 4);
+ for (j = 0; j < reader; j++) {
+ k = (49 * j) + (specialCaseRow ? 19 : 18);
+ if (leftToRight) {
+ for (i = 0; i < 15; i++) {
+ if ((seperatorBinary.charAt(i + k - 1) == '1')
+ && (seperatorBinary.charAt(i + k) == '1')) {
+ seperatorBinary = seperatorBinary.substring(0, (i + k))
+ + "0" + seperatorBinary.substring(i + k + 1);
+ }
+ }
+ } else {
+ for (i = 14; i >= 0; i--) {
+ if ((seperatorBinary.charAt(i + k + 1) == '1')
+ && (seperatorBinary.charAt(i + k) == '1')) {
+ seperatorBinary = seperatorBinary.substring(0, (i + k))
+ + "0" + seperatorBinary.substring(i + k + 1);
+ }
+ }
+ }
+ }
+ seperatorPattern = bin2pat(seperatorBinary);
+
+ if ((currentRow == 1) && linkageFlag) {
+ // Add composite code seperator
+ rowHeight[0] = 1;
+ pattern[0] = seperatorPattern;
+ }
+
+ if (currentRow != 1) {
+ /* middle separator pattern (above current row) */
+ pattern[symbolRow - 2 + compositeOffset] = "05";
+ for (j = 5; j < (49 * blocksPerRow); j += 2) {
+ pattern[symbolRow - 2 + compositeOffset] += "11";
+ }
+ rowHeight[symbolRow - 2 + compositeOffset] = 1;
+ /* bottom separator pattern (above current row) */
+ rowHeight[symbolRow - 1 + compositeOffset] = 1;
+ pattern[symbolRow - 1 + compositeOffset] = seperatorPattern;
+ }
+
+ if (currentRow != stackRows) {
+ rowHeight[symbolRow + 1 + compositeOffset] = 1;
+ pattern[symbolRow + 1 + compositeOffset] = seperatorPattern;
+ }
+
+ symbolRow += 4;
+ }
+ readable = new StringBuilder();
+ rowCount += compositeOffset;
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private boolean calculateBinaryString() {
+ /* Handles all data encodation from section 7.2.5 of ISO/IEC 24724 */
+ EncodeMode lastMode = EncodeMode.NUMERIC;
+ int encodingMethod, i, j, readPosn;
+ boolean latch;
+ int remainder, d1, d2, value;
+ StringBuilder padstring;
+ double weight;
+ int groupVal;
+ int currentLength;
+ String patch;
+
+ readPosn = 0;
+
+ /* Decide whether a compressed data field is required and if so what
+ method to use - method 2 = no compressed data field */
+
+ if ((source.length() >= 16) && ((source.charAt(0) == '0')
+ && (source.charAt(1) == '1'))) {
+ /* (01) and other AIs */
+ encodingMethod = 1;
+ } else {
+ /* any AIs */
+ encodingMethod = 2;
+ }
+
+ if (((source.length() >= 20) && (encodingMethod == 1))
+ && ((source.charAt(2) == '9') && (source.charAt(16) == '3'))) {
+ /* Possibly encoding method > 2 */
+
+ if ((source.length() >= 26) && (source.charAt(17) == '1')) {
+ /* Methods 3, 7, 9, 11 and 13 */
+
+ if (source.charAt(18) == '0') {
+ /* (01) and (310x) */
+ /* In kilos */
+
+ weight = 0.0;
+ for (i = 0; i < 6; i++) {
+ weight *= 10;
+ weight += (source.charAt(20 + i) - '0');
+ }
+
+ if (weight < 99999.0) { /* Maximum weight = 99999 */
+
+ if ((source.charAt(19) == '3') && (source.length() == 26)) {
+ /* (01) and (3103) */
+ weight /= 1000.0;
+
+ if (weight <= 32.767) {
+ encodingMethod = 3;
+ }
+ }
+
+ if (source.length() == 34) {
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '1')) {
+ /* (01), (310x) and (11) - metric weight and production date */
+ encodingMethod = 7;
+ }
+
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '3')) {
+ /* (01), (310x) and (13) - metric weight and packaging date */
+ encodingMethod = 9;
+ }
+
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '5')) {
+ /* (01), (310x) and (15) - metric weight and "best before" date */
+ encodingMethod = 11;
+ }
+
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '7')) {
+ /* (01), (310x) and (17) - metric weight and expiration date */
+ encodingMethod = 13;
+ }
+ }
+ }
+ }
+ }
+
+ if ((source.length() >= 26) && (source.charAt(17) == '2')) {
+ /* Methods 4, 8, 10, 12 and 14 */
+
+ if (source.charAt(18) == '0') {
+ /* (01) and (320x) */
+ /* In pounds */
+
+ weight = 0.0;
+ for (i = 0; i < 6; i++) {
+ weight *= 10;
+ weight += (source.charAt(20 + i) - '0');
+ }
+
+ if (weight < 99999.0) { /* Maximum weight = 99999 */
+
+ if (((source.charAt(19) == '2') || (source.charAt(19) == '3'))
+ && (source.length() == 26)) {
+ /* (01) and (3202)/(3203) */
+
+ if (source.charAt(19) == '3') {
+ weight /= 1000.0;
+ if (weight <= 22.767) {
+ encodingMethod = 4;
+ }
+ } else {
+ weight /= 100.0;
+ if (weight <= 99.99) {
+ encodingMethod = 4;
+ }
+ }
+
+ }
+
+ if (source.length() == 34) {
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '1')) {
+ /* (01), (320x) and (11) - English weight and production date */
+ encodingMethod = 8;
+ }
+
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '3')) {
+ /* (01), (320x) and (13) - English weight and packaging date */
+ encodingMethod = 10;
+ }
+
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '5')) {
+ /* (01), (320x) and (15) - English weight and "best before" date */
+ encodingMethod = 12;
+ }
+
+ if ((source.charAt(26) == '1') && (source.charAt(27) == '7')) {
+ /* (01), (320x) and (17) - English weight and expiration date */
+ encodingMethod = 14;
+ }
+ }
+ }
+ }
+ }
+
+ if (source.charAt(17) == '9') {
+ /* Methods 5 and 6 */
+ if ((source.charAt(18) == '2') && ((source.charAt(19) >= '0')
+ && (source.charAt(19) <= '3'))) {
+ /* (01) and (392x) */
+ encodingMethod = 5;
+ }
+ if ((source.charAt(18) == '3') && ((source.charAt(19) >= '0')
+ && (source.charAt(19) <= '3'))) {
+ /* (01) and (393x) */
+ encodingMethod = 6;
+ }
+ }
+ }
+
+ encodeInfo.append("Encoding Method: ").append(Integer.toString(encodingMethod)).append("\n");
+ switch (encodingMethod) { /* Encoding method - Table 10 */
+ case 1:
+ binaryString.append("1XX");
+ readPosn = 16;
+ break;
+ case 2:
+ binaryString.append("00XX");
+ readPosn = 0;
+ break;
+ case 3:
+ binaryString.append("0100");
+ readPosn = source.length();
+ break;
+ case 4:
+ binaryString.append("0101");
+ readPosn = source.length();
+ break;
+ case 5:
+ binaryString.append("01100XX");
+ readPosn = 20;
+ break;
+ case 6:
+ binaryString.append("01101XX");
+ readPosn = 23;
+ break;
+ case 7:
+ binaryString.append("0111000");
+ readPosn = source.length();
+ break;
+ case 8:
+ binaryString.append("0111001");
+ readPosn = source.length();
+ break;
+ case 9:
+ binaryString.append("0111010");
+ readPosn = source.length();
+ break;
+ case 10:
+ binaryString.append("0111011");
+ readPosn = source.length();
+ break;
+ case 11:
+ binaryString.append("0111100");
+ readPosn = source.length();
+ break;
+ case 12:
+ binaryString.append("0111101");
+ readPosn = source.length();
+ break;
+ case 13:
+ binaryString.append("0111110");
+ readPosn = source.length();
+ break;
+ case 14:
+ binaryString.append("0111111");
+ readPosn = source.length();
+ break;
+ }
+
+
+ /* Variable length symbol bit field is just given a place holder (XX)
+ for the time being */
+
+ /* Verify that the data to be placed in the compressed data field is all
+ numeric data before carrying out compression */
+ for (i = 0; i < readPosn; i++) {
+ if ((source.charAt(i) < '0') || (source.charAt(i) > '9')) {
+ if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) {
+ /* Something is wrong */
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+ }
+ }
+
+ /* Now encode the compressed data field */
+
+ if (encodingMethod == 1) {
+ /* Encoding method field "1" - general item identification data */
+ groupVal = source.charAt(2) - '0';
+
+ for (j = 0; j < 4; j++) {
+ if ((groupVal & (0x08 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+
+ for (i = 1; i < 5; i++) {
+ groupVal = 100 * (source.charAt(i * 3) - '0');
+ groupVal += 10 * (source.charAt((i * 3) + 1) - '0');
+ groupVal += source.charAt((i * 3) + 2) - '0';
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+ }
+
+ if (encodingMethod == 3) {
+ /* Encoding method field "0100" - variable weight item
+ (0,001 kilogram icrements) */
+
+ for (i = 1; i < 5; i++) {
+ groupVal = 100 * (source.charAt(i * 3) - '0');
+ groupVal += 10 * (source.charAt((i * 3) + 1) - '0');
+ groupVal += (source.charAt((i * 3) + 2) - '0');
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ groupVal = 0;
+ for (i = 0; i < 6; i++) {
+ groupVal *= 10;
+ groupVal += source.charAt(20 + i) - '0';
+ }
+
+ for (j = 0; j < 15; j++) {
+ if ((groupVal & (0x4000 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if (encodingMethod == 4) {
+ /* Encoding method field "0101" - variable weight item (0,01 or
+ 0,001 pound increment) */
+
+ for (i = 1; i < 5; i++) {
+ groupVal = 100 * (source.charAt(i * 3) - '0');
+ groupVal += 10 * (source.charAt((i * 3) + 1) - '0');
+ groupVal += (source.charAt((i * 3) + 2) - '0');
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+
+ groupVal = 0;
+ for (i = 0; i < 6; i++) {
+ groupVal *= 10;
+ groupVal += source.charAt(20 + i) - '0';
+ }
+
+ if (source.charAt(19) == '3') {
+ groupVal = groupVal + 10000;
+ }
+
+ for (j = 0; j < 15; j++) {
+ if ((groupVal & (0x4000 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if (encodingMethod >= 7) {
+ /* Encoding method fields "0111000" through "0111111" - variable
+ weight item plus date */
+
+ for (i = 1; i < 5; i++) {
+ groupVal = 100 * (source.charAt(i * 3) - '0');
+ groupVal += 10 * (source.charAt((i * 3) + 1) - '0');
+ groupVal += (source.charAt((i * 3) + 2) - '0');
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ groupVal = source.charAt(19) - '0';
+
+ for (i = 0; i < 5; i++) {
+ groupVal *= 10;
+ groupVal += source.charAt(21 + i) - '0';
+ }
+
+ for (j = 0; j < 20; j++) {
+ if ((groupVal & (0x80000 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+
+ if (source.length() == 34) {
+ /* Date information is included */
+ groupVal = ((10 * (source.charAt(28) - '0'))
+ + (source.charAt(29) - '0')) * 384;
+ groupVal += (((10 * (source.charAt(30) - '0'))
+ + (source.charAt(31) - '0')) - 1) * 32;
+ groupVal += (10 * (source.charAt(32) - '0'))
+ + (source.charAt(33) - '0');
+ } else {
+ groupVal = 38400;
+ }
+
+ for (j = 0; j < 16; j++) {
+ if ((groupVal & (0x8000 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ if (encodingMethod == 5) {
+ /* Encoding method field "01100" - variable measure item and price */
+
+ for (i = 1; i < 5; i++) {
+ groupVal = 100 * (source.charAt(i * 3) - '0');
+ groupVal += 10 * (source.charAt((i * 3) + 1) - '0');
+ groupVal += (source.charAt((i * 3) + 2) - '0');
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ switch (source.charAt(19)) {
+ case '0':
+ binaryString.append("00");
+ break;
+ case '1':
+ binaryString.append("01");
+ break;
+ case '2':
+ binaryString.append("10");
+ break;
+ case '3':
+ binaryString.append("11");
+ break;
+ }
+ }
+
+ if (encodingMethod == 6) {
+ /* Encoding method "01101" - variable measure item and price with ISO 4217
+ Currency Code */
+
+ for (i = 1; i < 5; i++) {
+ groupVal = 100 * (source.charAt(i * 3) - '0');
+ groupVal += 10 * (source.charAt((i * 3) + 1) - '0');
+ groupVal += (source.charAt((i * 3) + 2) - '0');
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ switch (source.charAt(19)) {
+ case '0':
+ binaryString.append("00");
+ break;
+ case '1':
+ binaryString.append("01");
+ break;
+ case '2':
+ binaryString.append("10");
+ break;
+ case '3':
+ binaryString.append("11");
+ break;
+ }
+
+ groupVal = 0;
+ for (i = 0; i < 3; i++) {
+ groupVal *= 10;
+ groupVal += source.charAt(20 + i) - '0';
+ }
+
+ for (j = 0; j < 10; j++) {
+ if ((groupVal & (0x200 >> j)) == 0) {
+ binaryString.append("0");
+ } else {
+ binaryString.append("1");
+ }
+ }
+ }
+
+ /* The compressed data field has been processed if appropriate - the
+ rest of the data (if any) goes into a general-purpose data compaction field */
+
+ generalField = source.substring(readPosn);
+ generalFieldType = new EncodeMode[generalField.length()];
+
+ if (generalField.length() != 0) {
+ latch = false;
+ for (i = 0; i < generalField.length(); i++) {
+ /* Table 13 - ISO/IEC 646 encodation */
+ if ((generalField.charAt(i) < ' ') || (generalField.charAt(i) > 'z')) {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ } else {
+ generalFieldType[i] = EncodeMode.ISOIEC;
+ }
+
+ if (generalField.charAt(i) == '#') {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == '$') {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == '@') {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == 92) {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == '^') {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ }
+ if (generalField.charAt(i) == 96) {
+ generalFieldType[i] = EncodeMode.INVALID_CHAR;
+ latch = true;
+ }
+
+ /* Table 12 - Alphanumeric encodation */
+ if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) {
+ generalFieldType[i] = EncodeMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '*') {
+ generalFieldType[i] = EncodeMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == ',') {
+ generalFieldType[i] = EncodeMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '-') {
+ generalFieldType[i] = EncodeMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '.') {
+ generalFieldType[i] = EncodeMode.ALPHA_OR_ISO;
+ }
+ if (generalField.charAt(i) == '/') {
+ generalFieldType[i] = EncodeMode.ALPHA_OR_ISO;
+ }
+
+ /* Numeric encodation */
+ if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) {
+ generalFieldType[i] = EncodeMode.ANY_ENC;
+ }
+ if (generalField.charAt(i) == '[') {
+ /* FNC1 can be encoded in any system */
+ generalFieldType[i] = EncodeMode.ANY_ENC;
+ }
+ }
+
+ if (latch) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ for (i = 0; i < generalField.length() - 1; i++) {
+ if ((generalFieldType[i] == EncodeMode.ISOIEC)
+ && (generalField.charAt(i + 1) == '[')) {
+ generalFieldType[i + 1] = EncodeMode.ISOIEC;
+ }
+ }
+
+ for (i = 0; i < generalField.length() - 1; i++) {
+ if ((generalFieldType[i] == EncodeMode.ALPHA_OR_ISO)
+ && (generalField.charAt(i + 1) == '[')) {
+ generalFieldType[i + 1] = EncodeMode.ALPHA_OR_ISO;
+ }
+ }
+
+ latch = applyGeneralFieldRules();
+
+ /* Set initial mode if not NUMERIC */
+ if (generalFieldType[0] == EncodeMode.ALPHA) {
+ binaryString.append("0000"); /* Alphanumeric latch */
+ lastMode = EncodeMode.ALPHA;
+ }
+ if (generalFieldType[0] == EncodeMode.ISOIEC) {
+ binaryString.append("0000"); /* Alphanumeric latch */
+ binaryString.append("00100"); /* ISO/IEC 646 latch */
+ lastMode = EncodeMode.ISOIEC;
+ }
+
+ i = 0;
+ do {
+ switch (generalFieldType[i]) {
+ case NUMERIC:
+
+ if (lastMode != EncodeMode.NUMERIC) {
+ binaryString.append("000"); /* Numeric latch */
+ }
+
+ if (generalField.charAt(i) != '[') {
+ d1 = generalField.charAt(i) - '0';
+ } else {
+ d1 = 10;
+ }
+
+ if (generalField.charAt(i + 1) != '[') {
+ d2 = generalField.charAt(i + 1) - '0';
+ } else {
+ d2 = 10;
+ }
+
+ value = (11 * d1) + d2 + 8;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+
+ i += 2;
+ lastMode = EncodeMode.NUMERIC;
+ break;
+
+ case ALPHA:
+ if (i != 0) {
+ if (lastMode == EncodeMode.NUMERIC) {
+ binaryString.append("0000"); /* Alphanumeric latch */
+ }
+ if (lastMode == EncodeMode.ISOIEC) {
+ binaryString.append("00100"); /* Alphanumeric latch */
+ }
+ }
+
+ if ((generalField.charAt(i) >= '0') && (generalField.charAt(i) <= '9')) {
+
+ value = generalField.charAt(i) - 43;
+
+ for (j = 0; j < 5; j++) {
+ if ((value & (0x10 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+
+ if ((generalField.charAt(i) >= 'A') && (generalField.charAt(i) <= 'Z')) {
+
+ value = generalField.charAt(i) - 33;
+
+ for (j = 0; j < 6; j++) {
+ if ((value & (0x20 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+
+ lastMode = EncodeMode.ALPHA;
+ if (generalField.charAt(i) == '[') {
+ binaryString.append("01111");
+ lastMode = EncodeMode.NUMERIC;
+ } /* FNC1/Numeric latch */
+ if (generalField.charAt(i) == '*') {
+ binaryString.append("111010"); /* asterisk */
+ }
+ if (generalField.charAt(i) == ',') {
+ binaryString.append("111011"); /* comma */
+ }
+ if (generalField.charAt(i) == '-') {
+ binaryString.append("111100"); /* minus or hyphen */
+ }
+ if (generalField.charAt(i) == '.') {
+ binaryString.append("111101"); /* period or full stop */
+ }
+ if (generalField.charAt(i) == '/') {
+ binaryString.append("111110"); /* slash or solidus */
+ }
+
+ i++;
+ break;
+
+ case ISOIEC:
+ if (i != 0) {
+ if (lastMode == EncodeMode.NUMERIC) {
+ binaryString.append("0000"); /* Alphanumeric latch */
+ binaryString.append("00100"); /* ISO/IEC 646 latch */
+ }
+ if (lastMode == EncodeMode.ALPHA) {
+ binaryString.append("00100"); /* ISO/IEC 646 latch */
+ }
+ }
+
+ if ((generalField.charAt(i) >= '0')
+ && (generalField.charAt(i) <= '9')) {
+
+ value = generalField.charAt(i) - 43;
+
+ for (j = 0; j < 5; j++) {
+ if ((value & (0x10 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+
+ if ((generalField.charAt(i) >= 'A')
+ && (generalField.charAt(i) <= 'Z')) {
+
+ value = generalField.charAt(i) - 1;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+
+ if ((generalField.charAt(i) >= 'a')
+ && (generalField.charAt(i) <= 'z')) {
+
+ value = generalField.charAt(i) - 7;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+
+ lastMode = EncodeMode.ISOIEC;
+ if (generalField.charAt(i) == '[') {
+ binaryString.append("01111");
+ lastMode = EncodeMode.NUMERIC;
+ } /* FNC1/Numeric latch */
+ if (generalField.charAt(i) == '!') {
+ binaryString.append("11101000"); /* exclamation mark */
+ }
+ if (generalField.charAt(i) == 34) {
+ binaryString.append("11101001"); /* quotation mark */
+ }
+ if (generalField.charAt(i) == 37) {
+ binaryString.append("11101010"); /* percent sign */
+ }
+ if (generalField.charAt(i) == '&') {
+ binaryString.append("11101011"); /* ampersand */
+ }
+ if (generalField.charAt(i) == 39) {
+ binaryString.append("11101100"); /* apostrophe */
+ }
+ if (generalField.charAt(i) == '(') {
+ binaryString.append("11101101"); /* left parenthesis */
+ }
+ if (generalField.charAt(i) == ')') {
+ binaryString.append("11101110"); /* right parenthesis */
+ }
+ if (generalField.charAt(i) == '*') {
+ binaryString.append("11101111"); /* asterisk */
+ }
+ if (generalField.charAt(i) == '+') {
+ binaryString.append("11110000"); /* plus sign */
+ }
+ if (generalField.charAt(i) == ',') {
+ binaryString.append("11110001"); /* comma */
+ }
+ if (generalField.charAt(i) == '-') {
+ binaryString.append("11110010"); /* minus or hyphen */
+ }
+ if (generalField.charAt(i) == '.') {
+ binaryString.append("11110011"); /* period or full stop */
+ }
+ if (generalField.charAt(i) == '/') {
+ binaryString.append("11110100"); /* slash or solidus */
+ }
+ if (generalField.charAt(i) == ':') {
+ binaryString.append("11110101"); /* colon */
+ }
+ if (generalField.charAt(i) == ';') {
+ binaryString.append("11110110"); /* semicolon */
+ }
+ if (generalField.charAt(i) == '<') {
+ binaryString.append("11110111"); /* less-than sign */
+ }
+ if (generalField.charAt(i) == '=') {
+ binaryString.append("11111000"); /* equals sign */
+ }
+ if (generalField.charAt(i) == '>') {
+ binaryString.append("11111001"); /* greater-than sign */
+ }
+ if (generalField.charAt(i) == '?') {
+ binaryString.append("11111010"); /* question mark */
+ }
+ if (generalField.charAt(i) == '_') {
+ binaryString.append("11111011"); /* underline or low line */
+ }
+ if (generalField.charAt(i) == ' ') {
+ binaryString.append("11111100"); /* space */
+ }
+
+ i++;
+ break;
+ }
+ currentLength = i;
+ if (latch) {
+ currentLength++;
+ }
+ } while (currentLength < generalField.length());
+
+ remainder = calculateRemainder(binaryString.length());
+
+ if (latch) {
+ /* There is still one more numeric digit to encode */
+
+ if (lastMode == EncodeMode.NUMERIC) {
+ if ((remainder >= 4) && (remainder <= 6)) {
+ value = generalField.charAt(i) - '0';
+ value++;
+
+ for (j = 0; j < 4; j++) {
+ if ((value & (0x08 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ } else {
+ d1 = generalField.charAt(i) - '0';
+ d2 = 10;
+
+ value = (11 * d1) + d2 + 8;
+
+ for (j = 0; j < 7; j++) {
+ if ((value & (0x40 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+ } else {
+ value = generalField.charAt(i) - 43;
+
+ for (j = 0; j < 5; j++) {
+ if ((value & (0x10 >> j)) != 0) {
+ binaryString.append("1");
+ } else {
+ binaryString.append("0");
+ }
+ }
+ }
+ }
+ }
+
+ if (binaryString.length() > 252) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ remainder = calculateRemainder(binaryString.length());
+
+ /* Now add padding to binary string (7.2.5.5.4) */
+ i = remainder;
+ if ((generalField.length() != 0) && (lastMode == EncodeMode.NUMERIC)) {
+ padstring = new StringBuilder("0000");
+ i -= 4;
+ } else {
+ padstring = new StringBuilder();
+ }
+ for (; i > 0; i -= 5) {
+ padstring.append("00100");
+ }
+
+ binaryString.append(padstring.substring(0, remainder));
+
+ /* Patch variable length symbol bit field */
+ patch = "";
+ if ((((binaryString.length() / 12) + 1) & 1) == 0) {
+ patch += "0";
+ } else {
+ patch += "1";
+ }
+ if (binaryString.length() <= 156) {
+ patch += "0";
+ } else {
+ patch += "1";
+ }
+
+ if (encodingMethod == 1) {
+ binaryString = new StringBuilder(binaryString.substring(0, 2))
+ .append(patch)
+ .append(binaryString.substring(4));
+ }
+ if (encodingMethod == 2) {
+ binaryString = new StringBuilder(binaryString.substring(0, 3))
+ .append(patch)
+ .append(binaryString.substring(5));
+ }
+ if ((encodingMethod == 5) || (encodingMethod == 6)) {
+ binaryString = new StringBuilder(binaryString.substring(0, 6))
+ .append(patch)
+ .append(binaryString.substring(8));
+ }
+
+ encodeInfo.append("Binary length: ").append(Integer.toString(binaryString.length())).append("\n");
+ displayBinaryString();
+ return true;
+ }
+
+ private void displayBinaryString() {
+ int i, nibble;
+ /* Display binary string as hexadecimal */
+
+ encodeInfo.append("Binary String: ");
+ nibble = 0;
+ for (i = 0; i < binaryString.length(); i++) {
+ switch (i % 4) {
+ case 0:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 8;
+ }
+ break;
+ case 1:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 4;
+ }
+ break;
+ case 2:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 2;
+ }
+ break;
+ case 3:
+ if (binaryString.charAt(i) == '1') {
+ nibble += 1;
+ }
+ encodeInfo.append(Integer.toHexString(nibble));
+ nibble = 0;
+ break;
+ }
+ }
+
+ if ((binaryString.length() % 4) != 0) {
+ encodeInfo.append(Integer.toHexString(nibble));
+ }
+ encodeInfo.append("\n");
+ }
+
+ private boolean applyGeneralFieldRules() {
+ /* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3
+ of ISO/IEC 24724:2006 */
+
+ int blockCount, i, j, k;
+ EncodeMode current, next, last;
+ int[] blockLength = new int[200];
+ EncodeMode[] blockType = new EncodeMode[200];
+
+ blockCount = 0;
+
+ blockLength[blockCount] = 1;
+ blockType[blockCount] = generalFieldType[0];
+
+ for (i = 1; i < generalField.length(); i++) {
+ current = generalFieldType[i];
+ last = generalFieldType[i - 1];
+
+ if (current == last) {
+ blockLength[blockCount] = blockLength[blockCount] + 1;
+ } else {
+ blockCount++;
+ blockLength[blockCount] = 1;
+ blockType[blockCount] = generalFieldType[i];
+ }
+ }
+
+ blockCount++;
+
+ for (i = 0; i < blockCount; i++) {
+ current = blockType[i];
+ next = blockType[i + 1];
+
+ if ((current == EncodeMode.ISOIEC) && (i != (blockCount - 1))) {
+ if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] >= 4)) {
+ blockType[i + 1] = EncodeMode.NUMERIC;
+ }
+ if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] < 4)) {
+ blockType[i + 1] = EncodeMode.ISOIEC;
+ }
+ if ((next == EncodeMode.ALPHA_OR_ISO) && (blockLength[i + 1] >= 5)) {
+ blockType[i + 1] = EncodeMode.ALPHA;
+ }
+ if ((next == EncodeMode.ALPHA_OR_ISO) && (blockLength[i + 1] < 5)) {
+ blockType[i + 1] = EncodeMode.ISOIEC;
+ }
+ }
+
+ if (current == EncodeMode.ALPHA_OR_ISO) {
+ blockType[i] = EncodeMode.ALPHA;
+ current = EncodeMode.ALPHA;
+ }
+
+ if ((current == EncodeMode.ALPHA) && (i != (blockCount - 1))) {
+ if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] >= 6)) {
+ blockType[i + 1] = EncodeMode.NUMERIC;
+ }
+ if ((next == EncodeMode.ANY_ENC) && (blockLength[i + 1] < 6)) {
+ if ((i == blockCount - 2) && (blockLength[i + 1] >= 4)) {
+ blockType[i + 1] = EncodeMode.NUMERIC;
+ } else {
+ blockType[i + 1] = EncodeMode.ALPHA;
+ }
+ }
+ }
+
+ if (current == EncodeMode.ANY_ENC) {
+ blockType[i] = EncodeMode.NUMERIC;
+ }
+ }
+
+ if (blockCount > 1) {
+ i = 1;
+ while (i < blockCount) {
+ if (blockType[i - 1] == blockType[i]) {
+ /* bring together */
+ blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
+ j = i + 1;
+
+ /* decreace the list */
+ while (j < blockCount) {
+ blockLength[j - 1] = blockLength[j];
+ blockType[j - 1] = blockType[j];
+ j++;
+ }
+ blockCount--;
+ i--;
+ }
+ i++;
+ }
+ }
+
+ for (i = 0; i < blockCount - 1; i++) {
+ if ((blockType[i] == EncodeMode.NUMERIC) && ((blockLength[i] & 1) != 0)) {
+ /* Odd size numeric block */
+ blockLength[i] = blockLength[i] - 1;
+ blockLength[i + 1] = blockLength[i + 1] + 1;
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < blockCount; i++) {
+ for (k = 0; k < blockLength[i]; k++) {
+ generalFieldType[j] = blockType[i];
+ j++;
+ }
+ }
+
+ /* If the last block is numeric and an odd size, further
+ processing needs to be done outside this procedure */
+ return (blockType[blockCount - 1] == EncodeMode.NUMERIC)
+ && ((blockLength[blockCount - 1] & 1) != 0);
+ }
+
+ private int getCombinations(int n, int r) {
+ int i, j;
+ int maxDenom, minDenom;
+ int val;
+
+ if (n - r > r) {
+ minDenom = r;
+ maxDenom = n - r;
+ } else {
+ minDenom = n - r;
+ maxDenom = r;
+ }
+ val = 1;
+ j = 1;
+ for (i = n; i > maxDenom; i--) {
+ val *= i;
+ if (j <= minDenom) {
+ val /= j;
+ j++;
+ }
+ }
+ for (; j <= minDenom; j++) {
+ val /= j;
+ }
+ return (val);
+ }
+
+ private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) {
+ int bar;
+ int elmWidth;
+ int mxwElement;
+ int subVal, lessVal;
+ int narrowMask = 0;
+ for (bar = 0; bar < elements - 1; bar++) {
+ for (elmWidth = 1, narrowMask |= (1 << bar); ;
+ elmWidth++, narrowMask &= ~(1 << bar)) {
+ /* get all combinations */
+ subVal = getCombinations(n - elmWidth - 1, elements - bar - 2);
+ /* less combinations with no single-module element */
+ if ((noNarrow == 0) && (narrowMask == 0)
+ && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
+ subVal -= getCombinations(n - elmWidth - (elements - bar),
+ elements - bar - 2);
+ }
+ /* less combinations with elements > maxVal */
+ if (elements - bar - 1 > 1) {
+ lessVal = 0;
+ for (mxwElement = n - elmWidth - (elements - bar - 2);
+ mxwElement > maxWidth;
+ mxwElement--) {
+ lessVal += getCombinations(n - elmWidth - mxwElement - 1,
+ elements - bar - 3);
+ }
+ subVal -= lessVal * (elements - 1 - bar);
+ } else if (n - elmWidth > maxWidth) {
+ subVal--;
+ }
+ val -= subVal;
+ if (val < 0) break;
+ }
+ val += subVal;
+ n -= elmWidth;
+ widths[bar] = elmWidth;
+ }
+ widths[bar] = n;
+ }
+
+ private enum dbeMode {
+ UNSTACKED, STACKED
+ }
+
+ private enum EncodeMode {
+ NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java
new file mode 100755
index 0000000..4163543
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java
@@ -0,0 +1,476 @@
+package org.xbib.graphics.barcode;
+
+import java.math.BigInteger;
+
+/**
+ * Implements GS1 DataBar Limited according to ISO/IEC 24724:2011.
+ * Input data should be a 12 digit Global Trade Identification Number
+ * without check digit or Application Identifier [01].
+ */
+public class DataBarLimited extends Symbol {
+
+ private static final int[] t_even_ltd = {
+ 28, 728, 6454, 203, 2408, 1, 16632
+ };
+ private static final int[] modules_odd_ltd = {
+ 17, 13, 9, 15, 11, 19, 7
+ };
+ private static final int[] modules_even_ltd = {
+ 9, 13, 17, 11, 15, 7, 19
+ };
+ private static final int[] widest_odd_ltd = {
+ 6, 5, 3, 5, 4, 8, 1
+ };
+ private static final int[] widest_even_ltd = {
+ 3, 4, 6, 4, 5, 1, 8
+ };
+ private static final int[] checksum_weight_ltd = { /* Table 7 */
+ 1, 3, 9, 27, 81, 65, 17, 51, 64, 14, 42, 37, 22, 66,
+ 20, 60, 2, 6, 18, 54, 73, 41, 34, 13, 39, 28, 84, 74
+ };
+ private static final int[] finder_pattern_ltd = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1,
+ 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1,
+ 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1,
+ 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1,
+ 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1,
+ 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1,
+ 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1,
+ 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1,
+ 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1,
+ 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1,
+ 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1,
+ 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1,
+ 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1,
+ 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1,
+ 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1,
+ 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1,
+ 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1,
+ 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1,
+ 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1,
+ 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1,
+ 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1,
+ 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1,
+ 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1,
+ 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1 /* ГОСТ ISO/IEC 24724-2011 Ñтраница 57 */
+ };
+
+ private boolean linkageFlag;
+ private int[] widths = new int[8];
+
+ public DataBarLimited() {
+ linkageFlag = false;
+ }
+
+ @Override
+ public void setDataType(DataType dummy) {
+ // Do nothing!
+ }
+
+ protected void setLinkageFlag() {
+ linkageFlag = true;
+ }
+
+ @Override
+ public boolean encode() {
+ BigInteger accum;
+ BigInteger left_reg;
+ BigInteger right_reg;
+ int left_group;
+ int right_group;
+ int i, j;
+ int left_character;
+ int right_character;
+ int left_odd;
+ int right_odd;
+ int left_even;
+ int right_even;
+ int[] left_widths = new int[14];
+ int[] right_widths = new int[14];
+ int checksum;
+ int[] check_elements = new int[14];
+ int[] total_widths = new int[46];
+ StringBuilder bin;
+ StringBuilder notbin;
+ boolean bar_latch;
+ int writer;
+ int check_digit;
+ int count = 0;
+ StringBuilder hrt;
+ int compositeOffset = 0;
+
+ if (content.length() > 13) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+?"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (content.length() == 13) {
+ if ((content.charAt(0) != '0') && (content.charAt(0) != '1')) {
+ errorMsg.append("Input out of range");
+ return false;
+ }
+ }
+
+ accum = new BigInteger(content);
+
+ if (linkageFlag) {
+ /* Add symbol linkage flag */
+ accum = accum.add(new BigInteger("2015133531096"));
+ }
+
+ /* Calculate left and right pair values */
+ left_reg = accum.divide(new BigInteger("2013571"));
+ right_reg = accum.mod(new BigInteger("2013571"));
+
+ left_group = 0;
+ if (left_reg.compareTo(new BigInteger("183063")) > 0) {
+ left_group = 1;
+ }
+ if (left_reg.compareTo(new BigInteger("820063")) > 0) {
+ left_group = 2;
+ }
+ if (left_reg.compareTo(new BigInteger("1000775")) > 0) {
+ left_group = 3;
+ }
+ if (left_reg.compareTo(new BigInteger("1491020")) > 0) {
+ left_group = 4;
+ }
+ if (left_reg.compareTo(new BigInteger("1979844")) > 0) {
+ left_group = 5;
+ }
+ if (left_reg.compareTo(new BigInteger("1996938")) > 0) {
+ left_group = 6;
+ }
+
+ right_group = 0;
+ if (right_reg.compareTo(new BigInteger("183063")) > 0) {
+ right_group = 1;
+ }
+ if (right_reg.compareTo(new BigInteger("820063")) > 0) {
+ right_group = 2;
+ }
+ if (right_reg.compareTo(new BigInteger("1000775")) > 0) {
+ right_group = 3;
+ }
+ if (right_reg.compareTo(new BigInteger("1491020")) > 0) {
+ right_group = 4;
+ }
+ if (right_reg.compareTo(new BigInteger("1979844")) > 0) {
+ right_group = 5;
+ }
+ if (right_reg.compareTo(new BigInteger("1996938")) > 0) {
+ right_group = 6;
+ }
+
+ encodeInfo.append("Data Characters: ").append(Integer.toString(left_group + 1)).append(" ").append(Integer.toString(right_group + 1)).append("\n");
+
+ switch (left_group) {
+ case 1:
+ left_reg = left_reg.subtract(new BigInteger("183064"));
+ break;
+ case 2:
+ left_reg = left_reg.subtract(new BigInteger("820064"));
+ break;
+ case 3:
+ left_reg = left_reg.subtract(new BigInteger("1000776"));
+ break;
+ case 4:
+ left_reg = left_reg.subtract(new BigInteger("1491021"));
+ break;
+ case 5:
+ left_reg = left_reg.subtract(new BigInteger("1979845"));
+ break;
+ case 6:
+ left_reg = left_reg.subtract(new BigInteger("1996939"));
+ break;
+ }
+
+ switch (right_group) {
+ case 1:
+ right_reg = right_reg.subtract(new BigInteger("183064"));
+ break;
+ case 2:
+ right_reg = right_reg.subtract(new BigInteger("820064"));
+ break;
+ case 3:
+ right_reg = right_reg.subtract(new BigInteger("1000776"));
+ break;
+ case 4:
+ right_reg = right_reg.subtract(new BigInteger("1491021"));
+ break;
+ case 5:
+ right_reg = right_reg.subtract(new BigInteger("1979845"));
+ break;
+ case 6:
+ right_reg = right_reg.subtract(new BigInteger("1996939"));
+ break;
+ }
+
+ left_character = left_reg.intValue();
+ right_character = right_reg.intValue();
+
+ left_odd = left_character / t_even_ltd[left_group];
+ left_even = left_character % t_even_ltd[left_group];
+ right_odd = right_character / t_even_ltd[right_group];
+ right_even = right_character % t_even_ltd[right_group];
+
+ getWidths(left_odd, modules_odd_ltd[left_group], 7, widest_odd_ltd[left_group], 1);
+ left_widths[0] = widths[0];
+ left_widths[2] = widths[1];
+ left_widths[4] = widths[2];
+ left_widths[6] = widths[3];
+ left_widths[8] = widths[4];
+ left_widths[10] = widths[5];
+ left_widths[12] = widths[6];
+ getWidths(left_even, modules_even_ltd[left_group], 7, widest_even_ltd[left_group], 0);
+ left_widths[1] = widths[0];
+ left_widths[3] = widths[1];
+ left_widths[5] = widths[2];
+ left_widths[7] = widths[3];
+ left_widths[9] = widths[4];
+ left_widths[11] = widths[5];
+ left_widths[13] = widths[6];
+ getWidths(right_odd, modules_odd_ltd[right_group], 7, widest_odd_ltd[right_group], 1);
+ right_widths[0] = widths[0];
+ right_widths[2] = widths[1];
+ right_widths[4] = widths[2];
+ right_widths[6] = widths[3];
+ right_widths[8] = widths[4];
+ right_widths[10] = widths[5];
+ right_widths[12] = widths[6];
+ getWidths(right_even, modules_even_ltd[right_group], 7, widest_even_ltd[right_group], 0);
+ right_widths[1] = widths[0];
+ right_widths[3] = widths[1];
+ right_widths[5] = widths[2];
+ right_widths[7] = widths[3];
+ right_widths[9] = widths[4];
+ right_widths[11] = widths[5];
+ right_widths[13] = widths[6];
+
+ checksum = 0;
+ /* Calculate the checksum */
+ for (i = 0; i < 14; i++) {
+ checksum += checksum_weight_ltd[i] * left_widths[i];
+ checksum += checksum_weight_ltd[i + 14] * right_widths[i];
+ }
+ checksum %= 89;
+
+ encodeInfo.append("Checksum: ").append(Integer.toString(checksum)).append("\n");
+
+ for (i = 0; i < 14; i++) {
+ check_elements[i] = finder_pattern_ltd[i + (checksum * 14)];
+ }
+
+ total_widths[0] = 1;
+ total_widths[1] = 1;
+ total_widths[44] = 1;
+ total_widths[45] = 1;
+ for (i = 0; i < 14; i++) {
+ total_widths[i + 2] = left_widths[i];
+ total_widths[i + 16] = check_elements[i];
+ total_widths[i + 30] = right_widths[i];
+ }
+
+ bin = new StringBuilder();
+ notbin = new StringBuilder();
+ writer = 0;
+ bar_latch = false;
+ for (i = 0; i < 46; i++) {
+ for (j = 0; j < total_widths[i]; j++) {
+ if (bar_latch) {
+ bin.append("1");
+ notbin.append("0");
+ } else {
+ bin.append("0");
+ notbin.append("1");
+ }
+ writer++;
+ }
+ bar_latch = !bar_latch;
+ }
+
+ if (symbolWidth < (writer + 20)) {
+ symbolWidth = writer + 20;
+ }
+
+ /* Calculate check digit from Annex A and place human readable text */
+
+ readable = new StringBuilder("(01)");
+ hrt = new StringBuilder();
+ for (i = content.length(); i < 13; i++) {
+ hrt.append("0");
+ }
+ hrt.append(content);
+
+ for (i = 0; i < 13; i++) {
+ count += (hrt.charAt(i) - '0');
+
+ if ((i & 1) == 0) {
+ count += 2 * (hrt.charAt(i) - '0');
+ }
+ }
+
+ check_digit = 10 - (count % 10);
+ if (check_digit == 10) {
+ check_digit = 0;
+ }
+
+ hrt.append((char) (check_digit + '0'));
+ readable.append(hrt);
+
+ if (linkageFlag) {
+ compositeOffset = 1;
+ }
+
+ rowCount = 1 + compositeOffset;
+ rowHeight = new int[1 + compositeOffset];
+ rowHeight[compositeOffset] = -1;
+ pattern = new String[1 + compositeOffset];
+ pattern[compositeOffset] = "0:" + bin2pat(bin.toString());
+
+ if (linkageFlag) {
+ // Add composite symbol seperator
+ notbin = new StringBuilder(notbin.substring(4, 70));
+ rowHeight[0] = 1;
+ pattern[0] = "0:04" + bin2pat(notbin.toString());
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private int getCombinations(int n, int r) {
+ int i, j;
+ int maxDenom, minDenom;
+ int val;
+
+ if (n - r > r) {
+ minDenom = r;
+ maxDenom = n - r;
+ } else {
+ minDenom = n - r;
+ maxDenom = r;
+ }
+ val = 1;
+ j = 1;
+ for (i = n; i > maxDenom; i--) {
+ val *= i;
+ if (j <= minDenom) {
+ val /= j;
+ j++;
+ }
+ }
+ for (; j <= minDenom; j++) {
+ val /= j;
+ }
+ return (val);
+ }
+
+ private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) {
+ int bar;
+ int elmWidth;
+ int mxwElement;
+ int subVal, lessVal;
+ int narrowMask = 0;
+ for (bar = 0; bar < elements - 1; bar++) {
+ for (elmWidth = 1, narrowMask |= (1 << bar); ;
+ elmWidth++, narrowMask &= ~(1 << bar)) {
+ /* get all combinations */
+ subVal = getCombinations(n - elmWidth - 1, elements - bar - 2);
+ /* less combinations with no single-module element */
+ if ((noNarrow == 0) && (narrowMask == 0)
+ && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
+ subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2);
+ }
+ /* less combinations with elements > maxVal */
+ if (elements - bar - 1 > 1) {
+ lessVal = 0;
+ for (mxwElement = n - elmWidth - (elements - bar - 2);
+ mxwElement > maxWidth;
+ mxwElement--) {
+ lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3);
+ }
+ subVal -= lessVal * (elements - 1 - bar);
+ } else if (n - elmWidth > maxWidth) {
+ subVal--;
+ }
+ val -= subVal;
+ if (val < 0) break;
+ }
+ val += subVal;
+ n -= elmWidth;
+ widths[bar] = elmWidth;
+ }
+ widths[bar] = n;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java b/barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java
new file mode 100755
index 0000000..774db2a
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java
@@ -0,0 +1,1636 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+
+/**
+ * Implements Data Matrix ECC 200 bar code symbology according to ISO/IEC
+ * 16022:2006.
+ * Data Matrix is a 2D matrix symbology capable of encoding characters in the
+ * ISO/IEC 8859-1 (Latin-1) character set.
+ */
+public class DataMatrix extends Symbol {
+
+ private static final int[] c40_shift = {
+ 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, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2,
+ 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
+ };
+
+ private static final int[] c40_value = {
+ 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, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16,
+ 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, 23, 24, 25, 26,
+ 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, 27, 28, 29, 30, 31
+ };
+
+ private static final int[] text_shift = {
+ 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, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 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, 2, 2, 2, 2, 2,
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 3, 3, 3, 3
+ };
+
+ private static final int[] text_value = {
+ 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, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16,
+ 17, 18, 19, 20, 21, 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, 22, 23, 24, 25, 26, 0, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31
+ };
+
+ private static final int[] intsymbol = {
+ 0, 1, 3, 5, 7, 8, 10, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 2, 4, 6, 9, 11, 14
+ };
+
+ private static final int[] matrixH = {
+ 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 32, 36, 40,
+ 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144
+ };
+
+ private static final int[] matrixW = {
+ 10, 12, 18, 14, 32, 16, 26, 18, 20, 36, 22, 36, 24, 26, 48, 32, 36, 40,
+ 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144
+ };
+
+ private static final int[] matrixFH = {
+ 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 16, 18, 20,
+ 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24
+ };
+
+ private static final int[] matrixFW = {
+ 10, 12, 18, 14, 16, 16, 26, 18, 20, 18, 22, 18, 24, 26, 24, 16, 18, 20,
+ 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24
+ };
+
+ private static final int[] matrixbytes = {
+ 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114,
+ 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558
+ };
+
+ private static final int[] matrixdatablock = {
+ 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114,
+ 144, 174, 102, 140, 92, 114, 144, 174, 136, 175, 163, 156
+ };
+
+ private static final int[] matrixrsblock = {
+ 5, 7, 7, 10, 11, 12, 14, 14, 18, 18, 20, 24, 24, 28, 28, 36, 42, 48, 56,
+ 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62
+ };
+ private int[] target = new int[2200];
+ private int[] binary = new int[2200];
+ private int binary_length;
+ private dm_mode last_mode;
+ private int[] places;
+ private boolean isSquare;
+ private int[] inputData;
+ private int preferredSize = 0;
+ private int process_p;
+ private int[] process_buffer = new int[8];
+
+ public DataMatrix() {
+ isSquare = true;
+ }
+
+ /**
+ * Override selection of symbol size. When set as false
the
+ * symbol will be the smallest available for the amount of data given. When
+ * set as true
the encoding will not use rectangular symbols.
+ *
+ * @param input Forces a square symbol when set to true
+ */
+ public void forceSquare(boolean input) {
+ isSquare = input;
+ }
+
+ /**
+ * Set the prefereed symbol size according to the values in the following
+ * table. Values may be ignored if the data is too big to fit in the
+ * specified symbol, or if forceSquare
mode has been invoked.
+ *
+ * Available Data Matrix symbol sizes
+ *
+ *
+ *
+ * Input
+ *
+ * Symbol Size
+ *
+ * Input
+ *
+ * Symbol Size
+ *
+ *
+ *
+ * 1
+ *
+ * 10 x 10
+ *
+ * 16
+ *
+ * 64 x 64
+ *
+ *
+ *
+ * 2
+ *
+ * 12 x 12
+ *
+ * 17
+ *
+ * 72 x 72
+ *
+ *
+ *
+ * 3
+ *
+ * 14 x 14
+ *
+ * 18
+ *
+ * 80 x 80
+ *
+ *
+ *
+ * 4
+ *
+ * 16 x 16
+ *
+ * 19
+ *
+ * 88 x 88
+ *
+ *
+ *
+ * 5
+ *
+ * 18 x 18
+ *
+ * 20
+ *
+ * 96 x 96
+ *
+ *
+ *
+ * 6
+ *
+ * 20 x 20
+ *
+ * 21
+ *
+ * 104 x 104
+ *
+ *
+ *
+ * 7
+ *
+ * 22 x 22
+ *
+ * 22
+ *
+ * 120 x 120
+ *
+ *
+ *
+ * 8
+ *
+ * 24 x 24
+ *
+ * 23
+ *
+ * 132 x 132
+ *
+ *
+ *
+ * 9
+ *
+ * 26 x 26
+ *
+ * 24
+ *
+ * 144 x 144
+ *
+ *
+ *
+ * 10
+ *
+ * 32 x 32
+ *
+ * 25
+ *
+ * 8 x 18
+ *
+ *
+ *
+ * 11
+ *
+ * 36 x 36
+ *
+ * 26
+ *
+ * 8 x 32
+ *
+ *
+ *
+ * 12
+ *
+ * 40 x 40
+ *
+ * 27
+ *
+ * 12 x 26
+ *
+ *
+ *
+ * 13
+ *
+ * 44 x 44
+ *
+ * 28
+ *
+ * 12 x 36
+ *
+ *
+ *
+ * 14
+ *
+ * 48 x 48
+ *
+ * 29
+ *
+ * 16 x 36
+ *
+ *
+ *
+ * 15
+ *
+ * 52 x 52
+ *
+ * 30
+ *
+ * 16 x 48
+ *
+ *
+ *
+ *
+ * @param size Symbol size
+ */
+ public void setPreferredSize(int size) {
+ preferredSize = size;
+ }
+
+ @Override
+ public boolean encode() {
+ int i, binlen, skew = 0;
+ int symbolsize, optionsize, calcsize;
+ int taillength;
+ int H, W, FH, FW, datablock, bytes, rsblock;
+ int x, y, NC, NR, v;
+ int[] grid;
+ StringBuilder bin;
+
+ eciProcess(); // Get ECI mode
+
+ inputData = new int[content.length()];
+ for (i = 0; i < content.length(); i++) {
+ inputData[i] = inputBytes[i] & 0xFF;
+ }
+
+ binlen = generateCodewords();
+
+ if (binlen == 0) {
+ errorMsg.append("Data too long to fit in symbol");
+ return false;
+ }
+
+ if ((preferredSize >= 1) && (preferredSize <= 30)) {
+ optionsize = intsymbol[preferredSize - 1];
+ } else {
+ optionsize = -1;
+ }
+
+ calcsize = 29;
+ for (i = 29; i > -1; i--) {
+ if (matrixbytes[i] >= (binlen + process_p)) {
+ calcsize = i;
+ }
+ }
+
+ if (isSquare) {
+ // Force to use square symbol
+ switch (calcsize) {
+ case 2:
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ case 14:
+ calcsize++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ symbolsize = optionsize;
+ if (calcsize > optionsize) {
+ symbolsize = calcsize;
+ if (optionsize != -1) {
+ /* flag an error */
+ errorMsg.append("Data does not fit in selected symbol size");
+ return false;
+ }
+ }
+
+ // Now we know the symbol size we can handle the remaining data in the process buffer.
+ if (process_p != 0) {
+ binlen = encodeRemainder(matrixbytes[symbolsize] - binlen, binlen);
+ }
+
+ H = matrixH[symbolsize];
+ W = matrixW[symbolsize];
+ FH = matrixFH[symbolsize];
+ FW = matrixFW[symbolsize];
+ bytes = matrixbytes[symbolsize];
+ datablock = matrixdatablock[symbolsize];
+ rsblock = matrixrsblock[symbolsize];
+
+ taillength = bytes - binlen;
+
+ if (taillength != 0) {
+ addPadBits(binlen, taillength);
+ }
+
+ // ecc code
+ if (symbolsize == 29) {
+ skew = 1;
+ }
+ calculateErrorCorrection(bytes, datablock, rsblock, skew);
+ NC = W - 2 * (W / FW);
+ NR = H - 2 * (H / FH);
+ places = new int[NC * NR];
+ placeData(NR, NC);
+ grid = new int[W * H];
+ for (i = 0; i < (W * H); i++) {
+ grid[i] = 0;
+ }
+ for (y = 0; y < H; y += FH) {
+ for (x = 0; x < W; x++) {
+ grid[y * W + x] = 1;
+ }
+ for (x = 0; x < W; x += 2) {
+ grid[(y + FH - 1) * W + x] = 1;
+ }
+ }
+ for (x = 0; x < W; x += FW) {
+ for (y = 0; y < H; y++) {
+ grid[y * W + x] = 1;
+ }
+ for (y = 0; y < H; y += 2) {
+ grid[y * W + x + FW - 1] = 1;
+ }
+ }
+ for (y = 0; y < NR; y++) {
+ for (x = 0; x < NC; x++) {
+ v = places[(NR - y - 1) * NC + x];
+ if (v == 1 || (v > 7 && (target[(v >> 3) - 1] & (1 << (v & 7))) != 0)) {
+ grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1;
+ }
+ }
+ }
+
+ readable = new StringBuilder();
+ pattern = new String[H];
+ rowCount = H;
+ rowHeight = new int[H];
+ for (y = H - 1; y >= 0; y--) {
+ bin = new StringBuilder();
+ for (x = 0; x < W; x++) {
+ if (grid[W * y + x] == 1) {
+ bin.append("1");
+ } else {
+ bin.append("0");
+ }
+ }
+ pattern[(H - y) - 1] = bin2pat(bin.toString());
+ rowHeight[(H - y) - 1] = 1;
+ }
+
+ encodeInfo.append("Grid Size: ").append(W).append(" X ").append(H).append("\n");
+ encodeInfo.append("Data Codewords: ").append(datablock).append("\n");
+ encodeInfo.append("ECC Codewords: ").append(rsblock).append("\n");
+
+ plotSymbol();
+ return true;
+ }
+
+ private int generateCodewords() {
+ /* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate */
+ /* Supports encoding FNC1 in supporting systems */
+ /* Supports ECI encoding for whole message only, not inline switching */
+
+ encodeInfo.append("Encoding: ");
+ int sp, tp, i;
+ dm_mode current_mode, next_mode;
+ int inputlen = content.length();
+
+ sp = 0;
+ tp = 0;
+ process_p = 0;
+
+ for (i = 0; i < 8; i++) {
+ process_buffer[i] = 0;
+ }
+ binary_length = 0;
+
+ /* step (a) */
+ current_mode = dm_mode.DM_ASCII;
+ next_mode = dm_mode.DM_ASCII;
+
+ if (inputDataType == DataType.GS1) {
+ target[tp] = 232;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("FNC1 ");
+ } /* FNC1 */
+
+ if (eciMode != 3) {
+ target[tp] = 241; // ECI
+ tp++;
+
+ if (eciMode <= 126) {
+ target[tp] = eciMode + 1;
+ tp++;
+ }
+
+ if ((eciMode >= 127) && (eciMode <= 16382)) {
+ target[tp] = ((eciMode - 127) / 254) + 128;
+ tp++;
+ target[tp] = ((eciMode - 127) % 254) + 1;
+ tp++;
+ }
+
+ if (eciMode >= 16383) {
+ target[tp] = ((eciMode - 16383) / 64516) + 192;
+ tp++;
+ target[tp] = (((eciMode - 16383) / 254) % 254) + 1;
+ tp++;
+ target[tp] = ((eciMode - 16383) & 254) + 1;
+ tp++;
+ }
+
+ encodeInfo.append("ECI ");
+ }
+
+ if (readerInit) {
+ if (inputDataType == DataType.GS1) {
+ errorMsg.append("Cannot encode in GS1 mode and Reader Initialisation at the same time");
+ return 0;
+ } else {
+ target[tp] = 234;
+ tp++; /* Reader Programming */
+
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("RP ");
+ }
+ }
+
+ /* Check for Macro05/Macro06 */
+ /* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */
+ /* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */
+ if (tp == 0 & sp == 0 && inputlen >= 9) {
+ if (inputData[0] == '[' && inputData[1] == ')' && inputData[2] == '>'
+ && inputData[3] == '\u001e' && inputData[4] == '0'
+ && (inputData[5] == '5' || inputData[5] == '6')
+ && inputData[6] == '\u001d'
+ && inputData[inputlen - 2] == '\u001e'
+ && inputData[inputlen - 1] == '\u0004') {
+ /* Output macro Codeword */
+ if (inputData[5] == '5') {
+ target[tp] = 236;
+ encodeInfo.append("Micro05 ");
+ } else {
+ target[tp] = 237;
+ encodeInfo.append("Macro06 ");
+ }
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ /* Remove macro characters from input string */
+ sp = 7;
+ inputlen -= 2;
+ }
+ }
+
+ while (sp < inputlen) {
+
+ current_mode = next_mode;
+
+ /* step (b) - ASCII encodation */
+ if (current_mode == dm_mode.DM_ASCII) {
+ next_mode = dm_mode.DM_ASCII;
+
+ for (i = 0; i < 8; i++) {
+ process_buffer[i] = 0;
+ }
+
+ if (isTwoDigits(sp)) {
+ target[tp] = (10 * Character.getNumericValue(inputData[sp]))
+ + Character.getNumericValue(inputData[sp + 1]) + 130;
+ encodeInfo.append(Integer.toString(target[tp] - 130)).append(" ");
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ sp += 2;
+ } else {
+ next_mode = lookAheadTest(sp, current_mode);
+
+ if (next_mode != dm_mode.DM_ASCII) {
+ switch (next_mode) {
+ case DM_C40:
+ target[tp] = 230;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("C40 ");
+ break;
+ case DM_TEXT:
+ target[tp] = 239;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("TEX ");
+ break;
+ case DM_X12:
+ target[tp] = 238;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("X12 ");
+ break;
+ case DM_EDIFACT:
+ target[tp] = 240;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("EDI ");
+ break;
+ case DM_BASE256:
+ target[tp] = 231;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("BAS ");
+ break;
+ }
+ } else {
+ if (inputData[sp] > 127) {
+ target[tp] = 235; /* FNC4 */
+
+ encodeInfo.append("FNC4 ");
+ tp++;
+ target[tp] = (inputData[sp] - 128) + 1;
+ encodeInfo.append(Integer.toString(target[tp] - 1)).append(" ");
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ } else {
+ if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) {
+ target[tp] = 232; /* FNC1 */
+ encodeInfo.append("FNC1 ");
+ } else {
+ target[tp] = inputData[sp] + 1;
+ encodeInfo.append(Integer.toString(target[tp] - 1)).append(" ");
+ }
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ }
+ sp++;
+ }
+ }
+
+ }
+
+ /* step (c) C40 encodation */
+ if (current_mode == dm_mode.DM_C40) {
+ int shift_set, value;
+
+ next_mode = dm_mode.DM_C40;
+ if (process_p == 0) {
+ next_mode = lookAheadTest(sp, current_mode);
+ }
+
+ if (next_mode != dm_mode.DM_C40) {
+ target[tp] = 254;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++; /* Unlatch */
+
+ next_mode = dm_mode.DM_ASCII;
+ encodeInfo.append("ASC ");
+ } else {
+ if (inputData[sp] > 127) {
+ process_buffer[process_p] = 1;
+ process_p++;
+ process_buffer[process_p] = 30;
+ process_p++; /* Upper Shift */
+
+ shift_set = c40_shift[inputData[sp] - 128];
+ value = c40_value[inputData[sp] - 128];
+ } else {
+ shift_set = c40_shift[inputData[sp]];
+ value = c40_value[inputData[sp]];
+ }
+
+ if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) {
+ shift_set = 2;
+ value = 27; /* FNC1 */
+
+ }
+
+ if (shift_set != 0) {
+ process_buffer[process_p] = shift_set - 1;
+ process_p++;
+ }
+ process_buffer[process_p] = value;
+ process_p++;
+
+ if (process_p >= 3) {
+ int iv;
+
+ iv = (1600 * process_buffer[0]) + (40 * process_buffer[1])
+ + (process_buffer[2]) + 1;
+ target[tp] = iv / 256;
+ tp++;
+ target[tp] = iv % 256;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("(").append(Integer.toString(process_buffer[0]))
+ .append(" ").append(Integer.toString(process_buffer[1]))
+ .append(" ").append(Integer.toString(process_buffer[2]))
+ .append(") ");
+
+ process_buffer[0] = process_buffer[3];
+ process_buffer[1] = process_buffer[4];
+ process_buffer[2] = process_buffer[5];
+ process_buffer[3] = 0;
+ process_buffer[4] = 0;
+ process_buffer[5] = 0;
+ process_p -= 3;
+ }
+ sp++;
+ }
+ }
+
+ /* step (d) Text encodation */
+ if (current_mode == dm_mode.DM_TEXT) {
+ int shift_set, value;
+
+ next_mode = dm_mode.DM_TEXT;
+ if (process_p == 0) {
+ next_mode = lookAheadTest(sp, current_mode);
+ }
+
+ if (next_mode != dm_mode.DM_TEXT) {
+ target[tp] = 254;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++; /* Unlatch */
+
+ next_mode = dm_mode.DM_ASCII;
+ encodeInfo.append("ASC ");
+ } else {
+ if (inputData[sp] > 127) {
+ process_buffer[process_p] = 1;
+ process_p++;
+ process_buffer[process_p] = 30;
+ process_p++; /* Upper Shift */
+
+ shift_set = text_shift[inputData[sp] - 128];
+ value = text_value[inputData[sp] - 128];
+ } else {
+ shift_set = text_shift[inputData[sp]];
+ value = text_value[inputData[sp]];
+ }
+
+ if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) {
+ shift_set = 2;
+ value = 27; /* FNC1 */
+
+ }
+
+ if (shift_set != 0) {
+ process_buffer[process_p] = shift_set - 1;
+ process_p++;
+ }
+ process_buffer[process_p] = value;
+ process_p++;
+
+ if (process_p >= 3) {
+ int iv;
+
+ iv = (1600 * process_buffer[0]) + (40 * process_buffer[1])
+ + (process_buffer[2]) + 1;
+ target[tp] = iv / 256;
+ tp++;
+ target[tp] = iv % 256;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("(").append(Integer.toString(process_buffer[0])).append(" ")
+ .append(Integer.toString(process_buffer[1])).append(" ")
+ .append(Integer.toString(process_buffer[2])).append(") ");
+
+ process_buffer[0] = process_buffer[3];
+ process_buffer[1] = process_buffer[4];
+ process_buffer[2] = process_buffer[5];
+ process_buffer[3] = 0;
+ process_buffer[4] = 0;
+ process_buffer[5] = 0;
+ process_p -= 3;
+ }
+ sp++;
+ }
+ }
+
+ /* step (e) X12 encodation */
+ if (current_mode == dm_mode.DM_X12) {
+ int value = 0;
+
+ next_mode = dm_mode.DM_X12;
+ if (process_p == 0) {
+ next_mode = lookAheadTest(sp, current_mode);
+ }
+
+ if (next_mode != dm_mode.DM_X12) {
+ target[tp] = 254;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++; /* Unlatch */
+
+ next_mode = dm_mode.DM_ASCII;
+ encodeInfo.append("ASC ");
+ } else {
+ if (inputData[sp] == 13) {
+ value = 0;
+ }
+ if (inputData[sp] == '*') {
+ value = 1;
+ }
+ if (inputData[sp] == '>') {
+ value = 2;
+ }
+ if (inputData[sp] == ' ') {
+ value = 3;
+ }
+ if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) {
+ value = (inputData[sp] - '0') + 4;
+ }
+ if ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')) {
+ value = (inputData[sp] - 'A') + 14;
+ }
+
+ process_buffer[process_p] = value;
+ process_p++;
+
+ if (process_p >= 3) {
+ int iv;
+
+ iv = (1600 * process_buffer[0]) + (40 * process_buffer[1])
+ + (process_buffer[2]) + 1;
+ target[tp] = iv / 256;
+ tp++;
+ target[tp] = iv % 256;
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("(").append(Integer.toString(process_buffer[0])).append(" ")
+ .append(Integer.toString(process_buffer[1])).append(" ")
+ .append(Integer.toString(process_buffer[2])).append(") ");
+
+ process_buffer[0] = process_buffer[3];
+ process_buffer[1] = process_buffer[4];
+ process_buffer[2] = process_buffer[5];
+ process_buffer[3] = 0;
+ process_buffer[4] = 0;
+ process_buffer[5] = 0;
+ process_p -= 3;
+ }
+ sp++;
+ }
+ }
+
+ /* step (f) EDIFACT encodation */
+ if (current_mode == dm_mode.DM_EDIFACT) {
+ int value = 0;
+
+ next_mode = dm_mode.DM_EDIFACT;
+ if (process_p == 3) {
+ next_mode = lookAheadTest(sp, current_mode);
+ }
+
+ if (next_mode != dm_mode.DM_EDIFACT) {
+ process_buffer[process_p] = 31;
+ process_p++;
+ next_mode = dm_mode.DM_ASCII;
+ } else {
+ if ((inputData[sp] >= '@') && (inputData[sp] <= '^')) {
+ value = inputData[sp] - '@';
+ }
+ if ((inputData[sp] >= ' ') && (inputData[sp] <= '?')) {
+ value = inputData[sp];
+ }
+
+ process_buffer[process_p] = value;
+ process_p++;
+ sp++;
+ }
+
+ if (process_p >= 4) {
+ target[tp] = (process_buffer[0] << 2)
+ + ((process_buffer[1] & 0x30) >> 4);
+ tp++;
+ target[tp] = ((process_buffer[1] & 0x0f) << 4)
+ + ((process_buffer[2] & 0x3c) >> 2);
+ tp++;
+ target[tp] = ((process_buffer[2] & 0x03) << 6)
+ + process_buffer[3];
+ tp++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ binary[binary_length] = ' ';
+ binary_length++;
+ encodeInfo.append("(").append(Integer.toString(process_buffer[0])).append(" ")
+ .append(Integer.toString(process_buffer[1])).append(" ")
+ .append(Integer.toString(process_buffer[2])).append(") ");
+
+ process_buffer[0] = process_buffer[4];
+ process_buffer[1] = process_buffer[5];
+ process_buffer[2] = process_buffer[6];
+ process_buffer[3] = process_buffer[7];
+ process_buffer[4] = 0;
+ process_buffer[5] = 0;
+ process_buffer[6] = 0;
+ process_buffer[7] = 0;
+ process_p -= 4;
+ }
+ }
+
+ /* step (g) Base 256 encodation */
+ if (current_mode == dm_mode.DM_BASE256) {
+ next_mode = lookAheadTest(sp, current_mode);
+
+ if (next_mode == dm_mode.DM_BASE256) {
+ target[tp] = inputData[sp];
+ encodeInfo.append(Integer.toString(target[tp])).append(" ");
+ tp++;
+ sp++;
+ binary[binary_length] = 'b';
+ binary_length++;
+ } else {
+ next_mode = dm_mode.DM_ASCII;
+ encodeInfo.append("ASC ");
+ }
+ }
+
+ if (tp > 1558) {
+ return 0;
+ }
+
+ } /* while */
+
+ /* Add length and randomising algorithm to b256 */
+ i = 0;
+ while (i < tp) {
+ if (binary[i] == 'b') {
+ if ((i == 0) || ((i != 0) && (binary[i - 1] != 'b'))) {
+ /* start of binary data */
+ int binary_count; /* length of b256 data */
+
+ for (binary_count = 0; binary[binary_count + i] == 'b';
+ binary_count++)
+ ;
+
+ if (binary_count <= 249) {
+ insertAt(i, 'b');
+ insertValueAt(i, tp, (char) binary_count);
+ tp++;
+ } else {
+ insertAt(i, 'b');
+ insertAt(i + 1, 'b');
+ insertValueAt(i, tp, (char) ((binary_count / 250) + 249));
+ tp++;
+ insertValueAt(i + 1, tp, (char) (binary_count % 250));
+ tp++;
+ }
+ }
+ }
+ i++;
+ }
+
+ for (i = 0; i < tp; i++) {
+ if (binary[i] == 'b') {
+ int prn, temp;
+
+ prn = ((149 * (i + 1)) % 255) + 1;
+ temp = target[i] + prn;
+ if (temp <= 255) {
+ target[i] = temp;
+ } else {
+ target[i] = temp - 256;
+ }
+ }
+ }
+
+ encodeInfo.append("\n");
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < tp; i++) {
+ encodeInfo.append(Integer.toString(target[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+ last_mode = current_mode;
+ return tp;
+ }
+
+ private int encodeRemainder(int symbols_left, int target_length) {
+
+ int inputlen = content.length();
+
+ switch (last_mode) {
+ case DM_C40:
+ case DM_TEXT:
+ if (symbols_left == process_p) { // No unlatch required!
+
+ if (process_p == 1) { // 1 data character left to encode.
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 2) { // 2 data characters left to encode.
+
+ // Pad with shift 1 value (0) and encode as double.
+ int intValue = (1600 * process_buffer[0]) + (40 * process_buffer[1]) + 1; // ie (0 + 1).
+ target[target_length] = intValue / 256;
+ target_length++;
+ target[target_length] = intValue % 256;
+ target_length++;
+ }
+ }
+
+ if (symbols_left > process_p) {
+ target[target_length] = (254);
+ target_length++; // Unlatch and encode remaining data in ascii.
+ if (process_p == 1 || (process_p == 2 && process_buffer[0] < 3)) { // Check for a shift value.
+
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ } else if (process_p == 2) {
+ target[target_length] = inputData[inputlen - 2] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+ }
+ break;
+
+ case DM_X12:
+ if (symbols_left == process_p) { // Unlatch not required!
+
+ if (process_p == 1) { // 1 data character left to encode.
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 2) {
+ // Encode last 2 bytes as ascii.
+ target[target_length] = inputData[inputlen - 2] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+ }
+
+ if (symbols_left > process_p) { // Unlatch and encode remaining data in ascii.
+
+ target[target_length] = (254);
+ target_length++; // Unlatch.
+
+ if (process_p == 1) {
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 2) {
+ target[target_length] = inputData[inputlen - 2] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+ }
+ break;
+
+ case DM_EDIFACT:
+ if (symbols_left == process_p) // Unlatch not required!
+ {
+ if (process_p == 1) {
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 2) {
+ target[target_length] = inputData[inputlen - 2] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 3) { // Append edifact unlatch value (31) and encode as triple.
+ target[target_length] = (process_buffer[0] << 2)
+ + ((process_buffer[1] & 0x30) >> 4);
+ target_length++;
+ target[target_length] = ((process_buffer[1] & 0x0f) << 4)
+ + ((process_buffer[2] & 0x3c) >> 2);
+ target_length++;
+ target[target_length] = ((process_buffer[2] & 0x03) << 6)
+ + 31;
+ target_length++;
+ }
+ }
+
+ if (symbols_left > process_p) // Unlatch and encode remaining data in ascii.
+ {
+ // Edifact unlatch.
+ if (symbols_left < 3) {
+ target[target_length] = 31;
+ target_length++;
+ } else {
+ target[target_length] = (31 << 2);
+ target_length++;
+ }
+
+ if (process_p == 1) {
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 2) {
+ target[target_length] = inputData[inputlen - 2] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+
+ if (process_p == 3) {
+ target[target_length] = inputData[inputlen - 3] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 2] + 1;
+ target_length++;
+ target[target_length] = inputData[inputlen - 1] + 1;
+ target_length++;
+ }
+ }
+ break;
+ }
+
+ return target_length;
+ }
+
+ private boolean isTwoDigits(int pos) {
+ if (Character.isDigit((char) inputData[pos])) {
+ if (pos + 1 >= content.length()) {
+ return false;
+ }
+ if (Character.isDigit((char) inputData[pos + 1])) {
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+
+ private dm_mode lookAheadTest(int position, dm_mode current_mode) {
+ /* 'look ahead test' from Annex P */
+
+ double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count;
+ int sp;
+ int sourcelen = content.length();
+ dm_mode best_scheme = dm_mode.NULL;
+
+ /* step (j) */
+ if (current_mode == dm_mode.DM_ASCII) {
+ ascii_count = 0.0;
+ c40_count = 1.0;
+ text_count = 1.0;
+ x12_count = 1.0;
+ edf_count = 1.0;
+ b256_count = 1.25;
+ } else {
+ ascii_count = 1.0;
+ c40_count = 2.0;
+ text_count = 2.0;
+ x12_count = 2.0;
+ edf_count = 2.0;
+ b256_count = 2.25;
+ }
+
+ switch (current_mode) {
+ case DM_C40: // (j)(2)
+ c40_count = 0.0;
+ break;
+ case DM_TEXT: // (j)(3)
+ text_count = 0.0;
+ break;
+ case DM_X12: // (j)(4)
+ x12_count = 0.0;
+ break;
+ case DM_EDIFACT: // (j)(5)
+ edf_count = 0.0;
+ break;
+ case DM_BASE256: // (j)(6)
+ b256_count = 0.0;
+ break;
+ }
+
+ sp = position;
+
+ do {
+ if (sp == (sourcelen - 1)) {
+ /* At the end of data ... step (k) */
+ ascii_count = Math.ceil(ascii_count);
+ b256_count = Math.ceil(b256_count);
+ edf_count = Math.ceil(edf_count);
+ text_count = Math.ceil(text_count);
+ x12_count = Math.ceil(x12_count);
+ c40_count = Math.ceil(c40_count);
+
+ best_count = c40_count;
+ best_scheme = dm_mode.DM_C40; // (k)(7)
+
+ if (x12_count < best_count) {
+ best_count = x12_count;
+ best_scheme = dm_mode.DM_X12; // (k)(6)
+ }
+
+ if (text_count < best_count) {
+ best_count = text_count;
+ best_scheme = dm_mode.DM_TEXT; // (k)(5)
+ }
+
+ if (edf_count < best_count) {
+ best_count = edf_count;
+ best_scheme = dm_mode.DM_EDIFACT; // (k)(4)
+ }
+
+ if (b256_count < best_count) {
+ best_count = b256_count;
+ best_scheme = dm_mode.DM_BASE256; // (k)(3)
+ }
+
+ if (ascii_count <= best_count) {
+ best_scheme = dm_mode.DM_ASCII; // (k)(2)
+ }
+ } else {
+
+ /* ascii ... step (l) */
+ if ((inputData[sp] >= '0') && (inputData[sp] <= '9')) {
+ ascii_count += 0.5; // (l)(1)
+ } else {
+ if (inputData[sp] > 127) {
+ ascii_count = Math.ceil(ascii_count) + 2.0; // (l)(2)
+ } else {
+ ascii_count = Math.ceil(ascii_count) + 1.0; // (l)(3)
+ }
+ }
+
+ /* c40 ... step (m) */
+ if ((inputData[sp] == ' ') ||
+ (((inputData[sp] >= '0') && (inputData[sp] <= '9')) ||
+ ((inputData[sp] >= 'A') && (inputData[sp] <= 'Z')))) {
+ c40_count += (2.0 / 3.0); // (m)(1)
+ } else {
+ if (inputData[sp] > 127) {
+ c40_count += (8.0 / 3.0); // (m)(2)
+ } else {
+ c40_count += (4.0 / 3.0); // (m)(3)
+ }
+ }
+
+ /* text ... step (n) */
+ if ((inputData[sp] == ' ') ||
+ (((inputData[sp] >= '0') && (inputData[sp] <= '9')) ||
+ ((inputData[sp] >= 'a') && (inputData[sp] <= 'z')))) {
+ text_count += (2.0 / 3.0); // (n)(1)
+ } else {
+ if (inputData[sp] > 127) {
+ text_count += (8.0 / 3.0); // (n)(2)
+ } else {
+ text_count += (4.0 / 3.0); // (n)(3)
+ }
+ }
+
+ /* x12 ... step (o) */
+ if (isX12(inputData[sp])) {
+ x12_count += (2.0 / 3.0); // (o)(1)
+ } else {
+ if (inputData[sp] > 127) {
+ x12_count += (13.0 / 3.0); // (o)(2)
+ } else {
+ x12_count += (10.0 / 3.0); // (o)(3)
+ }
+ }
+
+ /* edifact ... step (p) */
+ if ((inputData[sp] >= ' ') && (inputData[sp] <= '^')) {
+ edf_count += (3.0 / 4.0); // (p)(1)
+ } else {
+ if (inputData[sp] > 127) {
+ edf_count += (17.0 / 4.0); // (p)(2)
+ } else {
+ edf_count += (13.0 / 4.0); // (p)(3)
+ }
+ }
+ if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) {
+ edf_count += 6.0;
+ }
+
+ /* base 256 ... step (q) */
+ if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) {
+ b256_count += 4.0; // (q)(1)
+ } else {
+ b256_count += 1.0; // (q)(2)
+ }
+ }
+
+
+ if (sp > (position + 3)) {
+ /* 4 data characters processed ... step (r) */
+
+ /* step (r)(6) */
+ if (((c40_count + 1.0) < ascii_count) &&
+ ((c40_count + 1.0) < b256_count) &&
+ ((c40_count + 1.0) < edf_count) &&
+ ((c40_count + 1.0) < text_count)) {
+
+ if (c40_count < x12_count) {
+ best_scheme = dm_mode.DM_C40;
+ }
+
+ if (c40_count == x12_count) {
+ if (p_r_6_2_1(sp, sourcelen)) {
+ // Test (r)(6)(ii)(i)
+ best_scheme = dm_mode.DM_X12;
+ } else {
+ best_scheme = dm_mode.DM_C40;
+ }
+ }
+ }
+
+ /* step (r)(5) */
+ if (((x12_count + 1.0) < ascii_count) &&
+ ((x12_count + 1.0) < b256_count) &&
+ ((x12_count + 1.0) < edf_count) &&
+ ((x12_count + 1.0) < text_count) &&
+ ((x12_count + 1.0) < c40_count)) {
+ best_scheme = dm_mode.DM_X12;
+ }
+
+ /* step (r)(4) */
+ if (((text_count + 1.0) < ascii_count) &&
+ ((text_count + 1.0) < b256_count) &&
+ ((text_count + 1.0) < edf_count) &&
+ ((text_count + 1.0) < x12_count) &&
+ ((text_count + 1.0) < c40_count)) {
+ best_scheme = dm_mode.DM_TEXT;
+ }
+
+ /* step (r)(3) */
+ if (((edf_count + 1.0) < ascii_count) &&
+ ((edf_count + 1.0) < b256_count) &&
+ ((edf_count + 1.0) < text_count) &&
+ ((edf_count + 1.0) < x12_count) &&
+ ((edf_count + 1.0) < c40_count)) {
+ best_scheme = dm_mode.DM_EDIFACT;
+ }
+
+ /* step (r)(2) */
+ if (((b256_count + 1.0) <= ascii_count) ||
+ (((b256_count + 1.0) < edf_count) &&
+ ((b256_count + 1.0) < text_count) &&
+ ((b256_count + 1.0) < x12_count) &&
+ ((b256_count + 1.0) < c40_count))) {
+ best_scheme = dm_mode.DM_BASE256;
+ }
+
+ /* step (r)(1) */
+ if (((ascii_count + 1.0) <= b256_count) &&
+ ((ascii_count + 1.0) <= edf_count) &&
+ ((ascii_count + 1.0) <= text_count) &&
+ ((ascii_count + 1.0) <= x12_count) &&
+ ((ascii_count + 1.0) <= c40_count)) {
+ best_scheme = dm_mode.DM_ASCII;
+ }
+ }
+
+ sp++;
+ } while (best_scheme == dm_mode.NULL); // step (s)
+
+ return best_scheme;
+ }
+
+ private boolean p_r_6_2_1(int position, int sourcelen) {
+ /* Annex P section (r)(6)(ii)(I)
+ "If one of the three X12 terminator/separator characters first
+ occurs in the yet to be processed data before a non-X12 character..."
+ */
+
+ int i;
+ int nonX12Position = 0;
+ int specialX12Position = 0;
+ boolean retval = false;
+
+ for (i = position; i < sourcelen; i++) {
+ if (nonX12Position == 0 && !isX12(i)) {
+ nonX12Position = i;
+ }
+
+ if (specialX12Position == 0) {
+ if ((inputData[i] == (char) 13) ||
+ (inputData[i] == '*') ||
+ (inputData[i] == '>')) {
+ specialX12Position = i;
+ }
+ }
+ }
+
+ if ((nonX12Position != 0) && (specialX12Position != 0)) {
+ if (specialX12Position < nonX12Position) {
+ retval = true;
+ }
+ }
+
+ return retval;
+ }
+
+ private boolean isX12(int source) {
+ if (source == 13) {
+ return true;
+ }
+ if (source == 42) {
+ return true;
+ }
+ if (source == 62) {
+ return true;
+ }
+ if (source == 32) {
+ return true;
+ }
+ if ((source >= '0') && (source <= '9')) {
+ return true;
+ }
+ if ((source >= 'A') && (source <= 'Z')) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void calculateErrorCorrection(int bytes, int datablock, int rsblock, int skew) {
+ // calculate and append ecc code, and if necessary interleave
+ int blocks = (bytes + 2) / datablock, b;
+ int n, p;
+ ReedSolomon rs = new ReedSolomon();
+
+ rs.init_gf(0x12d);
+ rs.init_code(rsblock, 1);
+
+ for (b = 0; b < blocks; b++) {
+ int[] buf = new int[256];
+ int[] ecc = new int[256];
+
+ p = 0;
+ for (n = b; n < bytes; n += blocks) {
+ buf[p++] = target[n];
+ }
+ rs.encode(p, buf);
+ for (n = 0; n < rsblock; n++) {
+ ecc[n] = rs.getResult(n);
+ }
+ p = rsblock - 1; // comes back reversed
+ for (n = b; n < rsblock * blocks; n += blocks) {
+ if (skew == 1) {
+ /* Rotate ecc data to make 144x144 size symbols acceptable */
+ /* See http://groups.google.com/group/postscriptbarcode/msg/5ae8fda7757477da */
+ if (b < 8) {
+ target[bytes + n + 2] = ecc[p--];
+ } else {
+ target[bytes + n - 8] = ecc[p--];
+ }
+ } else {
+ target[bytes + n] = ecc[p--];
+ }
+ }
+ }
+ }
+
+ private void insertAt(int pos, char newbit) {
+ /* Insert a character into the middle of a string at position posn */
+ int i;
+
+ for (i = binary_length; i > pos; i--) {
+ binary[i] = binary[i - 1];
+ }
+ binary[pos] = newbit;
+ }
+
+ private void insertValueAt(int posn, int streamlen, char newbit) {
+ int i;
+
+ for (i = streamlen; i > posn; i--) {
+ target[i] = target[i - 1];
+ }
+ target[posn] = newbit;
+ }
+
+ private void addPadBits(int tp, int tail_length) {
+ /* adds unlatch and pad bits */
+ int i, prn, temp;
+
+ switch (last_mode) {
+ case DM_C40:
+ case DM_TEXT:
+ case DM_X12:
+ target[tp] = 254;
+ tp++; /* Unlatch */
+
+ tail_length--;
+ }
+
+ for (i = tail_length; i > 0; i--) {
+ if (i == tail_length) {
+ target[tp] = 129;
+ tp++; /* Pad */
+
+ } else {
+ prn = ((149 * (tp + 1)) % 253) + 1;
+ temp = 129 + prn;
+ if (temp <= 254) {
+ target[tp] = temp;
+ tp++;
+ } else {
+ target[tp] = temp - 254;
+ tp++;
+ }
+ }
+ }
+ }
+
+ private void placeData(int NR, int NC) {
+ int r, c, p;
+ // invalidate
+ for (r = 0; r < NR; r++) {
+ for (c = 0; c < NC; c++) {
+ places[r * NC + c] = 0;
+ }
+ }
+ // start
+ p = 1;
+ r = 4;
+ c = 0;
+ do {
+ // check corner
+ if (r == NR && (c == 0)) {
+ placeCornerA(NR, NC, p++);
+ }
+ if (r == NR - 2 && (c == 0) && ((NC % 4) != 0)) {
+ placeCornerB(NR, NC, p++);
+ }
+ if (r == NR - 2 && (c == 0) && (NC % 8) == 4) {
+ placeCornerC(NR, NC, p++);
+ }
+ if (r == NR + 4 && c == 2 && ((NC % 8) == 0)) {
+ placeCornerD(NR, NC, p++);
+ }
+ // up/right
+ do {
+ if (r < NR && c >= 0 && (places[r * NC + c] == 0)) {
+ placeBlock(NR, NC, r, c, p++);
+ }
+ r -= 2;
+ c += 2;
+ } while (r >= 0 && c < NC);
+ r++;
+ c += 3;
+ // down/left
+ do {
+ if (r >= 0 && c < NC && (places[r * NC + c] == 0)) {
+ placeBlock(NR, NC, r, c, p++);
+ }
+ r += 2;
+ c -= 2;
+ } while (r < NR && c >= 0);
+ r += 3;
+ c++;
+ } while (r < NR || c < NC);
+ // unfilled corner
+ if (places[NR * NC - 1] == 0) {
+ places[NR * NC - 1] = places[NR * NC - NC - 2] = 1;
+ }
+ }
+
+ private void placeCornerA(int NR, int NC, int p) {
+ placeBit(NR, NC, NR - 1, 0, p, 7);
+ placeBit(NR, NC, NR - 1, 1, p, 6);
+ placeBit(NR, NC, NR - 1, 2, p, 5);
+ placeBit(NR, NC, 0, NC - 2, p, 4);
+ placeBit(NR, NC, 0, NC - 1, p, 3);
+ placeBit(NR, NC, 1, NC - 1, p, 2);
+ placeBit(NR, NC, 2, NC - 1, p, 1);
+ placeBit(NR, NC, 3, NC - 1, p, 0);
+ }
+
+ private void placeCornerB(int NR, int NC, int p) {
+ placeBit(NR, NC, NR - 3, 0, p, 7);
+ placeBit(NR, NC, NR - 2, 0, p, 6);
+ placeBit(NR, NC, NR - 1, 0, p, 5);
+ placeBit(NR, NC, 0, NC - 4, p, 4);
+ placeBit(NR, NC, 0, NC - 3, p, 3);
+ placeBit(NR, NC, 0, NC - 2, p, 2);
+ placeBit(NR, NC, 0, NC - 1, p, 1);
+ placeBit(NR, NC, 1, NC - 1, p, 0);
+ }
+
+ private void placeCornerC(int NR, int NC, int p) {
+ placeBit(NR, NC, NR - 3, 0, p, 7);
+ placeBit(NR, NC, NR - 2, 0, p, 6);
+ placeBit(NR, NC, NR - 1, 0, p, 5);
+ placeBit(NR, NC, 0, NC - 2, p, 4);
+ placeBit(NR, NC, 0, NC - 1, p, 3);
+ placeBit(NR, NC, 1, NC - 1, p, 2);
+ placeBit(NR, NC, 2, NC - 1, p, 1);
+ placeBit(NR, NC, 3, NC - 1, p, 0);
+ }
+
+ private void placeCornerD(int NR, int NC, int p) {
+ placeBit(NR, NC, NR - 1, 0, p, 7);
+ placeBit(NR, NC, NR - 1, NC - 1, p, 6);
+ placeBit(NR, NC, 0, NC - 3, p, 5);
+ placeBit(NR, NC, 0, NC - 2, p, 4);
+ placeBit(NR, NC, 0, NC - 1, p, 3);
+ placeBit(NR, NC, 1, NC - 3, p, 2);
+ placeBit(NR, NC, 1, NC - 2, p, 1);
+ placeBit(NR, NC, 1, NC - 1, p, 0);
+ }
+
+ private void placeBlock(int NR, int NC, int r, int c, int p) {
+ placeBit(NR, NC, r - 2, c - 2, p, 7);
+ placeBit(NR, NC, r - 2, c - 1, p, 6);
+ placeBit(NR, NC, r - 1, c - 2, p, 5);
+ placeBit(NR, NC, r - 1, c - 1, p, 4);
+ placeBit(NR, NC, r - 1, c, p, 3);
+ placeBit(NR, NC, r, c - 2, p, 2);
+ placeBit(NR, NC, r, c - 1, p, 1);
+ placeBit(NR, NC, r, c, p, 0);
+ }
+
+ private void placeBit(int NR, int NC, int r, int c, int p, int b) {
+ if (r < 0) {
+ r += NR;
+ c += 4 - ((NR + 4) % 8);
+ }
+ if (c < 0) {
+ c += NC;
+ r += 4 - ((NC + 4) % 8);
+ }
+ places[r * NC + c] = (p << 3) + b;
+ }
+
+ private enum dm_mode {
+
+ NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Ean.java b/barcode/src/main/java/org/xbib/graphics/barcode/Ean.java
new file mode 100755
index 0000000..d378a06
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Ean.java
@@ -0,0 +1,366 @@
+package org.xbib.graphics.barcode;
+
+import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
+import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
+import org.xbib.graphics.barcode.util.AddOn;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements EAN bar code symbology according to BS EN 797:1996.
+ * European Article Number data can be encoded in EAN-8 or EAN-13 format
+ * requiring a 7-digit or 12-digit input respectively. EAN-13 numbers map to
+ * Global Trade Identification Numbers (GTIN) whereas EAN-8 symbols are
+ * generally for internal use only. Check digit is calculated and should not
+ * be in input data. Leading zeroes are added as required.
+ */
+public class Ean extends Symbol {
+
+ private boolean useAddOn;
+
+ private String addOnContent;
+ private Mode mode;
+ private boolean linkageFlag;
+ private String[] EAN13Parity = {
+ "AAAAAA", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA",
+ "ABABAB", "ABABBA", "ABBABA"
+ };
+ private String[] EANsetA = {
+ "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213",
+ "3112"
+ };
+ private String[] EANsetB = {
+ "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121",
+ "2113"
+ };
+
+ public Ean() {
+ mode = Mode.EAN13;
+ useAddOn = false;
+ addOnContent = "";
+ linkageFlag = false;
+ }
+
+ public Mode getMode() {
+ return mode;
+ }
+
+ public void setMode(Mode mode) {
+ this.mode = mode;
+ }
+
+ protected void setLinkageFlag() {
+ linkageFlag = true;
+ }
+
+ @Override
+ public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) {
+ if (humanReadableLocation == TOP) {
+ throw new IllegalArgumentException("Cannot display human-readable text above EAN bar codes.");
+ } else {
+ super.setHumanReadableLocation(humanReadableLocation);
+ }
+ }
+
+ @Override
+ public boolean encode() {
+ boolean retval = false;
+ AddOn addOn = new AddOn();
+ String addOnData;
+
+ separateContent();
+
+ if (content.length() == 0) {
+ errorMsg.append("Missing EAN data");
+ retval = false;
+ } else {
+ switch (mode) {
+ case EAN8:
+ retval = ean8();
+ break;
+ case EAN13:
+ retval = ean13();
+ break;
+ }
+ }
+
+ if ((retval) && (useAddOn)) {
+ addOnData = addOn.calcAddOn(addOnContent);
+ if (addOnData.length() == 0) {
+ errorMsg.append("Invalid Add-On data");
+ retval = false;
+ } else {
+ pattern[0] = pattern[0] + "9" + addOnData;
+
+ //add leading zeroes to add-on text
+ if (addOnContent.length() == 1) {
+ addOnContent = "0" + addOnContent;
+ }
+ if (addOnContent.length() == 3) {
+ addOnContent = "0" + addOnContent;
+ }
+ if (addOnContent.length() == 4) {
+ addOnContent = "0" + addOnContent;
+ }
+ }
+ }
+
+ if (retval) {
+ plotSymbol();
+ }
+
+ return retval;
+ }
+
+ private void separateContent() {
+ int splitPoint;
+
+ splitPoint = content.indexOf('+');
+ if (splitPoint != -1) {
+ // There is a '+' in the input data, use an add-on EAN2 or EAN5
+ useAddOn = true;
+ addOnContent = content.substring(splitPoint + 1);
+ content = content.substring(0, splitPoint);
+ }
+ }
+
+ private boolean ean13() {
+ StringBuilder accumulator = new StringBuilder();
+ StringBuilder dest;
+ String parity;
+ int i;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (content.length() > 12) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ for (i = content.length(); i < 12; i++) {
+ accumulator.append("0");
+ }
+ accumulator.append(content);
+
+ accumulator.append(calcDigit(accumulator.toString()));
+
+ parity = EAN13Parity[accumulator.charAt(0) - '0'];
+
+ encodeInfo.append("Parity Digit: ").append(accumulator.charAt(0)).append("\n");
+
+ /* Start character */
+ dest = new StringBuilder("111");
+
+ for (i = 1; i < 13; i++) {
+ if (i == 7) {
+ dest.append("11111");
+ }
+
+ if ((i >= 1) && (i <= 6)) {
+ if (parity.charAt(i - 1) == 'B') {
+ dest.append(EANsetB[accumulator.charAt(i) - '0']);
+ } else {
+ dest.append(EANsetA[accumulator.charAt(i) - '0']);
+ }
+ } else {
+ dest.append(EANsetA[accumulator.charAt(i) - '0']);
+ }
+ }
+
+ dest.append("111");
+
+ readable = new StringBuilder(accumulator.toString());
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ return true;
+ }
+
+ private boolean ean8() {
+ StringBuilder accumulator = new StringBuilder();
+ int i;
+ StringBuilder dest;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (content.length() > 7) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ for (i = content.length(); i < 7; i++) {
+ accumulator.append("0");
+ }
+ accumulator.append(content);
+
+ accumulator.append(calcDigit(accumulator.toString()));
+
+ dest = new StringBuilder("111");
+ for (i = 0; i < 8; i++) {
+ if (i == 4) {
+ dest.append("11111");
+ }
+ dest.append(EANsetA[Character.getNumericValue(accumulator.charAt(i))]);
+ }
+ dest.append("111");
+
+ readable = new StringBuilder(accumulator.toString());
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ return true;
+ }
+
+ private char calcDigit(String x) {
+ int count = 0;
+ int c, cdigit;
+ int p = 0;
+ for (int i = x.length() - 1; i >= 0; i--) {
+ c = Character.getNumericValue(x.charAt(i));
+ if ((p % 2) == 0) {
+ c = c * 3;
+ }
+ count += c;
+ p++;
+ }
+ cdigit = 10 - (count % 10);
+ if (cdigit == 10) {
+ cdigit = 0;
+ }
+
+ encodeInfo.append("Check Digit: ").append(cdigit).append("\n");
+
+ return (char) (cdigit + '0');
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ boolean black;
+ int compositeOffset = 0;
+ int shortLongDiff = 5;
+ getRectangles().clear();
+ getTexts().clear();
+ black = true;
+ x = 0;
+ if (linkageFlag) {
+ compositeOffset = 6;
+ }
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ if (black) {
+ y = 0;
+ black = false;
+ w = pattern[0].charAt(xBlock) - '0';
+ h = defaultHeight;
+ /* Add extension to guide bars */
+ if (mode == Mode.EAN13) {
+ if ((x < 3) || (x > 91)) {
+ h += shortLongDiff;
+ }
+ if ((x > 45) && (x < 49)) {
+ h += shortLongDiff;
+ }
+ if (x > 95) {
+ // Drop add-on
+ h -= 8;
+ y = 8;
+ }
+ if (linkageFlag) {
+ if ((x == 0) || (x == 94)) {
+ h += 2;
+ y -= 2;
+ }
+ }
+ }
+ if (mode == Mode.EAN8) {
+ if ((x < 3) || (x > 62)) {
+ h += shortLongDiff;
+ }
+ if ((x > 30) && (x < 35)) {
+ h += shortLongDiff;
+ }
+ if (x > 66) {
+ // Drop add-on
+ h -= 8;
+ y = 8;
+ }
+ if (linkageFlag) {
+ if ((x == 0) || (x == 66)) {
+ h += 2;
+ y -= 2;
+ }
+ }
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x + 6, y + compositeOffset, w, h);
+ getRectangles().add(rect);
+ if ((x + w + 12) > symbolWidth) {
+ symbolWidth = x + w + 12;
+ }
+ } else {
+ black = true;
+ }
+ x += (double) (pattern[0].charAt(xBlock) - '0');
+
+ }
+
+ if (linkageFlag) {
+ // Add separator for composite symbology
+ if (mode == Mode.EAN13) {
+ getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(94 + 6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(95 + 6, 2, 1, 2));
+ } else {
+ getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(66 + 6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(67 + 6, 2, 1, 2));
+ }
+ }
+
+ symbolHeight = defaultHeight + 5;
+
+ /* Now add the text */
+ if (getHumanReadableLocation() != NONE) {
+ double baseline = getHeight() + fontSize - shortLongDiff + compositeOffset;
+ double addOnBaseline = 6.0 + compositeOffset;
+ if (mode == Mode.EAN13) {
+ getTexts().add(new TextBox(3, baseline, readable.substring(0, 1)));
+ getTexts().add(new TextBox(30, baseline, readable.substring(1, 7)));
+ getTexts().add(new TextBox(77, baseline, readable.substring(7, 13)));
+ if (useAddOn) {
+ if (addOnContent.length() == 2) {
+ getTexts().add(new TextBox(118, addOnBaseline, addOnContent));
+ } else {
+ getTexts().add(new TextBox(133, addOnBaseline, addOnContent));
+ }
+ }
+ } else { // EAN8
+ getTexts().add(new TextBox(23, baseline, readable.substring(0, 4)));
+ getTexts().add(new TextBox(55, baseline, readable.substring(4, 8)));
+ if (useAddOn) {
+ if (addOnContent.length() == 2) {
+ getTexts().add(new TextBox(93, addOnBaseline, addOnContent));
+ } else {
+ getTexts().add(new TextBox(105, addOnBaseline, addOnContent));
+ }
+ }
+ }
+ }
+ }
+
+ public enum Mode {
+ EAN8, EAN13
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java b/barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java
new file mode 100755
index 0000000..59835e0
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java
@@ -0,0 +1,2015 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements Grid Matrix bar code symbology according to AIMD014.
+ * Grid Matrix is a matrix symbology which can encode characters in the ISO/IEC
+ * 8859-1 (Latin-1) character set as well as those in the GB-2312 character set.
+ * Input is assumed to be formatted as a UTF string.
+ */
+public class GridMatrix extends Symbol {
+
+ private final char[] shift_set = {
+ /* From Table 7 - Encoding of control characters */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* NULL -> SI */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* DLE -> US */
+ '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':',
+ ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'
+ };
+
+ private final int[] gm_recommend_cw = {
+ 9, 30, 59, 114, 170, 237, 315, 405, 506, 618, 741, 875, 1021
+ };
+ private final int[] gm_max_cw = {
+ 11, 40, 79, 146, 218, 305, 405, 521, 650, 794, 953, 1125, 1313
+ };
+
+ private final int[] gm_data_codewords = {
+ 0, 15, 13, 11, 9,
+ 45, 40, 35, 30, 25,
+ 89, 79, 69, 59, 49,
+ 146, 130, 114, 98, 81,
+ 218, 194, 170, 146, 121,
+ 305, 271, 237, 203, 169,
+ 405, 360, 315, 270, 225,
+ 521, 463, 405, 347, 289,
+ 650, 578, 506, 434, 361,
+ 794, 706, 618, 530, 441,
+ 953, 847, 741, 635, 529,
+ 1125, 1000, 875, 750, 625,
+ 1313, 1167, 1021, 875, 729
+ };
+
+ private final int[] gm_n1 = {
+ 18, 50, 98, 81, 121, 113, 113, 116, 121, 126, 118, 125, 122
+ };
+ private final int[] gm_b1 = {
+ 1, 1, 1, 2, 2, 2, 2, 3, 2, 7, 5, 10, 6
+ };
+ private final int[] gm_b2 = {
+ 0, 0, 0, 0, 0, 1, 2, 2, 4, 0, 4, 0, 6
+ };
+
+ private final int[] gm_ebeb = {
+ /* E1 B3 E2 B4 */
+ 0, 0, 0, 0, // version 1
+ 3, 1, 0, 0,
+ 5, 1, 0, 0,
+ 7, 1, 0, 0,
+ 9, 1, 0, 0,
+ 5, 1, 0, 0, // version 2
+ 10, 1, 0, 0,
+ 15, 1, 0, 0,
+ 20, 1, 0, 0,
+ 25, 1, 0, 0,
+ 9, 1, 0, 0, // version 3
+ 19, 1, 0, 0,
+ 29, 1, 0, 0,
+ 39, 1, 0, 0,
+ 49, 1, 0, 0,
+ 8, 2, 0, 0, // version 4
+ 16, 2, 0, 0,
+ 24, 2, 0, 0,
+ 32, 2, 0, 0,
+ 41, 1, 10, 1,
+ 12, 2, 0, 0, // version 5
+ 24, 2, 0, 0,
+ 36, 2, 0, 0,
+ 48, 2, 0, 0,
+ 61, 1, 60, 1,
+ 11, 3, 0, 0, // version 6
+ 23, 1, 22, 2,
+ 34, 2, 33, 1,
+ 45, 3, 0, 0,
+ 57, 1, 56, 2,
+ 12, 1, 11, 3, // version 7
+ 23, 2, 22, 2,
+ 34, 3, 33, 1,
+ 45, 4, 0, 0,
+ 57, 1, 56, 3,
+ 12, 2, 11, 3, // version 8
+ 23, 5, 0, 0,
+ 35, 3, 34, 2,
+ 47, 1, 46, 4,
+ 58, 4, 57, 1,
+ 12, 6, 0, 0, // version 9
+ 24, 6, 0, 0,
+ 36, 6, 0, 0,
+ 48, 6, 0, 0,
+ 61, 1, 60, 5,
+ 13, 4, 12, 3, // version 10
+ 26, 1, 25, 6,
+ 38, 5, 37, 2,
+ 51, 2, 50, 5,
+ 63, 7, 0, 0,
+ 12, 6, 11, 3, // version 11
+ 24, 4, 23, 5,
+ 36, 2, 35, 7,
+ 47, 9, 0, 0,
+ 59, 7, 58, 2,
+ 13, 5, 12, 5, // version 12
+ 25, 10, 0, 0,
+ 38, 5, 37, 5,
+ 50, 10, 0, 0,
+ 63, 5, 62, 5,
+ 13, 1, 12, 11, //version 13
+ 25, 3, 24, 9,
+ 37, 5, 36, 7,
+ 49, 7, 48, 5,
+ 61, 9, 60, 3
+ };
+
+ private final int[] gm_macro_matrix = {
+ 728, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650,
+ 727, 624, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 651,
+ 726, 623, 528, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 553, 652,
+ 725, 622, 527, 440, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 463, 554, 653,
+ 724, 621, 526, 439, 360, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 381, 464, 555, 654,
+ 723, 620, 525, 438, 359, 288, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 307, 382, 465, 556, 655,
+ 722, 619, 524, 437, 358, 287, 224, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 241, 308, 383, 466, 557, 656,
+ 721, 618, 523, 436, 357, 286, 223, 168, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 183, 242, 309, 384, 467, 558, 657,
+ 720, 617, 522, 435, 356, 285, 222, 167, 120, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 133, 184, 243, 310, 385, 468, 559, 658,
+ 719, 616, 521, 434, 355, 284, 221, 166, 119, 80, 49, 50, 51, 52, 53, 54, 55, 56, 91, 134, 185, 244, 311, 386, 469, 560, 659,
+ 718, 615, 520, 433, 354, 283, 220, 165, 118, 79, 48, 25, 26, 27, 28, 29, 30, 57, 92, 135, 186, 245, 312, 387, 470, 561, 660,
+ 717, 614, 519, 432, 353, 282, 219, 164, 117, 78, 47, 24, 9, 10, 11, 12, 31, 58, 93, 136, 187, 246, 313, 388, 471, 562, 661,
+ 716, 613, 518, 431, 352, 281, 218, 163, 116, 77, 46, 23, 8, 1, 2, 13, 32, 59, 94, 137, 188, 247, 314, 389, 472, 563, 662,
+ 715, 612, 517, 430, 351, 280, 217, 162, 115, 76, 45, 22, 7, 0, 3, 14, 33, 60, 95, 138, 189, 248, 315, 390, 473, 564, 663,
+ 714, 611, 516, 429, 350, 279, 216, 161, 114, 75, 44, 21, 6, 5, 4, 15, 34, 61, 96, 139, 190, 249, 316, 391, 474, 565, 664,
+ 713, 610, 515, 428, 349, 278, 215, 160, 113, 74, 43, 20, 19, 18, 17, 16, 35, 62, 97, 140, 191, 250, 317, 392, 475, 566, 665,
+ 712, 609, 514, 427, 348, 277, 214, 159, 112, 73, 42, 41, 40, 39, 38, 37, 36, 63, 98, 141, 192, 251, 318, 393, 476, 567, 666,
+ 711, 608, 513, 426, 347, 276, 213, 158, 111, 72, 71, 70, 69, 68, 67, 66, 65, 64, 99, 142, 193, 252, 319, 394, 477, 568, 667,
+ 710, 607, 512, 425, 346, 275, 212, 157, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 143, 194, 253, 320, 395, 478, 569, 668,
+ 709, 606, 511, 424, 345, 274, 211, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 195, 254, 321, 396, 479, 570, 669,
+ 708, 605, 510, 423, 344, 273, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 255, 322, 397, 480, 571, 670,
+ 707, 604, 509, 422, 343, 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 323, 398, 481, 572, 671,
+ 706, 603, 508, 421, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331, 330, 329, 328, 327, 326, 325, 324, 399, 482, 573, 672,
+ 705, 602, 507, 420, 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, 402, 401, 400, 483, 574, 673,
+ 704, 601, 506, 505, 504, 503, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, 485, 484, 575, 674,
+ 703, 600, 599, 598, 597, 596, 595, 594, 593, 592, 591, 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, 577, 576, 675,
+ 702, 701, 700, 699, 698, 697, 696, 695, 694, 693, 692, 691, 690, 689, 688, 687, 686, 685, 684, 683, 682, 681, 680, 679, 678, 677, 676
+ };
+
+ private final char[] MIXED_ALPHANUM_SET = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', ' '
+ };
+ private int[] inputIntArray;
+
+ private StringBuilder binary = new StringBuilder("1");
+ private int[] word = new int[1460];
+ private boolean[] grid;
+ private gmMode appxDnextSection = gmMode.NULL;
+ private gmMode appxDlastSection = gmMode.NULL;
+ private int preferredVersion = 0;
+ private int preferredEccLevel = -1;
+
+ /**
+ * Set preferred size, or "version" of the symbol according to the following
+ * table. This value may be ignored if the data to be encoded does not fit
+ * into a symbol of the selected size.
+ * Available Grid Matrix symbol sizes
+ *
+ *
+ *
+ * Input
+ *
+ * Size
+ *
+ *
+ *
+ * 1
+ *
+ * 18 x 18
+ *
+ *
+ *
+ * 2
+ *
+ * 30 x 30
+ *
+ *
+ *
+ * 3
+ *
+ * 42 x 42
+ *
+ *
+ *
+ * 4
+ *
+ * 54 x 54
+ *
+ *
+ *
+ * 5
+ *
+ * 66 x 66
+ *
+ *
+ *
+ * 6
+ *
+ * 78 x 78
+ *
+ *
+ *
+ * 7
+ *
+ * 90x 90
+ *
+ *
+ *
+ * 8
+ *
+ * 102 x 102
+ *
+ *
+ *
+ * 9
+ *
+ * 114 x 114
+ *
+ *
+ *
+ * 10
+ *
+ * 126 x 126
+ *
+ *
+ *
+ * 11
+ *
+ * 138 x 138
+ *
+ *
+ *
+ * 12
+ *
+ * 150 x 150
+ *
+ *
+ *
+ * 13
+ *
+ * 162 x 162
+ *
+ *
+ *
+ *
+ * @param version Symbol version
+ */
+ public void setPreferredVersion(int version) {
+ preferredVersion = version;
+ }
+
+ /**
+ * Set the preferred amount of the symbol which should be dedicated to error
+ * correction data. Values should be selected from the following table:
+ * Available options for error correction capacity
+ *
+ *
+ *
+ * Mode
+ *
+ * Error Correction Capacity
+ *
+ *
+ *
+ * 1
+ *
+ * Approximately 10%
+ *
+ *
+ *
+ * 2
+ *
+ * Approximately 20%
+ *
+ *
+ *
+ * 3
+ *
+ * Approximately 30%
+ *
+ *
+ *
+ * 4
+ *
+ * Approximately 40%
+ *
+ *
+ *
+ * 5
+ *
+ * Approximately 50%
+ *
+ *
+ *
+ *
+ * @param eccLevel Error correction mode
+ */
+ public void setPreferredEccLevel(int eccLevel) {
+ preferredEccLevel = eccLevel;
+ }
+
+ @Override
+ public boolean encode() {
+ int size, modules, dark, error_number;
+ int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level;
+ int x, y, i;
+ int data_cw, input_latch = 0;
+ int data_max;
+ int length;
+ StringBuilder bin;
+ int qmarksBefore, qmarksAfter;
+
+ for (i = 0; i < 1460; i++) {
+ word[i] = 0;
+ }
+
+ try {
+ /* Try converting to GB2312 */
+ qmarksBefore = 0;
+ for (i = 0; i < content.length(); i++) {
+ if (content.charAt(i) == '?') {
+ qmarksBefore++;
+ }
+ }
+ inputBytes = content.getBytes("EUC_CN");
+ qmarksAfter = 0;
+ for (i = 0; i < inputBytes.length; i++) {
+ if (inputBytes[i] == '?') {
+ qmarksAfter++;
+ }
+ }
+ if (qmarksBefore == qmarksAfter) {
+ /* GB2312 encoding worked, use chinese compaction */
+ inputIntArray = new int[inputBytes.length];
+ length = 0;
+ for (i = 0; i < inputBytes.length; i++) {
+ if (((inputBytes[i] & 0xFF) >= 0xA1) && ((inputBytes[i] & 0xFF) <= 0xF7)) {
+ /* Double byte character */
+ inputIntArray[length] = ((inputBytes[i] & 0xFF) * 256) + (inputBytes[i + 1] & 0xFF);
+ i++;
+ length++;
+ } else {
+ /* Single byte character */
+ inputIntArray[length] = inputBytes[i] & 0xFF;
+ length++;
+ }
+ }
+ encodeInfo.append("Using GB2312 character encoding\n");
+ eciMode = 29;
+ } else {
+ /* GB2312 encoding didn't work, use other ECI mode */
+ eciProcess(); // Get ECI mode
+ length = inputBytes.length;
+ inputIntArray = new int[length];
+ for (i = 0; i < length; i++) {
+ inputIntArray[i] = inputBytes[i] & 0xFF;
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Byte conversion encoding error");
+ return false;
+ }
+
+ error_number = encodeGridMatrixBinary(length, readerInit);
+ if (error_number != 0) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ /* Determine the size of the symbol */
+ data_cw = binary.length() / 7;
+
+ auto_layers = 1;
+ for (i = 0; i < 13; i++) {
+ if (gm_recommend_cw[i] < data_cw) {
+ auto_layers = i + 1;
+ }
+ }
+
+ min_layers = 13;
+ for (i = 12; i > 0; i--) {
+ if (gm_max_cw[(i - 1)] >= data_cw) {
+ min_layers = i;
+ }
+ }
+ layers = auto_layers;
+ auto_ecc_level = 3;
+ if (layers == 1) {
+ auto_ecc_level = 5;
+ }
+ if ((layers == 2) || (layers == 3)) {
+ auto_ecc_level = 4;
+ }
+ min_ecc_level = 1;
+ if (layers == 1) {
+ min_ecc_level = 4;
+ }
+ if ((layers == 2) || (layers == 3)) {
+ min_ecc_level = 2;
+ }
+ ecc_level = auto_ecc_level;
+
+ if ((preferredVersion >= 1) && (preferredVersion <= 13)) {
+ input_latch = 1;
+ if (preferredVersion > min_layers) {
+ layers = preferredVersion;
+ } else {
+ layers = min_layers;
+ }
+ }
+
+ if (input_latch == 1) {
+ auto_ecc_level = 3;
+ if (layers == 1) {
+ auto_ecc_level = 5;
+ }
+ if ((layers == 2) || (layers == 3)) {
+ auto_ecc_level = 4;
+ }
+ ecc_level = auto_ecc_level;
+ if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) {
+ layers++;
+ }
+ }
+
+ if (input_latch == 0) {
+ if ((preferredEccLevel >= 1) && (preferredEccLevel <= 5)) {
+ if (preferredEccLevel > min_ecc_level) {
+ ecc_level = preferredEccLevel;
+ } else {
+ ecc_level = min_ecc_level;
+ }
+ }
+ if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) {
+ do {
+ layers++;
+ } while ((data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) && (layers <= 13));
+ }
+ }
+
+ data_max = 1313;
+ switch (ecc_level) {
+ case 2:
+ data_max = 1167;
+ break;
+ case 3:
+ data_max = 1021;
+ break;
+ case 4:
+ data_max = 875;
+ break;
+ case 5:
+ data_max = 729;
+ break;
+ }
+
+ if (data_cw > data_max) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ addErrorCorrection(data_cw, layers, ecc_level);
+ size = 6 + (layers * 12);
+ modules = 1 + (layers * 2);
+
+ encodeInfo.append("Layers: ").append(layers).append("\n");
+ encodeInfo.append("ECC Level: ").append(ecc_level).append("\n");
+ encodeInfo.append("Data Codewords: ").append(data_cw).append("\n");
+ encodeInfo.append("ECC Codewords: ").append(gm_data_codewords[((layers - 1) * 5)
+ + (ecc_level - 1)]).append("\n");
+ encodeInfo.append("Grid Size: ").append(modules).append(" X ").append(modules).append("\n");
+
+ grid = new boolean[size * size];
+
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ grid[(y * size) + x] = false;
+ }
+ }
+
+ placeDataInGrid(modules, size);
+ addLayerId(size, layers, modules, ecc_level);
+
+ /* Add macromodule frames */
+ for (x = 0; x < modules; x++) {
+ dark = 1 - (x & 1);
+ for (y = 0; y < modules; y++) {
+ if (dark == 1) {
+ for (i = 0; i < 5; i++) {
+ grid[((y * 6) * size) + (x * 6) + i] = true;
+ grid[(((y * 6) + 5) * size) + (x * 6) + i] = true;
+ grid[(((y * 6) + i) * size) + (x * 6)] = true;
+ grid[(((y * 6) + i) * size) + (x * 6) + 5] = true;
+ }
+ grid[(((y * 6) + 5) * size) + (x * 6) + 5] = true;
+ dark = 0;
+ } else {
+ dark = 1;
+ }
+ }
+ }
+
+ /* Copy values to symbol */
+ symbolWidth = size;
+ rowCount = size;
+ rowHeight = new int[rowCount];
+ pattern = new String[rowCount];
+
+ for (x = 0; x < size; x++) {
+ bin = new StringBuilder();
+ for (y = 0; y < size; y++) {
+ if (grid[(x * size) + y]) {
+ bin.append("1");
+ } else {
+ bin.append("0");
+ }
+ }
+ rowHeight[x] = 1;
+ pattern[x] = bin2pat(bin.toString());
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private int encodeGridMatrixBinary(int length, boolean reader) {
+ /* Create a binary stream representation of the input data.
+ 7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters,
+ Mixed numerals and latters, Control characters and 8-bit binary data */
+ int sp, glyph = 0;
+ gmMode current_mode, next_mode, last_mode;
+ int c1, c2;
+ boolean done;
+ int p = 0, ppos;
+ int punt = 0;
+ int number_pad_posn;
+ int byte_count_posn = 0, byte_count = 0;
+ int shift, i;
+ int[] numbuf = new int[3];
+ StringBuilder temp_binary;
+ gmMode[] modeMap = calculateModeMap(length);
+
+ binary = new StringBuilder();
+
+ sp = 0;
+ current_mode = gmMode.NULL;
+ number_pad_posn = 0;
+
+ encodeInfo.append("Encoding: ");
+
+ if (reader) {
+ binary.append("1010"); /* FNC3 - Reader Initialisation */
+ encodeInfo.append("INIT ");
+ }
+
+ if ((eciMode != 3) && (eciMode != 29)) {
+ binary.append("1100"); /* ECI */
+
+ if ((eciMode >= 0) && (eciMode <= 1023)) {
+ binary.append("0");
+ for (i = 0x200; i > 0; i = i >> 1) {
+ if ((eciMode & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ }
+
+ if ((eciMode >= 1024) && (eciMode <= 32767)) {
+ binary.append("10");
+ for (i = 0x4000; i > 0; i = i >> 1) {
+ if ((eciMode & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ }
+
+ if ((eciMode >= 32768) && (eciMode <= 811799)) {
+ binary.append("11");
+ for (i = 0x80000; i > 0; i = i >> 1) {
+ if ((eciMode & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ }
+
+ encodeInfo.append("ECI ").append(Integer.toString(eciMode)).append(" ");
+ }
+
+ do {
+ next_mode = modeMap[sp];
+
+ if (next_mode != current_mode) {
+ switch (current_mode) {
+ case NULL:
+ switch (next_mode) {
+ case GM_CHINESE:
+ binary.append("0001");
+ break;
+ case GM_NUMBER:
+ binary.append("0010");
+ break;
+ case GM_LOWER:
+ binary.append("0011");
+ break;
+ case GM_UPPER:
+ binary.append("0100");
+ break;
+ case GM_MIXED:
+ binary.append("0101");
+ break;
+ case GM_BYTE:
+ binary.append("0111");
+ break;
+ }
+ break;
+ case GM_CHINESE:
+ switch (next_mode) {
+ case GM_NUMBER:
+ binary.append("1111111100001");
+ break; // 8161
+ case GM_LOWER:
+ binary.append("1111111100010");
+ break; // 8162
+ case GM_UPPER:
+ binary.append("1111111100011");
+ break; // 8163
+ case GM_MIXED:
+ binary.append("1111111100100");
+ break; // 8164
+ case GM_BYTE:
+ binary.append("1111111100101");
+ break; // 8165
+ }
+ break;
+ case GM_NUMBER:
+ /* add numeric block padding value */
+ temp_binary = new StringBuilder(binary.substring(0, number_pad_posn));
+ switch (p) {
+ case 1:
+ temp_binary.append("10");
+ break; // 2 pad digits
+ case 2:
+ temp_binary.append("01");
+ break; // 1 pad digit
+ case 3:
+ temp_binary.append("00");
+ break; // 0 pad digits
+ }
+ temp_binary.append(binary.substring(number_pad_posn, binary.length()));
+ binary = temp_binary;
+
+ switch (next_mode) {
+ case GM_CHINESE:
+ binary.append("1111111011");
+ break; // 1019
+ case GM_LOWER:
+ binary.append("1111111100");
+ break; // 1020
+ case GM_UPPER:
+ binary.append("1111111101");
+ break; // 1021
+ case GM_MIXED:
+ binary.append("1111111110");
+ break; // 1022
+ case GM_BYTE:
+ binary.append("1111111111");
+ break; // 1023
+ }
+ break;
+ case GM_LOWER:
+ case GM_UPPER:
+ switch (next_mode) {
+ case GM_CHINESE:
+ binary.append("11100");
+ break; // 28
+ case GM_NUMBER:
+ binary.append("11101");
+ break; // 29
+ case GM_LOWER:
+ case GM_UPPER:
+ binary.append("11110");
+ break; // 30
+ case GM_MIXED:
+ binary.append("1111100");
+ break; // 124
+ case GM_BYTE:
+ binary.append("1111110");
+ break; // 126
+ }
+ break;
+ case GM_MIXED:
+ switch (next_mode) {
+ case GM_CHINESE:
+ binary.append("1111110001");
+ break; // 1009
+ case GM_NUMBER:
+ binary.append("1111110010");
+ break; // 1010
+ case GM_LOWER:
+ binary.append("1111110011");
+ break; // 1011
+ case GM_UPPER:
+ binary.append("1111110100");
+ break; // 1012
+ case GM_BYTE:
+ binary.append("1111110111");
+ break; // 1015
+ }
+ break;
+ case GM_BYTE:
+ /* add byte block length indicator */
+ addByteCount(byte_count_posn, byte_count);
+ byte_count = 0;
+ switch (next_mode) {
+ case GM_CHINESE:
+ binary.append("0001");
+ break; // 1
+ case GM_NUMBER:
+ binary.append("0010");
+ break; // 2
+ case GM_LOWER:
+ binary.append("0011");
+ break; // 3
+ case GM_UPPER:
+ binary.append("0100");
+ break; // 4
+ case GM_MIXED:
+ binary.append("0101");
+ break; // 5
+ }
+ break;
+ }
+
+ switch (next_mode) {
+ case GM_CHINESE:
+ encodeInfo.append("CHIN ");
+ break;
+ case GM_NUMBER:
+ encodeInfo.append("NUMB ");
+ break;
+ case GM_LOWER:
+ encodeInfo.append("LOWR ");
+ break;
+ case GM_UPPER:
+ encodeInfo.append("UPPR ");
+ break;
+ case GM_MIXED:
+ encodeInfo.append("MIXD ");
+ break;
+ case GM_BYTE:
+ encodeInfo.append("BYTE ");
+ break;
+ }
+
+ }
+ last_mode = current_mode;
+ current_mode = next_mode;
+
+ switch (current_mode) {
+ case GM_CHINESE:
+ done = false;
+ if (inputIntArray[sp] > 0xff) {
+ /* GB2312 character */
+ c1 = (inputIntArray[sp] & 0xff00) >> 8;
+ c2 = inputIntArray[sp] & 0xff;
+
+ if ((c1 >= 0xa0) && (c1 <= 0xa9)) {
+ glyph = (0x60 * (c1 - 0xa1)) + (c2 - 0xa0);
+ }
+ if ((c1 >= 0xb0) && (c1 <= 0xf7)) {
+ glyph = (0x60 * (c1 - 0xb0 + 9)) + (c2 - 0xa0);
+ }
+ done = true;
+ }
+ if (!(done)) {
+ if (sp != (length - 1)) {
+ if ((inputIntArray[sp] == 13) && (inputIntArray[sp + 1] == 10)) {
+ /* End of Line */
+ glyph = 7776;
+ sp++;
+ }
+ done = true;
+ }
+ }
+ if (!(done)) {
+ if (sp != (length - 1)) {
+ if (((inputIntArray[sp] >= '0') && (inputIntArray[sp] <= '9')) && ((inputIntArray[sp + 1] >= '0') && (inputIntArray[sp + 1] <= '9'))) {
+ /* Two digits */
+ glyph = 8033 + (10 * (inputIntArray[sp] - '0')) + (inputIntArray[sp + 1] - '0');
+ sp++;
+ }
+ }
+ }
+ if (!(done)) {
+ /* Byte value */
+ glyph = 7777 + inputIntArray[sp];
+ }
+
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x1000; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ sp++;
+ break;
+
+ case GM_NUMBER:
+ if (last_mode != current_mode) {
+ /* Reserve a space for numeric digit padding value (2 bits) */
+ number_pad_posn = binary.length();
+ }
+ p = 0;
+ ppos = -1;
+
+ /* Numeric compression can also include certain combinations of
+ non-numeric character */
+ numbuf[0] = '0';
+ numbuf[1] = '0';
+ numbuf[2] = '0';
+ do {
+ if ((inputIntArray[sp] >= '0') && (inputIntArray[sp] <= '9')) {
+ numbuf[p] = inputIntArray[sp];
+ p++;
+ }
+ switch (inputIntArray[sp]) {
+ case ' ':
+ case '+':
+ case '-':
+ case '.':
+ case ',':
+ punt = inputIntArray[sp];
+ ppos = p;
+ break;
+ }
+ if (sp < (length - 1)) {
+ if ((inputIntArray[sp] == 13) && (inputIntArray[sp + 1] == 10)) {
+ /* */
+ punt = inputIntArray[sp];
+ sp++;
+ ppos = p;
+ }
+ }
+ sp++;
+ } while ((p < 3) && (sp < length));
+
+ if (ppos != -1) {
+ switch (punt) {
+ case ' ':
+ glyph = 0;
+ break;
+ case '+':
+ glyph = 3;
+ break;
+ case '-':
+ glyph = 6;
+ break;
+ case '.':
+ glyph = 9;
+ break;
+ case ',':
+ glyph = 12;
+ break;
+ case 0x13:
+ glyph = 15;
+ break;
+ }
+ glyph += ppos;
+ glyph += 1000;
+
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x200; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ }
+
+ glyph = (100 * (numbuf[0] - '0')) + (10 * (numbuf[1] - '0')) + (numbuf[2] - '0');
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x200; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ break;
+
+ case GM_BYTE:
+ if (last_mode != current_mode) {
+ /* Reserve space for byte block length indicator (9 bits) */
+ byte_count_posn = binary.length();
+ }
+ if (byte_count == 512) {
+ /* Maximum byte block size is 512 bytes. If longer is needed then start a new block */
+ addByteCount(byte_count_posn, byte_count);
+ binary.append("0111");
+ byte_count_posn = binary.length();
+ byte_count = 0;
+ }
+
+ glyph = inputIntArray[sp];
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+ for (i = 0x80; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ sp++;
+ byte_count++;
+ break;
+
+ case GM_MIXED:
+ shift = 1;
+ if ((inputIntArray[sp] >= '0') && (inputIntArray[sp] <= '9')) {
+ shift = 0;
+ }
+ if ((inputIntArray[sp] >= 'A') && (inputIntArray[sp] <= 'Z')) {
+ shift = 0;
+ }
+ if ((inputIntArray[sp] >= 'a') && (inputIntArray[sp] <= 'z')) {
+ shift = 0;
+ }
+ if (inputIntArray[sp] == ' ') {
+ shift = 0;
+ }
+
+ if (shift == 0) {
+ /* Mixed Mode character */
+ glyph = positionOf((char) inputIntArray[sp], MIXED_ALPHANUM_SET);
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x20; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ } else {
+ /* Shift Mode character */
+ binary.append("1111110110"); /* 1014 - shift indicator */
+
+ addShiftCharacter(inputIntArray[sp]);
+ }
+
+ sp++;
+ break;
+
+ case GM_UPPER:
+ shift = 1;
+ if ((inputIntArray[sp] >= 'A') && (inputIntArray[sp] <= 'Z')) {
+ shift = 0;
+ }
+ if (inputIntArray[sp] == ' ') {
+ shift = 0;
+ }
+
+ if (shift == 0) {
+ /* Upper Case character */
+ glyph = positionOf((char) inputIntArray[sp], MIXED_ALPHANUM_SET) - 10;
+ if (glyph == 52) {
+ // Space character
+ glyph = 26;
+ }
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x10; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+
+ } else {
+ /* Shift Mode character */
+ binary.append("1111101"); /* 127 - shift indicator */
+
+ addShiftCharacter(inputIntArray[sp]);
+ }
+
+ sp++;
+ break;
+
+ case GM_LOWER:
+ shift = 1;
+ if ((inputIntArray[sp] >= 'a') && (inputIntArray[sp] <= 'z')) {
+ shift = 0;
+ }
+ if (inputIntArray[sp] == ' ') {
+ shift = 0;
+ }
+
+ if (shift == 0) {
+ /* Lower Case character */
+ glyph = positionOf((char) inputIntArray[sp], MIXED_ALPHANUM_SET) - 36;
+ encodeInfo.append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x10; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+
+ } else {
+ /* Shift Mode character */
+ binary.append("1111101"); /* 127 - shift indicator */
+
+ addShiftCharacter(inputIntArray[sp]);
+ }
+
+ sp++;
+ break;
+ }
+ if (binary.length() > 9191) {
+ return 1;
+ }
+
+ } while (sp < length);
+
+ encodeInfo.append("\n");
+
+ if (current_mode == gmMode.GM_NUMBER) {
+ /* add numeric block padding value */
+ temp_binary = new StringBuilder(binary.substring(0, number_pad_posn));
+ switch (p) {
+ case 1:
+ temp_binary.append("10");
+ break; // 2 pad digits
+ case 2:
+ temp_binary.append("01");
+ break; // 1 pad digit
+ case 3:
+ temp_binary.append("00");
+ break; // 0 pad digits
+ }
+ temp_binary.append(binary.substring(number_pad_posn, binary.length()));
+ binary = temp_binary;
+ }
+
+ if (current_mode == gmMode.GM_BYTE) {
+ /* Add byte block length indicator */
+ addByteCount(byte_count_posn, byte_count);
+ }
+
+ /* Add "end of data" character */
+ switch (current_mode) {
+ case GM_CHINESE:
+ binary.append("1111111100000");
+ break; // 8160
+ case GM_NUMBER:
+ binary.append("1111111010");
+ break; // 1018
+ case GM_LOWER:
+ case GM_UPPER:
+ binary.append("11011");
+ break; // 27
+ case GM_MIXED:
+ binary.append("1111110000");
+ break; // 1008
+ case GM_BYTE:
+ binary.append("0000");
+ break; // 0
+ }
+
+ /* Add padding bits if required */
+ p = 7 - (binary.length() % 7);
+ if (p == 7) {
+ p = 0;
+ }
+ for (i = 0; i < p; i++) {
+ binary.append("0");
+ }
+
+ if (binary.length() > 9191) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private gmMode[] calculateModeMap(int length) {
+ gmMode[] modeMap = new gmMode[length];
+ int i;
+ int digitStart, digitLength;
+ boolean digits;
+ int spaceStart, spaceLength;
+ boolean spaces;
+ int[] segmentLength;
+ gmMode[] segmentType;
+ int[] segmentStart;
+ int segmentCount;
+
+ // Step 1
+ // Characters in GB2312 are encoded as Chinese characters
+ for (i = 0; i < length; i++) {
+ modeMap[i] = gmMode.NULL;
+ if (inputIntArray[i] > 0xFF) {
+ modeMap[i] = gmMode.GM_CHINESE;
+ }
+ }
+
+ // Consecutive characters, if preceeded by or followed
+ // by chinese characters, are encoded as chinese characters.
+ if (length > 3) {
+ i = 1;
+ do {
+ if ((inputIntArray[i] == 13) && (inputIntArray[i + 1] == 10)) {
+ // End of line (CR/LF)
+
+ if (modeMap[i - 1] == gmMode.GM_CHINESE) {
+ modeMap[i] = gmMode.GM_CHINESE;
+ modeMap[i + 1] = gmMode.GM_CHINESE;
+ }
+ i += 2;
+ } else {
+ i++;
+ }
+ } while (i < length - 1);
+
+ i = length - 3;
+ do {
+ if ((inputIntArray[i] == 13) && (inputIntArray[i + 1] == 10)) {
+ // End of line (CR/LF)
+ if (modeMap[i + 2] == gmMode.GM_CHINESE) {
+ modeMap[i] = gmMode.GM_CHINESE;
+ modeMap[i + 1] = gmMode.GM_CHINESE;
+ }
+ i -= 2;
+ } else {
+ i--;
+ }
+ } while (i > 0);
+ }
+
+ // Digit pairs between chinese characters encode as chinese characters.
+ digits = false;
+ digitLength = 0;
+ digitStart = 0;
+ for (i = 1; i < length - 1; i++) {
+ if ((inputIntArray[i] >= 48) && (inputIntArray[i] <= 57)) {
+ // '0' to '9'
+ if (!digits) {
+ digits = true;
+ digitLength = 1;
+ digitStart = i;
+ } else {
+ digitLength++;
+ }
+ } else {
+ if (digits) {
+ if ((digitLength % 2) == 0) {
+ if ((modeMap[digitStart - 1] == gmMode.GM_CHINESE) &&
+ (modeMap[i] == gmMode.GM_CHINESE)) {
+ for (int j = 0; j < digitLength; j++) {
+ modeMap[i - j - 1] = gmMode.GM_CHINESE;
+ }
+ }
+ }
+ digits = false;
+ }
+ }
+ }
+
+ // Step 2: all characters 'a' to 'z' are lowercase.
+ for (i = 0; i < length; i++) {
+ if ((inputIntArray[i] >= 97) && (inputIntArray[i] <= 122)) {
+ modeMap[i] = gmMode.GM_LOWER;
+ }
+ }
+
+ // Step 3: all characters 'A' to 'Z' are uppercase.
+ for (i = 0; i < length; i++) {
+ if ((inputIntArray[i] >= 65) && (inputIntArray[i] <= 90)) {
+ modeMap[i] = gmMode.GM_UPPER;
+ }
+ }
+
+ // Step 4: find consecutive characters preceeded or followed
+ // by uppercase or lowercase.
+ spaces = false;
+ spaceLength = 0;
+ spaceStart = 0;
+ for (i = 1; i < length - 1; i++) {
+ if (inputIntArray[i] == 32) {
+ if (!spaces) {
+ spaces = true;
+ spaceLength = 1;
+ spaceStart = i;
+ } else {
+ spaceLength++;
+ }
+ } else {
+ if (spaces) {
+
+ gmMode modeX = modeMap[spaceStart - 1];
+ gmMode modeY = modeMap[i];
+
+ if ((modeX == gmMode.GM_LOWER) || (modeX == gmMode.GM_UPPER)) {
+ for (int j = 0; j < spaceLength; j++) {
+ modeMap[i - j - 1] = modeX;
+ }
+ } else {
+ if ((modeY == gmMode.GM_LOWER) || (modeY == gmMode.GM_UPPER)) {
+ for (int j = 0; j < spaceLength; j++) {
+ modeMap[i - j - 1] = modeY;
+ }
+ }
+ }
+ spaces = false;
+ }
+ }
+ }
+
+ // Step 5: Unassigned characters '0' to '9' are assigned as numerals.
+ // Non-numeric characters in table 7 are also assigned as numerals.
+ for (i = 0; i < length; i++) {
+ if (modeMap[i] == gmMode.NULL) {
+ if ((inputIntArray[i] >= 48) && (inputIntArray[i] <= 57)) {
+ // '0' to '9'
+ modeMap[i] = gmMode.GM_NUMBER;
+ } else {
+ switch (inputIntArray[i]) {
+ case 32: // Space
+ case 43: // '+'
+ case 45: // '-'
+ case 46: // "."
+ case 44: // ","
+ modeMap[i] = gmMode.GM_NUMBER;
+ break;
+ case 13: // CR
+ if (i < length - 1) {
+ if (inputIntArray[i + 1] == 10) { // LF
+ //
+ modeMap[i] = gmMode.GM_NUMBER;
+ modeMap[i + 1] = gmMode.GM_NUMBER;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Step 6: The remining unassigned bytes are assigned as 8-bit binary
+ for (i = 0; i < length; i++) {
+ if (modeMap[i] == gmMode.NULL) {
+ modeMap[i] = gmMode.GM_BYTE;
+ }
+ }
+
+ // break into segments
+ segmentLength = new int[length];
+ segmentType = new gmMode[length];
+ segmentStart = new int[length];
+
+ segmentCount = 0;
+ segmentLength[0] = 1;
+ segmentType[0] = modeMap[0];
+ segmentStart[0] = 0;
+ for (i = 1; i < length; i++) {
+ if (modeMap[i] == modeMap[i - 1]) {
+ segmentLength[segmentCount]++;
+ } else {
+ segmentCount++;
+ segmentLength[segmentCount] = 1;
+ segmentType[segmentCount] = modeMap[i];
+ segmentStart[segmentCount] = i;
+ }
+ }
+
+ // A segment can be a control segment if
+ // a) It is not the start segment of the data stream
+ // b) All characters are control characters
+ // c) The length of the segment is no more than 3
+ // d) The previous segment is not chinese
+ if (segmentCount > 1) {
+ for (i = 1; i < segmentCount; i++) { // (a)
+ if ((segmentLength[i] <= 3) && (segmentType[i - 1] != gmMode.GM_CHINESE)) { // (c) and (d)
+ boolean controlLatch = true;
+ for (int j = 0; j < segmentLength[i]; j++) {
+ boolean thischarLatch = false;
+ for (int k = 0; k < 63; k++) {
+ if (inputIntArray[segmentStart[i] + j] == shift_set[k]) {
+ thischarLatch = true;
+ }
+ }
+
+ if (!(thischarLatch)) {
+ // This character is not a control character
+ controlLatch = false;
+ }
+ }
+
+ if (controlLatch) { // (b)
+ segmentType[i] = gmMode.GM_CONTROL;
+ }
+ }
+ }
+ }
+
+ // Stages 7 to 9
+ if (segmentCount >= 3) {
+ for (i = 0; i < segmentCount - 1; i++) {
+ gmMode pm, tm, nm, lm;
+ int tl, nl, ll, position;
+ boolean lastSegment = false;
+
+ if (i == 0) {
+ pm = gmMode.NULL;
+ } else {
+ pm = segmentType[i - 1];
+ }
+
+ tm = segmentType[i];
+ tl = segmentLength[i];
+
+ nm = segmentType[i + 1];
+ nl = segmentLength[i + 1];
+
+ lm = segmentType[i + 2];
+ ll = segmentLength[i + 2];
+
+ position = segmentStart[i];
+
+ if (i + 2 == segmentCount) {
+ lastSegment = true;
+ }
+
+ segmentType[i] = getBestMode(pm, tm, nm, lm, tl, nl, ll, position, lastSegment);
+
+ if (segmentType[i] == gmMode.GM_CONTROL) {
+ segmentType[i] = segmentType[i - 1];
+ }
+ }
+
+ segmentType[i] = appxDnextSection;
+ segmentType[i + 1] = appxDlastSection;
+
+ if (segmentType[i] == gmMode.GM_CONTROL) {
+ segmentType[i] = segmentType[i - 1];
+ }
+ if (segmentType[i + 1] == gmMode.GM_CONTROL) {
+ segmentType[i + 1] = segmentType[i];
+ }
+
+// Uncomment these lines to override mode selection and generate symbol as shown
+// in image D.1 for the test data "AAT2556 ç”µæ± å……ç”µå™¨ï¼‹é™åŽ‹è½¬æ¢å™¨ 200mA至2A tel:86 019 82512738"
+// segmentType[9] = gmMode.GM_LOWER;
+// segmentType[10] = gmMode.GM_LOWER;
+ }
+
+ // Copy segments back to modeMap
+ for (i = 0; i < segmentCount; i++) {
+ for (int j = 0; j < segmentLength[i]; j++) {
+ modeMap[segmentStart[i] + j] = segmentType[i];
+ }
+ }
+
+ return modeMap;
+ }
+
+ private boolean isTransitionValid(gmMode previousMode, gmMode thisMode) {
+ // Filters possible encoding data types from table D.1
+ boolean isValid = false;
+
+ switch (previousMode) {
+ case GM_CHINESE:
+ switch (thisMode) {
+ case GM_CHINESE:
+ case GM_BYTE:
+ isValid = true;
+ break;
+ }
+ break;
+ case GM_NUMBER:
+ switch (thisMode) {
+ case GM_NUMBER:
+ case GM_MIXED:
+ case GM_BYTE:
+ case GM_CHINESE:
+ isValid = true;
+ break;
+ }
+ break;
+ case GM_LOWER:
+ switch (thisMode) {
+ case GM_LOWER:
+ case GM_MIXED:
+ case GM_BYTE:
+ case GM_CHINESE:
+ isValid = true;
+ break;
+ }
+ break;
+ case GM_UPPER:
+ switch (thisMode) {
+ case GM_UPPER:
+ case GM_MIXED:
+ case GM_BYTE:
+ case GM_CHINESE:
+ isValid = true;
+ break;
+ }
+ break;
+ case GM_CONTROL:
+ switch (thisMode) {
+ case GM_CONTROL:
+ case GM_BYTE:
+ case GM_CHINESE:
+ isValid = true;
+ break;
+ }
+ break;
+ case GM_BYTE:
+ switch (thisMode) {
+ case GM_BYTE:
+ case GM_CHINESE:
+ isValid = true;
+ break;
+ }
+ break;
+ }
+
+ return isValid;
+ }
+
+ private gmMode intToMode(int input) {
+ gmMode retVal;
+
+ switch (input) {
+ case 1:
+ retVal = gmMode.GM_CHINESE;
+ break;
+ case 2:
+ retVal = gmMode.GM_BYTE;
+ break;
+ case 3:
+ retVal = gmMode.GM_CONTROL;
+ break;
+ case 4:
+ retVal = gmMode.GM_MIXED;
+ break;
+ case 5:
+ retVal = gmMode.GM_UPPER;
+ break;
+ case 6:
+ retVal = gmMode.GM_LOWER;
+ break;
+ case 7:
+ retVal = gmMode.GM_NUMBER;
+ break;
+ default:
+ retVal = gmMode.NULL;
+ break;
+ }
+
+ return retVal;
+ }
+
+ private gmMode getBestMode(gmMode pm, gmMode tm, gmMode nm, gmMode lm, int tl, int nl, int ll, int position, boolean lastSegment) {
+ int tmi, nmi, lmi;
+ gmMode bestMode = tm;
+ int binaryLength;
+ int bestBinaryLength = Integer.MAX_VALUE;
+
+ for (tmi = 1; tmi < 8; tmi++) {
+ if (isTransitionValid(tm, intToMode(tmi))) {
+ for (nmi = 1; nmi < 8; nmi++) {
+ if (isTransitionValid(nm, intToMode(nmi))) {
+ for (lmi = 1; lmi < 8; lmi++) {
+ if (isTransitionValid(lm, intToMode(lmi))) {
+ binaryLength = getBinaryLength(pm, intToMode(tmi), intToMode(nmi), intToMode(lmi), tl, nl, ll, position, lastSegment);
+
+ if (binaryLength <= bestBinaryLength) {
+ bestMode = intToMode(tmi);
+ appxDnextSection = intToMode(nmi);
+ appxDlastSection = intToMode(lmi);
+ bestBinaryLength = binaryLength;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bestMode;
+ }
+
+ private int getBinaryLength(gmMode pm, gmMode tm, gmMode nm, gmMode lm, int tl, int nl, int ll, int position, boolean lastSegment) {
+ int binaryLength;
+
+ binaryLength = getChunkLength(pm, tm, tl, position);
+ binaryLength += getChunkLength(tm, nm, nl, (position + tl));
+ binaryLength += getChunkLength(nm, lm, ll, (position + tl + nl));
+
+ if (lastSegment) {
+ switch (lm) {
+ case GM_CHINESE:
+ binaryLength += 13;
+ break;
+ case GM_NUMBER:
+ binaryLength += 10;
+ break;
+ case GM_LOWER:
+ case GM_UPPER:
+ binaryLength += 5;
+ break;
+ case GM_MIXED:
+ binaryLength += 10;
+ break;
+ case GM_BYTE:
+ binaryLength += 4;
+ break;
+ }
+ }
+
+ return binaryLength;
+ }
+
+ private int getChunkLength(gmMode lastMode, gmMode thisMode, int thisLength, int position) {
+ int byteLength;
+
+ switch (thisMode) {
+ case GM_CHINESE:
+ byteLength = calcChineseLength(position, thisLength);
+ break;
+ case GM_NUMBER:
+ byteLength = calcNumberLength(position, thisLength);
+ break;
+ case GM_LOWER:
+ byteLength = 5 * thisLength;
+ break;
+ case GM_UPPER:
+ byteLength = 5 * thisLength;
+ break;
+ case GM_MIXED:
+ byteLength = calcMixedLength(position, thisLength);
+ break;
+ case GM_CONTROL:
+ byteLength = 6 * thisLength;
+ break;
+ default:
+ //case GM_BYTE:
+ byteLength = calcByteLength(position, thisLength);
+ break;
+ }
+
+ switch (lastMode) {
+ case NULL:
+ byteLength += 4;
+ break;
+ case GM_CHINESE:
+ if ((thisMode != gmMode.GM_CHINESE) && (thisMode != gmMode.GM_CONTROL)) {
+ byteLength += 13;
+ }
+ break;
+ case GM_NUMBER:
+ if ((thisMode != gmMode.GM_CHINESE) && (thisMode != gmMode.GM_CONTROL)) {
+ byteLength += 10;
+ }
+ break;
+ case GM_LOWER:
+ switch (thisMode) {
+ case GM_CHINESE:
+ case GM_NUMBER:
+ case GM_UPPER:
+ byteLength += 5;
+ break;
+ case GM_MIXED:
+ case GM_CONTROL:
+ case GM_BYTE:
+ byteLength += 7;
+ break;
+ }
+ break;
+ case GM_UPPER:
+ switch (thisMode) {
+ case GM_CHINESE:
+ case GM_NUMBER:
+ case GM_LOWER:
+ byteLength += 5;
+ break;
+ case GM_MIXED:
+ case GM_CONTROL:
+ case GM_BYTE:
+ byteLength += 7;
+ break;
+ }
+ break;
+ case GM_MIXED:
+ if (thisMode != gmMode.GM_MIXED) {
+ byteLength += 10;
+ }
+ break;
+ case GM_BYTE:
+ if (thisMode != gmMode.GM_BYTE) {
+ byteLength += 4;
+ }
+ break;
+ }
+
+ if ((lastMode != gmMode.GM_BYTE) && (thisMode == gmMode.GM_BYTE)) {
+ byteLength += 9;
+ }
+
+ if ((lastMode != gmMode.GM_NUMBER) && (thisMode == gmMode.GM_NUMBER)) {
+ byteLength += 2;
+ }
+
+ return byteLength;
+ }
+
+ private int calcChineseLength(int position, int length) {
+ int i = 0;
+ int bits = 0;
+
+ do {
+ bits += 13;
+
+ if (i < length) {
+ if ((inputIntArray[position + i] == 13) && (inputIntArray[position + i + 1] == 10)) {
+ //
+ i++;
+ }
+
+ if (((inputIntArray[position + i] >= 48) && (inputIntArray[position + i] <= 57)) &&
+ ((inputIntArray[position + i + 1] >= 48) && (inputIntArray[position + i + 1] <= 57))) {
+ // two digits
+ i++;
+ }
+ }
+ i++;
+ } while (i < length);
+
+ return bits;
+ }
+
+ private int calcMixedLength(int position, int length) {
+ int bits = 0;
+ int i;
+
+ for (i = 0; i < length; i++) {
+ bits += 6;
+ for (int k = 0; k < 63; k++) {
+ if (inputIntArray[position + i] == shift_set[k]) {
+ bits += 10;
+ }
+ }
+ }
+
+ return bits;
+ }
+
+ private int calcNumberLength(int position, int length) {
+ int i;
+ int bits = 0;
+ int numbers = 0;
+ int nonnumbers = 0;
+
+ for (i = 0; i < length; i++) {
+ if ((inputIntArray[position + i] >= 48) && (inputIntArray[position + i] <= 57)) {
+ numbers++;
+ } else {
+ nonnumbers++;
+ }
+
+ if (i != 0) {
+ if ((inputIntArray[position + i] == 10) && (inputIntArray[position + i - 1] == 13)) {
+ //
+ nonnumbers--;
+ }
+ }
+
+ if (numbers == 3) {
+ if (nonnumbers == 1) {
+ bits += 20;
+ } else {
+ bits += 10;
+ }
+ if (nonnumbers > 1) {
+ // Invalid encoding
+ bits += 100;
+ }
+ numbers = 0;
+ nonnumbers = 0;
+ }
+ }
+
+ if (numbers > 0) {
+ if (nonnumbers == 1) {
+ bits += 20;
+ } else {
+ bits += 10;
+ }
+ }
+
+ if (nonnumbers > 1) {
+ // Invalid
+ bits += 100;
+ }
+
+ if (!((inputIntArray[position + i - 1] >= 48) && (inputIntArray[position + i - 1] <= 57))) {
+ // Data must end with a digit
+ bits += 100;
+ }
+
+
+ return bits;
+ }
+
+ private int calcByteLength(int position, int length) {
+ int i;
+ int bits = 0;
+
+ for (i = 0; i < length; i++) {
+ if (inputIntArray[position + i] <= 0xFF) {
+ bits += 8;
+ } else {
+ bits += 16;
+ }
+ }
+
+ return bits;
+ }
+
+ private void addByteCount(int byte_count_posn, int byte_count) {
+ /* Add the length indicator for byte encoded blocks */
+ int i;
+ StringBuilder temp_binary;
+
+ temp_binary = new StringBuilder(binary.substring(0, byte_count_posn));
+ for (i = 0; i < 9; i++) {
+ if ((byte_count & (0x100 >> i)) != 0) {
+ temp_binary.append("0");
+ } else {
+ temp_binary.append("1");
+ }
+ }
+ temp_binary.append(binary.substring(byte_count_posn, binary.length()));
+ binary = temp_binary;
+ }
+
+ void addShiftCharacter(int shifty) {
+ /* Add a control character to the data stream */
+ int i;
+ int glyph = 0;
+
+ for (i = 0; i < 64; i++) {
+ if (shift_set[i] == shifty) {
+ glyph = i;
+ }
+ }
+
+ encodeInfo.append("SHT/").append(Integer.toString(glyph)).append(" ");
+
+ for (i = 0x20; i > 0; i = i >> 1) {
+ if ((glyph & i) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+ }
+
+ private void addErrorCorrection(int data_posn, int layers, int ecc_level) {
+ int data_cw, i, j, wp;
+ int n1, b1, n2, b2, e1, b3, e2;
+ int block_size, data_size, ecc_size;
+ int[] data = new int[1320];
+ int[] block = new int[130];
+ int[] data_block = new int[115];
+ int[] ecc_block = new int[70];
+ ReedSolomon rs = new ReedSolomon();
+
+ data_cw = gm_data_codewords[((layers - 1) * 5) + (ecc_level - 1)];
+
+ for (i = 0; i < 1320; i++) {
+ data[i] = 0;
+ }
+
+ /* Convert from binary sream to 7-bit codewords */
+ for (i = 0; i < data_posn; i++) {
+ for (j = 0; j < 7; j++) {
+ if (binary.charAt((i * 7) + j) == '1') {
+ data[i] += 0x40 >> j;
+ }
+ }
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < data_posn; i++) {
+ encodeInfo.append(Integer.toString(data[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Add padding codewords */
+ data[data_posn] = 0x00;
+ for (i = (data_posn + 1); i < data_cw; i++) {
+ if ((i & 1) != 0) {
+ data[i] = 0x7e;
+ } else {
+ data[i] = 0x00;
+ }
+ }
+
+ /* Get block sizes */
+ n1 = gm_n1[(layers - 1)];
+ b1 = gm_b1[(layers - 1)];
+ n2 = n1 - 1;
+ b2 = gm_b2[(layers - 1)];
+ e1 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4)];
+ b3 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 1];
+ e2 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 2];
+
+ /* Split the data into blocks */
+ wp = 0;
+ for (i = 0; i < (b1 + b2); i++) {
+ if (i < b1) {
+ block_size = n1;
+ } else {
+ block_size = n2;
+ }
+ if (i < b3) {
+ ecc_size = e1;
+ } else {
+ ecc_size = e2;
+ }
+ data_size = block_size - ecc_size;
+
+ /* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size);*/
+ for (j = 0; j < data_size; j++) {
+ data_block[j] = data[wp];
+ wp++;
+ }
+
+ /* Calculate ECC data for this block */
+ rs.init_gf(0x89);
+ rs.init_code(ecc_size, 1);
+ rs.encode(data_size, data_block);
+ for (j = 0; j < ecc_size; j++) {
+ ecc_block[j] = rs.getResult(j);
+ }
+
+ /* Correct error correction data but in reverse order */
+ for (j = 0; j < data_size; j++) {
+ block[j] = data_block[j];
+ }
+ for (j = 0; j < ecc_size; j++) {
+ block[(j + data_size)] = ecc_block[ecc_size - j - 1];
+ }
+
+ for (j = 0; j < n2; j++) {
+ word[((b1 + b2) * j) + i] = block[j];
+ }
+ if (block_size == n1) {
+ word[((b1 + b2) * (n1 - 1)) + i] = block[(n1 - 1)];
+ }
+ }
+ }
+
+ private void placeDataInGrid(int modules, int size) {
+ int x, y, macromodule, offset;
+
+ offset = 13 - ((modules - 1) / 2);
+ for (y = 0; y < modules; y++) {
+ for (x = 0; x < modules; x++) {
+ macromodule = gm_macro_matrix[((y + offset) * 27) + (x + offset)];
+ placeMacroModule(x, y, word[macromodule * 2], word[(macromodule * 2) + 1], size);
+ }
+ }
+ }
+
+ private void placeMacroModule(int x, int y, int word1, int word2, int size) {
+ int i, j;
+
+ i = (x * 6) + 1;
+ j = (y * 6) + 1;
+
+ if ((word2 & 0x40) != 0) {
+ grid[(j * size) + i + 2] = true;
+ }
+ if ((word2 & 0x20) != 0) {
+ grid[(j * size) + i + 3] = true;
+ }
+ if ((word2 & 0x10) != 0) {
+ grid[((j + 1) * size) + i] = true;
+ }
+ if ((word2 & 0x08) != 0) {
+ grid[((j + 1) * size) + i + 1] = true;
+ }
+ if ((word2 & 0x04) != 0) {
+ grid[((j + 1) * size) + i + 2] = true;
+ }
+ if ((word2 & 0x02) != 0) {
+ grid[((j + 1) * size) + i + 3] = true;
+ }
+ if ((word2 & 0x01) != 0) {
+ grid[((j + 2) * size) + i] = true;
+ }
+ if ((word1 & 0x40) != 0) {
+ grid[((j + 2) * size) + i + 1] = true;
+ }
+ if ((word1 & 0x20) != 0) {
+ grid[((j + 2) * size) + i + 2] = true;
+ }
+ if ((word1 & 0x10) != 0) {
+ grid[((j + 2) * size) + i + 3] = true;
+ }
+ if ((word1 & 0x08) != 0) {
+ grid[((j + 3) * size) + i] = true;
+ }
+ if ((word1 & 0x04) != 0) {
+ grid[((j + 3) * size) + i + 1] = true;
+ }
+ if ((word1 & 0x02) != 0) {
+ grid[((j + 3) * size) + i + 2] = true;
+ }
+ if ((word1 & 0x01) != 0) {
+ grid[((j + 3) * size) + i + 3] = true;
+ }
+ }
+
+ private void addLayerId(int size, int layers, int modules, int ecc_level) {
+ /* Place the layer ID into each macromodule */
+
+ int i, j, layer, start, stop;
+ int[] layerid = new int[layers + 1];
+ int[] id = new int[modules * modules];
+
+
+ /* Calculate Layer IDs */
+ for (i = 0; i <= layers; i++) {
+ if (ecc_level == 1) {
+ layerid[i] = 3 - (i % 4);
+ } else {
+ layerid[i] = (i + 5 - ecc_level) % 4;
+ }
+ }
+
+ for (i = 0; i < modules; i++) {
+ for (j = 0; j < modules; j++) {
+ id[(i * modules) + j] = 0;
+ }
+ }
+
+ /* Calculate which value goes in each macromodule */
+ start = modules / 2;
+ stop = modules / 2;
+ for (layer = 0; layer <= layers; layer++) {
+ for (i = start; i <= stop; i++) {
+ id[(start * modules) + i] = layerid[layer];
+ id[(i * modules) + start] = layerid[layer];
+ id[((modules - start - 1) * modules) + i] = layerid[layer];
+ id[(i * modules) + (modules - start - 1)] = layerid[layer];
+ }
+ start--;
+ stop++;
+ }
+
+ /* Place the data in the grid */
+ for (i = 0; i < modules; i++) {
+ for (j = 0; j < modules; j++) {
+ if ((id[(i * modules) + j] & 0x02) != 0) {
+ grid[(((i * 6) + 1) * size) + (j * 6) + 1] = true;
+ }
+ if ((id[(i * modules) + j] & 0x01) != 0) {
+ grid[(((i * 6) + 1) * size) + (j * 6) + 2] = true;
+ }
+ }
+ }
+ }
+
+ private enum gmMode {
+
+ NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java b/barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java
new file mode 100755
index 0000000..423f4df
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java
@@ -0,0 +1,22 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * The location of a bar code's human-readable text.
+ */
+public enum HumanReadableLocation {
+
+ /**
+ * Display the human-readable text below the bar code.
+ */
+ BOTTOM,
+
+ /**
+ * Display the human-readable text above the bar code.
+ */
+ TOP,
+
+ /**
+ * Do not display the human-readable text.
+ */
+ NONE
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java b/barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java
new file mode 100755
index 0000000..ebfab19
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java
@@ -0,0 +1,138 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+import java.util.Locale;
+
+/**
+ * Implements the Japanese Postal Code symbology as used to encode address
+ * data for mail items in Japan. Valid input characters are digits 0-9,
+ * characters A-Z and the dash (-) character. A modulo-19 check digit is
+ * added and should not be included in the input data.
+ */
+public class JapanPost extends Symbol {
+
+ private static final String[] JAPAN_TABLE = {
+ "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT",
+ "TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF"
+ };
+
+ private static final char[] KASUT_SET = {
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c',
+ 'd', 'e', 'f', 'g', 'h'
+ };
+
+ private static final char[] CH_KASUT_SET = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c',
+ 'd', 'e', 'f', 'g', 'h'
+ };
+
+ @Override
+ public boolean encode() {
+ StringBuilder dest;
+ StringBuilder inter;
+ int i, sum, check;
+ char c;
+
+ content = content.toUpperCase(Locale.ENGLISH);
+ if (!(content.matches("[0-9A-Z\\-]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ inter = new StringBuilder();
+
+ for (i = 0;
+ (i < content.length()) && (inter.length() < 20); i++) {
+ c = content.charAt(i);
+
+ if ((c >= '0') && (c <= '9')) {
+ inter.append(c);
+ }
+ if (c == '-') {
+ inter.append(c);
+ }
+ if ((c >= 'A') && (c <= 'J')) {
+ inter.append('a');
+ inter.append(CH_KASUT_SET[(c - 'A')]);
+ }
+
+ if ((c >= 'K') && (c <= 'O')) {
+ inter.append('b');
+ inter.append(CH_KASUT_SET[(c - 'K')]);
+ }
+
+ if ((c >= 'U') && (c <= 'Z')) {
+ inter.append('c');
+ inter.append(CH_KASUT_SET[(c - 'U')]);
+ }
+ }
+
+ for (i = inter.length(); i < 20; i++) {
+ inter.append("d");
+ }
+
+ dest = new StringBuilder("FD");
+
+ sum = 0;
+ for (i = 0; i < 20; i++) {
+ dest.append(JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]);
+ sum += positionOf(inter.charAt(i), CH_KASUT_SET);
+ }
+
+ /* Calculate check digit */
+ check = 19 - (sum % 19);
+ if (check == 19) {
+ check = 0;
+ }
+ dest.append(JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]);
+ dest.append("DF");
+
+ encodeInfo.append("Encoding: ").append(dest).append("\n");
+ encodeInfo.append("Check Digit: ").append(check).append("\n");
+
+ readable = new StringBuilder();
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+
+ plotSymbol();
+
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ getRectangles().clear();
+ x = 0;
+ w = 1;
+ y = 0;
+ h = 0;
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ switch (pattern[0].charAt(xBlock)) {
+ case 'A':
+ y = 0;
+ h = 5;
+ break;
+ case 'D':
+ y = 3;
+ h = 5;
+ break;
+ case 'F':
+ y = 0;
+ h = 8;
+ break;
+ case 'T':
+ y = 3;
+ h = 2;
+ break;
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ x += 2;
+ }
+ symbolWidth = pattern[0].length() * 3;
+ symbolHeight = 8;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java
new file mode 100755
index 0000000..faecb34
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java
@@ -0,0 +1,95 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+import java.util.Locale;
+
+/**
+ * Implements Dutch Post KIX Code as used by Royal Dutch TPG Post
+ * (Netherlands). Input data can consist of digits 0-9 and characters A-Z.
+ * Input should be 11 characters in length. No check digit is added.
+ */
+public class KixCode extends Symbol {
+
+ /* Handles Dutch Post TNT KIX symbols */
+ /* The same as RM4SCC but without check digit */
+ /* Specification at http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx */
+
+ private final String[] RoyalTable = {
+ "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA",
+ "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
+ "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT",
+ "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT"
+ };
+
+ private final char[] krSet = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
+ };
+
+ @Override
+ public boolean encode() {
+ StringBuilder dest;
+ int i;
+
+ content = content.toUpperCase(Locale.ENGLISH);
+
+ if (!(content.matches("[0-9A-Z]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ dest = new StringBuilder();
+
+ for (i = 0; i < content.length(); i++) {
+ dest.append(RoyalTable[positionOf(content.charAt(i), krSet)]);
+ }
+
+ encodeInfo.append("Encoding: ").append(dest).append("\n");
+
+ readable = new StringBuilder();
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ getRectangles().clear();
+ x = 0;
+ w = 1;
+ y = 0;
+ h = 0;
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ switch (pattern[0].charAt(xBlock)) {
+ case 'A':
+ y = 0;
+ h = 5;
+ break;
+ case 'D':
+ y = 3;
+ h = 5;
+ break;
+ case 'F':
+ y = 0;
+ h = 8;
+ break;
+ case 'T':
+ y = 3;
+ h = 2;
+ break;
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ x += 2;
+ }
+ symbolWidth = pattern[0].length() * 3;
+ symbolHeight = 8;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java b/barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java
new file mode 100755
index 0000000..6ca94e3
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java
@@ -0,0 +1,61 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements Korea Post Barcode. Input should consist of of a six-digit
+ * number. A Modulo-10 check digit is calculated and added, and should not form
+ * part of the input data.
+ */
+public class KoreaPost extends Symbol {
+
+ String[] koreaTable = {
+ "1313150613", "0713131313", "0417131313", "1506131313", "0413171313",
+ "17171313", "1315061313", "0413131713", "17131713", "13171713"
+ };
+
+ @Override
+ public boolean encode() {
+ StringBuilder accumulator = new StringBuilder();
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (content.length() > 6) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ StringBuilder add_zero = new StringBuilder();
+ int i, j, total = 0, checkd;
+
+ for (i = 0; i < (6 - content.length()); i++) {
+ add_zero.append("0");
+ }
+ add_zero.append(content);
+
+
+ for (i = 0; i < add_zero.length(); i++) {
+ j = Character.getNumericValue(add_zero.charAt(i));
+ accumulator.append(koreaTable[j]);
+ total += j;
+ }
+
+ checkd = 10 - (total % 10);
+ if (checkd == 10) {
+ checkd = 0;
+ }
+ encodeInfo.append("Check Digit: ").append(checkd).append("\n");
+
+ accumulator.append(koreaTable[checkd]);
+
+ readable.append(add_zero).append(checkd);
+ pattern = new String[1];
+ pattern[0] = accumulator.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java b/barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java
new file mode 100755
index 0000000..71ea571
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java
@@ -0,0 +1,97 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements the LOGMARS (Logistics Applications of Automated Marking
+ * and Reading Symbols) standard used by the US Department of Defense.
+ * Input data can be of any length and supports the characters 0-9, A-Z, dash
+ * (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%).
+ * A Modulo-43 check digit is calculated and added, and should not form part
+ * of the input data.
+ */
+public class Logmars extends Symbol {
+
+ private static final String[] CODE39LM = {
+ "1113313111", "3113111131", "1133111131", "3133111111", "1113311131",
+ "3113311111", "1133311111", "1113113131", "3113113111", "1133113111",
+ "3111131131", "1131131131", "3131131111", "1111331131", "3111331111",
+ "1131331111", "1111133131", "3111133111", "1131133111", "1111333111",
+ "3111111331", "1131111331", "3131111311", "1111311331", "3111311311",
+ "1131311311", "1111113331", "3111113311", "1131113311", "1111313311",
+ "3311111131", "1331111131", "3331111111", "1311311131", "3311311111",
+ "1331311111", "1311113131", "3311113111", "1331113111", "1313131111",
+ "1313111311", "1311131311", "1113131311"
+ };
+
+ private static final char[] LOOKUP = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+',
+ '%'
+ };
+
+ /**
+ * Ratio of wide bar width to narrow bar width.
+ */
+ private double moduleWidthRatio = 3;
+
+ /**
+ * Returns the ratio of wide bar width to narrow bar width.
+ *
+ * @return the ratio of wide bar width to narrow bar width
+ */
+ public double getModuleWidthRatio() {
+ return moduleWidthRatio;
+ }
+
+ /**
+ * Sets the ratio of wide bar width to narrow bar width. Valid values are usually
+ * between {@code 2} and {@code 3}. The default value is {@code 3}.
+ *
+ * @param moduleWidthRatio the ratio of wide bar width to narrow bar width
+ */
+ public void setModuleWidthRatio(double moduleWidthRatio) {
+ this.moduleWidthRatio = moduleWidthRatio;
+ }
+
+ @Override
+ protected double getModuleWidth(int originalWidth) {
+ if (originalWidth == 1) {
+ return 1;
+ } else {
+ return moduleWidthRatio;
+ }
+ }
+
+ @Override
+ public boolean encode() {
+
+ if (!(content.matches("[0-9A-Z\\. \\-$/+%]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ StringBuilder p = new StringBuilder();
+ int l = content.length();
+ int charval, counter = 0;
+ char thischar;
+ char checkDigit;
+ for (int i = 0; i < l; i++) {
+ thischar = content.charAt(i);
+ charval = positionOf(thischar, LOOKUP);
+ counter += charval;
+ p.append(CODE39LM[charval]);
+ }
+
+ counter = counter % 43;
+ checkDigit = LOOKUP[counter];
+ encodeInfo.append("Check Digit: ").append(checkDigit).append("\n");
+ p.append(CODE39LM[counter]);
+
+ readable = new StringBuilder(content).append(checkDigit);
+ pattern = new String[]{"1311313111" + p + "131131311"};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+ plotSymbol();
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java
new file mode 100755
index 0000000..dc7ecb2
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java
@@ -0,0 +1,890 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.Hexagon;
+import org.xbib.graphics.barcode.util.ReedSolomon;
+import java.awt.geom.Ellipse2D;
+import java.util.Arrays;
+
+/**
+ * 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.
+ */
+public class MaxiCode extends Symbol {
+
+ /**
+ * MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid).
+ */
+ private static final 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).
+ */
+ private static final 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.
+ */
+ private static final 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
+ };
+
+ private int mode;
+ private int structuredAppendPosition = 1;
+ private int structuredAppendTotal = 1;
+ private String primaryData = "";
+ private int[] codewords;
+ private int[] source;
+ private final int[] set = new int[144];
+ private final int[] character = new int[144];
+ private final boolean[][] grid = new boolean[33][30];
+
+ /**
+ * 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
+ */
+ private static int[] getMode2PrimaryCodewords(String postcode, int country, int service) {
+ for (int i = 0; i < postcode.length(); i++) {
+ if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') {
+ postcode = postcode.substring(0, i);
+ break;
+ }
+ }
+ int postcodeNum = Integer.parseInt(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
+ */
+ private static int[] getMode3PrimaryCodewords(String postcode, int country, int service) {
+
+ int[] postcodeNums = new int[postcode.length()];
+
+ postcode = postcode.toUpperCase();
+ for (int i = 0; i < postcodeNums.length; i++) {
+ postcodeNums[i] = postcode.charAt(i);
+ if (postcode.charAt(i) >= 'A' && postcode.charAt(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;
+ }
+
+ /**
+ * 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
+ */
+ private 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;
+ }
+
+ /**
+ * Returns the MaxiCode mode being used. Only modes 2 to 6 are supported.
+ *
+ * @return the MaxiCode mode being used
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /**
+ * 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 IllegalArgumentException("Invalid MaxiCode mode: " + mode);
+ }
+ this.mode = mode;
+ }
+
+ /**
+ * 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
+ * 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 IllegalArgumentException("Invalid MaxiCode structured append position: " + position);
+ }
+ this.structuredAppendPosition = position;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 IllegalArgumentException("Invalid MaxiCode structured append total: " + total);
+ }
+ this.structuredAppendTotal = total;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following structure:
+ * Expected primary data structure
+ * Characters Meaning
+ * 1-9 Postal 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-12 Three-digit country code according to ISO-3166.
+ * 13-15 Three digit service code. This depends on your parcel courier.
+ *
+ *
+ * @param primary the primary data
+ */
+ public void setPrimary(String primary) {
+ primaryData = primary;
+ }
+
+ @Override
+ public boolean encode() {
+
+ // copy input data over into source
+ int sourcelen = content.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.charAt(i) < '0') || (primaryData.charAt(i) > '9')) {
+ mode = 3;
+ break;
+ }
+ }
+ }
+
+ // initialize the set and character arrays
+ if (!processText()) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ // 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 = Arrays.copyOf(character, character.length);
+ 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 = Arrays.copyOfRange(codewords, 0, totalMax);
+ }
+
+ // insert primary error correction between primary message and secondary message (always EEC)
+ int[] primary = Arrays.copyOfRange(codewords, 0, 10);
+ int[] primaryCheck = getErrorCorrection(primary, 10);
+ codewords = insert(codewords, 10, primaryCheck);
+
+ // calculate secondary error correction
+ int[] secondary = Arrays.copyOfRange(codewords, 20, codewords.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
+ 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.append("Mode: ").append(mode).append("\n");
+ encodeInfo.append("ECC Codewords: ").append(secondaryECMax).append("\n");
+ encodeInfo.append("Codewords: ");
+ for (int codeword : codewords) {
+ encodeInfo.append(Integer.toString(codeword)).append(" ");
+ }
+ encodeInfo.append("\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);
+
+ grid[i][j] = bit_pattern[bit] != 0;
+ }
+ }
+ }
+
+ // 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
+ rowCount = 33;
+ readable = new StringBuilder();
+ pattern = new String[33];
+ rowHeight = 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());
+ rowHeight[i] = 1;
+ }
+ symbolHeight = 72;
+ symbolWidth = 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
+ */
+ private int[] getPrimaryCodewords() {
+
+ assert mode == 2 || mode == 3;
+
+ if (primaryData.length() != 15) {
+ errorMsg.append("Invalid Primary String");
+ return null;
+ }
+
+ for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */
+ if ((primaryData.charAt(i) < '0') || (primaryData.charAt(i) > '9')) {
+ errorMsg.append("Invalid Primary String");
+ return null;
+ }
+ }
+
+ 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 = Integer.parseInt(primaryData.substring(9, 12));
+ int service = Integer.parseInt(primaryData.substring(12, 15));
+
+ if (mode == 2) {
+ return getMode2PrimaryCodewords(postcode, country, service);
+ } else { // mode == 3
+ return getMode3PrimaryCodewords(postcode, country, service);
+ }
+ }
+
+ /**
+ * 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
+ */
+ private boolean processText() {
+
+ int length = content.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 IllegalStateException("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. */
+ return ((mode != 2 && mode != 3) || length <= 84) && ((mode != 4 && mode != 6) || length <= 93) && (mode != 5 || length <= 77);
+ }
+
+ /**
+ * 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
+ */
+ private int bestSurroundingSet(int index, int length, 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
+ */
+ private 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;
+ }
+
+ @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;
+ getHexagons().add(new Hexagon(x, y));
+ }
+ }
+ }
+
+ // circles
+ double[] radii = {10.85, 8.97, 7.10, 5.22, 3.31, 1.43};
+ for (double aRadii : radii) {
+ Ellipse2D.Double circle = new Ellipse2D.Double();
+ circle.setFrameFromCenter(35.76, 35.60, 35.76 + aRadii, 35.60 + aRadii);
+ getTarget().add(circle);
+ }
+ }
+
+ @Override
+ protected int[] getCodewords() {
+ return codewords;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java
new file mode 100755
index 0000000..a66698c
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java
@@ -0,0 +1,1593 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements Micro QR Code
+ * According to ISO/IEC 18004:2006
+ * A miniature version of the QR Code symbol for short messages.
+ * QR Code symbols can encode characters in the Latin-1 set and Kanji
+ * characters which are members of the Shift-JIS encoding scheme.
+ */
+public class MicroQrCode extends Symbol {
+ /* Table 5 - Encoding/Decoding table for Alphanumeric mode */
+ private static final char[] RHODIUM = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-',
+ '.', '/', ':'
+ };
+ private static final int[] QR_ANNEX_C1 = {
+ /* Micro QR Code format information */
+ 0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793,
+ 0x62a4, 0x6dfd, 0x68ca, 0x7678, 0x734f, 0x7c16, 0x7921, 0x06de, 0x03e9,
+ 0x0cb0, 0x0987, 0x1735, 0x1202, 0x1d5b, 0x186c, 0x2508, 0x203f, 0x2f66,
+ 0x2a51, 0x34e3, 0x31d4, 0x3e8d, 0x3bba
+ };
+ private static final int[] MICRO_QR_SIZES = {
+ 11, 13, 15, 17
+ };
+ private qrMode[] inputMode;
+ private StringBuilder binary;
+ private int[] binaryCount = new int[4];
+ private int[] grid;
+ private int[] eval;
+ private int preferredVersion;
+ private EccMode preferredEccLevel = EccMode.L;
+
+ /**
+ * Sets the preferred symbol size. This value may be ignored if the
+ * data string is too large to fit into the specified symbol. Input
+ * values correspond to symbol sizes as shown in the following table.
+ * Range of Micro QR symbol sizes
+ *
+ *
+ * Input
+ * Version
+ * Symbol Size
+ *
+ *
+ * 1
+ * M1
+ * 11 x 11
+ *
+ *
+ * 2
+ * M2
+ * 13 x 13
+ *
+ *
+ * 3
+ * M3
+ * 15 x 15
+ *
+ *
+ * 4
+ * M4
+ * 17 x 17
+ *
+ *
+ *
+ *
+ * @param version Symbol size
+ */
+ public void setPreferredVersion(int version) {
+ preferredVersion = version;
+ }
+
+ /**
+ * Set the amount of symbol space allocated to error correction.
+ * Levels are predefined according to the following table:
+ * Micro QR Error correction levels
+ *
+ *
+ * ECC Level
+ * Error Correction Capacity
+ * Recovery Capacity
+ *
+ *
+ * L (default)
+ * Approx 20% of symbol
+ * Approx 7%
+ *
+ *
+ * M
+ * Approx 37% of symbol
+ * Approx 15%
+ *
+ *
+ * Q
+ * Approx 55% of symbol
+ * Approx 25%
+ *
+ *
+ * H
+ * Approx 65% of symbol
+ * Approx 30%
+ *
+ *
+ *
+ *
+ * @param eccMode Error correction level
+ */
+ public void setEccMode(EccMode eccMode) {
+ preferredEccLevel = eccMode;
+ }
+
+ @Override
+ public boolean encode() {
+ int i, j, size;
+ boolean[] version_valid = new boolean[4];
+ int n_count, a_count;
+ EccMode ecc_level;
+ int version, autoversion;
+ int bitmask;
+ int format, format_full;
+ StringBuilder bin;
+ boolean byteModeUsed;
+ boolean alphanumModeUsed;
+ boolean kanjiModeUsed;
+
+ if (content.length() > 35) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ if (!inputCharCheck()) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ for (i = 0; i < 4; i++) {
+ version_valid[i] = true;
+ }
+
+ inputMode = new qrMode[40];
+ selectEncodingMode();
+
+ n_count = 0;
+ a_count = 0;
+ for (i = 0; i < content.length(); i++) {
+ if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) {
+ n_count++;
+ }
+ if (isAlphanumeric(content.charAt(i))) {
+ a_count++;
+ }
+ }
+
+ if (a_count == content.length()) {
+ /* All data can be encoded in Alphanumeric mode */
+ for (i = 0; i < content.length(); i++) {
+ inputMode[i] = qrMode.ALPHANUM;
+ }
+ }
+
+ if (n_count == content.length()) {
+ /* All data can be encoded in Numeric mode */
+ for (i = 0; i < content.length(); i++) {
+ inputMode[i] = qrMode.NUMERIC;
+ }
+ }
+
+ byteModeUsed = false;
+ alphanumModeUsed = false;
+ kanjiModeUsed = false;
+
+ for (i = 0; i < content.length(); i++) {
+ if (inputMode[i] == qrMode.BINARY) {
+ byteModeUsed = true;
+ }
+
+ if (inputMode[i] == qrMode.ALPHANUM) {
+ alphanumModeUsed = true;
+ }
+
+ if (inputMode[i] == qrMode.KANJI) {
+ kanjiModeUsed = true;
+ }
+ }
+
+ getBinaryLength();
+
+ /* Eliminate possivle versions depending on type of content */
+ if (byteModeUsed) {
+ version_valid[0] = false;
+ version_valid[1] = false;
+ }
+
+ if (alphanumModeUsed) {
+ version_valid[0] = false;
+ }
+
+ if (kanjiModeUsed) {
+ version_valid[0] = false;
+ version_valid[1] = false;
+ }
+
+ /* Eliminate possible versions depending on length of binary data */
+ if (binaryCount[0] > 20) {
+ version_valid[0] = false;
+ }
+ if (binaryCount[1] > 40) {
+ version_valid[1] = false;
+ }
+ if (binaryCount[2] > 84) {
+ version_valid[2] = false;
+ }
+ if (binaryCount[3] > 128) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ /* Eliminate possible versions depending on error correction level specified */
+ ecc_level = preferredEccLevel;
+
+ if (ecc_level == EccMode.H) {
+ errorMsg.append("Error correction level H not available");
+ return false;
+ }
+
+ if (ecc_level == EccMode.Q) {
+ version_valid[0] = false;
+ version_valid[1] = false;
+ version_valid[2] = false;
+ if (binaryCount[3] > 80) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+ }
+
+ if (ecc_level == EccMode.M) {
+ version_valid[0] = false;
+ if (binaryCount[1] > 32) {
+ version_valid[1] = false;
+ }
+ if (binaryCount[2] > 68) {
+ version_valid[2] = false;
+ }
+ if (binaryCount[3] > 112) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+ }
+
+ autoversion = 3;
+ if (version_valid[2]) {
+ autoversion = 2;
+ }
+ if (version_valid[1]) {
+ autoversion = 1;
+ }
+ if (version_valid[0]) {
+ autoversion = 0;
+ }
+
+ version = autoversion;
+ /* Get version from user */
+ if ((preferredVersion >= 1) && (preferredVersion <= 4)) {
+ if (preferredVersion >= autoversion) {
+ version = preferredVersion;
+ }
+ }
+
+ /* If there is enough unused space then increase the error correction level */
+ if (version == 3) {
+ if (binaryCount[3] <= 112) {
+ ecc_level = EccMode.M;
+ }
+ if (binaryCount[3] <= 80) {
+ ecc_level = EccMode.Q;
+ }
+ }
+
+ if (version == 2 && binaryCount[2] <= 68) {
+ ecc_level = EccMode.M;
+ }
+
+ if (version == 1 && binaryCount[1] <= 32) {
+ ecc_level = EccMode.M;
+ }
+
+ binary = new StringBuilder();
+ generateBinary(version);
+ if (binary.length() > 128) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ switch (version) {
+ case 0:
+ generateM1Symbol();
+ encodeInfo.append("Version: M1\n");
+ break;
+ case 1:
+ generateM2Symbol(ecc_level);
+ encodeInfo.append("Version: M2\n");
+ encodeInfo.append("ECC Level: ").append(levelToLetter(ecc_level)).append("\n");
+ break;
+ case 2:
+ generateM3Symbol(ecc_level);
+ encodeInfo.append("Version: M3\n");
+ encodeInfo.append("ECC Level: ").append(levelToLetter(ecc_level)).append("\n");
+ break;
+ case 3:
+ generateM4Symbol(ecc_level);
+ encodeInfo.append("Version: M4\n");
+ encodeInfo.append("ECC Level: ").append(levelToLetter(ecc_level)).append("\n");
+ break;
+ }
+
+ size = MICRO_QR_SIZES[version];
+
+ grid = new int[size * size];
+
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < size; j++) {
+ grid[(i * size) + j] = 0;
+ }
+ }
+
+ setupBitGrid(size);
+ populateBitGrid(size);
+ bitmask = applyBitmask(size);
+
+ encodeInfo.append("Mask Pattern: ").append(Integer.toBinaryString(bitmask)).append("\n");
+
+ /* Add format data */
+ format = 0;
+ switch (version) {
+ case 1:
+ switch (ecc_level) {
+ case L:
+ format = 1;
+ break;
+ case M:
+ format = 2;
+ break;
+ }
+ break;
+ case 2:
+ switch (ecc_level) {
+ case L:
+ format = 3;
+ break;
+ case M:
+ format = 4;
+ break;
+ }
+ break;
+ case 3:
+ switch (ecc_level) {
+ case L:
+ format = 5;
+ break;
+ case M:
+ format = 6;
+ break;
+ case Q:
+ format = 7;
+ break;
+ }
+ break;
+ }
+
+ format_full = QR_ANNEX_C1[(format << 2) + bitmask];
+
+ if ((format_full & 0x4000) != 0) {
+ grid[(8 * size) + 1] += 0x01;
+ }
+ if ((format_full & 0x2000) != 0) {
+ grid[(8 * size) + 2] += 0x01;
+ }
+ if ((format_full & 0x1000) != 0) {
+ grid[(8 * size) + 3] += 0x01;
+ }
+ if ((format_full & 0x800) != 0) {
+ grid[(8 * size) + 4] += 0x01;
+ }
+ if ((format_full & 0x400) != 0) {
+ grid[(8 * size) + 5] += 0x01;
+ }
+ if ((format_full & 0x200) != 0) {
+ grid[(8 * size) + 6] += 0x01;
+ }
+ if ((format_full & 0x100) != 0) {
+ grid[(8 * size) + 7] += 0x01;
+ }
+ if ((format_full & 0x80) != 0) {
+ grid[(8 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x40) != 0) {
+ grid[(7 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x20) != 0) {
+ grid[(6 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x10) != 0) {
+ grid[(5 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x08) != 0) {
+ grid[(4 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x04) != 0) {
+ grid[(3 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x02) != 0) {
+ grid[(2 * size) + 8] += 0x01;
+ }
+ if ((format_full & 0x01) != 0) {
+ grid[(size) + 8] += 0x01;
+ }
+
+ readable = new StringBuilder();
+ pattern = new String[size];
+ rowCount = size;
+ rowHeight = new int[size];
+ for (i = 0; i < size; i++) {
+ bin = new StringBuilder();
+ for (j = 0; j < size; j++) {
+ if ((grid[(i * size) + j] & 0x01) != 0) {
+ bin.append("1");
+ } else {
+ bin.append("0");
+ }
+ }
+ pattern[i] = bin2pat(bin.toString());
+ rowHeight[i] = 1;
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private boolean inputCharCheck() {
+ int qmarkBefore, qmarkAfter;
+ int i;
+ byte[] temp;
+
+ /* Check that input includes valid characters */
+
+ if (content.matches("[\u0000-\u00FF]+")) {
+ /* All characters in ISO 8859-1 */
+ return true;
+ }
+
+ /* Otherwise check for Shift-JIS characters */
+ qmarkBefore = 0;
+ for (i = 0; i < content.length(); i++) {
+ if (content.charAt(i) == '?') {
+ qmarkBefore++;
+ }
+ }
+
+ try {
+ temp = content.getBytes("SJIS");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Character encoding error");
+ return false;
+ }
+
+ qmarkAfter = 0;
+ for (i = 0; i < temp.length; i++) {
+ if (temp[i] == '?') {
+ qmarkAfter++;
+ }
+ }
+
+ /* If these values are the same, conversion was sucessful */
+ return (qmarkBefore == qmarkAfter);
+ }
+
+ private char levelToLetter(EccMode ecc_mode) {
+ switch (ecc_mode) {
+ case L:
+ return 'L';
+ case M:
+ return 'M';
+ case Q:
+ return 'Q';
+ case H:
+ return 'H';
+ default:
+ return ' ';
+ }
+ }
+
+ private void selectEncodingMode() {
+ int i, j;
+ int mlen;
+ int length = content.length();
+
+ for (i = 0; i < length; i++) {
+ if (content.charAt(i) > 0xff) {
+ inputMode[i] = qrMode.KANJI;
+ } else {
+ inputMode[i] = qrMode.BINARY;
+ if (isAlphanumeric(content.charAt(i))) {
+ inputMode[i] = qrMode.ALPHANUM;
+ }
+ if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) {
+ inputMode[i] = qrMode.NUMERIC;
+ }
+ }
+ }
+
+ /* If less than 6 numeric digits together then don't use numeric mode */
+ for (i = 0; i < length; i++) {
+ if (inputMode[i] == qrMode.NUMERIC) {
+ if (((i != 0) && (inputMode[i - 1] != qrMode.NUMERIC))
+ || (i == 0)) {
+ mlen = 0;
+ while (((mlen + i) < length)
+ && (inputMode[mlen + i] == qrMode.NUMERIC)) {
+ mlen++;
+ }
+ if (mlen < 6) {
+ for (j = 0; j < mlen; j++) {
+ inputMode[i + j] = qrMode.ALPHANUM;
+ }
+ }
+ }
+ }
+ }
+
+ /* If less than 4 alphanumeric characters together then don't use alphanumeric mode */
+ for (i = 0; i < length; i++) {
+ if (inputMode[i] == qrMode.ALPHANUM) {
+ if (((i != 0) && (inputMode[i - 1] != qrMode.ALPHANUM))
+ || (i == 0)) {
+ mlen = 0;
+ while (((mlen + i) < length)
+ && (inputMode[mlen + i] == qrMode.ALPHANUM)) {
+ mlen++;
+ }
+ if (mlen < 6) {
+ for (j = 0; j < mlen; j++) {
+ inputMode[i + j] = qrMode.BINARY;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean isAlphanumeric(char cglyph) {
+ /* Returns true if input glyph is in the Alphanumeric set */
+ boolean retval = false;
+
+ if ((cglyph >= '0') && (cglyph <= '9')) {
+ retval = true;
+ }
+ if ((cglyph >= 'A') && (cglyph <= 'Z')) {
+ retval = true;
+ }
+ switch (cglyph) {
+ case ' ':
+ case '$':
+ case '%':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ retval = true;
+ break;
+ }
+
+ return retval;
+ }
+
+ private String toBinary(int data, int h) {
+ StringBuilder argument = new StringBuilder();
+ for (;
+ (h != 0); h >>= 1) {
+ if ((data & h) != 0) {
+ argument.append("1");
+ } else {
+ argument.append("0");
+ }
+ }
+ return argument.toString();
+ }
+
+ private void getBinaryLength() {
+ int i;
+ qrMode currentMode = qrMode.NULL;
+ int blockLength;
+
+ /* Always include a terminator */
+ for (i = 0; i < 4; i++) {
+ binaryCount[i] = 0;
+ }
+
+ for (i = 0; i < content.length(); i++) {
+ if (currentMode != inputMode[i]) {
+
+ blockLength = 0;
+ do {
+ blockLength++;
+ } while (((i + blockLength) < content.length())
+ && (inputMode[i + blockLength] == inputMode[i]));
+
+ switch (inputMode[i]) {
+ case KANJI:
+ binaryCount[2] += 5 + (blockLength * 13);
+ binaryCount[3] += 7 + (blockLength * 13);
+
+ break;
+ case BINARY:
+ binaryCount[2] += 6 + (blockLength * 8);
+ binaryCount[3] += 8 + (blockLength * 8);
+ break;
+ case ALPHANUM:
+ int alphaLength;
+
+ if ((blockLength % 2) == 1) {
+ /* Odd length block */
+ alphaLength = ((blockLength - 1) / 2) * 11;
+ alphaLength += 6;
+ } else {
+ /* Even length block */
+ alphaLength = (blockLength / 2) * 11;
+ }
+
+ binaryCount[1] += 4 + alphaLength;
+ binaryCount[2] += 6 + alphaLength;
+ binaryCount[3] += 8 + alphaLength;
+ break;
+ case NUMERIC:
+ int numLength;
+
+ switch (blockLength % 3) {
+ case 1:
+ /* one digit left over */
+ numLength = ((blockLength - 1) / 3) * 10;
+ numLength += 4;
+ break;
+ case 2:
+ /* two digits left over */
+ numLength = ((blockLength - 2) / 3) * 10;
+ numLength += 7;
+ break;
+ default:
+ /* blockLength is a multiple of 3 */
+ numLength = (blockLength / 3) * 10;
+ break;
+ }
+
+ binaryCount[0] += 3 + numLength;
+ binaryCount[1] += 5 + numLength;
+ binaryCount[2] += 7 + numLength;
+ binaryCount[3] += 9 + numLength;
+ break;
+ }
+ currentMode = inputMode[i];
+ }
+ }
+
+ /* Add terminator */
+ if (binaryCount[1] < 37) {
+ binaryCount[1] += 5;
+ }
+
+ if (binaryCount[2] < 81) {
+ binaryCount[2] += 7;
+ }
+
+ if (binaryCount[3] < 125) {
+ binaryCount[3] += 9;
+ }
+ }
+
+ private void generateBinary(int version) {
+ int position = 0;
+ int blockLength, i;
+ qrMode data_block;
+ int msb, lsb, prod, jis;
+ String oneChar;
+ byte[] jisBytes;
+ int count, first, second, third;
+
+ encodeInfo.append("Encoding: ");
+
+ do {
+ data_block = inputMode[position];
+ blockLength = 0;
+ do {
+ blockLength++;
+ } while (((blockLength + position) < content.length())
+ && (inputMode[position + blockLength] == data_block));
+
+ switch (data_block) {
+ case KANJI:
+ /* Kanji mode */
+ /* Mode indicator */
+ switch (version) {
+ case 2:
+ binary.append("11");
+ break;
+ case 3:
+ binary.append("011");
+ break;
+ }
+
+ /* Character count indicator */
+ binary.append(toBinary(blockLength, 1 << version)); /* version = 2..3 */
+
+ encodeInfo.append("KANJ (").append(Integer.toString(blockLength)).append(") ");
+
+ /* Character representation */
+ for (i = 0; i < blockLength; i++) {
+ oneChar = "";
+ oneChar += content.charAt(position + i);
+
+ /* Convert Unicode input to Shift-JIS */
+ try {
+ jisBytes = oneChar.getBytes("SJIS");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Character encoding error");
+ return;
+ }
+
+ jis = ((jisBytes[0] & 0xFF) << 8) + (jisBytes[1] & 0xFF);
+
+ if (jis > 0x9fff) {
+ jis -= 0xc140;
+ } else {
+ jis -= 0x8140;
+ }
+ msb = (jis & 0xff00) >> 8;
+ lsb = (jis & 0xff);
+ prod = (msb * 0xc0) + lsb;
+
+ binary.append(toBinary(prod, 0x1000));
+
+ encodeInfo.append(Integer.toString(prod)).append(" ");
+ }
+
+ break;
+ case BINARY:
+ /* Byte mode */
+ /* Mode indicator */
+ switch (version) {
+ case 2:
+ binary.append("10");
+ break;
+ case 3:
+ binary.append("010");
+ break;
+ }
+
+ /* Character count indicator */
+ binary.append(toBinary(blockLength, 2 << version)); /* version = 2..3 */
+
+ encodeInfo.append("BYTE (").append(Integer.toString(blockLength)).append(") ");
+
+ /* Character representation */
+ for (i = 0; i < blockLength; i++) {
+ int lbyte = content.charAt(position + i);
+
+ binary.append(toBinary(lbyte, 0x80));
+
+ encodeInfo.append(Integer.toString(lbyte)).append(" ");
+ }
+
+ break;
+ case ALPHANUM:
+ /* Alphanumeric mode */
+ /* Mode indicator */
+ switch (version) {
+ case 1:
+ binary.append("1");
+ break;
+ case 2:
+ binary.append("01");
+ break;
+ case 3:
+ binary.append("001");
+ break;
+ }
+
+ /* Character count indicator */
+ binary.append(toBinary(blockLength, 2 << version)); /* version = 1..3 */
+
+ encodeInfo.append("ALPH (").append(Integer.toString(blockLength)).append(") ");
+
+ /* Character representation */
+ i = 0;
+ while (i < blockLength) {
+ first = positionOf(content.charAt(position + i), RHODIUM);
+ count = 1;
+ prod = first;
+
+ if ((i + 1) < blockLength) {
+ if (inputMode[position + i + 1] == qrMode.ALPHANUM) {
+ second = positionOf(content.charAt(position + i + 1), RHODIUM);
+ count = 2;
+ prod = (first * 45) + second;
+ }
+ }
+
+ binary.append(toBinary(prod, 1 << (5 * count))); /* count = 1..2 */
+
+ encodeInfo.append(Integer.toString(prod)).append(" ");
+
+ i += 2;
+ }
+
+ break;
+ case NUMERIC:
+ /* Numeric mode */
+ /* Mode indicator */
+ switch (version) {
+ case 1:
+ binary.append("0");
+ break;
+ case 2:
+ binary.append("00");
+ break;
+ case 3:
+ binary.append("000");
+ break;
+ }
+
+ /* Character count indicator */
+ binary.append(toBinary(blockLength, 4 << version)); /* version = 0..3 */
+
+ encodeInfo.append("NUMB (").append(Integer.toString(blockLength)).append(") ");
+
+ /* Character representation */
+ i = 0;
+ while (i < blockLength) {
+ first = Character.getNumericValue(content.charAt(position + i));
+ count = 1;
+ prod = first;
+
+ if ((i + 1) < blockLength) {
+ if (inputMode[position + i + 1] == qrMode.NUMERIC) {
+ second = Character.getNumericValue(content.charAt(position + i + 1));
+ count = 2;
+ prod = (prod * 10) + second;
+ }
+ }
+
+ if ((i + 2) < blockLength) {
+ if (inputMode[position + i + 2] == qrMode.NUMERIC) {
+ third = Character.getNumericValue(content.charAt(position + i + 2));
+ count = 3;
+ prod = (prod * 10) + third;
+ }
+ }
+
+ binary.append(toBinary(prod, 1 << (3 * count))); /* count = 1..3 */
+
+ encodeInfo.append(Integer.toString(prod)).append(" ");
+
+ i += 3;
+ }
+ break;
+ }
+
+ position += blockLength;
+ } while (position < content.length() - 1);
+
+ /* Add terminator */
+ switch (version) {
+ case 0:
+ binary.append("000");
+ break;
+ case 1:
+ if (binary.length() < 37) {
+ binary.append("00000");
+ }
+ break;
+ case 2:
+ if (binary.length() < 81) {
+ binary.append("0000000");
+ }
+ break;
+ case 3:
+ if (binary.length() < 125) {
+ binary.append("000000000");
+ }
+ break;
+ }
+
+ encodeInfo.append("\n");
+ }
+
+ private void generateM1Symbol() {
+ int i, latch;
+ int bits_total, bits_left, remainder;
+ int data_codewords, ecc_codewords;
+ int[] data_blocks = new int[4];
+ int[] ecc_blocks = new int[3];
+ ReedSolomon rs = new ReedSolomon();
+
+ bits_total = 20;
+ latch = 0;
+
+ /* Manage last (4-bit) block */
+ bits_left = bits_total - binary.length();
+ if (bits_left <= 4) {
+ for (i = 0; i < bits_left; i++) {
+ binary.append("0");
+ }
+ latch = 1;
+ }
+
+ if (latch == 0) {
+ /* Complete current byte */
+ remainder = 8 - (binary.length() % 8);
+ if (remainder == 8) {
+ remainder = 0;
+ }
+ for (i = 0; i < remainder; i++) {
+ binary.append("0");
+ }
+
+ /* Add padding */
+ bits_left = bits_total - binary.length();
+ if (bits_left > 4) {
+ remainder = (bits_left - 4) / 8;
+ for (i = 0; i < remainder; i++) {
+ if ((i & 1) != 0) {
+ binary.append("00010001");
+ } else {
+ binary.append("11101100");
+ }
+ }
+ }
+ binary.append("0000");
+ }
+
+ data_codewords = 3;
+ ecc_codewords = 2;
+
+ /* Copy data into codewords */
+ for (i = 0; i < (data_codewords - 1); i++) {
+ data_blocks[i] = 0;
+ if (binary.charAt(i * 8) == '1') {
+ data_blocks[i] += 0x80;
+ }
+ if (binary.charAt((i * 8) + 1) == '1') {
+ data_blocks[i] += 0x40;
+ }
+ if (binary.charAt((i * 8) + 2) == '1') {
+ data_blocks[i] += 0x20;
+ }
+ if (binary.charAt((i * 8) + 3) == '1') {
+ data_blocks[i] += 0x10;
+ }
+ if (binary.charAt((i * 8) + 4) == '1') {
+ data_blocks[i] += 0x08;
+ }
+ if (binary.charAt((i * 8) + 5) == '1') {
+ data_blocks[i] += 0x04;
+ }
+ if (binary.charAt((i * 8) + 6) == '1') {
+ data_blocks[i] += 0x02;
+ }
+ if (binary.charAt((i * 8) + 7) == '1') {
+ data_blocks[i] += 0x01;
+ }
+ }
+ data_blocks[2] = 0;
+ if (binary.charAt(16) == '1') {
+ data_blocks[2] += 0x08;
+ }
+ if (binary.charAt(17) == '1') {
+ data_blocks[2] += 0x04;
+ }
+ if (binary.charAt(18) == '1') {
+ data_blocks[2] += 0x02;
+ }
+ if (binary.charAt(19) == '1') {
+ data_blocks[2] += 0x01;
+ }
+
+ encodeInfo.append("Codewords: ");
+
+ for (i = 0; i < data_codewords; i++) {
+ encodeInfo.append(Integer.toString(data_blocks[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Calculate Reed-Solomon error codewords */
+ rs.init_gf(0x11d);
+ rs.init_code(ecc_codewords, 0);
+ rs.encode(data_codewords, data_blocks);
+ for (i = 0; i < ecc_codewords; i++) {
+ ecc_blocks[i] = rs.getResult(i);
+ }
+
+ /* Add Reed-Solomon codewords to binary data */
+ for (i = 0; i < ecc_codewords; i++) {
+ binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
+ }
+ }
+
+ private void generateM2Symbol(EccMode ecc_mode) {
+ int i;
+ int bits_total, bits_left, remainder;
+ int data_codewords, ecc_codewords;
+ int[] data_blocks = new int[6];
+ int[] ecc_blocks = new int[7];
+ ReedSolomon rs = new ReedSolomon();
+
+ bits_total = 40; // ecc_mode == EccMode.L
+ if (ecc_mode == EccMode.M) {
+ bits_total = 32;
+ }
+
+ /* Complete current byte */
+ remainder = 8 - (binary.length() % 8);
+ if (remainder == 8) {
+ remainder = 0;
+ }
+ for (i = 0; i < remainder; i++) {
+ binary.append("0");
+ }
+
+ /* Add padding */
+ bits_left = bits_total - binary.length();
+ remainder = bits_left / 8;
+ for (i = 0; i < remainder; i++) {
+ if ((i & 1) != 0) {
+ binary.append("00010001");
+ } else {
+ binary.append("11101100");
+ }
+ }
+
+ data_codewords = 5;
+ ecc_codewords = 5; // ecc_mode == EccMode.L
+ if (ecc_mode == EccMode.M) {
+ data_codewords = 4;
+ ecc_codewords = 6;
+ }
+
+ /* Copy data into codewords */
+ for (i = 0; i < data_codewords; i++) {
+ data_blocks[i] = 0;
+ if (binary.charAt(i * 8) == '1') {
+ data_blocks[i] += 0x80;
+ }
+ if (binary.charAt((i * 8) + 1) == '1') {
+ data_blocks[i] += 0x40;
+ }
+ if (binary.charAt((i * 8) + 2) == '1') {
+ data_blocks[i] += 0x20;
+ }
+ if (binary.charAt((i * 8) + 3) == '1') {
+ data_blocks[i] += 0x10;
+ }
+ if (binary.charAt((i * 8) + 4) == '1') {
+ data_blocks[i] += 0x08;
+ }
+ if (binary.charAt((i * 8) + 5) == '1') {
+ data_blocks[i] += 0x04;
+ }
+ if (binary.charAt((i * 8) + 6) == '1') {
+ data_blocks[i] += 0x02;
+ }
+ if (binary.charAt((i * 8) + 7) == '1') {
+ data_blocks[i] += 0x01;
+ }
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < data_codewords; i++) {
+ encodeInfo.append(Integer.toString(data_blocks[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Calculate Reed-Solomon error codewords */
+ rs.init_gf(0x11d);
+ rs.init_code(ecc_codewords, 0);
+ rs.encode(data_codewords, data_blocks);
+ for (i = 0; i < ecc_codewords; i++) {
+ ecc_blocks[i] = rs.getResult(i);
+ }
+
+ /* Add Reed-Solomon codewords to binary data */
+ for (i = 0; i < ecc_codewords; i++) {
+ binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
+ }
+ }
+
+ private void generateM3Symbol(EccMode ecc_mode) {
+ int i, latch;
+ int bits_total, bits_left, remainder;
+ int data_codewords, ecc_codewords;
+ int[] data_blocks = new int[12];
+ int[] ecc_blocks = new int[12];
+ ReedSolomon rs = new ReedSolomon();
+
+ latch = 0;
+
+ bits_total = 84; // ecc_mode == EccMode.L
+ if (ecc_mode == EccMode.M) {
+ bits_total = 68;
+ }
+
+ /* Manage last (4-bit) block */
+ bits_left = bits_total - binary.length();
+ if (bits_left <= 4) {
+ for (i = 0; i < bits_left; i++) {
+ binary.append("0");
+ }
+ latch = 1;
+ }
+
+ if (latch == 0) {
+ /* Complete current byte */
+ remainder = 8 - (binary.length() % 8);
+ if (remainder == 8) {
+ remainder = 0;
+ }
+ for (i = 0; i < remainder; i++) {
+ binary.append("0");
+ }
+
+ /* Add padding */
+ bits_left = bits_total - binary.length();
+ if (bits_left > 4) {
+ remainder = (bits_left - 4) / 8;
+ for (i = 0; i < remainder; i++) {
+ if ((i & 1) != 0) {
+ binary.append("00010001");
+ } else {
+ binary.append("11101100");
+ }
+ }
+ }
+ binary.append("0000");
+ }
+
+ data_codewords = 11;
+ ecc_codewords = 6; // ecc_mode == EccMode.L
+ if (ecc_mode == EccMode.M) {
+ data_codewords = 9;
+ ecc_codewords = 8;
+ }
+
+ /* Copy data into codewords */
+ for (i = 0; i < (data_codewords - 1); i++) {
+ data_blocks[i] = 0;
+ if (binary.charAt(i * 8) == '1') {
+ data_blocks[i] += 0x80;
+ }
+ if (binary.charAt((i * 8) + 1) == '1') {
+ data_blocks[i] += 0x40;
+ }
+ if (binary.charAt((i * 8) + 2) == '1') {
+ data_blocks[i] += 0x20;
+ }
+ if (binary.charAt((i * 8) + 3) == '1') {
+ data_blocks[i] += 0x10;
+ }
+ if (binary.charAt((i * 8) + 4) == '1') {
+ data_blocks[i] += 0x08;
+ }
+ if (binary.charAt((i * 8) + 5) == '1') {
+ data_blocks[i] += 0x04;
+ }
+ if (binary.charAt((i * 8) + 6) == '1') {
+ data_blocks[i] += 0x02;
+ }
+ if (binary.charAt((i * 8) + 7) == '1') {
+ data_blocks[i] += 0x01;
+ }
+ }
+
+ if (ecc_mode == EccMode.L) {
+ data_blocks[10] = 0;
+ if (binary.charAt(80) == '1') {
+ data_blocks[10] += 0x08;
+ }
+ if (binary.charAt(81) == '1') {
+ data_blocks[10] += 0x04;
+ }
+ if (binary.charAt(82) == '1') {
+ data_blocks[10] += 0x02;
+ }
+ if (binary.charAt(83) == '1') {
+ data_blocks[10] += 0x01;
+ }
+ }
+
+ if (ecc_mode == EccMode.M) {
+ data_blocks[8] = 0;
+ if (binary.charAt(64) == '1') {
+ data_blocks[8] += 0x08;
+ }
+ if (binary.charAt(65) == '1') {
+ data_blocks[8] += 0x04;
+ }
+ if (binary.charAt(66) == '1') {
+ data_blocks[8] += 0x02;
+ }
+ if (binary.charAt(67) == '1') {
+ data_blocks[8] += 0x01;
+ }
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < data_codewords; i++) {
+ encodeInfo.append(Integer.toString(data_blocks[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Calculate Reed-Solomon error codewords */
+ rs.init_gf(0x11d);
+ rs.init_code(ecc_codewords, 0);
+ rs.encode(data_codewords, data_blocks);
+ for (i = 0; i < ecc_codewords; i++) {
+ ecc_blocks[i] = rs.getResult(i);
+ }
+
+ /* Add Reed-Solomon codewords to binary data */
+ for (i = 0; i < ecc_codewords; i++) {
+ binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
+ }
+ }
+
+ private void generateM4Symbol(EccMode ecc_mode) {
+ int i;
+ int bits_total, bits_left, remainder;
+ int data_codewords, ecc_codewords;
+ int[] data_blocks = new int[17];
+ int[] ecc_blocks = new int[15];
+ ReedSolomon rs = new ReedSolomon();
+
+ bits_total = 128; // ecc_mode == EccMode.L
+ if (ecc_mode == EccMode.M) {
+ bits_total = 112;
+ }
+ if (ecc_mode == EccMode.Q) {
+ bits_total = 80;
+ }
+
+ /* Complete current byte */
+ remainder = 8 - (binary.length() % 8);
+ if (remainder == 8) {
+ remainder = 0;
+ }
+ for (i = 0; i < remainder; i++) {
+ binary.append("0");
+ }
+
+ /* Add padding */
+ bits_left = bits_total - binary.length();
+ remainder = bits_left / 8;
+ for (i = 0; i < remainder; i++) {
+ if ((i & 1) != 0) {
+ binary.append("00010001");
+ } else {
+ binary.append("11101100");
+ }
+ }
+
+ data_codewords = 16;
+ ecc_codewords = 8; // ecc_mode == EccMode.L
+ if (ecc_mode == EccMode.M) {
+ data_codewords = 14;
+ ecc_codewords = 10;
+ }
+ if (ecc_mode == EccMode.Q) {
+ data_codewords = 10;
+ ecc_codewords = 14;
+ }
+
+ /* Copy data into codewords */
+ for (i = 0; i < data_codewords; i++) {
+ data_blocks[i] = 0;
+ if (binary.charAt(i * 8) == '1') {
+ data_blocks[i] += 0x80;
+ }
+ if (binary.charAt((i * 8) + 1) == '1') {
+ data_blocks[i] += 0x40;
+ }
+ if (binary.charAt((i * 8) + 2) == '1') {
+ data_blocks[i] += 0x20;
+ }
+ if (binary.charAt((i * 8) + 3) == '1') {
+ data_blocks[i] += 0x10;
+ }
+ if (binary.charAt((i * 8) + 4) == '1') {
+ data_blocks[i] += 0x08;
+ }
+ if (binary.charAt((i * 8) + 5) == '1') {
+ data_blocks[i] += 0x04;
+ }
+ if (binary.charAt((i * 8) + 6) == '1') {
+ data_blocks[i] += 0x02;
+ }
+ if (binary.charAt((i * 8) + 7) == '1') {
+ data_blocks[i] += 0x01;
+ }
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < data_codewords; i++) {
+ encodeInfo.append(Integer.toString(data_blocks[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Calculate Reed-Solomon error codewords */
+ rs.init_gf(0x11d);
+ rs.init_code(ecc_codewords, 0);
+ rs.encode(data_codewords, data_blocks);
+ for (i = 0; i < ecc_codewords; i++) {
+ ecc_blocks[i] = rs.getResult(i);
+ }
+
+ /* Add Reed-Solomon codewords to binary data */
+ for (i = 0; i < ecc_codewords; i++) {
+ binary.append(toBinary(ecc_blocks[ecc_codewords - i - 1], 0x80));
+ }
+ }
+
+ private void setupBitGrid(int size) {
+ int i, toggle = 1;
+
+ /* Add timing patterns */
+ for (i = 0; i < size; i++) {
+ if (toggle == 1) {
+ grid[i] = 0x21;
+ grid[(i * size)] = 0x21;
+ toggle = 0;
+ } else {
+ grid[i] = 0x20;
+ grid[(i * size)] = 0x20;
+ toggle = 1;
+ }
+ }
+
+ /* Add finder patterns */
+ placeFinderPattern(size, 0, 0);
+
+ /* Add separators */
+ for (i = 0; i < 7; i++) {
+ grid[(7 * size) + i] = 0x10;
+ grid[(i * size) + 7] = 0x10;
+ }
+ grid[(7 * size) + 7] = 0x10;
+
+
+ /* Reserve space for format information */
+ for (i = 0; i < 8; i++) {
+ grid[(8 * size) + i] += 0x20;
+ grid[(i * size) + 8] += 0x20;
+ }
+ grid[(8 * size) + 8] += 0x20;
+ }
+
+ private void placeFinderPattern(int size, int x, int y) {
+ int xp, yp;
+
+ int[] finder = {
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 1,
+ 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1
+ };
+
+ for (xp = 0; xp < 7; xp++) {
+ for (yp = 0; yp < 7; yp++) {
+ if (finder[xp + (7 * yp)] == 1) {
+ grid[((yp + y) * size) + (xp + x)] = 0x11;
+ } else {
+ grid[((yp + y) * size) + (xp + x)] = 0x10;
+ }
+ }
+ }
+ }
+
+ private void populateBitGrid(int size) {
+ boolean goingUp = true;
+ int row = 0; /* right hand side */
+
+ int i, n, x, y;
+
+ n = binary.length();
+ y = size - 1;
+ i = 0;
+ do {
+ x = (size - 2) - (row * 2);
+
+ if ((grid[(y * size) + (x + 1)] & 0xf0) == 0) {
+ if (binary.charAt(i) == '1') {
+ grid[(y * size) + (x + 1)] = 0x01;
+ } else {
+ grid[(y * size) + (x + 1)] = 0x00;
+ }
+ i++;
+ }
+
+ if (i < n) {
+ if ((grid[(y * size) + x] & 0xf0) == 0) {
+ if (binary.charAt(i) == '1') {
+ grid[(y * size) + x] = 0x01;
+ } else {
+ grid[(y * size) + x] = 0x00;
+ }
+ i++;
+ }
+ }
+
+ if (goingUp) {
+ y--;
+ } else {
+ y++;
+ }
+ if (y == 0) {
+ /* reached the top */
+ row++;
+ y = 1;
+ goingUp = false;
+ }
+ if (y == size) {
+ /* reached the bottom */
+ row++;
+ y = size - 1;
+ goingUp = true;
+ }
+ } while (i < n);
+ }
+
+ private int applyBitmask(int size) {
+ int x, y;
+ int p;
+ int local_pattern;
+ int[] value = new int[8];
+ int best_val, best_pattern;
+ int bit;
+
+ int[] mask = new int[size * size];
+ eval = new int[size * size];
+
+ /* Perform data masking */
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ mask[(y * size) + x] = 0x00;
+
+ if ((grid[(y * size) + x] & 0xf0) == 0) {
+ if ((y & 1) == 0) {
+ mask[(y * size) + x] += 0x01;
+ }
+
+ if ((((y / 2) + (x / 3)) & 1) == 0) {
+ mask[(y * size) + x] += 0x02;
+ }
+
+ if (((((y * x) & 1) + ((y * x) % 3)) & 1) == 0) {
+ mask[(y * size) + x] += 0x04;
+ }
+
+ if (((((y + x) & 1) + ((y * x) % 3)) & 1) == 0) {
+ mask[(y * size) + x] += 0x08;
+ }
+ }
+ }
+ }
+
+
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ if ((grid[(y * size) + x] & 0x01) != 0) {
+ p = 0xff;
+ } else {
+ p = 0x00;
+ }
+
+ eval[(y * size) + x] = mask[(y * size) + x] ^ p;
+ }
+ }
+
+ /* Evaluate result */
+ for (local_pattern = 0; local_pattern < 4; local_pattern++) {
+ value[local_pattern] = evaluateBitmask(size, local_pattern);
+ }
+
+ best_pattern = 0;
+ best_val = value[0];
+ for (local_pattern = 1; local_pattern < 4; local_pattern++) {
+ if (value[local_pattern] > best_val) {
+ best_pattern = local_pattern;
+ best_val = value[local_pattern];
+ }
+ }
+
+ /* Apply mask */
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ bit = 0;
+ switch (best_pattern) {
+ case 0:
+ if ((mask[(y * size) + x] & 0x01) != 0) {
+ bit = 1;
+ }
+ break;
+ case 1:
+ if ((mask[(y * size) + x] & 0x02) != 0) {
+ bit = 1;
+ }
+ break;
+ case 2:
+ if ((mask[(y * size) + x] & 0x04) != 0) {
+ bit = 1;
+ }
+ break;
+ case 3:
+ if ((mask[(y * size) + x] & 0x08) != 0) {
+ bit = 1;
+ }
+ break;
+ }
+ if (bit == 1) {
+ if ((grid[(y * size) + x] & 0x01) != 0) {
+ grid[(y * size) + x] = 0x00;
+ } else {
+ grid[(y * size) + x] = 0x01;
+ }
+ }
+ }
+ }
+
+ return best_pattern;
+ }
+
+ private int evaluateBitmask(int size, int pattern) {
+ int sum1, sum2, i, filter = 0, retval;
+
+ switch (pattern) {
+ case 0:
+ filter = 0x01;
+ break;
+ case 1:
+ filter = 0x02;
+ break;
+ case 2:
+ filter = 0x04;
+ break;
+ case 3:
+ filter = 0x08;
+ break;
+ }
+
+ sum1 = 0;
+ sum2 = 0;
+ for (i = 1; i < size; i++) {
+ if ((eval[(i * size) + size - 1] & filter) != 0) {
+ sum1++;
+ }
+ if ((eval[((size - 1) * size) + i] & filter) != 0) {
+ sum2++;
+ }
+ }
+
+ if (sum1 <= sum2) {
+ retval = (sum1 * 16) + sum2;
+ } else {
+ retval = (sum2 * 16) + sum1;
+ }
+
+ return retval;
+ }
+
+ private enum qrMode {
+ NULL, KANJI, BINARY, ALPHANUM, NUMERIC
+ }
+
+ public enum EccMode {
+ L, M, Q, H
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java b/barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java
new file mode 100755
index 0000000..45a6f8d
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java
@@ -0,0 +1,188 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements the MSI (Modified Plessey) bar code symbology.
+ * MSI Plessey can encode a string of numeric digits and has a range
+ * of check digit options.
+ */
+public class MsiPlessey extends Symbol {
+
+ private final String[] MSI_PlessTable = {
+ "12121212", "12121221", "12122112", "12122121", "12211212", "12211221",
+ "12212112", "12212121", "21121212", "21121221"
+ };
+ private CheckDigit checkOption;
+
+ public MsiPlessey() {
+ checkOption = CheckDigit.NONE;
+ }
+
+ /**
+ * Set the check digit scheme to use. Options are: None, Modulo-10,
+ * 2 x Modulo-10, Modulo-11 and Modulo-11 & 10.
+ *
+ * @param checkMode Type of check digit to add to symbol
+ */
+ public void setCheckDigit(CheckDigit checkMode) {
+ checkOption = checkMode;
+ }
+
+ @Override
+ public boolean encode() {
+ StringBuilder intermediate;
+ int length = content.length();
+ int i;
+ StringBuilder evenString;
+ StringBuilder oddString;
+ String addupString;
+ int spacer;
+ int addup;
+ int weight;
+ int checkDigit1;
+ int checkDigit2;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ intermediate = new StringBuilder("21"); // Start
+ for (i = 0; i < length; i++) {
+ intermediate.append(MSI_PlessTable[Character.getNumericValue(content.charAt(i))]);
+ }
+
+ readable = new StringBuilder(content);
+
+ if ((checkOption == CheckDigit.MOD10) || (checkOption == CheckDigit.MOD10_MOD10)) {
+ /* Add Modulo-10 check digit */
+ evenString = new StringBuilder();
+ oddString = new StringBuilder();
+
+ spacer = content.length() & 1;
+
+ for (i = content.length() - 1; i >= 0; i--) {
+ if (spacer == 1) {
+ if ((i & 1) != 0) {
+ evenString.insert(0, content.charAt(i));
+ } else {
+ oddString.insert(0, content.charAt(i));
+ }
+ } else {
+ if ((i & 1) != 0) {
+ oddString.insert(0, content.charAt(i));
+ } else {
+ evenString.insert(0, content.charAt(i));
+ }
+ }
+ }
+
+ if (oddString.length() == 0) {
+ addupString = "0";
+ } else {
+ addupString = Integer.toString(Integer.parseInt(oddString.toString()) * 2);
+ }
+
+ addupString += evenString;
+
+ addup = 0;
+ for (i = 0; i < addupString.length(); i++) {
+ addup += addupString.charAt(i) - '0';
+ }
+
+ checkDigit1 = 10 - (addup % 10);
+ if (checkDigit1 == 10) {
+ checkDigit1 = 0;
+ }
+
+ intermediate.append(MSI_PlessTable[checkDigit1]);
+ readable.append(checkDigit1);
+ }
+
+ if ((checkOption == CheckDigit.MOD11) || (checkOption == CheckDigit.MOD11_MOD10)) {
+ /* Add a Modulo-11 check digit */
+ weight = 2;
+ addup = 0;
+ for (i = content.length() - 1; i >= 0; i--) {
+ addup += (content.charAt(i) - '0') * weight;
+ weight++;
+
+ if (weight == 8) {
+ weight = 2;
+ }
+ }
+
+ checkDigit1 = 11 - (addup % 11);
+
+ if (checkDigit1 == 11) {
+ checkDigit1 = 0;
+ }
+
+ readable.append(checkDigit1);
+ if (checkDigit1 == 10) {
+ intermediate.append(MSI_PlessTable[1]);
+ intermediate.append(MSI_PlessTable[0]);
+ } else {
+ intermediate.append(MSI_PlessTable[checkDigit1]);
+ }
+ }
+
+ if ((checkOption == CheckDigit.MOD10_MOD10) || (checkOption == CheckDigit.MOD11_MOD10)) {
+ /* Add a second Modulo-10 check digit */
+ evenString = new StringBuilder();
+ oddString = new StringBuilder();
+
+ spacer = readable.length() & 1;
+
+ for (i = readable.length() - 1; i >= 0; i--) {
+ if (spacer == 1) {
+ if ((i & 1) != 0) {
+ evenString.insert(0, readable.charAt(i));
+ } else {
+ oddString.insert(0, readable.charAt(i));
+ }
+ } else {
+ if ((i & 1) != 0) {
+ oddString.insert(0, readable.charAt(i));
+ } else {
+ evenString.insert(0, readable.charAt(i));
+ }
+ }
+ }
+
+ if (oddString.length() == 0) {
+ addupString = "0";
+ } else {
+ addupString = Integer.toString(Integer.parseInt(oddString.toString()) * 2);
+ }
+
+ addupString += evenString;
+
+ addup = 0;
+ for (i = 0; i < addupString.length(); i++) {
+ addup += addupString.charAt(i) - '0';
+ }
+
+ checkDigit2 = 10 - (addup % 10);
+ if (checkDigit2 == 10) {
+ checkDigit2 = 0;
+ }
+
+ intermediate.append(MSI_PlessTable[checkDigit2]);
+ readable.append(checkDigit2);
+ }
+
+ intermediate.append("121"); // Stop
+
+ pattern = new String[1];
+ pattern[0] = intermediate.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ public enum CheckDigit {
+ NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java b/barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java
new file mode 100755
index 0000000..021f732
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java
@@ -0,0 +1,72 @@
+package org.xbib.graphics.barcode;
+
+import java.util.stream.IntStream;
+
+/**
+ * Calculate NVE-18 (Nummer der Versandeinheit)
+ * Also SSCC-18 (Serial Shipping Container Code)
+ * Encodes a 17 digit number, adding a Modulo-10 check digit.
+ */
+public class Nve18 extends Symbol {
+
+ @Override
+ public boolean encode() {
+ StringBuilder gs1Equivalent = new StringBuilder();
+ int zeroes;
+ int count = 0;
+ int c, cdigit;
+ int p = 0;
+ Code128 code128 = new Code128();
+
+ if (content.length() > 17) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ // Add leading zeroes
+ zeroes = 17 - content.length();
+ IntStream.range(0, zeroes).mapToObj(i -> "0").forEach(gs1Equivalent::append);
+ gs1Equivalent.append(content);
+ // Add Modulus-10 check digit
+ for (int i = gs1Equivalent.length() - 1; i >= 0; i--) {
+ c = Character.getNumericValue(gs1Equivalent.charAt(i));
+ if ((p % 2) == 0) {
+ c = c * 3;
+ }
+ count += c;
+ p++;
+ }
+ cdigit = 10 - (count % 10);
+ if (cdigit == 10) {
+ cdigit = 0;
+ }
+
+ encodeInfo.append("NVE Check Digit: ").append(cdigit).append("\n");
+
+ content = "[00]" + gs1Equivalent + cdigit;
+
+ // Defer to Code 128
+ code128.setDataType(DataType.GS1);
+ code128.setHumanReadableLocation(getHumanReadableLocation());
+
+ try {
+ code128.setContent(content);
+ } catch (Exception e) {
+ errorMsg.append(e.getMessage());
+ return false;
+ }
+ getRectangles().clear();
+ getRectangles().addAll(code128.getRectangles());
+ getTexts().clear();
+ getTexts().addAll(code128.getTexts());
+ symbolHeight = code128.symbolHeight;
+ symbolWidth = code128.symbolWidth;
+ encodeInfo.append(code128.encodeInfo);
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java
new file mode 100755
index 0000000..32922ef
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java
@@ -0,0 +1,1704 @@
+package org.xbib.graphics.barcode;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implements PDF417 bar code symbology and MicroPDF417 bar code symbology
+ * according to ISO/IEC 15438:2006 and ISO/IEC 24728:2006 respectively.
+ * PDF417 supports encoding up to the ISO standard maximum symbol size of 925
+ * codewords which (at error correction level 0) allows a maximum data size
+ * of 1850 text characters, or 2710 digits. The maximum size MicroPDF417 symbol
+ * can hold 250 alphanumeric characters or 366 digits.
+ */
+public class Pdf417 extends Symbol {
+
+ private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44;
+
+ private static final int[] COEFRS = {
+ /* k = 2 */
+ 27, 917,
+
+ /* k = 4 */
+ 522, 568, 723, 809,
+
+ /* k = 8 */
+ 237, 308, 436, 284, 646, 653, 428, 379,
+
+ /* k = 16 */
+ 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
+
+ /* k = 32 */
+ 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
+ 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
+
+ /* k = 64 */
+ 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612,
+ 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184,
+ 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502,
+ 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543,
+
+ /* k = 128 */
+ 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415,
+ 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704,
+ 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569,
+ 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776,
+ 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898,
+ 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616,
+ 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34,
+ 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539,
+
+ /* k = 256 */
+ 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720,
+ 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757,
+ 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137,
+ 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884,
+ 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521,
+ 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470,
+ 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90,
+ 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134,
+ 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234,
+ 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621,
+ 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528,
+ 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550,
+ 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754,
+ 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532,
+ 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173,
+ 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10,
+
+ /* k = 512 */
+ 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492,
+ 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781,
+ 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534,
+ 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41,
+ 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741,
+ 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142,
+ 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258,
+ 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303,
+ 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402,
+ 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785,
+ 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543,
+ 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820,
+ 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578,
+ 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911,
+ 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408,
+ 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729,
+ 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772,
+ 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777,
+ 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45,
+ 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905,
+ 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341,
+ 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808,
+ 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249,
+ 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791,
+ 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437,
+ 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842,
+ 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316,
+ 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656,
+ 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433,
+ 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780,
+ 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647,
+ 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263
+ };
+
+ ;
+ private static final String[] CODAGEMC = {
+ "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA",
+ "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs",
+ "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk",
+ "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc",
+ "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw",
+ "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk",
+ "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk",
+ "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk",
+ "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk",
+ "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi",
+ "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr",
+ "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA",
+ "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk",
+ "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy",
+ "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs",
+ "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk",
+ "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss",
+ "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj",
+ "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE",
+ "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc",
+ "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii",
+ "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA",
+ "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs",
+ "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD",
+ "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb",
+ "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak",
+ "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD",
+ "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow",
+ "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj",
+ "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw",
+ "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw",
+ "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE",
+ "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa",
+ "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug",
+ "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb",
+ "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA",
+ "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE",
+ "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb",
+ "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE",
+ "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE",
+ "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa",
+ "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg",
+ "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc",
+ "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE",
+ "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn",
+ "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos",
+ "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg",
+ "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug",
+ "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk",
+ "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb",
+ "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa",
+ "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE",
+ "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD",
+ "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba",
+ "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc",
+ "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE",
+ "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc",
+ "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC",
+ "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso",
+ "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl",
+ "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl",
+ "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu",
+ "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari",
+ "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda",
+ "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD",
+ "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn",
+ "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB",
+ "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo",
+ "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl",
+ "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl",
+ "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd",
+ "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos",
+ "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA",
+ "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj",
+ "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw",
+ "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw",
+ "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi",
+ "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk",
+ "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig",
+ "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb",
+ "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb",
+ "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac",
+ "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC",
+ "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza",
+ "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC",
+ "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB",
+ "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm",
+ "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes",
+ "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi",
+ "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk",
+ "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg",
+ "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi",
+ "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk",
+ "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB",
+ "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB",
+ "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC",
+ "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC",
+ "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw",
+ "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn",
+ "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB",
+ "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc",
+ "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf",
+ "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv",
+ "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo",
+ "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku",
+ "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt",
+ "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa",
+ "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB",
+ "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc",
+ "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw",
+ "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn",
+ "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB",
+ "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc",
+ "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs",
+ "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy",
+ "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm",
+ "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo",
+ "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm",
+ "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy",
+ "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt",
+ "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu",
+ "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx",
+ "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE",
+ "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn",
+ "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy",
+ "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo",
+ "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv",
+ "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz",
+ "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu",
+ "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu",
+ "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh",
+ "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx",
+ "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz",
+ "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv",
+ "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd",
+ "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt",
+ "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx",
+ "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow",
+ "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj",
+ "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx",
+ "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus",
+ "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD",
+ "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr",
+ "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw",
+ "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq",
+ "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB",
+ "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors",
+ "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm",
+ "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv",
+ "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz",
+ "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu",
+ "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy",
+ "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy",
+ "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo",
+ "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz",
+ "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl",
+ "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj",
+ "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky",
+ "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz",
+ "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt",
+ "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj",
+ "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh",
+ "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk",
+ "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy",
+ "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj",
+ "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns",
+ "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz",
+ "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz",
+ "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz",
+ "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx",
+ "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks",
+ "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj",
+ "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy",
+ "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi",
+ "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb",
+ "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg",
+ "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj",
+ "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa",
+ "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz",
+ "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb",
+ "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk",
+ "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj",
+ "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw",
+ "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk",
+ "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD",
+ "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj",
+ "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB",
+ "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb",
+ "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg",
+ "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn",
+ "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw",
+ "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj",
+ "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw",
+ "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb",
+ "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb",
+ "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc",
+ "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva",
+ "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD",
+ "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB",
+ "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn",
+ "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl",
+ "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw",
+ "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi",
+ "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr",
+ "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC",
+ "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq",
+ "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm",
+ "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf",
+ "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu",
+ "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa",
+ "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn",
+ "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv",
+ "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt",
+ "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif",
+ "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp"
+ };
+ private static final char[] BR_SET = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '*', '+', '-'
+ };
+ private static final String[] PDF_TTF = {
+ "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111",
+ "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001",
+ "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010",
+ "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001"
+ };
+ private static final int[] ASCII_X = {
+ 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 12, 8, 8, 4, 8, 8, 8, 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, 8, 8, 8, 4, 8, 8, 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, 8, 8, 8, 8
+ };
+ private static final int[] ASCII_Y = {
+ 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 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, 4, 5, 6, 24, 7, 8, 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, 21, 27, 9
+ };
+ private static final int[] MICRO_AUTOSIZE = {
+ 4, 6, 7, 8, 8, 10, 10, 12, 12, 13, 14, 16, 18, 18, 19, 20, 24, 24, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, // max codeword counts
+ 1, 14, 2, 7, 24, 3, 15, 25, 4, 8, 16, 5, 17, 26, 9, 6, 10, 18, 27, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34 // corresponding variant
+ };
+ /* Rows, columns, error codewords, k-offset of valid MicroPDF417 sizes from ISO/IEC 24728:2006 */
+ private static final int[] MICRO_VARIANTS = {
+ 1, 1, 1, 1, 1, 1, 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, // columns
+ 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, // rows
+ 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, // k (EC codewords)
+ 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 // offset
+ };
+ /* Following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */
+ private static final int[] RAP_TABLE = {
+ 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, // left RAP
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, // centre RAP
+ 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, // right RAP
+ 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 // start cluster
+ };
+ /* Left and Right Row Address Pattern from Table 2 */
+ private static final String[] RAPLR = {
+ "", "221311", "311311", "312211", "222211", "213211", "214111", "223111",
+ "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211",
+ "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122",
+ "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121",
+ "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213",
+ "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311"
+ };
+ /* Centre Row Address Pattern from Table 2 */
+ private static final String[] RAPC = {
+ "", "112231", "121231", "122131", "131131", "131221", "132121", "141121",
+ "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111",
+ "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411",
+ "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113",
+ "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223",
+ "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141"
+ };
+ /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
+ private static final int[] MICRO_COEFFS = {
+ /* k = 7 */
+ 76, 925, 537, 597, 784, 691, 437,
+
+ /* k = 8 */
+ 237, 308, 436, 284, 646, 653, 428, 379,
+
+ /* k = 9 */
+ 567, 527, 622, 257, 289, 362, 501, 441, 205,
+
+ /* k = 10 */
+ 377, 457, 64, 244, 826, 841, 818, 691, 266, 612,
+
+ /* k = 11 */
+ 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904,
+
+ /* k = 12 */
+ 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851,
+
+ /* k = 13 */
+ 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692,
+
+ /* k = 14 */
+ 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215,
+
+ /* k = 15 */
+ 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642,
+
+ /* k = 16 */
+ 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
+
+ /* k = 18 */
+ 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756,
+ 760, 573,
+
+ /* k = 21 */
+ 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691,
+ 347, 165, 193, 259, 568,
+
+ /* k = 26 */
+ 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893,
+ 699, 245, 441, 454, 325, 858, 131, 847, 764, 169,
+
+ /* k = 32 */
+ 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
+ 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
+
+ /* k = 38 */
+ 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684,
+ 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771,
+ 554, 289, 231, 125, 117, 518,
+
+ /* k = 44 */
+ 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405,
+ 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213,
+ 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285,
+
+ /* k = 50 */
+ 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303,
+ 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26,
+ 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820,
+ 718, 435
+ };
+ private int[] codeWords = new int[2700];
+ private int codeWordCount;
+ private Mode symbolMode = Mode.NORMAL;
+ private int[] inputData;
+ private Integer columns;
+ private Integer rows;
+ private int preferredEccLevel = -1;
+ private int structuredAppendFileId = 0;
+ private int structuredAppendPosition = 1;
+ private int structuredAppendTotal = 1;
+
+ /**
+ * Creates a new PDF417 symbol instance.
+ */
+ public Pdf417() {
+ setBarHeight(3);
+ }
+
+ private static EncodingMode chooseMode(int codeascii) {
+ if (codeascii >= '0' && codeascii <= '9') {
+ return EncodingMode.NUM;
+ } else if (codeascii == '\t' || codeascii == '\n' || codeascii == '\r' || (codeascii >= ' ' && codeascii <= '~')) {
+ return EncodingMode.TEX;
+ } else {
+ return EncodingMode.BYT;
+ }
+ }
+
+ private static int getMicroPdf417Variant(int codeWordCount, Integer columns, Integer rows) {
+ for (int i = 0; i < 34; i++) {
+ int maxCodewordCount = MICRO_AUTOSIZE[i];
+ if (codeWordCount <= maxCodewordCount) {
+ int variant = MICRO_AUTOSIZE[i + 34];
+ int columnsForThisVariant = MICRO_VARIANTS[variant - 1];
+ int rowsForThisVariant = MICRO_VARIANTS[variant - 1 + 34];
+ if ((columns == null || columns == columnsForThisVariant) && (rows == null || rows == rowsForThisVariant)) {
+ return variant;
+ }
+ }
+ }
+ throw new IllegalStateException("Unable to determine MicroPDF417 variant for " + codeWordCount + " codewords");
+ }
+
+ /**
+ * Determines the encoding block groups for the specified data.
+ */
+ private static List createBlocks(int[] data) {
+
+ List blocks = new ArrayList<>();
+ Block current = null;
+
+ for (int aData : data) {
+ EncodingMode mode = chooseMode(aData);
+ if ((current != null && current.mode == mode) &&
+ (mode != EncodingMode.NUM || current.length < MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) {
+ current.length++;
+ } else {
+ current = new Block(mode);
+ blocks.add(current);
+ }
+ }
+
+ smoothBlocks(blocks);
+
+ return blocks;
+ }
+
+ /**
+ * Combines adjacent blocks of different types in very specific scenarios.
+ */
+ private static void smoothBlocks(List blocks) {
+
+ for (int i = 0; i < blocks.size(); i++) {
+ Block block = blocks.get(i);
+ EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE);
+ EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE);
+ if (block.mode == EncodingMode.NUM) {
+ if (i == 0) { /* first block */
+ if (next == EncodingMode.TEX && block.length < 8) {
+ block.mode = EncodingMode.TEX;
+ } else if (next == EncodingMode.BYT && block.length == 1) {
+ block.mode = EncodingMode.BYT;
+ }
+ } else if (i == blocks.size() - 1) { /* last block */
+ if (last == EncodingMode.TEX && block.length < 7) {
+ block.mode = EncodingMode.TEX;
+ } else if (last == EncodingMode.BYT && block.length == 1) {
+ block.mode = EncodingMode.BYT;
+ }
+ } else { /* not first or last block */
+ if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 4) {
+ block.mode = EncodingMode.BYT;
+ } else if (last == EncodingMode.BYT && next == EncodingMode.TEX && block.length < 4) {
+ block.mode = EncodingMode.TEX;
+ } else if (last == EncodingMode.TEX && next == EncodingMode.BYT && block.length < 5) {
+ block.mode = EncodingMode.TEX;
+ } else if (last == EncodingMode.TEX && next == EncodingMode.TEX && block.length < 8) {
+ block.mode = EncodingMode.TEX;
+ } else if (last == EncodingMode.NUM && next == EncodingMode.TEX && block.length < 8) {
+ block.mode = EncodingMode.TEX;
+ }
+ }
+ }
+ }
+
+ mergeBlocks(blocks);
+
+ for (int i = 0; i < blocks.size(); i++) {
+ Block block = blocks.get(i);
+ EncodingMode last = (i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE);
+ EncodingMode next = (i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE);
+ if (block.mode == EncodingMode.TEX && i > 0) { /* not the first */
+ if (i == blocks.size() - 1) { /* the last one */
+ if (last == EncodingMode.BYT && block.length == 1) {
+ block.mode = EncodingMode.BYT;
+ }
+ } else { /* not the last one */
+ if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 5) {
+ block.mode = EncodingMode.BYT;
+ }
+ if (((last == EncodingMode.BYT && next != EncodingMode.BYT) ||
+ (last != EncodingMode.BYT && next == EncodingMode.BYT)) && (block.length < 3)) {
+ block.mode = EncodingMode.BYT;
+ }
+ }
+ }
+ }
+
+ mergeBlocks(blocks);
+ }
+
+ /**
+ * Combines adjacent blocks of the same type.
+ */
+ private static void mergeBlocks(List blocks) {
+ for (int i = 1; i < blocks.size(); i++) {
+ Block b1 = blocks.get(i - 1);
+ Block b2 = blocks.get(i);
+ if ((b1.mode == b2.mode) &&
+ (b1.mode != EncodingMode.NUM || b1.length + b2.length <= MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) {
+ b1.length += b2.length;
+ blocks.remove(i);
+ i--;
+ }
+ }
+ }
+
+ /**
+ * Sets the default bar height (height of a single row) for this symbol (default value is 3
).
+ *
+ * @param barHeight the default bar height for this symbol
+ */
+ @Override
+ public void setBarHeight(int barHeight) {
+ super.setBarHeight(barHeight);
+ }
+
+ /**
+ * Returns the number of data columns used by this symbol, or {@code null}
+ * if the number of data columns has not been set.
+ *
+ * @return the number of data columns used by this symbol
+ */
+ public Integer getDataColumns() {
+ return columns;
+ }
+
+ /**
+ * Sets the width of the symbol by specifying the number of columns
+ * of data codewords. Valid values are 1-30 for PDF417 and 1-4
+ * for MicroPDF417.
+ *
+ * @param columns the number of data columns in the symbol
+ */
+ public void setDataColumns(int columns) {
+ this.columns = columns;
+ }
+
+ /**
+ * Returns the number of rows used by this symbol, or {@code null} if
+ * the number of rows has not been set.
+ *
+ * @return the number of rows used by this symbol
+ */
+ public Integer getRows() {
+ return rows;
+ }
+
+ /**
+ * Sets the height of the symbol by specifying the number of rows
+ * of data codewords. Valid values are 3-90 for PDF417 and 4-44
+ * for MicroPDF417.
+ *
+ * @param rows the number of rows in the symbol
+ */
+ public void setRows(int rows) {
+ this.rows = rows;
+ }
+
+ /**
+ * Set the amount of the symbol which is dedicated to error correction
+ * codewords. The number of codewords of error correction data is
+ * determined by 2(eccLevel + 1) . This attribute is ignored
+ * when using {@link Mode#MICRO micro} mode.
+ *
+ * @param eccLevel level of error correction (0-8)
+ */
+ public void setPreferredEccLevel(int eccLevel) {
+ if (eccLevel < 0 || eccLevel > 8) {
+ throw new IllegalArgumentException("ECC level must be between 0 and 8.");
+ }
+ preferredEccLevel = eccLevel;
+ }
+
+ /**
+ * Forces the use of the specified MicroPDF417 variant. Only valid
+ * when using {@link Mode#MICRO micro} mode.
+ *
+ * @param variant the MicroPDF417 variant to use
+ */
+ public void setVariant(int variant) {
+ if (symbolMode != Mode.MICRO) {
+ throw new IllegalArgumentException("Can only set variant when using MICRO mode.");
+ }
+ if (variant < 1 || variant > 34) {
+ throw new IllegalArgumentException("Variant must be between 1 and 34.");
+ }
+ this.columns = MICRO_VARIANTS[variant - 1];
+ this.rows = MICRO_VARIANTS[variant - 1 + 34];
+ }
+
+ /**
+ * Returns the position of this PDF417 symbol in a series of symbols using structured append
+ * (Macro PDF417). If this symbol is not part of such a series, this method will return 1
.
+ *
+ * @return the position of this PDF417 symbol in a series of symbols using structured append
+ */
+ public int getStructuredAppendPosition() {
+ return structuredAppendPosition;
+ }
+
+ /**
+ * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
+ * (Macro PDF417), this method sets the position of this symbol in the series. Valid values are
+ * 1 through 99,999 inclusive.
+ *
+ * @param position the position of this PDF417 symbol in the structured append series
+ */
+ public void setStructuredAppendPosition(int position) {
+ if (position < 1 || position > 99_999) {
+ throw new IllegalArgumentException("Invalid PDF417 structured append position: " + position);
+ }
+ this.structuredAppendPosition = position;
+ }
+
+ /**
+ * Returns the size of the series of PDF417 symbols using structured append (Macro PDF417) 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;
+ }
+
+ /**
+ * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
+ * (Macro PDF417), this method sets the total number of symbols in the series. Valid values are
+ * 1 through 99,999 inclusive. A value of 1 indicates that this symbol is not part of a structured
+ * append series.
+ *
+ * @param total the total number of PDF417 symbols in the structured append series
+ */
+ public void setStructuredAppendTotal(int total) {
+ if (total < 1 || total > 99_999) {
+ throw new IllegalArgumentException("Invalid PDF417 structured append total: " + total);
+ }
+ this.structuredAppendTotal = total;
+ }
+
+ /**
+ * Returns the unique file ID of the series of PDF417 symbols using structured append (Macro PDF417)
+ * that this symbol is part of. If this symbol is not part of a structured append series, this method
+ * will return 0
.
+ *
+ * @return the unique file ID for the series that this symbol is part of
+ */
+ public int getStructuredAppendFileId() {
+ return structuredAppendFileId;
+ }
+
+ /**
+ * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
+ * (Macro PDF417), this method sets the unique file ID for the series. Valid values are 0 through
+ * 899 inclusive.
+ *
+ * @param fileId the unique file ID for the series that this symbol is part of
+ */
+ public void setStructuredAppendFileId(int fileId) {
+ if (fileId < 0 || fileId > 899) {
+ throw new IllegalArgumentException("Invalid PDF417 structured append file ID: " + fileId);
+ }
+ this.structuredAppendFileId = fileId;
+ }
+
+ public Mode getMode() {
+ return symbolMode;
+ }
+
+ public void setMode(Mode mode) {
+ symbolMode = mode;
+ }
+
+ @Override
+ public boolean encode() {
+
+ eciProcess();
+
+ int sourceLength = inputBytes.length;
+ inputData = new int[sourceLength];
+ for (int i = 0; i < sourceLength; i++) {
+ inputData[i] = inputBytes[i] & 0xFF;
+ }
+
+ boolean ok;
+ switch (symbolMode) {
+ case MICRO:
+ ok = processMicroPdf417();
+ break;
+ case NORMAL:
+ case TRUNCATED:
+ default:
+ ok = processPdf417();
+ break;
+ }
+
+ if (ok) {
+ plotSymbol();
+ }
+
+ return ok;
+ }
+
+ private boolean processPdf417() {
+ int j, loop, offset;
+ int[] mccorrection = new int[520];
+ int total;
+ int c1, c2, c3;
+ int[] dummy = new int[35];
+ StringBuilder codebarre;
+ int selectedECCLevel;
+ String bin;
+
+ List blocks = createBlocks(inputData);
+
+ /* now compress the data */
+ codeWordCount = 0;
+
+ if (readerInit) {
+ codeWords[codeWordCount] = 921; /* Reader Initialisation */
+ codeWordCount++;
+ }
+
+ if (eciMode != 3) {
+ /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */
+ if (eciMode <= 899) {
+ codeWords[codeWordCount] = 927;
+ codeWordCount++;
+ codeWords[codeWordCount] = eciMode;
+ codeWordCount++;
+ }
+
+ if ((eciMode >= 900) && (eciMode <= 810899)) {
+ codeWords[codeWordCount] = 926;
+ codeWordCount++;
+ codeWords[codeWordCount] = (eciMode / 900) - 1;
+ codeWordCount++;
+ codeWords[codeWordCount] = eciMode % 900;
+ codeWordCount++;
+ }
+
+ if ((eciMode >= 810900) && (eciMode <= 811799)) {
+ codeWords[codeWordCount] = 925;
+ codeWordCount++;
+ codeWords[codeWordCount] = eciMode - 810900;
+ codeWordCount++;
+ }
+ }
+
+ int blockCount = 0;
+ for (int i = 0; i < blocks.size(); i++) {
+ Block block = blocks.get(i);
+ switch (block.mode) {
+ case TEX:
+ /* text mode */
+ boolean firstBlock = (i == 0);
+ processText(blockCount, block.length, firstBlock);
+ break;
+ case BYT:
+ /* octet stream mode */
+ EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode);
+ processBytes(blockCount, block.length, lastMode);
+ break;
+ case NUM:
+ /* numeric mode */
+ processNumbers(inputData, blockCount, block.length, false);
+ break;
+ default:
+ throw new IllegalStateException("Unknown block type: " + block.mode);
+ }
+ blockCount += block.length;
+ }
+
+ addMacroCodewords();
+
+ encodeInfo.append("Codewords: ");
+ for (int i = 0; i < codeWordCount; i++) {
+ encodeInfo.append(Integer.toString(codeWords[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* Now take care of the number of CWs per row */
+
+ // if we have to default the ECC level, do so per the
+ // recommendations in the specification (Table E.1)
+ selectedECCLevel = preferredEccLevel;
+ if (selectedECCLevel < 0) {
+ if (codeWordCount <= 40) {
+ selectedECCLevel = 2;
+ } else if (codeWordCount <= 160) {
+ selectedECCLevel = 3;
+ } else if (codeWordCount <= 320) {
+ selectedECCLevel = 4;
+ } else if (codeWordCount <= 863) {
+ selectedECCLevel = 5;
+ } else {
+ selectedECCLevel = 6;
+ }
+ }
+
+ int k = 1 << (selectedECCLevel + 1); // error correction codeword count
+ int dataCodeWordCount = codeWordCount + k + 1; // not including padding
+
+ if (validateRows(3, 90) || validateColumns(1, 30)) {
+ return false;
+ }
+
+ if (columns != null) {
+ if (rows != null) {
+ // user specified both columns and rows; make sure the data fits
+ if (columns * rows < dataCodeWordCount) {
+ errorMsg.append("Too few rows (" + rows + ") and columns (" + columns + ") to hold codewords (" + dataCodeWordCount + ")");
+ return false;
+ }
+ } else {
+ // user only specified column count; figure out row count
+ rows = (int) Math.ceil(dataCodeWordCount / (double) columns);
+ }
+ } else {
+ if (rows != null) {
+ // user only specified row count; figure out column count
+ columns = (int) Math.ceil(dataCodeWordCount / (double) rows);
+ } else {
+ // user didn't specify columns or rows; figure both out
+ columns = (int) (0.5 + Math.sqrt((dataCodeWordCount - 1) / 3.0));
+ rows = (int) Math.ceil(dataCodeWordCount / (double) columns);
+ }
+ }
+
+ if (validateRows(3, 90) || validateColumns(1, 30)) {
+ return false;
+ }
+
+ /* add the padding */
+ int paddingCount = (columns * rows) - codeWordCount - k - 1;
+ while (paddingCount > 0) {
+ codeWords[codeWordCount] = 900;
+ codeWordCount++;
+ paddingCount--;
+ }
+
+ /* add the length descriptor */
+ System.arraycopy(codeWords, 0, codeWords, 1, codeWordCount);
+ codeWordCount++;
+ codeWords[0] = codeWordCount;
+
+ /* 796 - we now take care of the Reed Solomon codes */
+ switch (selectedECCLevel) {
+ case 1:
+ offset = 2;
+ break;
+ case 2:
+ offset = 6;
+ break;
+ case 3:
+ offset = 14;
+ break;
+ case 4:
+ offset = 30;
+ break;
+ case 5:
+ offset = 62;
+ break;
+ case 6:
+ offset = 126;
+ break;
+ case 7:
+ offset = 254;
+ break;
+ case 8:
+ offset = 510;
+ break;
+ default:
+ offset = 0;
+ break;
+ }
+
+ for (loop = 0; loop < 520; loop++) {
+ mccorrection[loop] = 0;
+ }
+
+ for (int i = 0; i < codeWordCount; i++) {
+ total = (codeWords[i] + mccorrection[k - 1]) % 929;
+ for (j = k - 1; j > 0; j--) {
+ mccorrection[j] = (mccorrection[j - 1] + 929 - (total * COEFRS[offset + j]) % 929) % 929;
+ }
+ mccorrection[0] = (929 - (total * COEFRS[offset + j]) % 929) % 929;
+ }
+
+ encodeInfo.append("Data Codewords: ").append(codeWordCount).append("\n");
+ encodeInfo.append("ECC Codewords: ").append(k).append("\n");
+
+ /* we add these codes to the string */
+ for (int i = k - 1; i >= 0; i--) {
+ codeWords[codeWordCount++] = mccorrection[i] != 0 ? 929 - mccorrection[i] : 0;
+ }
+
+ /* make sure total codeword count isn't too high */
+ if (codeWordCount > 929) {
+ errorMsg.append("Too many codewords required (" + codeWordCount + ", but max is 929)");
+ return false;
+ }
+ /* 818 - The CW string is finished */
+ c1 = (rows - 1) / 3;
+ c2 = (selectedECCLevel * 3) + (rows - 1) % 3;
+ c3 = columns - 1;
+
+ readable = new StringBuilder();
+ rowCount = rows;
+ pattern = new String[rows];
+ rowHeight = new int[rows];
+ encodeInfo.append("Grid Size: ").append(columns).append(" X ").append(rows).append("\n");
+
+ /* we now encode each row */
+ for (int i = 0; i < rows; i++) {
+ for (j = 0; j < columns; j++) {
+ dummy[j + 1] = codeWords[i * columns + j];
+ }
+ k = (i / 3) * 30;
+ switch (i % 3) {
+ case 0:
+ offset = 0; // cluster 0
+ dummy[0] = k + c1; // left row indicator
+ dummy[columns + 1] = k + c3; // right row indicator
+ break;
+ case 1:
+ offset = 929; // cluster 3
+ dummy[0] = k + c2; // left row indicator
+ dummy[columns + 1] = k + c1; // right row indicator
+ break;
+ case 2:
+ offset = 1858; // cluster 6
+ dummy[0] = k + c3; // left row indicator
+ dummy[columns + 1] = k + c2; // right row indicator
+ break;
+ }
+ codebarre = new StringBuilder("+*");
+ for (j = 0; j <= columns + 1; j++) {
+ if (!(symbolMode == Mode.TRUNCATED && j > columns)) {
+ codebarre.append(CODAGEMC[offset + dummy[j]]);
+ codebarre.append("*");
+ }
+ }
+ if (symbolMode != Mode.TRUNCATED) {
+ codebarre.append("-");
+ }
+ bin = "";
+ for (j = 0; j < codebarre.length(); j++) {
+ bin += PDF_TTF[positionOf(codebarre.charAt(j), BR_SET)];
+ }
+ pattern[i] = bin2pat(bin);
+ rowHeight[i] = defaultHeight;
+ }
+ return true;
+ }
+
+ private boolean processMicroPdf417() { /* like PDF417 only much smaller! */
+
+ int k, j, longueur, offset;
+ int total;
+ int LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
+ int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop;
+ String codebarre;
+ int[] dummy = new int[5];
+ int[] mccorrection = new int[50];
+ StringBuilder bin;
+
+ /* Encoding starts out the same as PDF417, so use the same code */
+
+ List blocks = createBlocks(inputData);
+
+ /* 541 - now compress the data */
+ codeWordCount = 0;
+ if (readerInit) {
+ codeWords[codeWordCount] = 921; /* Reader Initialisation */
+ codeWordCount++;
+ }
+
+ if (eciMode != 3) {
+ /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */
+ if (eciMode <= 899) {
+ codeWords[codeWordCount] = 927;
+ codeWordCount++;
+ codeWords[codeWordCount] = eciMode;
+ codeWordCount++;
+ }
+
+ if ((eciMode >= 900) && (eciMode <= 810899)) {
+ codeWords[codeWordCount] = 926;
+ codeWordCount++;
+ codeWords[codeWordCount] = (eciMode / 900) - 1;
+ codeWordCount++;
+ codeWords[codeWordCount] = eciMode % 900;
+ codeWordCount++;
+ }
+
+ if ((eciMode >= 810900) && (eciMode <= 811799)) {
+ codeWords[codeWordCount] = 925;
+ codeWordCount++;
+ codeWords[codeWordCount] = eciMode - 810900;
+ codeWordCount++;
+ }
+ }
+
+ int blockCount = 0;
+ for (int i = 0; i < blocks.size(); i++) {
+ Block block = blocks.get(i);
+ switch (block.mode) {
+ case TEX:
+ /* text mode */
+ processText(blockCount, block.length, false); // TODO: this shouldn't always be false?
+ break;
+ case BYT:
+ /* octet stream mode */
+ EncodingMode lastMode = (i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode);
+ processBytes(blockCount, block.length, lastMode);
+ break;
+ case NUM:
+ /* numeric mode */
+ processNumbers(inputData, blockCount, block.length, false);
+ break;
+ default:
+ throw new IllegalStateException("Unknown block type: " + block.mode);
+ }
+ blockCount += block.length;
+ }
+
+ addMacroCodewords();
+
+ encodeInfo.append("Codewords: ");
+ for (int i = 0; i < codeWordCount; i++) {
+ encodeInfo.append(Integer.toString(codeWords[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* This is where it all changes! */
+
+ if (validateRows(4, 44) || validateColumns(1, 4)) {
+ return false;
+ }
+
+ if (columns != null) {
+ int max;
+ switch (columns) {
+ case 1:
+ max = 20;
+ break;
+ case 2:
+ max = 37;
+ break;
+ case 3:
+ max = 82;
+ break;
+ case 4:
+ max = 126;
+ break;
+ default:
+ throw new IllegalStateException("Invalid column count: " + columns);
+ }
+ if (codeWordCount > max) {
+ errorMsg.append("Too few columns (" + columns + ") to hold data codewords (" + codeWordCount + ")");
+ return false;
+ }
+ }
+
+ /* Now figure out which variant of the symbol to use and load values accordingly */
+
+ int variant = getMicroPdf417Variant(codeWordCount, columns, rows);
+
+ /* Now we have the variant we can load the data */
+
+ variant--;
+ columns = MICRO_VARIANTS[variant]; /* columns */
+ rows = MICRO_VARIANTS[variant + 34]; /* rows */
+ k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */
+ longueur = (columns * rows) - k; /* number of non-EC CWs */
+ int padding = longueur - codeWordCount; /* amount of padding required */
+ offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */
+
+ encodeInfo.append("Data Codewords: ").append(longueur).append("\n");
+ encodeInfo.append("ECC Codewords: ").append(k).append("\n");
+
+ /* We add the padding */
+ while (padding > 0) {
+ codeWords[codeWordCount] = 900;
+ codeWordCount++;
+ padding--;
+ }
+
+ /* Reed-Solomon error correction */
+ longueur = codeWordCount;
+ for (loop = 0; loop < 50; loop++) {
+ mccorrection[loop] = 0;
+ }
+
+ for (int i = 0; i < longueur; i++) {
+ total = (codeWords[i] + mccorrection[k - 1]) % 929;
+ for (j = k - 1; j >= 0; j--) {
+ if (j == 0) {
+ mccorrection[j] = (929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929;
+ } else {
+ mccorrection[j] = (mccorrection[j - 1] + 929 - (total * MICRO_COEFFS[offset + j]) % 929) % 929;
+ }
+ }
+ }
+
+ for (j = 0; j < k; j++) {
+ if (mccorrection[j] != 0) {
+ mccorrection[j] = 929 - mccorrection[j];
+ }
+ }
+ /* we add these codes to the string */
+ for (int i = k - 1; i >= 0; i--) {
+ codeWords[codeWordCount] = mccorrection[i];
+ codeWordCount++;
+ }
+
+ /* Now get the RAP (Row Address Pattern) start values */
+ LeftRAPStart = RAP_TABLE[variant];
+ CentreRAPStart = RAP_TABLE[variant + 34];
+ RightRAPStart = RAP_TABLE[variant + 68];
+ StartCluster = RAP_TABLE[variant + 102] / 3;
+
+ /* That's all values loaded, get on with the encoding */
+
+ LeftRAP = LeftRAPStart;
+ CentreRAP = CentreRAPStart;
+ RightRAP = RightRAPStart;
+ Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
+
+ readable = new StringBuilder();
+ pattern = new String[rows];
+ rowCount = rows;
+ rowHeight = new int[rows];
+
+ encodeInfo.append("Grid Size: ").append(columns).append(" X ").append(rowCount).append("\n");
+
+ for (int i = 0; i < rows; i++) {
+ codebarre = "";
+ offset = 929 * Cluster;
+ for (j = 0; j < 5; j++) {
+ dummy[j] = 0;
+ }
+ for (j = 0; j < columns; j++) {
+ dummy[j + 1] = codeWords[i * columns + j];
+ }
+
+ /* Copy the data into codebarre */
+ codebarre += RAPLR[LeftRAP];
+ codebarre += "1";
+ codebarre += CODAGEMC[offset + dummy[1]];
+ codebarre += "1";
+ if (columns == 3) {
+ codebarre += RAPC[CentreRAP];
+ }
+ if (columns >= 2) {
+ codebarre += "1";
+ codebarre += CODAGEMC[offset + dummy[2]];
+ codebarre += "1";
+ }
+ if (columns == 4) {
+ codebarre += RAPC[CentreRAP];
+ }
+ if (columns >= 3) {
+ codebarre += "1";
+ codebarre += CODAGEMC[offset + dummy[3]];
+ codebarre += "1";
+ }
+ if (columns == 4) {
+ codebarre += "1";
+ codebarre += CODAGEMC[offset + dummy[4]];
+ codebarre += "1";
+ }
+ codebarre += RAPLR[RightRAP];
+ codebarre += "1"; /* stop */
+
+ /* Now codebarre is a mixture of letters and numbers */
+
+ flip = 1;
+ bin = new StringBuilder();
+ for (loop = 0; loop < codebarre.length(); loop++) {
+ if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) {
+ for (k = 0; k < Character.getNumericValue(codebarre.charAt(loop)); k++) {
+ if (flip == 0) {
+ bin.append('0');
+ } else {
+ bin.append('1');
+ }
+ }
+ if (flip == 0) {
+ flip = 1;
+ } else {
+ flip = 0;
+ }
+ } else {
+ bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
+ }
+ }
+
+ /* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */
+ pattern[i] = bin2pat(bin.toString());
+ rowHeight[i] = defaultHeight;
+
+ /* Set up RAPs and Cluster for next row */
+ LeftRAP++;
+ CentreRAP++;
+ RightRAP++;
+ Cluster++;
+
+ if (LeftRAP == 53) {
+ LeftRAP = 1;
+ }
+ if (CentreRAP == 53) {
+ CentreRAP = 1;
+ }
+ if (RightRAP == 53) {
+ RightRAP = 1;
+ }
+ if (Cluster == 3) {
+ Cluster = 0;
+ }
+ }
+ return true;
+ }
+
+ private boolean validateRows(int min, int max) {
+ if (rows != null) {
+ if (rows < min) {
+ errorMsg.append("Too few rows (").append(rows).append(")");
+ return true;
+ } else if (rows > max) {
+ errorMsg.append("Too many rows (").append(rows).append(")");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean validateColumns(int min, int max) {
+ if (columns != null) {
+ if (columns < min) {
+ errorMsg.append("Too few columns (").append(columns).append(")");
+ return true;
+ } else if (columns > max) {
+ errorMsg.append("Too many columns (").append(columns).append(")");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void processText(int start, int length, boolean skipLatch) {
+ int j, blockIndext, curtable, wnet;
+ int codeascii;
+ int[] listet0 = new int[5000];
+ int[] listet1 = new int[5000];
+ int[] chainet = new int[5000];
+
+ wnet = 0;
+
+ for (j = 0; j < 1000; j++) {
+ listet0[j] = 0;
+ }
+ /* listet will contain the table numbers and the value of each characters */
+ for (blockIndext = 0; blockIndext < length; blockIndext++) {
+ codeascii = inputData[start + blockIndext];
+ switch (codeascii) {
+ case '\t':
+ listet0[blockIndext] = 12;
+ listet1[blockIndext] = 12;
+ break;
+ case '\n':
+ listet0[blockIndext] = 8;
+ listet1[blockIndext] = 15;
+ break;
+ case 13:
+ listet0[blockIndext] = 12;
+ listet1[blockIndext] = 11;
+ break;
+ default:
+ listet0[blockIndext] = ASCII_X[codeascii - 32];
+ listet1[blockIndext] = ASCII_Y[codeascii - 32];
+ break;
+ }
+ }
+
+ curtable = 1; /* default table */
+ for (j = 0; j < length; j++) {
+ if ((listet0[j] & curtable) != 0) { /* The character is in the current table */
+ chainet[wnet] = listet1[j];
+ wnet++;
+ } else { /* Obliged to change table */
+ boolean flag = false; /* True if we change table for only one character */
+ if (j == (length - 1)) {
+ flag = true;
+ } else {
+ if ((listet0[j] & listet0[j + 1]) == 0) {
+ flag = true;
+ }
+ }
+
+ if (flag) { /* we change only one character - look for temporary switch */
+ if (((listet0[j] & 1) != 0) && (curtable == 2)) { /* T_UPP */
+ chainet[wnet] = 27;
+ chainet[wnet + 1] = listet1[j];
+ wnet += 2;
+ }
+ if ((listet0[j] & 8) != 0) { /* T_PUN */
+ chainet[wnet] = 29;
+ chainet[wnet + 1] = listet1[j];
+ wnet += 2;
+ }
+ if (!((((listet0[j] & 1) != 0) && (curtable == 2)) || ((listet0[j] & 8) != 0))) {
+ /* No temporary switch available */
+ flag = false;
+ }
+ }
+
+ if (!(flag)) {
+ int newtable;
+
+ if (j == (length - 1)) {
+ newtable = listet0[j];
+ } else {
+ if ((listet0[j] & listet0[j + 1]) == 0) {
+ newtable = listet0[j];
+ } else {
+ newtable = listet0[j] & listet0[j + 1];
+ }
+ }
+
+ /* Maintain the first if several tables are possible */
+ switch (newtable) {
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ case 11:
+ case 13:
+ case 15:
+ newtable = 1;
+ break;
+ case 6:
+ case 10:
+ case 14:
+ newtable = 2;
+ break;
+ case 12:
+ newtable = 4;
+ break;
+ }
+
+ /* select the switch */
+ switch (curtable) {
+ case 1:
+ switch (newtable) {
+ case 2:
+ chainet[wnet] = 27;
+ wnet++;
+ break;
+ case 4:
+ chainet[wnet] = 28;
+ wnet++;
+ break;
+ case 8:
+ chainet[wnet] = 28;
+ wnet++;
+ chainet[wnet] = 25;
+ wnet++;
+ break;
+ }
+ break;
+ case 2:
+ switch (newtable) {
+ case 1:
+ chainet[wnet] = 28;
+ wnet++;
+ chainet[wnet] = 28;
+ wnet++;
+ break;
+ case 4:
+ chainet[wnet] = 28;
+ wnet++;
+ break;
+ case 8:
+ chainet[wnet] = 28;
+ wnet++;
+ chainet[wnet] = 25;
+ wnet++;
+ break;
+ }
+ break;
+ case 4:
+ switch (newtable) {
+ case 1:
+ chainet[wnet] = 28;
+ wnet++;
+ break;
+ case 2:
+ chainet[wnet] = 27;
+ wnet++;
+ break;
+ case 8:
+ chainet[wnet] = 25;
+ wnet++;
+ break;
+ }
+ break;
+ case 8:
+ switch (newtable) {
+ case 1:
+ chainet[wnet] = 29;
+ wnet++;
+ break;
+ case 2:
+ chainet[wnet] = 29;
+ wnet++;
+ chainet[wnet] = 27;
+ wnet++;
+ break;
+ case 4:
+ chainet[wnet] = 29;
+ wnet++;
+ chainet[wnet] = 28;
+ wnet++;
+ break;
+ }
+ break;
+ }
+ curtable = newtable;
+ /* at last we add the character */
+ chainet[wnet] = listet1[j];
+ wnet++;
+ }
+ }
+ }
+
+ if ((wnet & 1) != 0) {
+ chainet[wnet] = 29;
+ wnet++;
+ }
+
+ /* Now translate the string chainet into codewords */
+
+ if (!skipLatch) {
+ // text compaction mode is the default mode for PDF417,
+ // so no need for an explicit latch if this is the first block
+ codeWords[codeWordCount] = 900;
+ codeWordCount++;
+ }
+
+ for (j = 0; j < wnet; j += 2) {
+ int cw_number;
+
+ cw_number = (30 * chainet[j]) + chainet[j + 1];
+ codeWords[codeWordCount] = cw_number;
+ codeWordCount++;
+ }
+ }
+
+ private void processBytes(int start, int length, EncodingMode lastMode) {
+ int len = 0;
+ int chunkLen = 0;
+ BigInteger mantisa;
+ BigInteger total;
+ BigInteger word;
+
+ mantisa = new BigInteger("0");
+ total = new BigInteger("0");
+
+ if (length == 1 && lastMode == EncodingMode.TEX) {
+ codeWords[codeWordCount++] = 913;
+ codeWords[codeWordCount++] = inputData[start];
+ } else {
+ /* select the switch for multiple of 6 bytes */
+ if (length % 6 == 0) {
+ codeWords[codeWordCount++] = 924;
+ } else {
+ codeWords[codeWordCount++] = 901;
+ }
+
+ while (len < length) {
+ chunkLen = length - len;
+ if (6 <= chunkLen) /* Take groups of 6 */ {
+ chunkLen = 6;
+ len += chunkLen;
+ total = BigInteger.valueOf(0);
+
+ while ((chunkLen--) != 0) {
+ mantisa = BigInteger.valueOf(inputData[start++]);
+ total = total.or(mantisa.shiftLeft(chunkLen * 8));
+ }
+
+ chunkLen = 5;
+
+ while ((chunkLen--) != 0) {
+
+ word = total.mod(BigInteger.valueOf(900));
+ codeWords[codeWordCount + chunkLen] = word.intValue();
+ total = total.divide(BigInteger.valueOf(900));
+ }
+ codeWordCount += 5;
+ } else /* If it remain a group of less than 6 bytes */ {
+ len += chunkLen;
+ while ((chunkLen--) != 0) {
+ codeWords[codeWordCount++] = inputData[start++];
+ }
+ }
+ }
+ }
+ }
+
+ private void processNumbers(int[] data, int start, int length, boolean skipLatch) {
+
+ BigInteger tVal, dVal;
+ int[] d = new int[16];
+ int cw_count;
+
+ if (!skipLatch) {
+ // we don't need to latch to numeric mode in some cases, e.g.
+ // during numeric compaction of the Macro PDF417 segment index
+ codeWords[codeWordCount++] = 902;
+ }
+
+ StringBuilder t = new StringBuilder(length + 1);
+ t.append('1');
+ for (int i = 0; i < length; i++) {
+ t.append((char) data[start + i]);
+ }
+
+ tVal = new BigInteger(t.toString());
+
+ cw_count = 0;
+ do {
+ dVal = tVal.mod(BigInteger.valueOf(900));
+ d[cw_count] = dVal.intValue();
+ tVal = tVal.divide(BigInteger.valueOf(900));
+ cw_count++;
+ } while (tVal.compareTo(BigInteger.ZERO) == 1);
+
+ for (int i = cw_count - 1; i >= 0; i--) {
+ codeWords[codeWordCount++] = d[i];
+ }
+ }
+
+ /**
+ * Adds the Macro PDF417 control block codewords (if any).
+ */
+ private void addMacroCodewords() {
+
+ // if the structured append series size is 1, this isn't
+ // actually part of a structured append series
+ if (structuredAppendTotal == 1) {
+ return;
+ }
+
+ // add the Macro marker codeword
+ codeWords[codeWordCount++] = 928;
+
+ // add the segment index, padded with leading zeros to five digits
+ // use numeric compaction, but no latch
+ int segmentIndex = structuredAppendPosition - 1;
+ int[] data = new int[5];
+ for (int x = data.length - 1; x >= 0; x--) {
+ data[x] = '0' + (segmentIndex % 10);
+ segmentIndex /= 10;
+ }
+ processNumbers(data, 0, data.length, true);
+
+ // add the file ID (base 900, which is easy since we limit
+ // file ID values to the range 0 to 899)
+ codeWords[codeWordCount++] = structuredAppendFileId;
+
+ // NOTE: we could add the optional segment count field here, but
+ // it doesn't appear to be necessary... if we do eventually decide
+ // to add it, it will probably be [923, 001, count1, count2]
+
+ // add the terminator to the last symbol of the series
+ boolean last = (structuredAppendPosition == structuredAppendTotal);
+ if (last) {
+ codeWords[codeWordCount++] = 922;
+ }
+ }
+
+ public enum Mode {
+ /**
+ * Normal PDF417.
+ */
+ NORMAL,
+ /**
+ * Truncated PDF417.
+ */
+ TRUNCATED,
+ /**
+ * MicroPDF417.
+ */
+ MICRO
+ }
+
+ private enum EncodingMode {
+ FALSE, TEX, BYT, NUM
+ }
+
+ private static class Block {
+
+ public EncodingMode mode;
+ public int length;
+
+ public Block(EncodingMode mode) {
+ this.mode = mode;
+ this.length = 1;
+ }
+
+ @Override
+ public String toString() {
+ return mode + "x" + length;
+ }
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java
new file mode 100755
index 0000000..573490a
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java
@@ -0,0 +1,66 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Implements the Pharmacode
+ * bar code symbology.
+ * Pharmacode is used for the identification of pharmaceuticals. The symbology
+ * is able to encode whole numbers between 3 and 131070.
+ */
+public class Pharmacode extends Symbol {
+
+ @Override
+ public boolean encode() {
+ int tester = 0;
+ int i;
+
+ StringBuilder inter = new StringBuilder();
+ StringBuilder dest = new StringBuilder();
+
+ if (content.length() > 6) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ for (i = 0; i < content.length(); i++) {
+ tester *= 10;
+ tester += Character.getNumericValue(content.charAt(i));
+ }
+
+ if ((tester < 3) || (tester > 131070)) {
+ errorMsg.append("Data out of range");
+ return false;
+ }
+
+ do {
+ if ((tester & 1) == 0) {
+ inter.append("W");
+ tester = (tester - 2) / 2;
+ } else {
+ inter.append("N");
+ tester = (tester - 1) / 2;
+ }
+ } while (tester != 0);
+
+ for (i = inter.length() - 1; i >= 0; i--) {
+ if (inter.charAt(i) == 'W') {
+ dest.append("32");
+ } else {
+ dest.append("12");
+ }
+ }
+
+ readable = new StringBuilder();
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java
new file mode 100755
index 0000000..6be57f6
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java
@@ -0,0 +1,106 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements the Two-Track Pharmacode bar code symbology.
+ * Pharmacode Two-Track is an alternative system to Pharmacode One-Track used
+ * for the identification of pharmaceuticals. The symbology is able to encode
+ * whole numbers between 4 and 64570080.
+ */
+public class Pharmacode2Track extends Symbol {
+
+ @Override
+ public boolean encode() {
+ int i, tester = 0;
+ StringBuilder inter;
+ StringBuilder dest;
+
+ if (content.length() > 8) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ for (i = 0; i < content.length(); i++) {
+ tester *= 10;
+ tester += Character.getNumericValue(content.charAt(i));
+ }
+
+ if ((tester < 4) || (tester > 64570080)) {
+ errorMsg.append("Data out of range");
+ return false;
+ }
+
+ inter = new StringBuilder();
+ do {
+ switch (tester % 3) {
+ case 0:
+ inter.append("F");
+ tester = (tester - 3) / 3;
+ break;
+ case 1:
+ inter.append("D");
+ tester = (tester - 1) / 3;
+ break;
+ case 2:
+ inter.append("A");
+ tester = (tester - 2) / 3;
+ break;
+ }
+ }
+ while (tester != 0);
+
+ dest = new StringBuilder();
+ for (i = (inter.length() - 1); i >= 0; i--) {
+ dest.append(inter.charAt(i));
+ }
+
+ encodeInfo.append("Encoding: ").append(dest).append("\n");
+
+ readable = new StringBuilder();
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ getRectangles().clear();
+ x = 0;
+ w = 1;
+ y = 0;
+ h = 0;
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ switch (pattern[0].charAt(xBlock)) {
+ case 'A':
+ y = 0;
+ h = defaultHeight / 2;
+ break;
+ case 'D':
+ y = defaultHeight / 2;
+ h = defaultHeight / 2;
+ break;
+ case 'F':
+ y = 0;
+ h = defaultHeight;
+ break;
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ x += 2;
+ }
+ symbolWidth = pattern[0].length() * 2;
+ symbolHeight = defaultHeight;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java
new file mode 100755
index 0000000..6e3bcad
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java
@@ -0,0 +1,69 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * PZN8 is a Code 39 based symbology used by the pharmaceutical industry in
+ * Germany. PZN8 encodes a 7 digit number and includes a modulo-10 check digit.
+ */
+public class Pharmazentralnummer extends Symbol {
+
+ /* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra
+ * check digit. Now generates PZN-8.
+ */
+ @Override
+ public boolean encode() {
+ int l = content.length();
+ StringBuilder localstr;
+ int zeroes, count = 0, check_digit;
+ Code3Of9 c = new Code3Of9();
+
+ if (l > 7) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ localstr = new StringBuilder("-");
+ zeroes = 7 - l + 1;
+ for (int i = 1; i < zeroes; i++)
+ localstr.append('0');
+
+ localstr.append(content);
+
+ for (int i = 1; i < 8; i++) {
+ count += i * Character.getNumericValue(localstr.charAt(i));
+ }
+
+ check_digit = count % 11;
+ if (check_digit == 11) {
+ check_digit = 0;
+ }
+ if (check_digit == 10) {
+ errorMsg.append("Not a valid PZN identifier");
+ return false;
+ }
+
+ encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
+
+ localstr.append((char) (check_digit + '0'));
+
+ try {
+ c.setContent(localstr.toString());
+ } catch (Exception e) {
+ errorMsg.append(e.getMessage());
+ return false;
+ }
+
+ readable = new StringBuilder("PZN").append(localstr);
+ pattern = new String[1];
+ pattern[0] = c.pattern[0];
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java b/barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java
new file mode 100755
index 0000000..eade347
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java
@@ -0,0 +1,185 @@
+package org.xbib.graphics.barcode;
+
+import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
+import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements POSTNET and
+ * PLANET
+ * bar code symbologies.
+ * POSTNET and PLANET both use numerical input data and include a modulo-10
+ * check digit.
+ */
+public class Postnet extends Symbol {
+
+ private static final String[] PN_TABLE = {
+ "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS"
+ };
+
+ ;
+ private static final String[] PL_TABLE = {
+ "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL"
+ };
+ private Mode mode;
+
+ public Postnet() {
+ this.mode = Mode.POSTNET;
+ this.defaultHeight = 12;
+ setHumanReadableLocation(HumanReadableLocation.NONE);
+ }
+
+ /**
+ * Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET.
+ *
+ * @return the barcode mode (PLANET or POSTNET)
+ */
+ public Mode getMode() {
+ return mode;
+ }
+
+ /**
+ * Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET.
+ *
+ * @param mode the barcode mode (PLANET or POSTNET)
+ */
+ public void setMode(Mode mode) {
+ this.mode = mode;
+ }
+
+ @Override
+ public boolean encode() {
+
+ boolean retval;
+ if (mode == Mode.POSTNET) {
+ retval = makePostnet();
+ } else {
+ retval = makePlanet();
+ }
+
+ if (retval) {
+ plotSymbol();
+ }
+
+ return retval;
+ }
+
+ private boolean makePostnet() {
+ int i, sum, check_digit;
+ StringBuilder dest;
+
+ if (content.length() > 38) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ sum = 0;
+ dest = new StringBuilder("L");
+
+ for (i = 0; i < content.length(); i++) {
+ dest.append(PN_TABLE[content.charAt(i) - '0']);
+ sum += content.charAt(i) - '0';
+ }
+
+ check_digit = (10 - (sum % 10)) % 10;
+ encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
+
+ dest.append(PN_TABLE[check_digit]);
+ dest.append("L");
+
+ encodeInfo.append("Encoding: ").append(dest).append("\n");
+ readable = new StringBuilder(content);
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+
+ return true;
+ }
+
+ private boolean makePlanet() {
+ int i, sum, check_digit;
+ StringBuilder dest;
+
+ if (content.length() > 38) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+
+ sum = 0;
+ dest = new StringBuilder("L");
+
+ for (i = 0; i < content.length(); i++) {
+ dest.append(PL_TABLE[content.charAt(i) - '0']);
+ sum += content.charAt(i) - '0';
+ }
+
+ check_digit = (10 - (sum % 10)) % 10;
+ encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
+
+ dest.append(PL_TABLE[check_digit]);
+ dest.append("L");
+
+ encodeInfo.append("Encoding: ").append(dest).append("\n");
+ readable = new StringBuilder(content);
+ pattern = new String[]{dest.toString()};
+ rowCount = 1;
+ rowHeight = new int[]{-1};
+
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock, shortHeight;
+ double x, y, w, h;
+ getRectangles().clear();
+ getTexts().clear();
+ int baseY;
+ if (getHumanReadableLocation() == TOP) {
+ baseY = getTheoreticalHumanReadableHeight();
+ } else {
+ baseY = 0;
+ }
+ x = 0;
+ w = moduleWidth;
+ shortHeight = (int) (0.4 * defaultHeight);
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ if (pattern[0].charAt(xBlock) == 'L') {
+ y = baseY;
+ h = defaultHeight;
+ } else {
+ y = baseY + defaultHeight - shortHeight;
+ h = shortHeight;
+ }
+ getRectangles().add(new Rectangle2D.Double(x, y, w, h));
+ x += (2.5 * w);
+ }
+ symbolWidth = (int) Math.ceil(((pattern[0].length() - 1) * 2.5 * w) + w); // final bar doesn't need extra whitespace
+ symbolHeight = defaultHeight;
+ if (getHumanReadableLocation() != NONE && readable.length() > 0) {
+ double baseline;
+ if (getHumanReadableLocation() == TOP) {
+ baseline = fontSize;
+ } else {
+ baseline = getHeight() + fontSize;
+ }
+ double centerX = getWidth() / 2.0;
+ getTexts().add(new TextBox(centerX, baseline, readable.toString()));
+ }
+ }
+
+ public enum Mode {
+ PLANET, POSTNET
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java
new file mode 100755
index 0000000..a528ddf
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java
@@ -0,0 +1,2008 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.ReedSolomon;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Implements QR Code bar code symbology According to ISO/IEC 18004:2015
+ * The maximum capacity of a (version 40) QR Code symbol is 7089 numeric digits,
+ * 4296 alphanumeric characters or 2953 bytes of data. QR Code symbols can also
+ * be used to encode GS1 data. QR Code symbols can encode characters in the
+ * Latin-1 set and Kanji characters which are members of the Shift-JIS encoding
+ * scheme.
+ */
+public class QrCode extends Symbol {
+
+ /* Table 5 - Encoding/Decoding table for Alphanumeric mode */
+ private final char[] rhodium = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
+ 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'
+ };
+ private final int[] qr_data_codewords_L = {
+ 19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647,
+ 721, 795, 861, 932, 1006, 1094, 1174, 1276, 1370, 1468, 1531, 1631,
+ 1735, 1843, 1955, 2071, 2191, 2306, 2434, 2566, 2702, 2812, 2956
+ };
+ private final int[] qr_data_codewords_M = {
+ 16, 28, 44, 64, 86, 108, 124, 154, 182, 216, 254, 290, 334, 365, 415, 453, 507,
+ 563, 627, 669, 714, 782, 860, 914, 1000, 1062, 1128, 1193, 1267,
+ 1373, 1455, 1541, 1631, 1725, 1812, 1914, 1992, 2102, 2216, 2334
+ };
+ private final int[] qr_data_codewords_Q = {
+ 13, 22, 34, 48, 62, 76, 88, 110, 132, 154, 180, 206, 244, 261, 295, 325, 367,
+ 397, 445, 485, 512, 568, 614, 664, 718, 754, 808, 871, 911,
+ 985, 1033, 1115, 1171, 1231, 1286, 1354, 1426, 1502, 1582, 1666
+ };
+ private final int[] qr_data_codewords_H = {
+ 9, 16, 26, 36, 46, 60, 66, 86, 100, 122, 140, 158, 180, 197, 223, 253, 283,
+ 313, 341, 385, 406, 442, 464, 514, 538, 596, 628, 661, 701,
+ 745, 793, 845, 901, 961, 986, 1054, 1096, 1142, 1222, 1276
+ };
+ private final int[] qr_blocks_L = {
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12,
+ 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25
+ };
+ private final int[] qr_blocks_M = {
+ 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20,
+ 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49
+ };
+ private final int[] qr_blocks_Q = {
+ 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25,
+ 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68
+ };
+ private final int[] qr_blocks_H = {
+ 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30,
+ 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81
+ };
+ private final int[] qr_total_codewords = {
+ 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, 532, 581, 655, 733, 815,
+ 901, 991, 1085, 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051,
+ 2185, 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706
+ };
+ private final int[] qr_sizes = {
+ 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97,
+ 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177
+ };
+ private final int[] qr_align_loopsize = {
+ 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7
+ };
+ private final int[] qr_table_e1 = {
+ 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
+ };
+ private final int[] qr_annex_c = {
+ /* Format information bit sequences */
+ 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, 0x77c4, 0x72f3, 0x7daa, 0x789d,
+ 0x662f, 0x6318, 0x6c41, 0x6976, 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b,
+ 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed
+ };
+ private final long[] qr_annex_d = {
+ /* Version information bit sequences */
+ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78,
+ 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab,
+ 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b,
+ 0x2542e, 0x26a64, 0x27541, 0x28c69
+ };
+ private qrMode[] inputMode;
+ private StringBuilder binary;
+ private int[] datastream;
+ private int[] fullstream;
+ private int[] inputData;
+ private byte[] grid;
+ private byte[] eval;
+ private int preferredVersion = 0;
+ private int inputLength;
+ private EccMode preferredEccLevel = EccMode.L;
+
+ /**
+ * Sets the preferred symbol size. This value may be ignored if the data
+ * string is too large to fit into the specified symbol. Input values
+ * correspond to symbol sizes as shown in the following table.
+ * Available QR Code sizes
+ *
+ *
+ *
+ * Input
+ *
+ * Symbol Size
+ *
+ * Input
+ *
+ * Symbol Size
+ *
+ *
+ *
+ * 1
+ *
+ * 21 x 21
+ *
+ * 21
+ *
+ * 101 x 101
+ *
+ *
+ *
+ * 2
+ *
+ * 25 x 25
+ *
+ * 22
+ *
+ * 105 x 105
+ *
+ *
+ *
+ * 3
+ *
+ * 29 x 29
+ *
+ * 23
+ *
+ * 109 x 109
+ *
+ *
+ *
+ * 4
+ *
+ * 33 x 33
+ *
+ * 24
+ *
+ * 113 x 113
+ *
+ *
+ *
+ * 5
+ *
+ * 37 x 37
+ *
+ * 25
+ *
+ * 117 x 117
+ *
+ *
+ *
+ * 6
+ *
+ * 41 x 41
+ *
+ * 26
+ *
+ * 121 x 121
+ *
+ *
+ *
+ * 7
+ *
+ * 45 x 45
+ *
+ * 27
+ *
+ * 125 x 125
+ *
+ *
+ *
+ * 8
+ *
+ * 49 x 49
+ *
+ * 28
+ *
+ * 129 x 129
+ *
+ *
+ *
+ * 9
+ *
+ * 53 x 53
+ *
+ * 29
+ *
+ * 133 x 133
+ *
+ *
+ *
+ * 10
+ *
+ * 57 x 57
+ *
+ * 30
+ *
+ * 137 x 137
+ *
+ *
+ *
+ * 11
+ *
+ * 61 x 61
+ *
+ * 31
+ *
+ * 141 x 141
+ *
+ *
+ *
+ * 12
+ *
+ * 65 x 65
+ *
+ * 32
+ *
+ * 145 x 145
+ *
+ *
+ *
+ * 13
+ *
+ * 69 x 69
+ *
+ * 33
+ *
+ * 149 x 149
+ *
+ *
+ *
+ * 14
+ *
+ * 73 x 73
+ *
+ * 34
+ *
+ * 153 x 153
+ *
+ *
+ *
+ * 15
+ *
+ * 77 x 77
+ *
+ * 35
+ *
+ * 157 x 157
+ *
+ *
+ *
+ * 16
+ *
+ * 81 x 81
+ *
+ * 36
+ *
+ * 161 x 161
+ *
+ *
+ *
+ * 17
+ *
+ * 85 x 85
+ *
+ * 37
+ *
+ * 165 x 165
+ *
+ *
+ *
+ * 18
+ *
+ * 89 x 89
+ *
+ * 38
+ *
+ * 169 x 169
+ *
+ *
+ *
+ * 19
+ *
+ * 93 x 93
+ *
+ * 39
+ *
+ * 173 x 173
+ *
+ *
+ *
+ * 20
+ *
+ * 97 x 97
+ *
+ * 40
+ *
+ * 177 x 177
+ *
+ *
+ *
+ *
+ * @param version Symbol version number required
+ */
+ public void setPreferredVersion(int version) {
+ preferredVersion = version;
+ }
+
+ /**
+ * Set the amount of symbol space allocated to error correction. Levels are
+ * predefined according to the following table:
+ * QR Code error correction levels
+ *
+ *
+ * ECC Level
+ * Error Correction Capacity
+ * Recovery Capacity
+ *
+ *
+ * L (default)
+ * Approx 20% of symbol
+ * Approx 7%
+ *
+ *
+ * M
+ * Approx 37% of symbol
+ * Approx 15%
+ *
+ *
+ * Q
+ * Approx 55% of symbol
+ * Approx 25%
+ *
+ *
+ * H
+ * Approx 65% of symbol
+ * Approx 30%
+ *
+ *
+ *
+ *
+ * @param eccMode Error correction level
+ */
+ public void setEccMode(EccMode eccMode) {
+ preferredEccLevel = eccMode;
+ }
+
+ @Override
+ public boolean encode() {
+ int i, j;
+ int est_binlen;
+ EccMode ecc_level;
+ int max_cw;
+ int autosize;
+ int targetCwCount, version, blocks;
+ int size;
+ int bitmask;
+ StringBuilder bin;
+ boolean canShrink;
+
+ /* This code uses modeFirstFix to make an estimate of the symbol size
+ needed to contain the data. It then optimises the encoding and
+ checks to see if the the data will fit in a smaller
+ symbol, recalculating the binary length if necessary.
+ */
+ inputMode = new qrMode[content.length()];
+ modeFirstFix();
+ est_binlen = estimate_binary_length();
+
+ ecc_level = preferredEccLevel;
+ switch (preferredEccLevel) {
+ case L:
+ max_cw = 2956;
+ break;
+ case M:
+ max_cw = 2334;
+ break;
+ case Q:
+ max_cw = 1666;
+ break;
+ case H:
+ max_cw = 1276;
+ break;
+ default:
+ max_cw = 2956;
+ break;
+ }
+
+ autosize = 40;
+ for (i = 39; i >= 0; i--) {
+ switch (ecc_level) {
+ case L:
+ if ((8 * qr_data_codewords_L[i]) >= est_binlen) {
+ autosize = i + 1;
+ }
+ break;
+ case M:
+ if ((8 * qr_data_codewords_M[i]) >= est_binlen) {
+ autosize = i + 1;
+ }
+ break;
+ case Q:
+ if ((8 * qr_data_codewords_Q[i]) >= est_binlen) {
+ autosize = i + 1;
+ }
+ break;
+ case H:
+ if ((8 * qr_data_codewords_H[i]) >= est_binlen) {
+ autosize = i + 1;
+ }
+ break;
+ }
+ }
+
+ // The first guess of symbol size is in autosize. Use this to optimise.
+ est_binlen = getBinaryLength(autosize);
+
+ if (est_binlen > (8 * max_cw)) {
+ errorMsg.append("Input too long for selected error correction level");
+ return false;
+ }
+
+ // Now see if the optimised binary will fit in a smaller symbol.
+ canShrink = true;
+
+ do {
+ if (autosize == 1) {
+ canShrink = false;
+ } else {
+ if (tribus(autosize - 1, 1, 2, 3) != tribus(autosize, 1, 2, 3)) {
+ // Length of binary needed to encode the data in the smaller symbol is different, recalculate
+ est_binlen = getBinaryLength(autosize - 1);
+ }
+
+ switch (ecc_level) {
+ case L:
+ if ((8 * qr_data_codewords_L[autosize - 2]) < est_binlen) {
+ canShrink = false;
+ }
+ break;
+ case M:
+ if ((8 * qr_data_codewords_M[autosize - 2]) < est_binlen) {
+ canShrink = false;
+ }
+ break;
+ case Q:
+ if ((8 * qr_data_codewords_Q[autosize - 2]) < est_binlen) {
+ canShrink = false;
+ }
+ break;
+ case H:
+ if ((8 * qr_data_codewords_H[autosize - 2]) < est_binlen) {
+ canShrink = false;
+ }
+ break;
+ }
+
+ if (canShrink) {
+ // Optimisation worked - data will fit in a smaller symbol
+ autosize--;
+ } else {
+ // Data did not fit in the smaller symbol, revert to original size
+ if (tribus(autosize - 1, 1, 2, 3) != tribus(autosize, 1, 2, 3)) {
+ est_binlen = getBinaryLength(autosize);
+ }
+ }
+ }
+ } while (canShrink);
+
+ version = autosize;
+
+ if ((preferredVersion >= 1) && (preferredVersion <= 40)) {
+ /* If the user has selected a larger symbol than the smallest available,
+ then use the size the user has selected, and re-optimise for this
+ symbol size.
+ */
+ if (preferredVersion > version) {
+ version = preferredVersion;
+ est_binlen = getBinaryLength(preferredVersion);
+ }
+ }
+
+ /* Ensure maxium error correction capacity */
+ if (est_binlen <= (qr_data_codewords_M[version - 1] * 8)) {
+ ecc_level = EccMode.M;
+ }
+ if (est_binlen <= (qr_data_codewords_Q[version - 1] * 8)) {
+ ecc_level = EccMode.Q;
+ }
+ if (est_binlen <= (qr_data_codewords_H[version - 1] * 8)) {
+ ecc_level = EccMode.H;
+ }
+
+ targetCwCount = qr_data_codewords_L[version - 1];
+ blocks = qr_blocks_L[version - 1];
+ switch (ecc_level) {
+ case M:
+ targetCwCount = qr_data_codewords_M[version - 1];
+ blocks = qr_blocks_M[version - 1];
+ break;
+ case Q:
+ targetCwCount = qr_data_codewords_Q[version - 1];
+ blocks = qr_blocks_Q[version - 1];
+ break;
+ case H:
+ targetCwCount = qr_data_codewords_H[version - 1];
+ blocks = qr_blocks_H[version - 1];
+ break;
+ }
+
+ datastream = new int[targetCwCount + 1];
+ fullstream = new int[qr_total_codewords[version - 1] + 1];
+
+ if (!(qr_binary(version, targetCwCount))) {
+ /* Invalid characters used - stop encoding */
+ return false;
+ }
+
+ add_ecc(version, targetCwCount, blocks);
+
+ size = qr_sizes[version - 1];
+
+ grid = new byte[size * size];
+
+ encodeInfo.append("Version: ").append(version).append("\n");
+ encodeInfo.append("ECC Level: ");
+ switch (ecc_level) {
+ case L:
+ encodeInfo.append("L\n");
+ break;
+ case M:
+ encodeInfo.append("M\n");
+ break;
+ case Q:
+ encodeInfo.append("Q\n");
+ break;
+ case H:
+ default:
+ encodeInfo.append("H\n");
+ break;
+ }
+
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < size; j++) {
+ grid[(i * size) + j] = 0;
+ }
+ }
+
+ setup_grid(size, version);
+ populate_grid(size, qr_total_codewords[version - 1]);
+ bitmask = apply_bitmask(size, ecc_level);
+ encodeInfo.append("Mask Pattern: ").append(Integer.toBinaryString(bitmask)).append("\n");
+ add_format_info(size, ecc_level, bitmask);
+ if (version >= 7) {
+ add_version_info(size, version);
+ }
+
+ readable = new StringBuilder();
+ pattern = new String[size];
+ rowCount = size;
+ rowHeight = new int[size];
+ for (i = 0; i < size; i++) {
+ bin = new StringBuilder();
+ for (j = 0; j < size; j++) {
+ if ((grid[(i * size) + j] & 0x01) != 0) {
+ bin.append("1");
+ } else {
+ bin.append("0");
+ }
+ }
+ pattern[i] = bin2pat(bin.toString());
+ rowHeight[i] = 1;
+ }
+
+ plotSymbol();
+ return true;
+ }
+
+ private void modeFirstFix() {
+ int i;
+
+ eciProcess(); // Get ECI mode
+
+ if (eciMode == 20) {
+ /* Shift-JIS encoding, use Kanji mode */
+ inputLength = content.length();
+ inputData = new int[inputLength];
+ for (i = 0; i < inputLength; i++) {
+ inputData[i] = (int) content.charAt(i);
+ }
+ } else {
+ /* Any other encoding method */
+ inputLength = inputBytes.length;
+ inputData = new int[inputLength];
+ for (i = 0; i < inputLength; i++) {
+ inputData[i] = inputBytes[i] & 0xFF;
+ }
+ }
+
+ inputMode = new qrMode[inputLength];
+
+ for (i = 0; i < inputLength; i++) {
+ if (inputData[i] > 0xff) {
+ inputMode[i] = qrMode.KANJI;
+ } else {
+ inputMode[i] = qrMode.BINARY;
+ if (isXAlpha((char) (inputData[i] & 0xFF))) {
+ inputMode[i] = qrMode.ALPHANUM;
+ }
+ if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) {
+ inputMode[i] = qrMode.ALPHANUM;
+ }
+ if (isXNumeric((char) (inputData[i] & 0xff))) {
+ inputMode[i] = qrMode.NUMERIC;
+ }
+ }
+ }
+ }
+
+ private int getBinaryLength(int version) {
+ /* Calculate the actial bitlength of the proposed binary string */
+ qrMode currentMode;
+ int i, j;
+ int count = 0;
+
+ applyOptimisation(version);
+
+ currentMode = qrMode.NULL;
+
+ if (eciMode != 3) {
+ count += 12;
+ }
+
+ if (inputDataType == DataType.GS1) {
+ count += 4;
+ }
+
+ for (i = 0; i < inputLength; i++) {
+ if (inputMode[i] != currentMode) {
+ count += 4;
+ switch (inputMode[i]) {
+ case KANJI:
+ count += tribus(version, 8, 10, 12);
+ count += (blockLength(i) * 13);
+ break;
+ case BINARY:
+ count += tribus(version, 8, 16, 16);
+ for (j = i; j < (i + blockLength(i)); j++) {
+ if (inputData[j] > 0xff) {
+ count += 16;
+ } else {
+ count += 8;
+ }
+ }
+ break;
+ case ALPHANUM:
+ count += tribus(version, 9, 11, 13);
+ switch (blockLength(i) % 2) {
+ case 0:
+ count += (blockLength(i) / 2) * 11;
+ break;
+ case 1:
+ count += ((blockLength(i) - 1) / 2) * 11;
+ count += 6;
+ break;
+ }
+ break;
+ case NUMERIC:
+ count += tribus(version, 10, 12, 14);
+ switch (blockLength(i) % 3) {
+ case 0:
+ count += (blockLength(i) / 3) * 10;
+ break;
+ case 1:
+ count += ((blockLength(i) - 1) / 3) * 10;
+ count += 4;
+ break;
+ case 2:
+ count += ((blockLength(i) - 2) / 3) * 10;
+ count += 7;
+ break;
+ }
+ break;
+ }
+ currentMode = inputMode[i];
+ }
+ }
+
+ return count;
+ }
+
+ private void applyOptimisation(int version) {
+ /* Implements a custom optimisation algorithm, because implementation
+ of the algorithm shown in Annex J.2 created LONGER binary sequences.
+ */
+
+ int blockCount = 0;
+ int i, j;
+ qrMode currentMode = qrMode.NULL;
+
+ for (i = 0; i < inputLength; i++) {
+ if (inputMode[i] != currentMode) {
+ currentMode = inputMode[i];
+ blockCount++;
+ }
+ }
+
+ int[] blockLength = new int[blockCount];
+ qrMode[] blockMode = new qrMode[blockCount];
+
+ j = -1;
+ currentMode = qrMode.NULL;
+ for (i = 0; i < inputLength; i++) {
+ if (inputMode[i] != currentMode) {
+ j++;
+ blockLength[j] = 1;
+ blockMode[j] = inputMode[i];
+ currentMode = inputMode[i];
+ } else {
+ blockLength[j]++;
+ }
+ }
+
+ if (blockCount > 1) {
+ // Search forward
+ for (i = 0; i <= (blockCount - 2); i++) {
+ if (blockMode[i] == qrMode.BINARY) {
+ switch (blockMode[i + 1]) {
+ case KANJI:
+ if (blockLength[i + 1] < tribus(version, 4, 5, 6)) {
+ blockMode[i + 1] = qrMode.BINARY;
+ }
+ break;
+ case ALPHANUM:
+ if (blockLength[i + 1] < tribus(version, 7, 8, 9)) {
+ blockMode[i + 1] = qrMode.BINARY;
+ }
+ break;
+ case NUMERIC:
+ if (blockLength[i + 1] < tribus(version, 3, 4, 5)) {
+ blockMode[i + 1] = qrMode.BINARY;
+ }
+ break;
+ }
+ }
+
+ if ((blockMode[i] == qrMode.ALPHANUM)
+ && (blockMode[i + 1] == qrMode.NUMERIC)) {
+ if (blockLength[i + 1] < tribus(version, 6, 8, 10)) {
+ blockMode[i + 1] = qrMode.ALPHANUM;
+ }
+ }
+ }
+
+ // Search backward
+ for (i = blockCount - 1; i > 0; i--) {
+ if (blockMode[i] == qrMode.BINARY) {
+ switch (blockMode[i - 1]) {
+ case KANJI:
+ if (blockLength[i - 1] < tribus(version, 4, 5, 6)) {
+ blockMode[i - 1] = qrMode.BINARY;
+ }
+ break;
+ case ALPHANUM:
+ if (blockLength[i - 1] < tribus(version, 7, 8, 9)) {
+ blockMode[i - 1] = qrMode.BINARY;
+ }
+ break;
+ case NUMERIC:
+ if (blockLength[i - 1] < tribus(version, 3, 4, 5)) {
+ blockMode[i - 1] = qrMode.BINARY;
+ }
+ break;
+ }
+ }
+
+ if ((blockMode[i] == qrMode.ALPHANUM)
+ && (blockMode[i - 1] == qrMode.NUMERIC)) {
+ if (blockLength[i - 1] < tribus(version, 6, 8, 10)) {
+ blockMode[i - 1] = qrMode.ALPHANUM;
+ }
+ }
+ }
+ }
+
+ j = 0;
+ for (int block = 0; block < blockCount; block++) {
+ currentMode = blockMode[block];
+ for (i = 0; i < blockLength[block]; i++) {
+ inputMode[j] = currentMode;
+ j++;
+ }
+ }
+ }
+
+ private int blockLength(int start) {
+ /* Find the length of the block starting from 'start' */
+ int i, count;
+ qrMode mode = inputMode[start];
+
+ count = 0;
+ i = start;
+
+ do {
+ count++;
+ } while (((i + count) < inputLength) && (inputMode[i + count] == mode));
+
+ return count;
+ }
+
+ private int tribus(int version, int a, int b, int c) {
+ /* Choose from three numbers based on version */
+ int RetVal;
+
+ RetVal = c;
+
+ if (version < 10) {
+ RetVal = a;
+ }
+
+ if ((version >= 10) && (version <= 26)) {
+ RetVal = b;
+ }
+
+ return RetVal;
+ }
+
+ private boolean isXAlpha(char cglyph) {
+ /* Returns true if input is in exclusive Alphanumeric set (Table J.1) */
+ boolean retval = false;
+
+ if ((cglyph >= 'A') && (cglyph <= 'Z')) {
+ retval = true;
+ }
+ switch (cglyph) {
+ case ' ':
+ case '$':
+ case '%':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ retval = true;
+ break;
+ }
+
+ return retval;
+ }
+
+ private boolean isXNumeric(char cglyph) {
+ /* Returns true if input is in exclusive Numeric set (Table J.1) */
+ boolean retval;
+
+ if ((cglyph >= '0') && (cglyph <= '9')) {
+ retval = true;
+ } else {
+ retval = false;
+ }
+
+ return retval;
+ }
+
+ private int estimate_binary_length() {
+ /* Make an estimate (worst case scenario) of how long the binary string will be */
+ int i, count = 0;
+ qrMode current = qrMode.NULL;
+ int a_count = 0;
+ int n_count = 0;
+
+ if (eciMode != 3) {
+ count += 12;
+ }
+
+ if (inputDataType == DataType.GS1) {
+ count += 4;
+ }
+
+ for (i = 0; i < inputLength; i++) {
+ if (inputMode[i] != current) {
+ switch (inputMode[i]) {
+ case KANJI:
+ count += 12 + 4;
+ current = qrMode.KANJI;
+ break;
+ case BINARY:
+ count += 16 + 4;
+ current = qrMode.BINARY;
+ break;
+ case ALPHANUM:
+ count += 13 + 4;
+ current = qrMode.ALPHANUM;
+ a_count = 0;
+ break;
+ case NUMERIC:
+ count += 14 + 4;
+ current = qrMode.NUMERIC;
+ n_count = 0;
+ break;
+ }
+ }
+
+ switch (inputMode[i]) {
+ case KANJI:
+ count += 13;
+ break;
+ case BINARY:
+ count += 8;
+ break;
+ case ALPHANUM:
+ a_count++;
+ if ((a_count & 1) == 0) {
+ count += 5; // 11 in total
+ a_count = 0;
+ } else {
+ count += 6;
+ }
+ break;
+ case NUMERIC:
+ n_count++;
+ if ((n_count % 3) == 0) {
+ count += 3; // 10 in total
+ n_count = 0;
+ } else if ((n_count & 1) == 0) {
+ count += 3; // 7 in total
+ } else {
+ count += 4;
+ }
+ break;
+ }
+ }
+
+ return count;
+ }
+
+ private boolean qr_binary(int version, int target_binlen) {
+ /* Convert input data to a binary stream and add padding */
+ int position = 0;
+ int short_data_block_length, i, scheme = 1;
+ int padbits;
+ int current_binlen, current_bytes;
+ int toggle;
+ boolean alphanumPercent;
+ String oneChar;
+ qrMode data_block;
+ int jis;
+ byte[] jisBytes;
+ int msb, lsb, prod;
+ int count, first, second, third;
+ int weight;
+
+ binary = new StringBuilder();
+
+ /* Note: Shift-JIS characters can be encoded in either Kanji
+ mode or Byte mode. If no ECI code is given, a sequence in Byte
+ mode could be interpreted in two ways - for example 0xE4 0x6E
+ could refer to U+E4 and U+6E (än) or U+8205 (舅). To avoid
+ Mojibake, if using ECI 20 (i.e. if only valid Shift-JIS
+ characters are found in the input data), this code will insert
+ an explicit ECI 20 instruction. Symbols without an ECI can then
+ be assumed to be ECI 3 (ISO 8859-1).
+ */
+ if (eciMode != 3) {
+ binary.append("0111"); /* ECI */
+
+ if ((eciMode >= 0) && (eciMode <= 127)) {
+ binary.append("0");
+ qr_bscan(eciMode, 0x40);
+ }
+
+ if ((eciMode >= 128) && (eciMode <= 16383)) {
+ binary.append("10");
+ qr_bscan(eciMode, 0x1000);
+ }
+
+ if ((eciMode >= 16384) && (eciMode <= 999999)) {
+ binary.append("110");
+ qr_bscan(eciMode, 0x100000);
+ }
+ }
+
+ if (inputDataType == DataType.GS1) {
+ binary.append("0101"); /* FNC1 */
+
+ }
+
+ if (version <= 9) {
+ scheme = 1;
+ } else if (version <= 26) {
+ scheme = 2;
+ } else {
+ scheme = 3;
+ }
+
+ encodeInfo.append("Encoding: ");
+
+ alphanumPercent = false;
+
+ do {
+ data_block = inputMode[position];
+ short_data_block_length = 0;
+ do {
+ short_data_block_length++;
+ } while (((short_data_block_length + position) < inputLength)
+ && (inputMode[position + short_data_block_length] == data_block));
+
+ switch (data_block) {
+ case KANJI:
+ /* Kanji mode */
+ /* Mode indicator */
+ binary.append("1000");
+
+ /* Character count indicator */
+ qr_bscan(short_data_block_length, 0x20 << (scheme * 2)); /* scheme = 1..3 */
+
+ encodeInfo.append("KNJI ");
+
+ /* Character representation */
+ for (i = 0; i < short_data_block_length; i++) {
+ oneChar = "";
+ oneChar += (char) inputData[position + i];
+
+ /* Convert Unicode input to Shift-JIS */
+ try {
+ jisBytes = oneChar.getBytes("SJIS");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Shift-JIS character conversion error");
+ return false;
+ }
+
+ jis = ((jisBytes[0] & 0xFF) << 8) + (jisBytes[1] & 0xFF);
+
+ if (jis > 0x9fff) {
+ jis -= 0xc140;
+ } else {
+ jis -= 0x8140;
+ }
+ msb = (jis & 0xff00) >> 8;
+ lsb = (jis & 0xff);
+ prod = (msb * 0xc0) + lsb;
+
+ qr_bscan(prod, 0x1000);
+
+ encodeInfo.append(Integer.toString(prod)).append(" ");
+ }
+ break;
+ case BINARY:
+ /* Byte mode */
+ /* Mode indicator */
+ binary.append("0100");
+ int kanjiModifiedLength = short_data_block_length;
+
+ for (i = 0; i < short_data_block_length; i++) {
+ if (inputData[position + i] > 0xff) {
+ kanjiModifiedLength++;
+ }
+ }
+
+ /* Character count indicator */
+ qr_bscan(kanjiModifiedLength, scheme > 1 ? 0x8000 : 0x80); /* scheme = 1..3 */
+
+ encodeInfo.append("BYTE ");
+
+ /* Character representation */
+ for (i = 0; i < short_data_block_length; i++) {
+ if (inputData[position + i] > 0xff) {
+ // Convert Kanji character to Shift-JIS
+ oneChar = "";
+ oneChar += (char) inputData[position + i];
+
+ /* Convert Unicode input to Shift-JIS */
+ try {
+ jisBytes = oneChar.getBytes("SJIS");
+ } catch (UnsupportedEncodingException e) {
+ errorMsg.append("Shift-JIS character conversion error");
+ return false;
+ }
+
+ qr_bscan((int) jisBytes[0] & 0xff, 0x80);
+ qr_bscan((int) jisBytes[1] & 0xff, 0x80);
+
+ encodeInfo.append("(").append(Integer.toString((int) jisBytes[0] & 0xff)).append(" ");
+ encodeInfo.append(Integer.toString((int) jisBytes[1] & 0xff)).append(") ");
+ } else {
+ // Process 8-bit byte
+ int lbyte = (inputData[position + i] & 0xFF);
+
+ if ((inputDataType == DataType.GS1) && (lbyte == '[')) {
+ lbyte = 0x1d; /* FNC1 */
+
+ }
+
+ qr_bscan(lbyte, 0x80);
+
+ encodeInfo.append(Integer.toString(lbyte)).append(" ");
+ }
+ }
+
+ break;
+ case ALPHANUM:
+ /* Alphanumeric mode */
+ /* Mode indicator */
+ binary.append("0010");
+
+ /* Character count indicator */
+ qr_bscan(short_data_block_length, 0x40 << (2 * scheme)); /* scheme = 1..3 */
+
+ encodeInfo.append("ALPH ");
+
+ /* Character representation */
+ i = 0;
+ while (i < short_data_block_length) {
+
+ if (!alphanumPercent) {
+ if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) {
+ first = positionOf('%', rhodium);
+ second = positionOf('%', rhodium);
+ count = 2;
+ prod = (first * 45) + second;
+ i++;
+ } else {
+ if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) {
+ first = positionOf('%', rhodium); /* FNC1 */
+
+ } else {
+ first = positionOf((char) (inputData[position + i] & 0xFF), rhodium);
+ }
+ count = 1;
+ i++;
+ prod = first;
+
+ if (i < short_data_block_length) {
+ if (inputMode[position + i] == qrMode.ALPHANUM) {
+ if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) {
+ second = positionOf('%', rhodium);
+ count = 2;
+ prod = (first * 45) + second;
+ alphanumPercent = true;
+ } else {
+ if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) {
+ second = positionOf('%', rhodium); /* FNC1 */
+
+ } else {
+ second = positionOf((char) (inputData[position + i] & 0xFF), rhodium);
+ }
+ count = 2;
+ i++;
+ prod = (first * 45) + second;
+ }
+ }
+ }
+ }
+ } else {
+ first = positionOf('%', rhodium);
+ count = 1;
+ i++;
+ prod = first;
+ alphanumPercent = false;
+
+ if (i < short_data_block_length) {
+ if (inputMode[position + i] == qrMode.ALPHANUM) {
+ if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) {
+ second = positionOf('%', rhodium);
+ count = 2;
+ prod = (first * 45) + second;
+ alphanumPercent = true;
+ } else {
+ if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) {
+ second = positionOf('%', rhodium); /* FNC1 */
+
+ } else {
+ second = positionOf((char) (inputData[position + i] & 0xFF), rhodium);
+ }
+ count = 2;
+ i++;
+ prod = (first * 45) + second;
+ }
+ }
+ }
+ }
+
+ qr_bscan(prod, count == 2 ? 0x400 : 0x20); /* count = 1..2 */
+
+ encodeInfo.append(Integer.toString(prod)).append(" ");
+ }
+ ;
+ break;
+ case NUMERIC:
+ /* Numeric mode */
+ /* Mode indicator */
+ binary.append("0001");
+
+ /* Character count indicator */
+ qr_bscan(short_data_block_length, 0x80 << (2 * scheme)); /* scheme = 1..3 */
+
+ encodeInfo.append("NUMB ");
+
+ /* Character representation */
+ i = 0;
+ while (i < short_data_block_length) {
+
+ first = Character.getNumericValue(inputData[position + i]);
+ count = 1;
+ prod = first;
+
+ if ((i + 1) < short_data_block_length) {
+ second = Character.getNumericValue(inputData[position + i + 1]);
+ count = 2;
+ prod = (prod * 10) + second;
+ }
+
+ if ((i + 2) < short_data_block_length) {
+ third = Character.getNumericValue(inputData[position + i + 2]);
+ count = 3;
+ prod = (prod * 10) + third;
+ }
+
+ qr_bscan(prod, 1 << (3 * count)); /* count = 1..3 */
+
+ encodeInfo.append(Integer.toString(prod)).append(" ");
+
+ i += count;
+ }
+ ;
+ break;
+ }
+ position += short_data_block_length;
+ } while (position < inputLength);
+
+ encodeInfo.append("\n");
+
+ /* Terminator */
+ binary.append("0000");
+
+ current_binlen = binary.length();
+ padbits = 8 - (current_binlen % 8);
+ if (padbits == 8) {
+ padbits = 0;
+ }
+ current_bytes = (current_binlen + padbits) / 8;
+
+ /* Padding bits */
+ for (i = 0; i < padbits; i++) {
+ binary.append("0");
+ }
+
+ /* Put data into 8-bit codewords */
+ for (i = 0; i < current_bytes; i++) {
+ datastream[i] = 0x00;
+ for (weight = 0; weight < 8; weight++) {
+ if (binary.charAt((i * 8) + weight) == '1') {
+ datastream[i] += (0x80 >> weight);
+ }
+ }
+ }
+
+ /* Add pad codewords */
+ toggle = 0;
+ for (i = current_bytes; i < target_binlen; i++) {
+ if (toggle == 0) {
+ datastream[i] = 0xec;
+ toggle = 1;
+ } else {
+ datastream[i] = 0x11;
+ toggle = 0;
+ }
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < target_binlen; i++) {
+ encodeInfo.append(Integer.toString(datastream[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ return true;
+ }
+
+ private void qr_bscan(int data, int h) {
+
+ for (;
+ (h != 0); h >>= 1) {
+ if ((data & h) != 0) {
+ binary.append("1");
+ } else {
+ binary.append("0");
+ }
+ }
+
+ }
+
+ private void add_ecc(int version, int data_cw, int blocks) {
+ /* Split data into blocks, add error correction and then interleave the blocks and error correction data */
+ int ecc_cw = qr_total_codewords[version - 1] - data_cw;
+ int short_data_block_length = data_cw / blocks;
+ int qty_long_blocks = data_cw % blocks;
+ int qty_short_blocks = blocks - qty_long_blocks;
+ int ecc_block_length = ecc_cw / blocks;
+ int i, j, k, length_this_block, posn;
+
+ int[] data_block = new int[short_data_block_length + 2];
+ int[] ecc_block = new int[ecc_block_length + 2];
+ int[] interleaved_data = new int[data_cw + 2];
+ int[] interleaved_ecc = new int[ecc_cw + 2];
+
+ posn = 0;
+
+ for (i = 0; i < blocks; i++) {
+ ReedSolomon rs = new ReedSolomon();
+ if (i < qty_short_blocks) {
+ length_this_block = short_data_block_length;
+ } else {
+ length_this_block = short_data_block_length + 1;
+ }
+
+ for (j = 0; j < ecc_block_length; j++) {
+ ecc_block[j] = 0;
+ }
+
+ for (j = 0; j < length_this_block; j++) {
+ data_block[j] = datastream[posn + j];
+ }
+
+ rs.init_gf(0x11d);
+ rs.init_code(ecc_block_length, 0);
+ rs.encode(length_this_block, data_block);
+ for (k = 0; k < ecc_block_length; k++) {
+ ecc_block[k] = rs.getResult(k);
+ }
+ for (j = 0; j < short_data_block_length; j++) {
+ interleaved_data[(j * blocks) + i] = data_block[j];
+ }
+
+ if (i >= qty_short_blocks) {
+ interleaved_data[(short_data_block_length * blocks) + (i - qty_short_blocks)] = data_block[short_data_block_length];
+ }
+
+ for (j = 0; j < ecc_block_length; j++) {
+ interleaved_ecc[(j * blocks) + i] = ecc_block[ecc_block_length - j - 1];
+ }
+
+ posn += length_this_block;
+ }
+
+ for (j = 0; j < data_cw; j++) {
+ fullstream[j] = interleaved_data[j];
+ }
+ for (j = 0; j < ecc_cw; j++) {
+ fullstream[j + data_cw] = interleaved_ecc[j];
+ }
+ }
+
+ private void setup_grid(int size, int version) {
+ int i;
+ int loopsize, x, y, xcoord, ycoord;
+ boolean toggle = true;
+
+ /* Add timing patterns */
+ for (i = 0; i < size; i++) {
+ if (toggle) {
+ grid[(6 * size) + i] = 0x21;
+ grid[(i * size) + 6] = 0x21;
+ toggle = false;
+ } else {
+ grid[(6 * size) + i] = 0x20;
+ grid[(i * size) + 6] = 0x20;
+ toggle = true;
+ }
+ }
+
+ /* Add finder patterns */
+ place_finder(size, 0, 0);
+ place_finder(size, 0, size - 7);
+ place_finder(size, size - 7, 0);
+
+ /* Add separators */
+ for (i = 0; i < 7; i++) {
+ grid[(7 * size) + i] = 0x10;
+ grid[(i * size) + 7] = 0x10;
+ grid[(7 * size) + (size - 1 - i)] = 0x10;
+ grid[(i * size) + (size - 8)] = 0x10;
+ grid[((size - 8) * size) + i] = 0x10;
+ grid[((size - 1 - i) * size) + 7] = 0x10;
+ }
+ grid[(7 * size) + 7] = 0x10;
+ grid[(7 * size) + (size - 8)] = 0x10;
+ grid[((size - 8) * size) + 7] = 0x10;
+
+ /* Add alignment patterns */
+ if (version != 1) {
+ /* Version 1 does not have alignment patterns */
+
+ loopsize = qr_align_loopsize[version - 1];
+ for (x = 0; x < loopsize; x++) {
+ for (y = 0; y < loopsize; y++) {
+ xcoord = qr_table_e1[((version - 2) * 7) + x];
+ ycoord = qr_table_e1[((version - 2) * 7) + y];
+
+ if ((grid[(ycoord * size) + xcoord] & 0x10) == 0) {
+ place_align(size, xcoord, ycoord);
+ }
+ }
+ }
+ }
+
+ /* Reserve space for format information */
+ for (i = 0; i < 8; i++) {
+ grid[(8 * size) + i] += 0x20;
+ grid[(i * size) + 8] += 0x20;
+ grid[(8 * size) + (size - 1 - i)] = 0x20;
+ grid[((size - 1 - i) * size) + 8] = 0x20;
+ }
+ grid[(8 * size) + 8] += 0x20;
+ grid[((size - 1 - 7) * size) + 8] = 0x21; /* Dark Module from Figure 25 */
+
+ /* Reserve space for version information */
+ if (version >= 7) {
+ for (i = 0; i < 6; i++) {
+ grid[((size - 9) * size) + i] = 0x20;
+ grid[((size - 10) * size) + i] = 0x20;
+ grid[((size - 11) * size) + i] = 0x20;
+ grid[(i * size) + (size - 9)] = 0x20;
+ grid[(i * size) + (size - 10)] = 0x20;
+ grid[(i * size) + (size - 11)] = 0x20;
+ }
+ }
+ }
+
+ private void place_finder(int size, int x, int y) {
+ int xp, yp;
+
+ int[] finder = {
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 0, 0, 1,
+ 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 1, 1, 1, 0, 1,
+ 1, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1
+ };
+
+ for (xp = 0; xp < 7; xp++) {
+ for (yp = 0; yp < 7; yp++) {
+ if (finder[xp + (7 * yp)] == 1) {
+ grid[((yp + y) * size) + (xp + x)] = 0x11;
+ } else {
+ grid[((yp + y) * size) + (xp + x)] = 0x10;
+ }
+ }
+ }
+ }
+
+ private void place_align(int size, int x, int y) {
+ int xp, yp;
+
+ int[] alignment = {
+ 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 1,
+ 1, 0, 1, 0, 1,
+ 1, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1
+ };
+
+ x -= 2;
+ y -= 2; /* Input values represent centre of pattern */
+
+ for (xp = 0; xp < 5; xp++) {
+ for (yp = 0; yp < 5; yp++) {
+ if (alignment[xp + (5 * yp)] == 1) {
+ grid[((yp + y) * size) + (xp + x)] = 0x11;
+ } else {
+ grid[((yp + y) * size) + (xp + x)] = 0x10;
+ }
+ }
+ }
+ }
+
+ private void populate_grid(int size, int cw) {
+ boolean goingUp = true;
+ int row = 0; /* right hand side */
+
+ int i, n, x, y;
+
+ n = cw * 8;
+ y = size - 1;
+ i = 0;
+ do {
+ x = (size - 2) - (row * 2);
+ if (x < 6) {
+ x--; /* skip over vertical timing pattern */
+
+ }
+
+ if ((grid[(y * size) + (x + 1)] & 0xf0) == 0) {
+ if (cwbit(i)) {
+ grid[(y * size) + (x + 1)] = 0x01;
+ } else {
+ grid[(y * size) + (x + 1)] = 0x00;
+ }
+ i++;
+ }
+
+ if (i < n) {
+ if ((grid[(y * size) + x] & 0xf0) == 0) {
+ if (cwbit(i)) {
+ grid[(y * size) + x] = 0x01;
+ } else {
+ grid[(y * size) + x] = 0x00;
+ }
+ i++;
+ }
+ }
+
+ if (goingUp) {
+ y--;
+ } else {
+ y++;
+ }
+ if (y == -1) {
+ /* reached the top */
+ row++;
+ y = 0;
+ goingUp = false;
+ }
+ if (y == size) {
+ /* reached the bottom */
+ row++;
+ y = size - 1;
+ goingUp = true;
+ }
+ } while (i < n);
+ }
+
+ private boolean cwbit(int i) {
+ boolean resultant = false;
+
+ if ((fullstream[i / 8] & (0x80 >> (i % 8))) != 0) {
+ resultant = true;
+ }
+
+ return resultant;
+ }
+
+ private int apply_bitmask(int size, EccMode ecc_level) {
+ int x, y;
+ char p;
+ int local_pattern;
+ int best_val, best_pattern;
+ int[] penalty = new int[8];
+ byte[] mask = new byte[size * size];
+ eval = new byte[size * size];
+
+
+ /* Perform data masking */
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ mask[(y * size) + x] = 0x00;
+
+ if ((grid[(y * size) + x] & 0xf0) == 0) {
+ if (((y + x) & 1) == 0) {
+ mask[(y * size) + x] += 0x01;
+ }
+ if ((y & 1) == 0) {
+ mask[(y * size) + x] += 0x02;
+ }
+ if ((x % 3) == 0) {
+ mask[(y * size) + x] += 0x04;
+ }
+ if (((y + x) % 3) == 0) {
+ mask[(y * size) + x] += 0x08;
+ }
+ if ((((y / 2) + (x / 3)) & 1) == 0) {
+ mask[(y * size) + x] += 0x10;
+ }
+ if ((((y * x) & 1) + ((y * x) % 3)) == 0) {
+ mask[(y * size) + x] += 0x20;
+ }
+ if (((((y * x) & 1) + ((y * x) % 3)) & 1) == 0) {
+ mask[(y * size) + x] += 0x40;
+ }
+ if (((((y + x) & 1) + ((y * x) % 3)) & 1) == 0) {
+ mask[(y * size) + x] += 0x80;
+ }
+ }
+ }
+ }
+
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ if ((grid[(y * size) + x] & 0x01) != 0) {
+ p = 0xff;
+ } else {
+ p = 0x00;
+ }
+
+ eval[(y * size) + x] = (byte) (mask[(y * size) + x] ^ p);
+ }
+ }
+
+
+ /* Evaluate result */
+ for (local_pattern = 0; local_pattern < 8; local_pattern++) {
+ add_format_info_eval(size, ecc_level, local_pattern);
+ penalty[local_pattern] = evaluate(size, local_pattern);
+ }
+
+ best_pattern = 0;
+ best_val = penalty[0];
+ for (local_pattern = 1; local_pattern < 8; local_pattern++) {
+ if (penalty[local_pattern] < best_val) {
+ best_pattern = local_pattern;
+ best_val = penalty[local_pattern];
+ }
+ }
+
+ /* Apply mask */
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ if ((mask[(y * size) + x] & (0x01 << best_pattern)) != 0) {
+ if ((grid[(y * size) + x] & 0x01) != 0) {
+ grid[(y * size) + x] = 0x00;
+ } else {
+ grid[(y * size) + x] = 0x01;
+ }
+ }
+ }
+ }
+
+ return best_pattern;
+ }
+
+ private void add_format_info_eval(int size, EccMode ecc_level, int pattern) {
+ /* Add format information to grid */
+
+ int format = pattern;
+ int seq;
+ int i;
+
+ switch (ecc_level) {
+ case L:
+ format += 0x08;
+ break;
+ case Q:
+ format += 0x18;
+ break;
+ case H:
+ format += 0x10;
+ break;
+ }
+
+ seq = qr_annex_c[format];
+
+ for (i = 0; i < 6; i++) {
+ if (((seq >> i) & 0x01) != 0) {
+ eval[(i * size) + 8] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(i * size) + 8] = 0;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (((seq >> i) & 0x01) != 0) {
+ eval[(8 * size) + (size - i - 1)] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(8 * size) + (size - i - 1)] = 0;
+ }
+ }
+
+ for (i = 0; i < 6; i++) {
+ if (((seq >> (i + 9)) & 0x01) != 0) {
+ eval[(8 * size) + (5 - i)] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(8 * size) + (5 - i)] = 0;
+ }
+ }
+
+ for (i = 0; i < 7; i++) {
+ if (((seq >> (i + 8)) & 0x01) != 0) {
+ eval[(((size - 7) + i) * size) + 8] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(((size - 7) + i) * size) + 8] = 0;
+ }
+ }
+
+ if (((seq >> 6) & 0x01) != 0) {
+ eval[(7 * size) + 8] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(7 * size) + 8] = 0;
+ }
+
+ if (((seq >> 7) & 0x01) != 0) {
+ eval[(8 * size) + 8] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(8 * size) + 8] = 0;
+ }
+
+ if (((seq >> 8) & 0x01) != 0) {
+ eval[(8 * size) + 7] = (byte) (0x01 >> pattern);
+ } else {
+ eval[(8 * size) + 7] = 0;
+ }
+ }
+
+ private int evaluate(int size, int pattern) {
+ int x, y, block;
+ int result = 0;
+ int state;
+ int p;
+ int weight;
+ int dark_mods;
+ int percentage, k;
+ int a, b, afterCount, beforeCount;
+ byte[] local = new byte[size * size];
+
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ if ((eval[(y * size) + x] & (0x01 << pattern)) != 0) {
+ local[(y * size) + x] = '1';
+ } else {
+ local[(y * size) + x] = '0';
+ }
+ }
+ }
+
+ /* Test 1: Adjacent modules in row/column in same colour */
+ /* Vertical */
+ for (x = 0; x < size; x++) {
+ state = local[x];
+ block = 0;
+ for (y = 0; y < size; y++) {
+ if (local[(y * size) + x] == state) {
+ block++;
+ } else {
+ if (block > 5) {
+ result += (3 + (block - 5));
+ }
+ block = 0;
+ state = local[(y * size) + x];
+ }
+ }
+ if (block > 5) {
+ result += (3 + (block - 5));
+ }
+ }
+
+ /* Horizontal */
+ for (y = 0; y < size; y++) {
+ state = local[y * size];
+ block = 0;
+ for (x = 0; x < size; x++) {
+ if (local[(y * size) + x] == state) {
+ block++;
+ } else {
+ if (block > 5) {
+ result += (3 + (block - 5));
+ }
+ block = 0;
+ state = local[(y * size) + x];
+ }
+ }
+ if (block > 5) {
+ result += (3 + (block - 5));
+ }
+ }
+
+ /* Test 2: Block of modules in same color */
+ for (x = 0; x < size - 1; x++) {
+ for (y = 0; y < size - 1; y++) {
+ if (((local[(y * size) + x] == local[((y + 1) * size) + x])
+ && (local[(y * size) + x] == local[(y * size) + (x + 1)]))
+ && (local[(y * size) + x] == local[((y + 1) * size) + (x + 1)])) {
+ result += 3;
+ }
+ }
+ }
+
+ /* Test 3: 1:1:3:1:1 ratio pattern in row/column */
+ /* Vertical */
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < (size - 7); y++) {
+ p = 0;
+ for (weight = 0; weight < 7; weight++) {
+ if (local[((y + weight) * size) + x] == '1') {
+ p += (0x40 >> weight);
+ }
+ }
+ if (p == 0x5d) {
+ /* Pattern found, check before and after */
+ beforeCount = 0;
+ for (b = (y - 4); b < y; b++) {
+ if (b < 0) {
+ beforeCount++;
+ } else {
+ if (local[(b * size) + x] == '0') {
+ beforeCount++;
+ } else {
+ beforeCount = 0;
+ }
+ }
+ }
+
+ afterCount = 0;
+ for (a = (y + 7); a <= (y + 10); a++) {
+ if (a >= size) {
+ afterCount++;
+ } else {
+ if (local[(a * size) + x] == '0') {
+ afterCount++;
+ } else {
+ afterCount = 0;
+ }
+ }
+ }
+
+ if ((beforeCount == 4) || (afterCount == 4)) {
+ /* Pattern is preceeded or followed by light area
+ 4 modules wide */
+ result += 40;
+ }
+ }
+ }
+ }
+
+ /* Horizontal */
+ for (y = 0; y < size; y++) {
+ for (x = 0; x < (size - 7); x++) {
+ p = 0;
+ for (weight = 0; weight < 7; weight++) {
+ if (local[(y * size) + x + weight] == '1') {
+ p += (0x40 >> weight);
+ }
+ }
+ if (p == 0x5d) {
+ /* Pattern found, check before and after */
+ beforeCount = 0;
+ for (b = (x - 4); b < x; b++) {
+ if (b < 0) {
+ beforeCount++;
+ } else {
+ if (local[(y * size) + b] == '0') {
+ beforeCount++;
+ } else {
+ beforeCount = 0;
+ }
+ }
+ }
+
+ afterCount = 0;
+ for (a = (x + 7); a <= (x + 10); a++) {
+ if (a >= size) {
+ afterCount++;
+ } else {
+ if (local[(y * size) + a] == '0') {
+ afterCount++;
+ } else {
+ afterCount = 0;
+ }
+ }
+ }
+
+ if ((beforeCount == 4) || (afterCount == 4)) {
+ /* Pattern is preceeded or followed by light area
+ 4 modules wide */
+ result += 40;
+ }
+ }
+ }
+ }
+
+ /* Test 4: Proportion of dark modules in entire symbol */
+ dark_mods = 0;
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ if (local[(y * size) + x] == '1') {
+ dark_mods++;
+ }
+ }
+ }
+ percentage = 100 * (dark_mods / (size * size));
+ if (percentage <= 50) {
+ k = ((100 - percentage) - 50) / 5;
+ } else {
+ k = (percentage - 50) / 5;
+ }
+
+ result += 10 * k;
+
+ return result;
+ }
+
+ private void add_format_info(int size, EccMode ecc_level, int pattern) {
+ /* Add format information to grid */
+
+ int format = pattern;
+ int seq;
+ int i;
+
+ switch (ecc_level) {
+ case L:
+ format += 0x08;
+ break;
+ case Q:
+ format += 0x18;
+ break;
+ case H:
+ format += 0x10;
+ break;
+ }
+
+ seq = qr_annex_c[format];
+
+ for (i = 0; i < 6; i++) {
+ grid[(i * size) + 8] += (seq >> i) & 0x01;
+ }
+
+ for (i = 0; i < 8; i++) {
+ grid[(8 * size) + (size - i - 1)] += (seq >> i) & 0x01;
+ }
+
+ for (i = 0; i < 6; i++) {
+ grid[(8 * size) + (5 - i)] += (seq >> (i + 9)) & 0x01;
+ }
+
+ for (i = 0; i < 7; i++) {
+ grid[(((size - 7) + i) * size) + 8] += (seq >> (i + 8)) & 0x01;
+ }
+
+ grid[(7 * size) + 8] += (seq >> 6) & 0x01;
+ grid[(8 * size) + 8] += (seq >> 7) & 0x01;
+ grid[(8 * size) + 7] += (seq >> 8) & 0x01;
+ }
+
+ private void add_version_info(int size, int version) {
+ /* Add version information */
+ int i;
+
+ long version_data = qr_annex_d[version - 7];
+ for (i = 0; i < 6; i++) {
+ grid[((size - 11) * size) + i] += (version_data >> (i * 3)) & 0x01;
+ grid[((size - 10) * size) + i] += (version_data >> ((i * 3) + 1)) & 0x01;
+ grid[((size - 9) * size) + i] += (version_data >> ((i * 3) + 2)) & 0x01;
+ grid[(i * size) + (size - 11)] += (version_data >> (i * 3)) & 0x01;
+ grid[(i * size) + (size - 10)] += (version_data >> ((i * 3) + 1)) & 0x01;
+ grid[(i * size) + (size - 9)] += (version_data >> ((i * 3) + 2)) & 0x01;
+ }
+ }
+
+ private enum qrMode {
+
+ NULL, KANJI, BINARY, ALPHANUM, NUMERIC
+ }
+
+ public enum EccMode {
+
+ L, M, Q, H
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java b/barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java
new file mode 100755
index 0000000..9fddf90
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java
@@ -0,0 +1,112 @@
+package org.xbib.graphics.barcode;
+
+import java.awt.geom.Rectangle2D;
+import java.util.Locale;
+
+/**
+ * Encodes data according to the Royal Mail 4-State Country Code
+ * Data input can consist of numbers 0-9 and letters A-Z and usually includes
+ * delivery postcode followed by house number. A check digit is calculated
+ * and added.
+ */
+public class RoyalMail4State extends Symbol {
+ /* Handles the 4 State barcodes used in the UK by Royal Mail */
+
+ private String[] RoyalTable = {
+ "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA",
+ "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
+ "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT",
+ "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT"
+ };
+
+ private char[] krSet = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
+ };
+
+ @Override
+ public boolean encode() {
+ StringBuilder dest;
+ int i, top = 0, bottom = 0;
+ int row, column;
+ int index;
+
+ content = content.toUpperCase(Locale.ENGLISH);
+ if (!(content.matches("[0-9A-Z]+"))) {
+ errorMsg.append("Invalid characters in data");
+ return false;
+ }
+ dest = new StringBuilder("A");
+
+ for (i = 0; i < content.length(); i++) {
+ index = positionOf(content.charAt(i), krSet);
+ dest.append(RoyalTable[index]);
+ top += (index + 1) % 6;
+ bottom += ((index / 6) + 1) % 6;
+ }
+
+ /* calculate check digit */
+ row = (top % 6) - 1;
+ column = (bottom % 6) - 1;
+ if (row == -1) {
+ row = 5;
+ }
+ if (column == -1) {
+ column = 5;
+ }
+
+ dest.append(RoyalTable[(6 * row) + column]);
+
+ encodeInfo.append("Check Digit: ").append((6 * row) + column).append("\n");
+
+ /* Stop character */
+ dest.append("F");
+
+ encodeInfo.append("Encoding: ").append(dest).append("\n");
+ readable = new StringBuilder();
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ getRectangles().clear();
+ x = 0;
+ w = 1;
+ y = 0;
+ h = 0;
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ switch (pattern[0].charAt(xBlock)) {
+ case 'A':
+ y = 0;
+ h = 5;
+ break;
+ case 'D':
+ y = 3;
+ h = 5;
+ break;
+ case 'F':
+ y = 0;
+ h = 8;
+ break;
+ case 'T':
+ y = 3;
+ h = 2;
+ break;
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ x += 2;
+ }
+ symbolWidth = pattern[0].length() * 3;
+ symbolHeight = 8;
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java b/barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java
new file mode 100755
index 0000000..77b321b
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java
@@ -0,0 +1,1058 @@
+package org.xbib.graphics.barcode;
+
+import static org.xbib.graphics.barcode.HumanReadableLocation.BOTTOM;
+import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
+import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
+import org.xbib.graphics.barcode.util.Hexagon;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Generic barcode symbology class.
+ * TODO: Setting attributes like module width, font size, etc should probably throw
+ * an exception if set *after* encoding has already been completed.
+ */
+public abstract class Symbol {
+
+ private final List rectangles = new ArrayList<>();
+
+ private final List texts = new ArrayList<>();
+
+ private final List hexagons = new ArrayList<>();
+
+ private final List target = new ArrayList<>();
+
+ protected String content;
+
+ protected StringBuilder readable;
+
+ protected String[] pattern;
+
+ protected int rowCount = 0;
+
+ protected int[] rowHeight;
+
+ protected StringBuilder errorMsg = new StringBuilder();
+
+ protected int symbolHeight = 0;
+
+ protected int symbolWidth = 0;
+
+ protected int defaultHeight = 40;
+
+ private HumanReadableLocation humanReadableLocation = BOTTOM;
+
+ protected StringBuilder encodeInfo = new StringBuilder();
+
+ protected byte[] inputBytes;
+
+ protected DataType inputDataType = DataType.ECI;
+
+ int moduleWidth = 1;
+
+ double fontSize = 8;
+
+ boolean readerInit;
+
+ int eciMode = 3;
+
+ private int quietZoneHorizontal = 0;
+
+ private int quietZoneVertical = 0;
+
+ private String fontName = "Helvetica";
+
+ public Symbol() {
+ unsetReaderInit();
+ }
+
+ public List getRectangles() {
+ return rectangles;
+ }
+
+ public List getTexts() {
+ return texts;
+ }
+
+ public List getHexagons() {
+ return hexagons;
+ }
+
+ public List getTarget() {
+ return target;
+ }
+
+ /**
+ * Inserts the specified array into the specified original array at the specified index.
+ *
+ * @param original the original array into which we want to insert another array
+ * @param index the index at which we want to insert the array
+ * @param inserted the array that we want to insert
+ * @return the combined array
+ */
+ static int[] insert(int[] original, int index, int[] inserted) {
+ int[] modified = new int[original.length + inserted.length];
+ System.arraycopy(original, 0, modified, 0, index);
+ System.arraycopy(inserted, 0, modified, index, inserted.length);
+ System.arraycopy(original, index, modified, index + inserted.length, modified.length - index - inserted.length);
+ return modified;
+ }
+
+ /**
+ * Returns true if the specified array contains the specified value.
+ *
+ * @param values the array to check in
+ * @param value the value to check for
+ * @return true if the specified array contains the specified value
+ */
+ static boolean contains(int[] values, int value) {
+ for (int value1 : values) {
+ if (value1 == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean roughlyEqual(double d1, double d2) {
+ return Math.abs(d1 - d2) < 0.0001;
+ }
+
+ /**
+ * Sets the type of input data. This setting influences what
+ * pre-processing is done on data before encoding in the symbol.
+ * For example: for GS1
mode the AI data will be used to
+ * calculate the position of 'FNC1' characters.
+ * Valid values are:
+ *
+ * UTF8
(default) Unicode encoding
+ * LATIN1
ISO 8859-1 (Latin-1) encoding
+ * BINARY
Byte encoding mode
+ * GS1
Application Identifier and data pairs in "[AI]DATA" format
+ * HIBC
Health Industry Bar Code number (without check digit)
+ * ECI
Extended Channel Interpretations
+ *
+ *
+ * @param dataType A DataType
value which specifies the type of data
+ */
+ public void setDataType(DataType dataType) {
+ inputDataType = dataType;
+ }
+
+ /**
+ * Prefixes symbol data with a "Reader Initialisation" or "Reader
+ * Programming" instruction.
+ */
+ public final void setReaderInit() {
+ readerInit = true;
+ }
+
+ /**
+ * Removes "Reader Initialisation" or "Reader Programming" instruction
+ * from symbol data.
+ */
+ private void unsetReaderInit() {
+ readerInit = false;
+ }
+
+ /**
+ * Returns the default bar height for this symbol.
+ *
+ * @return the default bar height for this symbol
+ */
+ public int getBarHeight() {
+ return defaultHeight;
+ }
+
+ /**
+ * Sets the default bar height for this symbol (default value is 40
).
+ *
+ * @param barHeight the default bar height for this symbol
+ */
+ public void setBarHeight(int barHeight) {
+ this.defaultHeight = barHeight;
+ }
+
+ /**
+ * Returns the module width for this symbol.
+ *
+ * @return the module width for this symbol
+ */
+ public int getModuleWidth() {
+ return moduleWidth;
+ }
+
+ /**
+ * Sets the module width for this symbol (default value is 1
).
+ *
+ * @param moduleWidth the module width for this symbol
+ */
+ public void setModuleWidth(int moduleWidth) {
+ this.moduleWidth = moduleWidth;
+ }
+
+ /**
+ * Returns the horizontal quiet zone (white space) added to the left and to the right of this symbol.
+ *
+ * @return the horizontal quiet zone (white space) added to the left and to the right of this symbol
+ */
+ public int getQuietZoneHorizontal() {
+ return quietZoneHorizontal;
+ }
+
+ /**
+ * Sets the horizontal quiet zone (white space) added to the left and to the right of this symbol.
+ *
+ * @param quietZoneHorizontal the horizontal quiet zone (white space) added to the left and to the right of this symbol
+ */
+ public void setQuietZoneHorizontal(int quietZoneHorizontal) {
+ this.quietZoneHorizontal = quietZoneHorizontal;
+ }
+
+ /**
+ * Returns the vertical quiet zone (white space) added above and below this symbol.
+ *
+ * @return the vertical quiet zone (white space) added above and below this symbol
+ */
+ public int getQuietZoneVertical() {
+ return quietZoneVertical;
+ }
+
+ /**
+ * Sets the vertical quiet zone (white space) added above and below this symbol.
+ *
+ * @param quietZoneVertical the vertical quiet zone (white space) added above and below this symbol
+ */
+ public void setQuietZoneVertical(int quietZoneVertical) {
+ this.quietZoneVertical = quietZoneVertical;
+ }
+
+ /**
+ * Returns the name of the font to use to render the human-readable text.
+ *
+ * @return the name of the font to use to render the human-readable text
+ */
+ public String getFontName() {
+ return fontName;
+ }
+
+ /**
+ * Sets the name of the font to use to render the human-readable text (default value is Helvetica
).
+ *
+ * @param fontName the name of the font to use to render the human-readable text
+ */
+ public void setFontName(String fontName) {
+ this.fontName = fontName;
+ }
+
+ /**
+ * Returns the size of the font to use to render the human-readable text.
+ *
+ * @return the size of the font to use to render the human-readable text
+ */
+ public double getFontSize() {
+ return fontSize;
+ }
+
+ /**
+ * Sets the size of the font to use to render the human-readable text (default value is 8
).
+ *
+ * @param fontSize the size of the font to use to render the human-readable text
+ */
+ public void setFontSize(double fontSize) {
+ this.fontSize = fontSize;
+ }
+
+ /**
+ * Gets the width of the encoded symbol, including the horizontal quiet zone.
+ *
+ * @return the width of the encoded symbol
+ */
+ public int getWidth() {
+ return symbolWidth + (2 * quietZoneHorizontal);
+ }
+
+ /**
+ * Returns the height of the symbol, including the human-readable text, if any, as well as the vertical
+ * quiet zone. This height is an approximation, since it is calculated without access to a font engine.
+ *
+ * @return the height of the symbol, including the human-readable text, if any, as well as the vertical
+ * quiet zone
+ */
+ public int getHeight() {
+ return symbolHeight + getHumanReadableHeight() + (2 * quietZoneVertical);
+ }
+
+ /**
+ * Returns the height of the human-readable text, including the space between the text and other symbols.
+ * This height is an approximation, since it is calculated without access to a font engine.
+ *
+ * @return the height of the human-readable text
+ */
+ public int getHumanReadableHeight() {
+ if (texts.isEmpty()) {
+ return 0;
+ } else {
+ return getTheoreticalHumanReadableHeight();
+ }
+ }
+
+ /**
+ * Returns the height of the human-readable text, assuming this symbol had human-readable text.
+ *
+ * @return the height of the human-readable text, assuming this symbol had human-readable text
+ */
+ int getTheoreticalHumanReadableHeight() {
+ return (int) Math.ceil(fontSize * 1.2); // 0.2 space between bars and text
+ }
+
+ /**
+ * Returns a human readable summary of the decisions made by the encoder when creating a symbol.
+ *
+ * @return a human readable summary of the decisions made by the encoder when creating a symbol
+ */
+ public String getEncodeInfo() {
+ return encodeInfo.toString();
+ }
+
+ /**
+ * Returns the location of the human-readable text.
+ *
+ * @return the location of the human-readable text
+ */
+ public HumanReadableLocation getHumanReadableLocation() {
+ return humanReadableLocation;
+ }
+
+ /**
+ * Sets the location of the human-readable text (default value is {@link HumanReadableLocation#BOTTOM}).
+ *
+ * @param humanReadableLocation the location of the human-readable text
+ */
+ public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) {
+ this.humanReadableLocation = humanReadableLocation;
+ }
+
+ protected int positionOf(char thischar, char[] LookUp) {
+ int i, outval = 0;
+
+ for (i = 0; i < LookUp.length; i++) {
+ if (thischar == LookUp[i]) {
+ outval = i;
+ }
+ }
+ return outval;
+ }
+
+ protected String bin2pat(String bin) {
+ boolean black;
+ int i, l;
+ StringBuilder pat = new StringBuilder();
+
+ black = true;
+ l = 0;
+ for (i = 0; i < bin.length(); i++) {
+ if (black) {
+ if (bin.charAt(i) == '1') {
+ l++;
+ } else {
+ black = false;
+ pat.append((char) (l + '0'));
+ l = 1;
+ }
+ } else {
+ if (bin.charAt(i) == '0') {
+ l++;
+ } else {
+ black = true;
+ pat.append((char) (l + '0'));
+ l = 1;
+ }
+ }
+ }
+ pat.append((char) (l + '0'));
+
+ return pat.toString();
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ /**
+ * Set the data to be encoded. Input data will be assumed to be of
+ * the type set by setDataType
.
+ *
+ * @param inputData A String
containing the data to encode
+ */
+ public void setContent(String inputData) {
+ int i;
+ content = inputData;
+ if (inputDataType == DataType.GS1) {
+ content = gs1SanityCheck(inputData);
+ }
+ if (inputDataType == DataType.GS1) {
+ readable = new StringBuilder();
+ for (i = 0; i < inputData.length(); i++) {
+ switch (inputData.charAt(i)) {
+ case '[':
+ readable.append('(');
+ break;
+ case ']':
+ readable.append(')');
+ break;
+ default:
+ readable.append(inputData.charAt(i));
+ break;
+ }
+ }
+ }
+ if (inputDataType == DataType.HIBC) {
+ content = hibcProcess(inputData);
+ }
+ if (!content.isEmpty()) {
+ if (!encode()) {
+ throw new IllegalStateException(errorMsg.toString());
+ }
+ } else {
+ throw new IllegalStateException("No input data");
+ }
+ }
+
+ void eciProcess() {
+ int qmarksBefore, qmarksAfter;
+ int i;
+
+ qmarksBefore = 0;
+ for (i = 0; i < content.length(); i++) {
+ if (content.charAt(i) == '?') {
+ qmarksBefore++;
+ }
+ }
+
+ qmarksAfter = eciEncode("ISO8859_1");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 3;
+ encodeInfo.append("Encoding in ISO 8859-1 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_2");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 4;
+ encodeInfo.append("Encoding in ISO 8859-2 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_3");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 5;
+ encodeInfo.append("Encoding in ISO 8859-3 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_4");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 6;
+ encodeInfo.append("Encoding in ISO 8859-4 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_5");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 7;
+ encodeInfo.append("Encoding in ISO 8859-5 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_6");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 8;
+ encodeInfo.append("Encoding in ISO 8859-6 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_7");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 9;
+ encodeInfo.append("Encoding in ISO 8859-7 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_8");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 10;
+ encodeInfo.append("Encoding in ISO 8859-8 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_9");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 11;
+ encodeInfo.append("Encoding in ISO 8859-9 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_10");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 12;
+ encodeInfo.append("Encoding in ISO 8859-10 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_11");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 13;
+ encodeInfo.append("Encoding in ISO 8859-11 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_13");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 15;
+ encodeInfo.append("Encoding in ISO 8859-13 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_14");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 16;
+ encodeInfo.append("Encoding in ISO 8859-14 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_15");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 17;
+ encodeInfo.append("Encoding in ISO 8859-15 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("ISO8859_16");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 18;
+ encodeInfo.append("Encoding in ISO 8859-16 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("Windows_1250");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 21;
+ encodeInfo.append("Encoding in Windows-1250 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("Windows_1251");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 22;
+ encodeInfo.append("Encoding in Windows-1251 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("Windows_1252");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 23;
+ encodeInfo.append("Encoding in Windows-1252 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("Windows_1256");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 24;
+ encodeInfo.append("Encoding in Windows-1256 character set\n");
+ return;
+ }
+
+ qmarksAfter = eciEncode("SJIS");
+ if (qmarksAfter == qmarksBefore) {
+ eciMode = 20;
+ encodeInfo.append("Encoding in Shift-JIS character set\n");
+ return;
+ }
+
+ /* default */
+ qmarksAfter = eciEncode("UTF8");
+ eciMode = 26;
+ encodeInfo.append("Encoding in UTF-8 character set\n");
+ }
+
+ private int eciEncode(String charset) {
+ /* getBytes replaces unconverted characters to '?', so count
+ the number of question marks to find if conversion was sucessful.
+ */
+ int i, qmarksAfter;
+
+ try {
+ inputBytes = content.getBytes(charset);
+ } catch (UnsupportedEncodingException e) {
+ return -1;
+ }
+
+ qmarksAfter = 0;
+ for (i = 0; i < inputBytes.length; i++) {
+ if (inputBytes[i] == '?') {
+ qmarksAfter++;
+ }
+ }
+
+ return qmarksAfter;
+ }
+
+ abstract boolean encode();
+
+ protected void plotSymbol() {
+ int xBlock, yBlock;
+ double x, y, w, h;
+ boolean black;
+ rectangles.clear();
+ texts.clear();
+ int baseY;
+ if (humanReadableLocation == TOP) {
+ baseY = getTheoreticalHumanReadableHeight();
+ } else {
+ baseY = 0;
+ }
+ h = 0;
+ y = baseY;
+ for (yBlock = 0; yBlock < rowCount; yBlock++) {
+ black = true;
+ x = 0;
+ for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) {
+ char c = pattern[yBlock].charAt(xBlock);
+ w = getModuleWidth(c - '0') * moduleWidth;
+ if (black) {
+ if (rowHeight[yBlock] == -1) {
+ h = defaultHeight;
+ } else {
+ h = rowHeight[yBlock];
+ }
+ if (w != 0 && h != 0) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ rectangles.add(rect);
+ }
+ if (x + w > symbolWidth) {
+ symbolWidth = (int) Math.ceil(x + w);
+ }
+ }
+ black = !black;
+ x += w;
+ }
+ if ((y - baseY + h) > symbolHeight) {
+ symbolHeight = (int) Math.ceil(y - baseY + h);
+ }
+ y += h;
+ }
+ mergeVerticalBlocks();
+ if (humanReadableLocation != NONE && readable.length() > 0) {
+ double baseline;
+ if (humanReadableLocation == TOP) {
+ baseline = fontSize;
+ } else {
+ baseline = getHeight() + fontSize;
+ }
+ double centerX = getWidth() / 2.0;
+ texts.add(new TextBox(centerX, baseline, readable.toString()));
+ }
+ }
+
+ /**
+ * Returns the module width to use for the specified original module width, taking into account any module width ratio
+ * customizations. Intended to be overridden by subclasses that support such module width ratio customization.
+ *
+ * @param originalWidth the original module width
+ * @return the module width to use for the specified original module width
+ */
+ protected double getModuleWidth(int originalWidth) {
+ return originalWidth;
+ }
+
+ /**
+ * Search for rectangles which have the same width and x position, and
+ * which join together vertically and merge them together to reduce the
+ * number of rectangles needed to describe a symbol.
+ */
+ void mergeVerticalBlocks() {
+ for (int i = 0; i < rectangles.size() - 1; i++) {
+ for (int j = i + 1; j < rectangles.size(); j++) {
+ Rectangle2D.Double firstRect = rectangles.get(i);
+ Rectangle2D.Double secondRect = rectangles.get(j);
+ if (roughlyEqual(firstRect.x, secondRect.x) && roughlyEqual(firstRect.width, secondRect.width)) {
+ if (roughlyEqual(firstRect.y + firstRect.height, secondRect.y)) {
+ firstRect.height += secondRect.height;
+ rectangles.set(i, firstRect);
+ rectangles.remove(j);
+ }
+ }
+ }
+ }
+ }
+
+ private String gs1SanityCheck(String source) {
+ // Enforce compliance with GS1 General Specification
+ // http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf
+
+ StringBuilder reduced = new StringBuilder();
+
+ int i, j, lastAi;
+ boolean aiLatch;
+ int bracketLevel, maxBracketLevel, aiLength, maxAiLength, minAiLength;
+ int[] aiValue = new int[100];
+ int[] aiLocation = new int[100];
+ int aiCount;
+ int[] dataLocation = new int[100];
+ int[] dataLength = new int[100];
+ int srcLen = source.length();
+ int errorLatch;
+
+ /* Detect extended ASCII characters */
+ for (i = 0; i < srcLen; i++) {
+ if (source.charAt(i) >= 128) {
+ errorMsg.append("Extended ASCII characters are not supported by GS1");
+ return "";
+ }
+ if (source.charAt(i) < 32) {
+ errorMsg.append("Control characters are not supported by GS1");
+ return "";
+ }
+ }
+
+ if (source.charAt(0) != '[') {
+ errorMsg.append("Data does not start with an AI");
+ return "";
+ }
+
+ /* Check the position of the brackets */
+ bracketLevel = 0;
+ maxBracketLevel = 0;
+ aiLength = 0;
+ maxAiLength = 0;
+ minAiLength = 5;
+ j = 0;
+ aiLatch = false;
+ for (i = 0; i < srcLen; i++) {
+ aiLength += j;
+ if (((j == 1) && (source.charAt(i) != ']'))
+ && ((source.charAt(i) < '0') || (source.charAt(i) > '9'))) {
+ aiLatch = true;
+ }
+ if (source.charAt(i) == '[') {
+ bracketLevel++;
+ j = 1;
+ }
+ if (source.charAt(i) == ']') {
+ bracketLevel--;
+ if (aiLength < minAiLength) {
+ minAiLength = aiLength;
+ }
+ j = 0;
+ aiLength = 0;
+ }
+ if (bracketLevel > maxBracketLevel) {
+ maxBracketLevel = bracketLevel;
+ }
+ if (aiLength > maxAiLength) {
+ maxAiLength = aiLength;
+ }
+ }
+ minAiLength--;
+
+ if (bracketLevel != 0) {
+ /* Not all brackets are closed */
+ errorMsg.append("Malformed AI in input data (brackets don't match)");
+ return "";
+ }
+
+ if (maxBracketLevel > 1) {
+ /* Nested brackets */
+ errorMsg.append("Found nested brackets in input data");
+ return "";
+ }
+
+ if (maxAiLength > 4) {
+ /* AI is too long */
+ errorMsg.append("Invalid AI in input data (AI too long)");
+ return "";
+ }
+
+ if (minAiLength <= 1) {
+ /* AI is too short */
+ errorMsg.append("Invalid AI in input data (AI too short)");
+ return "";
+ }
+
+ if (aiLatch) {
+ /* Non-numeric data in AI */
+ errorMsg.append("Invalid AI in input data (non-numeric characters in AI)");
+ return "";
+ }
+
+ aiCount = 0;
+ for (i = 1; i < srcLen; i++) {
+ if (source.charAt(i - 1) == '[') {
+ aiLocation[aiCount] = i;
+ aiValue[aiCount] = 0;
+ for (j = 0; source.charAt(i + j) != ']'; j++) {
+ aiValue[aiCount] *= 10;
+ aiValue[aiCount] += Character.getNumericValue(source.charAt(i + j));
+ }
+ aiCount++;
+ }
+ }
+
+ for (i = 0; i < aiCount; i++) {
+ dataLocation[i] = aiLocation[i] + 3;
+ if (aiValue[i] >= 100) {
+ dataLocation[i]++;
+ }
+ if (aiValue[i] >= 1000) {
+ dataLocation[i]++;
+ }
+ dataLength[i] = source.length() - dataLocation[i];
+ for (j = source.length() - 1; j >= dataLocation[i]; j--) {
+ if (source.charAt(j) == '[') {
+ dataLength[i] = j - dataLocation[i];
+ }
+ }
+ }
+
+ for (i = 0; i < aiCount; i++) {
+ if (dataLength[i] == 0) {
+ /* No data for given AI */
+ errorMsg.append("Empty data field in input data");
+ return "";
+ }
+ }
+
+ errorLatch = 0;
+ for (i = 0; i < aiCount; i++) {
+ switch (aiValue[i]) {
+ case 0:
+ if (dataLength[i] != 18) {
+ errorLatch = 1;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ if (dataLength[i] != 14) {
+ errorLatch = 1;
+ }
+ break;
+ case 4:
+ if (dataLength[i] != 16) {
+ errorLatch = 1;
+ }
+ break;
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ if (dataLength[i] != 6) {
+ errorLatch = 1;
+ }
+ break;
+ case 20:
+ if (dataLength[i] != 2) {
+ errorLatch = 1;
+ }
+ break;
+ case 23:
+ case 24:
+ case 25:
+ case 39:
+ case 40:
+ case 41:
+ case 42:
+ case 70:
+ case 80:
+ case 81:
+ errorLatch = 2;
+ break;
+ }
+ if (
+ ((aiValue[i] >= 100) && (aiValue[i] <= 179))
+ || ((aiValue[i] >= 1000) && (aiValue[i] <= 1799))
+ || ((aiValue[i] >= 200) && (aiValue[i] <= 229))
+ || ((aiValue[i] >= 2000) && (aiValue[i] <= 2299))
+ || ((aiValue[i] >= 300) && (aiValue[i] <= 309))
+ || ((aiValue[i] >= 3000) && (aiValue[i] <= 3099))
+ || ((aiValue[i] >= 31) && (aiValue[i] <= 36))
+ || ((aiValue[i] >= 310) && (aiValue[i] <= 369))) {
+ errorLatch = 2;
+ }
+ if ((aiValue[i] >= 3100) && (aiValue[i] <= 3699) && dataLength[i] != 6) {
+ errorLatch = 1;
+ }
+ if (
+ ((aiValue[i] >= 370) && (aiValue[i] <= 379))
+ || ((aiValue[i] >= 3700) && (aiValue[i] <= 3799))) {
+ errorLatch = 2;
+ }
+ if ((aiValue[i] >= 410) && (aiValue[i] <= 415)) {
+ if (dataLength[i] != 13) {
+ errorLatch = 1;
+ }
+ }
+ if (
+ ((aiValue[i] >= 4100) && (aiValue[i] <= 4199))
+ || ((aiValue[i] >= 700) && (aiValue[i] <= 703))
+ || ((aiValue[i] >= 800) && (aiValue[i] <= 810))
+ || ((aiValue[i] >= 900) && (aiValue[i] <= 999))
+ || ((aiValue[i] >= 9000) && (aiValue[i] <= 9999))) {
+ errorLatch = 2;
+ }
+
+ if (errorLatch == 1) {
+ errorMsg = new StringBuilder("Invalid data length for AI");
+ return "";
+ }
+
+ if (errorLatch == 2) {
+ errorMsg = new StringBuilder("Invalid AI value");
+ return "";
+ }
+ }
+
+ /* Resolve AI data - put resulting string in 'reduced' */
+ aiLatch = false;
+ for (i = 0; i < srcLen; i++) {
+ if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) {
+ reduced.append(source.charAt(i));
+ }
+ if (source.charAt(i) == '[') {
+ /* Start of an AI string */
+ if (aiLatch) {
+ reduced.append('[');
+ }
+ lastAi = (10 * Character.getNumericValue(source.charAt(i + 1)))
+ + Character.getNumericValue(source.charAt(i + 2));
+ aiLatch = ((lastAi < 0) || (lastAi > 4))
+ && ((lastAi < 11) || (lastAi > 20))
+ && (lastAi != 23) /* legacy support - see 5.3.8.2.2 */
+ && ((lastAi < 31) || (lastAi > 36))
+ && (lastAi != 41);
+ }
+ /* The ']' character is simply dropped from the input */
+ }
+
+ /* the character '[' in the reduced string refers to the FNC1 character */
+ return reduced.toString();
+ }
+
+ private String hibcProcess(String source) {
+ char[] hibcCharTable = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$',
+ '/', '+', '%'};
+
+ int counter, i;
+ String toProcess;
+ char checkDigit;
+
+ if (source.length() > 36) {
+ errorMsg = new StringBuilder("Data too long for HIBC LIC");
+ return "";
+ }
+ source = source.toUpperCase();
+ if (!(source.matches("[A-Z0-9-\\. \\$/+\\%]+?"))) {
+ errorMsg = new StringBuilder("Invalid characters in input");
+ return "";
+ }
+
+ counter = 41;
+ for (i = 0; i < source.length(); i++) {
+ counter += positionOf(source.charAt(i), hibcCharTable);
+ }
+ counter = counter % 43;
+
+ if (counter < 10) {
+ checkDigit = (char) (counter + '0');
+ } else {
+ if (counter < 36) {
+ checkDigit = (char) ((counter - 10) + 'A');
+ } else {
+ switch (counter) {
+ case 36:
+ checkDigit = '-';
+ break;
+ case 37:
+ checkDigit = '.';
+ break;
+ case 38:
+ checkDigit = ' ';
+ break;
+ case 39:
+ checkDigit = '$';
+ break;
+ case 40:
+ checkDigit = '/';
+ break;
+ case 41:
+ checkDigit = '+';
+ break;
+ case 42:
+ checkDigit = '%';
+ break;
+ default:
+ checkDigit = ' ';
+ break; /* Keep compiler happy */
+ }
+ }
+ }
+
+ encodeInfo.append("HIBC Check Digit: ").append(counter).append(" (").append(checkDigit).append(")\n");
+
+ toProcess = "+" + source + checkDigit;
+ return toProcess;
+ }
+
+ /**
+ * Returns the intermediate coding of this bar code. Symbol types that use the test
+ * infrastructure should override this method.
+ *
+ * @return the intermediate coding of this bar code
+ */
+ protected int[] getCodewords() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns this bar code's pattern, converted into a set of corresponding codewords.
+ * Useful for bar codes that encode their content as a pattern.
+ *
+ * @param size the number of digits in each codeword
+ * @return this bar code's pattern, converted into a set of corresponding codewords
+ */
+ int[] getPatternAsCodewords(int size) {
+ if (pattern == null || pattern.length == 0) {
+ return new int[0];
+ } else {
+ int count = (int) Math.ceil(pattern[0].length() / (double) size);
+ int[] codewords = new int[pattern.length * count];
+ for (int i = 0; i < pattern.length; i++) {
+ String row = pattern[i];
+ for (int j = 0; j < count; j++) {
+ int substringStart = j * size;
+ int substringEnd = Math.min((j + 1) * size, row.length());
+ codewords[(i * count) + j] = Integer.parseInt(row.substring(substringStart, substringEnd));
+ }
+ }
+ return codewords;
+ }
+ }
+
+ public enum DataType {
+ UTF8, LATIN1, BINARY, GS1, HIBC, ECI
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java b/barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java
new file mode 100755
index 0000000..39f11a1
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java
@@ -0,0 +1,177 @@
+package org.xbib.graphics.barcode;
+
+/**
+ * Telepen (also known as Telepen Alpha) can encode ASCII text input and
+ * includes a modulo-127 check digit. Telepen Numeric allows compression of
+ * numeric data into a Telepen symbol. Data can consist of pairs of numbers
+ * or pairs consisting of a numerical digit followed an X character.
+ * Telepen Numeric also includes a modulo-127 check digit.
+ */
+public class Telepen extends Symbol {
+
+ public tp_mode mode;
+ private String[] TeleTable = {
+ "1111111111111111", "1131313111", "33313111", "1111313131",
+ "3111313111", "11333131", "13133131", "111111313111", "31333111",
+ "1131113131", "33113131", "1111333111", "3111113131", "1113133111",
+ "1311133111", "111111113131", "3131113111", "11313331", "333331",
+ "111131113111", "31113331", "1133113111", "1313113111", "1111113331",
+ "31131331", "113111113111", "3311113111", "1111131331", "311111113111",
+ "1113111331", "1311111331", "11111111113111", "31313311", "1131311131",
+ "33311131", "1111313311", "3111311131", "11333311", "13133311",
+ "111111311131", "31331131", "1131113311", "33113311", "1111331131",
+ "3111113311", "1113131131", "1311131131", "111111113311", "3131111131",
+ "1131131311", "33131311", "111131111131", "3111131311", "1133111131",
+ "1313111131", "111111131311", "3113111311", "113111111131",
+ "3311111131", "111113111311", "311111111131", "111311111311",
+ "131111111311", "11111111111131", "3131311111", "11313133", "333133",
+ "111131311111", "31113133", "1133311111", "1313311111", "1111113133",
+ "313333", "113111311111", "3311311111", "11113333", "311111311111",
+ "11131333", "13111333", "11111111311111", "31311133", "1131331111",
+ "33331111", "1111311133", "3111331111", "11331133", "13131133",
+ "111111331111", "3113131111", "1131111133", "33111133", "111113131111",
+ "3111111133", "111311131111", "131111131111", "111111111133",
+ "31311313", "113131111111", "3331111111", "1111311313", "311131111111",
+ "11331313", "13131313", "11111131111111", "3133111111", "1131111313",
+ "33111313", "111133111111", "3111111313", "111313111111",
+ "131113111111", "111111111313", "313111111111", "1131131113",
+ "33131113", "11113111111111", "3111131113", "113311111111",
+ "131311111111", "111111131113", "3113111113", "11311111111111",
+ "331111111111", "111113111113", "31111111111111", "111311111113",
+ "131111111113"
+ };
+
+ public Telepen() {
+ mode = tp_mode.NORMAL;
+ }
+
+ public void setNormalMode() {
+ mode = tp_mode.NORMAL;
+ }
+
+ public void setNumericMode() {
+ mode = tp_mode.NUMERIC;
+ }
+
+ @Override
+ public boolean encode() {
+ if (mode == tp_mode.NORMAL) {
+ return normal_mode();
+ } else {
+ return numeric_mode();
+ }
+ }
+
+ private boolean normal_mode() {
+ int count = 0, asciicode, check_digit;
+ StringBuilder p = new StringBuilder();
+ String dest;
+
+ int l = content.length();
+
+ if (!content.matches("[\u0000-\u007F]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ dest = TeleTable['_']; // Start
+ for (int i = 0; i < l; i++) {
+ asciicode = content.charAt(i);
+ p.append(TeleTable[asciicode]);
+ count += asciicode;
+ }
+
+ check_digit = 127 - (count % 127);
+ if (check_digit == 127) {
+ check_digit = 0;
+ }
+
+ p.append(TeleTable[check_digit]);
+
+ encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
+
+ dest += p;
+ dest += TeleTable['z']; // Stop
+
+ readable = new StringBuilder(content);
+ pattern = new String[1];
+ pattern[0] = dest;
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ public boolean numeric_mode() {
+ int count = 0, check_digit;
+ StringBuilder p = new StringBuilder();
+ String t;
+ String dest;
+ int l = content.length();
+ int tl, glyph;
+ char c1, c2;
+
+ //FIXME: Ensure no extended ASCII or Unicode charcters are entered
+ if (!(content.matches("[0-9X]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ /* If input is an odd length, add a leading zero */
+ if ((l & 1) == 1) {
+ t = "0" + content;
+ tl = l + 1;
+ } else {
+ t = content;
+ tl = l;
+ }
+
+ dest = TeleTable['_']; // Start
+ for (int i = 0; i < tl; i += 2) {
+
+ c1 = t.charAt(i);
+ c2 = t.charAt(i + 1);
+
+ /* Input nX is allowed, but Xn is not */
+ if (c1 == 'X') {
+ errorMsg.append("Invalid position of X in data");
+ return false;
+ }
+
+ if (c2 == 'X') {
+ glyph = (c1 - '0') + 17;
+ count += glyph;
+ } else {
+ glyph = ((10 * (c1 - '0')) + (c2 - '0')) + 27;
+ count += glyph;
+ }
+
+ p.append(TeleTable[glyph]);
+ }
+
+ check_digit = 127 - (count % 127);
+ if (check_digit == 127) {
+ check_digit = 0;
+ }
+
+ p.append(TeleTable[check_digit]);
+
+ encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
+
+ dest += p;
+ dest += TeleTable['z']; // Stop
+ readable = new StringBuilder(content);
+ pattern = new String[1];
+ pattern[0] = dest;
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+ return true;
+ }
+
+ public enum tp_mode {
+ NORMAL, NUMERIC
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/Upc.java b/barcode/src/main/java/org/xbib/graphics/barcode/Upc.java
new file mode 100755
index 0000000..43ca5dc
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/Upc.java
@@ -0,0 +1,445 @@
+package org.xbib.graphics.barcode;
+
+import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
+import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
+import org.xbib.graphics.barcode.util.AddOn;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Implements UPC bar code symbology.
+ * According to BS EN 797:1996
+ * UPC-A requires an 11 digit article number. The check digit is calculated.
+ * UPC-E is a zero-compressed version of UPC-A developed for smaller packages.
+ * The code requires a 6 digit article number (digits 0-9). The check digit
+ * is calculated. Also supports Number System 1 encoding by entering a 7-digit
+ * article number stating with the digit 1. In addition EAN-2 and EAN-5 add-on
+ * symbols can be added using the + character followed by the add-on data.
+ *
+ * @author Robert Elliott
+ */
+public class Upc extends Symbol {
+
+ private boolean useAddOn;
+
+ ;
+ private String addOnContent;
+ private Mode mode;
+ private boolean linkageFlag;
+ private String[] setAC = {
+ "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312",
+ "1213", "3112"
+ };
+ private String[] setB = {
+ "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131",
+ "3121", "2113"
+ };
+ private String[] UPCParity0 = {
+ "BBBAAA", "BBABAA", "BBAABA", "BBAAAB", "BABBAA", "BAABBA", "BAAABB",
+ "BABABA", "BABAAB", "BAABAB"
+ }; /* Number set for UPC-E symbol (EN Table 4) */
+ private String[] UPCParity1 = {
+ "AAABBB", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA",
+ "ABABAB", "ABABBA", "ABBABA"
+ }; /* Not covered by BS EN 797 */
+
+ public Upc() {
+ mode = Mode.UPCA;
+ linkageFlag = false;
+ }
+
+ public Mode getMode() {
+ return mode;
+ }
+
+ public void setMode(Mode mode) {
+ this.mode = mode;
+ }
+
+ public void setLinkageFlag() {
+ linkageFlag = true;
+ }
+
+ public void unsetLinkageFlag() {
+ linkageFlag = false;
+ }
+
+ @Override
+ public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) {
+ if (humanReadableLocation == TOP) {
+ throw new IllegalArgumentException("Cannot display human-readable text above UPC bar codes.");
+ } else {
+ super.setHumanReadableLocation(humanReadableLocation);
+ }
+ }
+
+ @Override
+ public boolean encode() {
+ boolean retval;
+ AddOn addOn = new AddOn();
+ String addOnData = "";
+
+ separateContent();
+ if (content.length() == 0) {
+ errorMsg.append("Missing UPC data");
+ retval = false;
+ } else {
+ if (mode == Mode.UPCA) {
+ retval = upca();
+ } else {
+ retval = upce();
+ }
+ }
+
+ if (useAddOn) {
+ addOnData = addOn.calcAddOn(addOnContent);
+ if (addOnData.length() == 0) {
+ errorMsg.append("Invalid Add-On data");
+ retval = false;
+ } else {
+ pattern[0] = pattern[0] + "9" + addOnData;
+
+ //add leading zeroes to add-on text
+ if (addOnContent.length() == 1) {
+ addOnContent = "0" + addOnContent;
+ }
+ if (addOnContent.length() == 3) {
+ addOnContent = "0" + addOnContent;
+ }
+ if (addOnContent.length() == 4) {
+ addOnContent = "0" + addOnContent;
+ }
+ }
+ }
+
+ if (retval) {
+ plotSymbol();
+ }
+ return retval;
+ }
+
+ private void separateContent() {
+ int splitPoint;
+
+ splitPoint = content.indexOf('+');
+ if (splitPoint != -1) {
+ // There is a '+' in the input data, use an add-on EAN2 or EAN5
+ useAddOn = true;
+ addOnContent = content.substring(splitPoint + 1);
+ content = content.substring(0, splitPoint);
+ }
+ }
+
+ private boolean upca() {
+ StringBuilder accumulator;
+ StringBuilder dest;
+ int i;
+ char check;
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (content.length() > 11) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ accumulator = new StringBuilder();
+ for (i = content.length(); i < 11; i++) {
+ accumulator.append("0");
+ }
+ accumulator.append(content);
+ check = calcDigit(accumulator.toString());
+ accumulator.append(check);
+ dest = new StringBuilder("111");
+ for (i = 0; i < 12; i++) {
+ if (i == 6) {
+ dest.append("11111");
+ }
+ dest.append(setAC[Character.getNumericValue(accumulator.charAt(i))]);
+ }
+ dest.append("111");
+
+ encodeInfo.append("Check Digit: ").append(check).append("\n");
+
+ readable = accumulator;
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ return true;
+ }
+
+ private boolean upce() {
+ int i, num_system;
+ char emode, check;
+ StringBuilder source;
+ String parity;
+ StringBuilder dest;
+ char[] equivalent = new char[12];
+ StringBuilder equiv = new StringBuilder();
+
+ if (!(content.matches("[0-9]+"))) {
+ errorMsg.append("Invalid characters in input");
+ return false;
+ }
+
+ if (content.length() > 7) {
+ errorMsg.append("Input data too long");
+ return false;
+ }
+
+ source = new StringBuilder();
+ for (i = content.length(); i < 7; i++) {
+ source.append("0");
+ }
+ source.append(content);
+
+ /* Two number systems can be used - system 0 and system 1 */
+ switch (source.charAt(0)) {
+ case '0':
+ num_system = 0;
+ break;
+ case '1':
+ num_system = 1;
+ break;
+ default:
+ errorMsg.append("Invalid input data");
+ return false;
+ }
+
+ /* Expand the zero-compressed UPCE code to make a UPCA equivalent (EN Table 5) */
+ emode = source.charAt(6);
+ for (i = 0; i < 11; i++) {
+ equivalent[i] = '0';
+ }
+ equivalent[0] = source.charAt(0);
+ equivalent[1] = source.charAt(1);
+ equivalent[2] = source.charAt(2);
+
+ switch (emode) {
+ case '0':
+ case '1':
+ case '2':
+ equivalent[3] = emode;
+ equivalent[8] = source.charAt(3);
+ equivalent[9] = source.charAt(4);
+ equivalent[10] = source.charAt(5);
+ break;
+ case '3':
+ equivalent[3] = source.charAt(3);
+ equivalent[9] = source.charAt(4);
+ equivalent[10] = source.charAt(5);
+ if (((source.charAt(3) == '0') || (source.charAt(3) == '1'))
+ || (source.charAt(3) == '2')) {
+ /* Note 1 - "X3 shall not be equal to 0, 1 or 2" */
+ errorMsg.append("Invalid UPC-E data");
+ return false;
+ }
+ break;
+ case '4':
+ equivalent[3] = source.charAt(3);
+ equivalent[4] = source.charAt(4);
+ equivalent[10] = source.charAt(5);
+ if (source.charAt(4) == '0') {
+ /* Note 2 - "X4 shall not be equal to 0" */
+ errorMsg.append("Invalid UPC-E data");
+ return false;
+ }
+ break;
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ equivalent[3] = source.charAt(3);
+ equivalent[4] = source.charAt(4);
+ equivalent[5] = source.charAt(5);
+ equivalent[10] = emode;
+ if (source.charAt(5) == '0') {
+ /* Note 3 - "X5 shall not be equal to 0" */
+ errorMsg.append("Invalid UPC-E data");
+ return false;
+ }
+ break;
+ }
+
+ for (i = 0; i < 11; i++) {
+ equiv.append(equivalent[i]);
+ }
+
+ /* Get the check digit from the expanded UPCA code */
+ check = calcDigit(equiv.toString());
+
+ encodeInfo.append("Check Digit: ").append(check).append("\n");
+
+ /* Use the number system and check digit information to choose a parity scheme */
+ if (num_system == 1) {
+ parity = UPCParity1[check - '0'];
+ } else {
+ parity = UPCParity0[check - '0'];
+ }
+
+ /* Take all this information and make the barcode pattern */
+
+ /* start character */
+ dest = new StringBuilder("111");
+
+ for (i = 0; i <= 5; i++) {
+ switch (parity.charAt(i)) {
+ case 'A':
+ dest.append(setAC[source.charAt(i + 1) - '0']);
+ break;
+ case 'B':
+ dest.append(setB[source.charAt(i + 1) - '0']);
+ break;
+ }
+ }
+
+ /* stop character */
+ dest.append("111111");
+
+ readable = new StringBuilder(source.toString()).append(check);
+ pattern = new String[1];
+ pattern[0] = dest.toString();
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ return true;
+ }
+
+ private char calcDigit(String x) {
+ int count = 0;
+ int c, cdigit;
+ for (int i = 0; i < 11; i++) {
+ c = Character.getNumericValue(x.charAt(i));
+ if ((i % 2) == 0) {
+ c = c * 3;
+ }
+ count = count + c;
+ }
+ cdigit = 10 - (count % 10);
+ if (cdigit == 10) {
+ cdigit = 0;
+ }
+
+ return (char) (cdigit + '0');
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ boolean black;
+ int compositeOffset = 0;
+ int shortLongDiff = 5;
+ getRectangles().clear();
+ getTexts().clear();
+ black = true;
+ x = 0;
+ if (linkageFlag) {
+ compositeOffset = 6;
+ }
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ if (black) {
+ y = 0;
+ black = false;
+ w = pattern[0].charAt(xBlock) - '0';
+ h = defaultHeight;
+ /* Add extension to guide bars */
+ if (mode == Mode.UPCA) {
+ if ((x < 10) || (x > 84)) {
+ h += shortLongDiff;
+ }
+ if ((x > 45) && (x < 49)) {
+ h += shortLongDiff;
+ }
+ if (x > 95) {
+ // Drop add-on
+ h -= 8;
+ y = 8;
+ }
+ if (linkageFlag && (x == 0) || (x == 94)) {
+ h += 2;
+ y -= 2;
+ }
+ } else {
+ if ((x < 4) || (x > 45)) {
+ h += shortLongDiff;
+ }
+ if (x > 52) {
+ // Drop add-on
+ h -= 8;
+ y = 8;
+ }
+ if (linkageFlag && (x == 0) || (x == 50)) {
+ h += 2;
+ y -= 2;
+ }
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x + 6, y + compositeOffset, w, h);
+ getRectangles().add(rect);
+ if ((x + w + 12) > symbolWidth) {
+ symbolWidth = x + w + 12;
+ }
+ } else {
+ black = true;
+ }
+ x += pattern[0].charAt(xBlock) - '0';
+ }
+
+ if (linkageFlag) {
+ // Add separator for composite symbology
+ if (mode == Mode.UPCA) {
+ getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(94 + 6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(95 + 6, 2, 1, 2));
+ } else { // UPCE
+ getRectangles().add(new Rectangle2D.Double(0 + 6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(50 + 6, 0, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
+ getRectangles().add(new Rectangle2D.Double(51 + 6, 2, 1, 2));
+ }
+ }
+
+ symbolHeight = defaultHeight + 5;
+
+ /* Now add the text */
+ if (getHumanReadableLocation() != NONE) {
+ double baseline = getHeight() + fontSize - shortLongDiff + compositeOffset;
+ double addOnBaseline = 6.0 + compositeOffset;
+ if (mode == Mode.UPCA) {
+ getTexts().add(new TextBox(3, baseline, readable.substring(0, 1)));
+ getTexts().add(new TextBox(34, baseline, readable.substring(1, 6)));
+ getTexts().add(new TextBox(73, baseline, readable.substring(6, 11)));
+ getTexts().add(new TextBox(104, baseline, readable.substring(11, 12)));
+ if (useAddOn) {
+ if (addOnContent.length() == 2) {
+ getTexts().add(new TextBox(118, addOnBaseline, addOnContent));
+ } else {
+ getTexts().add(new TextBox(133, addOnBaseline, addOnContent));
+ }
+ }
+ } else { // UPCE
+ getTexts().add(new TextBox(3, baseline, readable.substring(0, 1)));
+ getTexts().add(new TextBox(30, baseline, readable.substring(1, 7)));
+ getTexts().add(new TextBox(61, baseline, readable.substring(7, 8)));
+ if (useAddOn) {
+ if (addOnContent.length() == 2) {
+ getTexts().add(new TextBox(75, addOnBaseline, addOnContent));
+ } else {
+ getTexts().add(new TextBox(90, addOnBaseline, addOnContent));
+ }
+ }
+ }
+ }
+ }
+
+ public enum Mode {
+ UPCA, UPCE
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java b/barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java
new file mode 100755
index 0000000..e3461b8
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java
@@ -0,0 +1,451 @@
+package org.xbib.graphics.barcode;
+
+import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
+import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+import java.math.BigInteger;
+
+/**
+ * USPS OneCode (also known as Intelligent Mail Barcode)
+ * According to USPS-B-3200F
+ * Specification at https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf
+ * OneCode is a fixed length (65-bar) symbol which combines routing and
+ * customer information in a single symbol. Input data consists of a 20 digit
+ * tracking code, followed by a dash (-), followed by a delivery point
+ * zip-code which can be 0, 5, 9 or 11 digits in length.
+ */
+public class UspsOneCode extends Symbol {
+
+ /* The following lookup tables were generated using the code in Appendix C */
+
+ private int[] byte_array = new int[13];
+
+ private int[] AppxD_I = { /* Appendix D Table 1 - 5 of 13 characters */
+ 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780,
+ 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, 0x1740,
+ 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0,
+ 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0,
+ 0x007C, 0x07C0, 0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720,
+ 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0,
+ 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6, 0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0,
+ 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60,
+ 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360, 0x00DA, 0x0B60,
+ 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0,
+ 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0,
+ 0x00F8, 0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710,
+ 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90,
+ 0x0133, 0x1990, 0x0135, 0x1590, 0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90,
+ 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50,
+ 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159, 0x1350, 0x015A, 0x0B50,
+ 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0,
+ 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0,
+ 0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30,
+ 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30,
+ 0x019C, 0x0730, 0x01A3, 0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0,
+ 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0,
+ 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470, 0x01C6, 0x0C70, 0x01C9, 0x1270,
+ 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570,
+ 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8, 0x02F0,
+ 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08,
+ 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988,
+ 0x0235, 0x1588, 0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788,
+ 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948,
+ 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259, 0x1348, 0x025A, 0x0B48, 0x025C, 0x0748,
+ 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8,
+ 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8, 0x0278, 0x03C8,
+ 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928,
+ 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728,
+ 0x02A3, 0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8,
+ 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8,
+ 0x02C3, 0x1868, 0x02C5, 0x1468, 0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68,
+ 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368,
+ 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307, 0x1C18, 0x030B, 0x1A18,
+ 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18,
+ 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498,
+ 0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198,
+ 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458,
+ 0x0346, 0x0C58, 0x0349, 0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158,
+ 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8,
+ 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38, 0x0389, 0x1238, 0x038A, 0x0A38,
+ 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8,
+ 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4, 0x0478,
+ 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04,
+ 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984,
+ 0x0435, 0x1584, 0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784,
+ 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944,
+ 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459, 0x1344, 0x045A, 0x0B44, 0x045C, 0x0744,
+ 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4,
+ 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4, 0x0487, 0x1C24,
+ 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524,
+ 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4,
+ 0x04A5, 0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4,
+ 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464,
+ 0x04C6, 0x0C64, 0x04C9, 0x1264, 0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164,
+ 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14,
+ 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513, 0x1914, 0x0515, 0x1514,
+ 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894,
+ 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694,
+ 0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454,
+ 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154,
+ 0x0552, 0x0954, 0x0561, 0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434,
+ 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134,
+ 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4, 0x05C1, 0x1074, 0x05C2, 0x0874,
+ 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C,
+ 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C, 0x070C,
+ 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C,
+ 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C,
+ 0x0646, 0x0C4C, 0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C,
+ 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C,
+ 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691, 0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC,
+ 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C,
+ 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C, 0x0712, 0x091C,
+ 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C,
+ 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702,
+ 0x081E, 0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82,
+ 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82,
+ 0x0847, 0x1C42, 0x084B, 0x1A42, 0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942,
+ 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2,
+ 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A, 0x0AC2, 0x0871, 0x11C2,
+ 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22,
+ 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22,
+ 0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2,
+ 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62,
+ 0x08C9, 0x1262, 0x08CA, 0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2,
+ 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912,
+ 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312, 0x091A, 0x0B12, 0x0923, 0x1892,
+ 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192,
+ 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949, 0x1252,
+ 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432,
+ 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2,
+ 0x09C1, 0x1072, 0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A,
+ 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A,
+ 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26, 0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A,
+ 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A,
+ 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A, 0x0A86, 0x0C2A,
+ 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A,
+ 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A,
+ 0x0B41, 0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606,
+ 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306,
+ 0x0C23, 0x1886, 0x0C25, 0x1486, 0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186,
+ 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6,
+ 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91, 0x1126, 0x0CA1, 0x10A6,
+ 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116,
+ 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E,
+ 0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E,
+ 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701,
+ 0x1027, 0x1C81, 0x102B, 0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581,
+ 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941,
+ 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1, 0x1065, 0x14C1, 0x1069, 0x12C1,
+ 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921,
+ 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9, 0x12A1,
+ 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161,
+ 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511,
+ 0x1119, 0x1311, 0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191,
+ 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431,
+ 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B, 0x1A09, 0x120D, 0x1609, 0x1213, 0x1909,
+ 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289,
+ 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429, 0x1303, 0x1819,
+ 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905,
+ 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825,
+ 0x1503, 0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903,
+ 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952,
+ 0x08E2, 0x064C, 0x0554, 0x04E4, 0x0358, 0x02E8, 0x01F0
+ };
+ private int[] AppxD_II = { /* Appendix D Table II - 2 of 13 characters */
+ 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00,
+ 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018, 0x0300,
+ 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180,
+ 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140,
+ 0x0060, 0x00C0, 0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220,
+ 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210,
+ 0x0201, 0x1008, 0x0202, 0x0808, 0x0204, 0x0408, 0x0401, 0x1004, 0x0402, 0x0804,
+ 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0
+ };
+ private int[] AppxD_IV = { /* Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */
+ 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97,
+ 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94, 44, 77, 112, 70, 100, 39, 30, 107,
+ 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84,
+ 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9, 17, 49, 124, 79, 64, 91, 42, 69, 53,
+ 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55,
+ 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83, 13, 73, 3
+ };
+
+ public UspsOneCode() {
+ this.defaultHeight = 8;
+ setHumanReadableLocation(HumanReadableLocation.NONE);
+ }
+
+ @Override
+ public boolean encode() {
+ StringBuilder zip = new StringBuilder();
+ String zip_adder;
+ StringBuilder tracker = new StringBuilder();
+ int i, j;
+ int length = content.length();
+ BigInteger accum;
+ BigInteger x_reg;
+ BigInteger mask;
+ int usps_crc;
+ int[] codeword = new int[10];
+ int[] characters = new int[10];
+ boolean[] bar_map = new boolean[130];
+ char c;
+
+ if (!content.matches("[0-9\u002D]+")) {
+ errorMsg.append("Invalid characters in input data");
+ return false;
+ }
+
+ if (length > 32) {
+ errorMsg.append("Input too long");
+ return false;
+ }
+
+ /* separate the tracking code from the routing code */
+ j = 0;
+ for (i = 0; i < length; i++) {
+ if (content.charAt(i) == '-') {
+ j = 1;
+ } else {
+ if (j == 0) {
+ /* reading tracker */
+ tracker.append(content.charAt(i));
+ } else {
+ /* reading zip code */
+ zip.append(content.charAt(i));
+ }
+ }
+ }
+
+ if (tracker.length() != 20) {
+ errorMsg.append("Invalid length tracking code");
+ return false;
+ }
+
+ if (zip.length() > 11) {
+ errorMsg.append("Invalid ZIP code");
+ return false;
+ }
+
+ /* *** Step 1 - Conversion of Data Fields into Binary Data *** */
+ /* Routing code first */
+ if (zip.length() > 0) {
+ x_reg = new BigInteger(zip.toString());
+ } else {
+ x_reg = new BigInteger("0");
+ }
+
+ /* add weight to routing code */
+ if (zip.length() > 9) {
+ zip_adder = "1000100001";
+ } else {
+ if (zip.length() > 5) {
+ zip_adder = "100001";
+ } else {
+ if (zip.length() > 0) {
+ zip_adder = "1";
+ } else {
+ zip_adder = "0";
+ }
+ }
+ }
+
+ accum = new BigInteger(zip_adder);
+ accum = accum.add(x_reg);
+ accum = accum.multiply(BigInteger.valueOf(10));
+ accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0))));
+ accum = accum.multiply(BigInteger.valueOf(5));
+ accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1))));
+ for (i = 2; i < tracker.length(); i++) {
+ accum = accum.multiply(BigInteger.valueOf(10));
+ accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i))));
+ }
+
+ /* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */
+
+ for (i = 0; i < 13; i++) {
+ mask = accum.shiftRight(96 - (8 * i));
+ mask = mask.and(new BigInteger("255"));
+ byte_array[i] = mask.intValue();
+ }
+
+ usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence();
+
+ /* *** Step 3 - Conversion from Binary Data to Codewords *** */
+ /* start with codeword J which is base 636 */
+
+ x_reg = accum.mod(BigInteger.valueOf(636));
+ codeword[9] = x_reg.intValue();
+ accum = accum.subtract(x_reg);
+ accum = accum.divide(BigInteger.valueOf(636));
+
+ for (i = 8; i >= 0; i--) {
+ x_reg = accum.mod(BigInteger.valueOf(1365));
+ codeword[i] = x_reg.intValue();
+ accum = accum.subtract(x_reg);
+ accum = accum.divide(BigInteger.valueOf(1365));
+ }
+
+ for (i = 0; i < 9; i++) {
+ if (codeword[i] == 1365) {
+ codeword[i] = 0;
+ }
+ }
+
+ /* *** Step 4 - Inserting Additional Information into Codewords *** */
+ codeword[9] = codeword[9] * 2;
+
+ if (usps_crc >= 1024) {
+ codeword[0] += 659;
+ }
+
+ encodeInfo.append("Codewords: ");
+ for (i = 0; i < 10; i++) {
+ encodeInfo.append(Integer.toString(codeword[i])).append(" ");
+ }
+ encodeInfo.append("\n");
+
+ /* *** Step 5 - Conversion from Codewords to Characters *** */
+
+ for (i = 0; i < 10; i++) {
+ if (codeword[i] < 1287) {
+ characters[i] = AppxD_I[codeword[i]];
+ } else {
+ characters[i] = AppxD_II[codeword[i] - 1287];
+ }
+ }
+
+ for (i = 0; i < 10; i++) {
+ if ((usps_crc & (1 << i)) != 0) {
+ characters[i] = 0x1FFF - characters[i];
+ }
+ }
+
+ /* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */
+
+ for (i = 0; i < 10; i++) {
+ for (j = 0; j < 13; j++) {
+ bar_map[AppxD_IV[(13 * i) + j] - 1] = (characters[i] & (1 << j)) != 0;
+ }
+ }
+
+ readable = new StringBuilder(content);
+ pattern = new String[1];
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+
+ pattern[0] = "";
+ for (i = 0; i < 65; i++) {
+ c = 'T';
+ if (bar_map[i]) {
+ c = 'D';
+ }
+ if (bar_map[i + 65]) {
+ c = 'A';
+ }
+ if (bar_map[i] && bar_map[i + 65]) {
+ c = 'F';
+ }
+ pattern[0] += c;
+ }
+
+ encodeInfo.append("Encoding: ").append(pattern[0]).append("\n");
+
+ plotSymbol();
+ return true;
+ }
+
+ private int USPS_MSB_Math_CRC11GenerateFrameCheckSequence() {
+ int GeneratorPolynomial = 0x0F35;
+ int FrameCheckSequence = 0x07FF;
+ int Data;
+ int ByteIndex, Bit;
+ int ByteArrayPtr = 0;
+
+ /* Do most significant byte skipping the 2 most significant bits */
+ Data = byte_array[ByteArrayPtr] << 5;
+ ByteArrayPtr++;
+ for (Bit = 2; Bit < 8; Bit++) {
+ if (((FrameCheckSequence ^ Data) & 0x400) != 0)
+ FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial;
+ else
+ FrameCheckSequence = (FrameCheckSequence << 1);
+ FrameCheckSequence &= 0x7FF;
+ Data <<= 1;
+ }
+ /* Do rest of the bytes */
+ for (ByteIndex = 1; ByteIndex < 13; ByteIndex++) {
+ Data = byte_array[ByteArrayPtr] << 3;
+ ByteArrayPtr++;
+ for (Bit = 0; Bit < 8; Bit++) {
+ if (((FrameCheckSequence ^ Data) & 0x0400) != 0) {
+ FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial;
+ } else {
+ FrameCheckSequence = (FrameCheckSequence << 1);
+ }
+ FrameCheckSequence &= 0x7FF;
+ Data <<= 1;
+ }
+ }
+ return FrameCheckSequence;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock, shortHeight, longHeight;
+ double x, y, w, h;
+ getRectangles().clear();
+ getTexts().clear();
+ int baseY;
+ if (getHumanReadableLocation() == TOP) {
+ baseY = getTheoreticalHumanReadableHeight();
+ } else {
+ baseY = 0;
+ }
+
+ x = 0;
+ w = moduleWidth;
+ y = 0;
+ h = 0;
+ shortHeight = (int) (0.25 * defaultHeight);
+ longHeight = (int) (0.625 * defaultHeight);
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+
+ switch (pattern[0].charAt(xBlock)) {
+ case 'A':
+ y = baseY;
+ h = longHeight;
+ break;
+ case 'D':
+ y = baseY + defaultHeight - longHeight;
+ h = longHeight;
+ break;
+ case 'F':
+ y = baseY;
+ h = defaultHeight;
+ break;
+ case 'T':
+ y = baseY + defaultHeight - longHeight;
+ h = shortHeight;
+ break;
+ }
+ Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
+ getRectangles().add(rect);
+ x += (2.43 * w);
+ }
+ symbolWidth = (int) Math.ceil(((pattern[0].length() - 1) * 2.43 * w) + w); // final bar doesn't need extra whitespace
+ symbolHeight = defaultHeight;
+ if (getHumanReadableLocation() != NONE && readable.length() > 0) {
+ double baseline;
+ if (getHumanReadableLocation() == TOP) {
+ baseline = fontSize;
+ } else {
+ baseline = getHeight() + fontSize;
+ }
+ double centerX = getWidth() / 2.0;
+ getTexts().add(new TextBox(centerX, baseline, readable.toString()));
+ }
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java b/barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java
new file mode 100755
index 0000000..0289810
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java
@@ -0,0 +1,114 @@
+package org.xbib.graphics.barcode;
+
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * USPS Intelligent Mail Package Barcode (IMpb)
+ * A linear barcode based on GS1-128. Includes additional data checks.
+ * Specification at https://ribbs.usps.gov/intelligentmail_package/documents/tech_guides/BarcodePackageIMSpec.pdf
+ */
+public class UspsPackage extends Symbol {
+
+ @Override
+ public boolean encode() {
+ StringBuilder hrt;
+ StringBuilder spacedHrt;
+ boolean fourTwenty = false;
+ int bracketCount = 0;
+
+ if (!(content.matches("[0-9\\[\\]]+"))) {
+ /* Input must be numeric only */
+ errorMsg.append("Invalid IMpd data");
+ return false;
+ }
+
+ if ((content.length() % 2) != 0) {
+ /* Input must be even length */
+ errorMsg.append("Invalid IMpd data");
+ return false;
+ }
+
+ Code128 code128 = new Code128();
+ code128.unsetCc();
+ code128.setDataType(DataType.GS1);
+ code128.setContent(content);
+
+ if (content.length() > 4) {
+ fourTwenty = ((content.charAt(1) == '4') && (content.charAt(2) == '2') &&
+ (content.charAt(3) == '0'));
+ }
+
+ hrt = new StringBuilder();
+ for (int i = 0; i < content.length(); i++) {
+ if (content.charAt(i) == '[') {
+ bracketCount++;
+ }
+ if (!(fourTwenty && bracketCount < 2)) {
+ if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) {
+ hrt.append(content.charAt(i));
+ }
+ }
+ }
+
+ spacedHrt = new StringBuilder();
+ for (int i = 0; i < hrt.length(); i++) {
+ spacedHrt.append(hrt.charAt(i));
+ if (i % 4 == 3) {
+ spacedHrt.append(" ");
+ }
+ }
+
+ readable = new StringBuilder(spacedHrt.toString());
+ pattern = new String[1];
+ pattern[0] = code128.pattern[0];
+ rowCount = 1;
+ rowHeight = new int[1];
+ rowHeight[0] = -1;
+ plotSymbol();
+
+ return true;
+ }
+
+ @Override
+ protected void plotSymbol() {
+ int xBlock;
+ int x, y, w, h;
+ boolean black;
+ int offset = 20;
+ int yoffset = 15;
+ String banner = "USPS TRACKING #";
+ getRectangles().clear();
+ getTexts().clear();
+ y = yoffset;
+ h = 0;
+ black = true;
+ x = 0;
+ for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
+ w = pattern[0].charAt(xBlock) - '0';
+ if (black) {
+ if (rowHeight[0] == -1) {
+ h = defaultHeight;
+ } else {
+ h = rowHeight[0];
+ }
+ if (w != 0 && h != 0) {
+ Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h);
+ getRectangles().add(rect);
+ }
+ symbolWidth = x + w + (2 * offset);
+ }
+ black = !black;
+ x += w;
+ }
+ symbolHeight = h + (2 * yoffset);
+ // Add boundary bars
+ Rectangle2D.Double topBar = new Rectangle2D.Double(0, 0, symbolWidth, 2);
+ Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, symbolHeight - 2, symbolWidth, 2);
+ getRectangles().add(topBar);
+ getRectangles().add(bottomBar);
+ double centerX = getWidth() / 2.0;
+ getTexts().add(new TextBox(centerX, getHeight() - 6.0, readable.toString()));
+ getTexts().add(new TextBox(centerX, 12.0, banner));
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java b/barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java
new file mode 100755
index 0000000..59f2609
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java
@@ -0,0 +1,150 @@
+package org.xbib.graphics.barcode.render;
+
+import org.xbib.graphics.barcode.HumanReadableLocation;
+import org.xbib.graphics.barcode.Symbol;
+import org.xbib.graphics.barcode.util.Hexagon;
+import org.xbib.graphics.barcode.util.TextBox;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.font.TextAttribute;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Renders symbologies using the Java Graphics API.
+ */
+public class GraphicsRenderer {
+
+ /**
+ * The graphics to render to.
+ */
+ private final Graphics2D g2d;
+
+ /**
+ * The scaling factor.
+ */
+ private final double scalingFactor;
+
+ /**
+ * The paper (background) color.
+ */
+ private final Color background;
+
+ /**
+ * The ink (foreground) color.
+ */
+ private final Color foreground;
+
+ private final boolean antialias;
+
+ private final boolean transparentBackground;
+
+ /**
+ * Creates a new Java 2D renderer.
+ *
+ * @param g2d the graphics to render to
+ * @param scalingFactor the scaling factor to apply
+ * @param background the paper (background) color
+ * @param foreground the ink (foreground) color
+ * @param antialias if true give anti alias hint
+ */
+ public GraphicsRenderer(Graphics2D g2d,
+ double scalingFactor,
+ Color background,
+ Color foreground,
+ boolean antialias,
+ boolean transparentBackground) {
+ this.g2d = g2d;
+ this.scalingFactor = scalingFactor;
+ this.background = background;
+ this.foreground = foreground;
+ this.antialias = antialias;
+ this.transparentBackground = transparentBackground;
+ }
+
+ public void render(Symbol symbol) {
+ RenderingHints oldRenderingHints = g2d.getRenderingHints();
+ Color oldColor = g2d.getColor();
+ Color oldBackground = g2d.getBackground();
+ if (antialias) {
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ } else {
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+ }
+ double marginX = symbol.getQuietZoneHorizontal() * scalingFactor;
+ double marginY = symbol.getQuietZoneVertical() * scalingFactor;
+ g2d.setBackground(background);
+ if (!transparentBackground) {
+ g2d.setColor(background);
+ g2d.fill(g2d.getDeviceConfiguration().getBounds());
+ g2d.setColor(foreground);
+ }
+ for (Rectangle2D.Double rect : symbol.getRectangles()) {
+ double x = rect.x * scalingFactor + marginX;
+ double y = rect.y * scalingFactor + marginY;
+ double w = rect.width * scalingFactor;
+ double h = rect.height * scalingFactor;
+ Path2D path = new Path2D.Double();
+ path.moveTo(x, y);
+ path.lineTo(x + w, y);
+ path.lineTo(x + w, y + h);
+ path.lineTo(x, y + h);
+ path.closePath();
+ g2d.fill(path);
+ }
+ if (symbol.getHumanReadableLocation() != HumanReadableLocation.NONE) {
+ Map attributes = new HashMap<>();
+ attributes.put(TextAttribute.TRACKING, 0);
+ Font f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * scalingFactor)).deriveFont(attributes);
+ Font oldFont = g2d.getFont();
+ g2d.setFont(f);
+ FontMetrics fm = g2d.getFontMetrics();
+ for (TextBox text : symbol.getTexts()) {
+ Rectangle2D bounds = fm.getStringBounds(text.text, g2d);
+ double x = (text.x * scalingFactor) - (bounds.getWidth() / 2) + marginX;
+ double y = (text.y * scalingFactor) + marginY;
+ g2d.drawString(text.text, (float) x, (float) y);
+ }
+ g2d.setFont(oldFont);
+ }
+ for (Hexagon hexagon : symbol.getHexagons()) {
+ Path2D path = new Path2D.Double();
+ path.moveTo(hexagon.pointX[0] * scalingFactor + marginX, hexagon.pointY[0] * scalingFactor + marginY);
+ for(int i = 1; i < 6; ++i) {
+ double x = hexagon.pointX[i] * scalingFactor + marginX;
+ double y = hexagon.pointY[i] * scalingFactor + marginY;
+ path.lineTo(x, y);
+ }
+ path.closePath();
+ g2d.fill(path);
+ }
+ List targets = symbol.getTarget();
+ for (int i = 0; i < targets.size(); i++) {
+ Ellipse2D.Double ellipse = targets.get(i);
+ double x = ellipse.x * scalingFactor + marginX;
+ double y = ellipse.y * scalingFactor + marginY;
+ double w = ellipse.width * scalingFactor;
+ double h = ellipse.height * scalingFactor;
+ if ((i & 1) == 0) {
+ g2d.setColor(foreground);
+ } else {
+ g2d.setColor(background);
+ }
+ g2d.fill(new Ellipse2D.Double(x, y, w, h));
+ }
+ g2d.setColor(oldColor);
+ g2d.setBackground(oldBackground);
+ g2d.setRenderingHints(oldRenderingHints);
+ }
+
+ public void close() {
+ g2d.dispose();
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java
new file mode 100755
index 0000000..4499e80
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java
@@ -0,0 +1,112 @@
+package org.xbib.graphics.barcode.util;
+
+/**
+ * Encode Add-On barcodes from UPC/EAN.
+ */
+public class AddOn {
+ private String content;
+ private String dest;
+
+ private String[] EANsetA = {
+ "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213",
+ "3112"
+ };
+ private String[] EANsetB = {
+ "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121",
+ "2113"
+ };
+
+ private String[] EAN2Parity = {
+ "AA", "AB", "BA", "BB"
+ };
+ private String[] EAN5Parity = {
+ "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA",
+ "ABAAB", "AABAB"
+ };
+
+ public String calcAddOn(String input) {
+ dest = "";
+ content = input;
+
+ if (!(content.matches("[0-9]{1,5}"))) {
+ return "";
+ }
+
+ if (content.length() > 2) {
+ ean5();
+ } else {
+ ean2();
+ }
+
+ return dest;
+ }
+
+ private void ean2() {
+ String parity;
+ String accumulator = "";
+ int i, code_value;
+
+ if (!(content.matches("[0-9]+?"))) {
+ return;
+ }
+
+ for (i = content.length(); i < 2; i++) {
+ accumulator += "0";
+ }
+ accumulator += content;
+
+ code_value = ((accumulator.charAt(0) - '0') * 10)
+ + (accumulator.charAt(1) - '0');
+ parity = EAN2Parity[code_value % 4];
+
+ dest = "112"; /* Start */
+ for (i = 0; i < 2; i++) {
+ if ((parity.charAt(i) == 'B')) {
+ dest += EANsetB[Character.getNumericValue(accumulator.charAt(i))];
+ } else {
+ dest += EANsetA[Character.getNumericValue(accumulator.charAt(i))];
+ }
+ if (i != 1) { /* Glyph separator */
+ dest += "11";
+ }
+ }
+ }
+
+ private void ean5() {
+ String parity;
+ String accumulator = "";
+ int i, parity_sum;
+
+ if (!(content.matches("[0-9]+?"))) {
+ return;
+ }
+
+ for (i = content.length(); i < 5; i++) {
+ accumulator += "0";
+ }
+ accumulator += content;
+
+ parity_sum = 0;
+ for (i = 0; i < 5; i++) {
+ if ((i % 2) == 0) {
+ parity_sum += 3 * (accumulator.charAt(i) - '0');
+ } else {
+ parity_sum += 9 * (accumulator.charAt(i) - '0');
+ }
+ }
+
+ parity = EAN5Parity[parity_sum % 10];
+
+ dest = "112"; /* Start */
+ for (i = 0; i < 5; i++) {
+ if ((parity.charAt(i) == 'B')) {
+ dest += EANsetB[Character.getNumericValue(accumulator.charAt(i))];
+ } else {
+ dest += EANsetA[Character.getNumericValue(accumulator.charAt(i))];
+ }
+ if (i != 4) { /* Glyph separator */
+ dest += "11";
+ }
+ }
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java
new file mode 100755
index 0000000..8b27f37
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java
@@ -0,0 +1,23 @@
+package org.xbib.graphics.barcode.util;
+
+/**
+ * Calculate a set of points to make a hexagon.
+ */
+public class Hexagon {
+
+ private static final double INK_SPREAD = 1.25;
+
+ private static final double[] OFFSET_X = {0.0, 0.86, 0.86, 0.0, -0.86, -0.86};
+
+ private static final double[] OFFSET_Y = {1.0, 0.5, -0.5, -1.0, -0.5, 0.5};
+
+ public final double[] pointX = new double[6];
+ public final double[] pointY = new double[6];
+
+ public Hexagon(double centreX, double centreY) {
+ for (int i = 0; i < 6; i++) {
+ pointX[i] = centreX + (OFFSET_X[i] * INK_SPREAD);
+ pointY[i] = centreY + (OFFSET_Y[i] * INK_SPREAD);
+ }
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java
new file mode 100755
index 0000000..30770dc
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java
@@ -0,0 +1,86 @@
+package org.xbib.graphics.barcode.util;
+
+/**
+ */
+public class ReedSolomon {
+ private int[] res;
+ private int logmod;
+ private int rlen;
+ private int[] logt;
+ private int[] alog;
+ private int[] rspoly;
+
+ 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;
+ }
+ }
+ }
+}
diff --git a/barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java b/barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java
new file mode 100755
index 0000000..e5a438e
--- /dev/null
+++ b/barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java
@@ -0,0 +1,40 @@
+package org.xbib.graphics.barcode.util;
+
+/**
+ * A simple text item class.
+ */
+public class TextBox {
+
+ /**
+ * X position that the text should be centered on horizontally.
+ */
+ public final double x;
+
+ /**
+ * Y position of the text baseline.
+ */
+ public final double y;
+
+ /**
+ * Text value.
+ */
+ public final String text;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param x the X position that the text should be centered on horizontally
+ * @param y the Y position of the text baseline
+ * @param text the text value
+ */
+ public TextBox(double x, double y, String text) {
+ this.x = x;
+ this.y = y;
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return "TextBox[x=" + x + ", y=" + y + ", text=" + text + "]";
+ }
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java
new file mode 100755
index 0000000..b0aecce
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java
@@ -0,0 +1,19 @@
+package org.xbib.graphics.barcode;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
+
+/**
+ * {@link MaxiCode} tests that can't be run via the {@link SymbolTest}.
+ */
+public class MaxiCodeTest {
+
+ @Test
+ public void testHumanReadableHeight() {
+ MaxiCode maxicode = new MaxiCode();
+ maxicode.setMode(4);
+ maxicode.setContent("ABC");
+ assertEquals(0, maxicode.getHumanReadableHeight());
+ }
+
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java b/barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java
new file mode 100644
index 0000000..41a5eac
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/ParameterizedExtension.java
@@ -0,0 +1,253 @@
+package org.xbib.graphics.barcode;
+
+import static java.util.Collections.singletonList;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toList;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.Extension;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.junit.jupiter.api.extension.TestInstancePostProcessor;
+import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
+import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
+import org.junit.platform.commons.util.CollectionUtils;
+import org.junit.platform.commons.util.ReflectionUtils;
+import org.junit.runners.Parameterized;
+
+public class ParameterizedExtension implements TestTemplateInvocationContextProvider {
+
+ private final static ExtensionContext.Namespace PARAMETERS = ExtensionContext.Namespace.create(
+ ParameterizedExtension.class);
+
+ /**
+ * Indicate whether we can provide parameterized support.
+ * This requires the testClass to either have a static {@code @Parameters} method
+ * and correct {@code @Parameter} and their corresponding values
+ * or to have a constructor that could be injected.
+ */
+ public boolean supportsTestTemplate(ExtensionContext context) {
+ return hasParametersMethod(context) && validInjectionMix(context);
+ }
+
+ private static boolean validInjectionMix(ExtensionContext context) {
+ List fields = parametersFields(context);
+ boolean hasParameterFields = !fields.isEmpty();
+ boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields);
+ boolean hasArgsConstructor = hasArgsConstructor(context);
+
+ if (hasArgsConstructor) {
+ return !hasParameterFields;
+ }
+ else {
+ return !hasParameterFields || hasCorrectParameterFields;
+ }
+ }
+
+ @Override
+ public Stream provideTestTemplateInvocationContexts(ExtensionContext context) {
+ // grabbing the parent ensures the PARAMETERS are stored in the same store across multiple TestTemplates.
+ return context.getParent().flatMap(ParameterizedExtension::parameters).map(
+ o -> testTemplateContextsFromParameters(o, context)).orElse(Stream.empty());
+ }
+
+ private static boolean areParametersFormedCorrectly(List fields) {
+ List parameterValues = parameterIndexes(fields);
+ List duplicateIndexes = duplicatedIndexes(parameterValues);
+ boolean hasAllIndexes = indexRangeComplete(parameterValues);
+
+ return hasAllIndexes && duplicateIndexes.isEmpty();
+ }
+
+ private static List parameterIndexes(List fields) {
+ // @formatter:off
+ return fields.stream()
+ .map(f -> f.getAnnotation(Parameterized.Parameter.class))
+ .map(Parameterized.Parameter::value)
+ .collect(toList());
+ // @formatter:on
+ }
+
+ private static List duplicatedIndexes(List parameterValues) {
+ // @formatter:off
+ return parameterValues.stream().collect(groupingBy(identity())).entrySet().stream()
+ .filter(e -> e.getValue().size() > 1)
+ .map(Map.Entry::getKey)
+ .collect(toList());
+ // @formatter:on
+ }
+
+ private static Boolean indexRangeComplete(List parameterValues) {
+ // @formatter:off
+ return parameterValues.stream()
+ .max(Integer::compareTo)
+ .map(i -> parameterValues.containsAll(IntStream.range(0, i).boxed().collect(toList())))
+ .orElse(false);
+ // @formatter:on
+ }
+
+ private static Optional> parameters(ExtensionContext context) {
+ return context.getStore(PARAMETERS).getOrComputeIfAbsent("parameterMethod",
+ k -> new ParameterWrapper(callParameters(context)), ParameterWrapper.class).getValue();
+
+ }
+
+ private static Optional> callParameters(ExtensionContext context) {
+ // @formatter:off
+ return findParametersMethod(context)
+ .map(m -> ReflectionUtils.invokeMethod(m, null))
+ .map(ParameterizedExtension::convertParametersMethodReturnType);
+ // @formatter:on
+ }
+
+ private static boolean hasParametersMethod(ExtensionContext context) {
+ return findParametersMethod(context).isPresent();
+ }
+
+ private static Optional findParametersMethod(ExtensionContext extensionContext) {
+ // @formatter:off
+ return extensionContext.getTestClass()
+ .flatMap(ParameterizedExtension::ensureSingleParametersMethod)
+ .filter(ReflectionUtils::isPublic);
+ // @formatter:on
+ }
+
+ private static Optional ensureSingleParametersMethod(Class> testClass) {
+ return ReflectionUtils.findMethods(testClass,
+ m -> m.isAnnotationPresent(Parameterized.Parameters.class)).stream().findFirst();
+ }
+
+ private static Stream testTemplateContextsFromParameters(Collection o,
+ ExtensionContext context) {
+ List fields = parametersFields(context);
+ boolean hasParameterFields = !fields.isEmpty();
+ boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields);
+
+ if (!hasParameterFields) {
+ return o.stream().map(ParameterizedExtension::parameterResolver);
+ }
+ else if (hasCorrectParameterFields) {
+ return o.stream().map(ParameterizedExtension::contextFactory);
+ }
+
+ return Stream.empty();
+ }
+
+ private static TestTemplateInvocationContext parameterResolver(Object[] objects) {
+ List parameterResolvers = singletonList(new ParameterResolver() {
+ @Override
+ public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ final Executable declaringExecutable = parameterContext.getDeclaringExecutable();
+ return declaringExecutable instanceof Constructor
+ && declaringExecutable.getParameterCount() == objects.length;
+ }
+
+ @Override
+ public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return objects[parameterContext.getIndex()];
+ }
+ });
+
+ return templateWithExtensions(parameterResolvers);
+ }
+
+ private static TestTemplateInvocationContext contextFactory(Object[] parameters) {
+ return templateWithExtensions(singletonList(new InjectionExtension(parameters)));
+ }
+
+ private static class InjectionExtension implements TestInstancePostProcessor {
+
+ private final Object[] parameters;
+
+ public InjectionExtension(Object[] parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
+ List parameters = parametersFields(context);
+
+ if (!parameters.isEmpty() && parameters.size() != this.parameters.length) {
+ throw unMatchedAmountOfParametersException();
+ }
+
+ for (Field param : parameters) {
+ Parameterized.Parameter annotation = param.getAnnotation(Parameterized.Parameter.class);
+ int paramIndex = annotation.value();
+ param.set(testInstance, this.parameters[paramIndex]);
+ }
+ }
+
+ }
+
+ private static TestTemplateInvocationContext templateWithExtensions(List extensions) {
+ return new TestTemplateInvocationContext() {
+ @Override
+ public List getAdditionalExtensions() {
+ return extensions;
+ }
+ };
+ }
+
+ private static boolean hasArgsConstructor(ExtensionContext context) {
+ // @formatter:off
+ return context.getTestClass()
+ .map(ReflectionUtils::getDeclaredConstructor)
+ .filter(c -> c.getParameterCount() > 0)
+ .isPresent();
+ // @formatter:on
+ }
+
+ private static List parametersFields(ExtensionContext context) {
+ // @formatter:off
+ Stream fieldStream = context.getTestClass()
+ .map(Class::getDeclaredFields)
+ .map(Stream::of)
+ .orElse(Stream.empty());
+ // @formatter:on
+
+ return fieldStream.filter(f -> f.isAnnotationPresent(Parameterized.Parameter.class)).filter(
+ ReflectionUtils::isPublic).collect(toList());
+ }
+
+ private static ParameterResolutionException unMatchedAmountOfParametersException() {
+ return new ParameterResolutionException(
+ "The amount of parametersFields in the constructor doesn't match those in the provided parametersFields");
+ }
+
+ private static Collection convertParametersMethodReturnType(Object obj) {
+ return CollectionUtils.toStream(obj).map(o -> {
+ if (o instanceof Object[]) {
+ return (Object[]) o;
+ }
+ return new Object[] { o };
+ }).collect(toList());
+ }
+
+ private static class ParameterWrapper {
+ private final Optional> value;
+
+ public ParameterWrapper(Optional> value) {
+ this.value = value;
+ }
+
+ public Optional> getValue() {
+ return value;
+ }
+ }
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java
new file mode 100755
index 0000000..ef199be
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java
@@ -0,0 +1,608 @@
+package org.xbib.graphics.barcode;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.Reader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.oned.CodaBarReader;
+import com.google.zxing.oned.Code39Reader;
+import com.google.zxing.oned.Code93Reader;
+import com.google.zxing.oned.EAN13Reader;
+import com.google.zxing.oned.EAN8Reader;
+import com.google.zxing.oned.UPCAReader;
+import com.google.zxing.oned.UPCEReader;
+import com.google.zxing.pdf417.PDF417Reader;
+import com.google.zxing.qrcode.QRCodeReader;
+import org.junit.jupiter.api.TestTemplate;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.runners.Parameterized;
+import org.reflections.Reflections;
+import org.xbib.graphics.barcode.render.GraphicsRenderer;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.imageio.ImageIO;
+
+/**
+ * Scans the test resources for file-based bar code tests.
+ *
+ * Tests that verify successful behavior will contain the following sets of files:
+ *
+ *
+ * /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].properties (bar code initialization attributes)
+ * /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].codewords (expected intermediate coding of the bar code)
+ * /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].png (expected final rendering of the bar code)
+ *
+ *
+ *
+ * Tests that verify error conditions will contain the following sets of files:
+ *
+ *
+ * /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].properties (bar code initialization attributes)
+ * /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].error (expected error message)
+ *
+ *
+ * If a properties file is found with no matching expectation files, we assume that it was recently added to the test suite and
+ * that we need to generate suitable expectation files for it.
+ *
+ * A single properties file can contain multiple test configurations (separated by an empty line), as long as the expected output
+ * is the same for all of those tests.
+ */
+@ExtendWith(ParameterizedExtension.class)
+public class SymbolTest {
+
+ /** The font used to render human-readable text when drawing the symbologies; allows for consistent results across operating systems. */
+ private static Font DEJA_VU_SANS;
+
+ /** The type of symbology being tested. */
+ private final Class< ? extends Symbol> symbolType;
+
+ /** The test configuration properties. */
+ private final Map< String, String > properties;
+
+ /** The file containing the expected intermediate coding of the bar code, if this test verifies successful behavior. */
+ private final File codewordsFile;
+
+ /** The file containing the expected final rendering of the bar code, if this test verifies successful behavior. */
+ private final File pngFile;
+
+ /** The file containing the expected error message, if this test verifies a failure. */
+ private final File errorFile;
+
+ /**
+ * Finds all test resources and returns the information that JUnit needs to dynamically create the corresponding test cases.
+ *
+ * @return the test data needed to dynamically create the test cases
+ * @throws IOException if there is an error reading a file
+ */
+ @Parameterized.Parameters
+ public static List< Object[] > data() throws IOException {
+ String path = "/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf";
+ try {
+ InputStream is = SymbolTest.class.getResourceAsStream(path);
+ DEJA_VU_SANS = Font.createFont(Font.TRUETYPE_FONT, is);
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ boolean registered = ge.registerFont(DEJA_VU_SANS);
+ assertTrue(registered);
+ } catch (IOException | FontFormatException e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ String backend = "org.xbib.graphics.barcode";
+ Reflections reflections = new Reflections(backend);
+ Set< Class< ? extends Symbol >> symbols = reflections.getSubTypesOf(Symbol.class);
+ List< Object[] > data = new ArrayList<>();
+ for (Class< ? extends Symbol > symbol : symbols) {
+ String symbolName = symbol.getSimpleName().toLowerCase();
+ String dir = "src/test/resources/" + backend.replace('.', '/') + "/" + symbolName;
+ for (File file : getPropertiesFiles(dir)) {
+ String fileBaseName = file.getName().replaceAll(".properties", "");
+ File codewordsFile = new File(file.getParentFile(), fileBaseName + ".codewords");
+ File pngFile = new File(file.getParentFile(), fileBaseName + ".png");
+ File errorFile = new File(file.getParentFile(), fileBaseName + ".error");
+ for (Map< String, String > properties : readProperties(file)) {
+ data.add(new Object[] { symbol, properties, codewordsFile, pngFile, errorFile });
+ }
+ }
+ }
+ return data;
+ }
+ /**
+ * Creates a new test.
+ *
+ * @param symbolType the type of symbol being tested
+ * @param properties the test configuration properties
+ * @param codewordsFile the file containing the expected intermediate coding of the bar code, if this test verifies successful behavior
+ * @param pngFile the file containing the expected final rendering of the bar code, if this test verifies successful behavior
+ * @param errorFile the file containing the expected error message, if this test verifies a failure
+ */
+ public SymbolTest(Class< ? extends Symbol > symbolType,
+ Map< String, String > properties,
+ File codewordsFile,
+ File pngFile,
+ File errorFile) {
+ this.symbolType = symbolType;
+ this.properties = properties;
+ this.codewordsFile = codewordsFile;
+ this.pngFile = pngFile;
+ this.errorFile = errorFile;
+ }
+
+ /**
+ * Runs the test. If there are no expectation files yet, we generate them instead of checking against them.
+ *
+ * @throws Exception if any error occurs during the test
+ */
+ @TestTemplate
+ public void test() throws Exception {
+ Symbol symbol = symbolType.getDeclaredConstructor().newInstance();
+ symbol.setFontName(DEJA_VU_SANS.getFontName());
+ try {
+ setProperties(symbol, properties);
+ } catch (InvocationTargetException e) {
+ symbol.errorMsg.append(e.getCause().getMessage());
+ }
+ if (codewordsFile.exists()) {
+ verifySuccess(symbol);
+ } else if (errorFile.exists()) {
+ verifyError(symbol);
+ }
+ if (!pngFile.exists()) {
+ generateExpectationFiles(symbol);
+ }
+ }
+
+ /**
+ * Verifies that the specified symbol was encoded and rendered in a way that matches expectations.
+ *
+ * @param symbol the symbol to check
+ * @throws IOException if there is any I/O error
+ * @throws ReaderException if ZXing has an issue decoding the barcode image
+ */
+ private void verifySuccess(Symbol symbol) throws IOException, ReaderException {
+ if (symbol.errorMsg.length() > 0) {
+ fail("got error message: " + symbol.errorMsg);
+ }
+ List< String > expectedList = Files.readAllLines(codewordsFile.toPath(), StandardCharsets.UTF_8);
+ try {
+ // try to verify codewords
+ int[] actualCodewords = symbol.getCodewords();
+ assertEquals(expectedList.size(), actualCodewords.length);
+ for (int i = 0; i < actualCodewords.length; i++) {
+ int expected = getInt(expectedList.get(i));
+ int actual = actualCodewords[i];
+ assertEquals(expected, actual, "at codeword index " + i);
+ }
+ } catch (UnsupportedOperationException e) {
+ // codewords aren't supported, try to verify patterns
+ String[] actualPatterns = symbol.pattern;
+ if (actualPatterns != null) {
+ assertEquals(expectedList.size(), actualPatterns.length);
+ for (int i = 0; i < actualPatterns.length; i++) {
+ String expected = expectedList.get(i);
+ String actual = actualPatterns[i];
+ assertEquals(expected, actual, "at pattern index " + i);
+ }
+ }
+ }
+ // make sure the barcode images match
+ if (pngFile.exists()) {
+ BufferedImage expected = ImageIO.read(pngFile);
+ BufferedImage actual = draw(symbol, 10.0d);
+ if (expected != null && actual != null) {
+ assertEqualImage(pngFile.getName(), expected, actual);
+ }
+ // if possible, ensure an independent third party (ZXing) can read the generated barcode and agrees on what it represents
+ Reader zxingReader = findReader(symbol);
+ if (zxingReader != null && expected != null) {
+ LuminanceSource source = new BufferedImageLuminanceSource(expected);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ Map hints = Collections.singletonMap(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
+ Result result = zxingReader.decode(bitmap, hints);
+ String zxingData = removeChecksum(result.getText(), symbol);
+ String ourData = removeStartStopChars(symbol.getContent(), symbol);
+ assertEquals(ourData, zxingData, "checking against ZXing results");
+ }
+ }
+ }
+
+ /**
+ * Returns a ZXing reader that can read the specified symbol.
+ *
+ * @param symbol the symbol to be read
+ * @return a ZXing reader that can read the specified symbol
+ */
+ private static Reader findReader(Symbol symbol) {
+ if (symbol instanceof Code93) {
+ return new Code93Reader();
+ } else if (symbol instanceof Code3Of9) {
+ return new Code39Reader();
+ } else if (symbol instanceof Codabar) {
+ return new CodaBarReader();
+ } else if (symbol instanceof QrCode) {
+ return new QRCodeReader();
+ } else if (symbol instanceof Ean) {
+ Ean ean = (Ean) symbol;
+ if (ean.getMode() == Ean.Mode.EAN8) {
+ return new EAN8Reader();
+ } else {
+ return new EAN13Reader();
+ }
+ } else if (symbol instanceof Pdf417) {
+ Pdf417 pdf417 = (Pdf417) symbol;
+ if (pdf417.getMode() != Pdf417.Mode.MICRO) {
+ return new PDF417Reader();
+ }
+ } else if (symbol instanceof Upc) {
+ Upc upc = (Upc) symbol;
+ if (upc.getMode() == Upc.Mode.UPCA) {
+ return new UPCAReader();
+ } else {
+ return new UPCEReader();
+ }
+ }
+ // no corresponding ZXing reader exists, or it behaves badly so we don't use it for testing
+ return null;
+ }
+
+ /**
+ * Removes the checksum from the specified barcode content, according to the type of symbol that encoded the content.
+ *
+ * @param s the barcode content
+ * @param symbol the symbol which encoded the content
+ * @return the barcode content, without the checksum
+ */
+ private static String removeChecksum(String s, Symbol symbol) {
+ if (symbol instanceof Ean || symbol instanceof Upc) {
+ return s.substring(0, s.length() - 1);
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Removes the start/stop characters from the specified barcode content, according to the type of symbol that encoded the
+ * content.
+ *
+ * @param s the barcode content
+ * @param symbol the symbol which encoded the content
+ * @return the barcode content, without the start/stop characters
+ */
+ private static String removeStartStopChars(String s, Symbol symbol) {
+ if (symbol instanceof Codabar) {
+ return s.substring(1, s.length() - 1);
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Verifies that the specified symbol encountered the expected error during encoding.
+ *
+ * @param symbol the symbol to check
+ * @throws IOException if there is any I/O error
+ */
+ private void verifyError(Symbol symbol) throws IOException {
+ String expectedError = Files.readAllLines(errorFile.toPath(), StandardCharsets.UTF_8).get(0);
+ assertTrue(symbol.errorMsg.toString().startsWith(expectedError));
+ }
+
+ /**
+ * Generates the expectation files for the specified symbol.
+ *
+ * @param symbol the symbol to generate expectation files for
+ * @throws IOException if there is any I/O error
+ */
+ private void generateExpectationFiles(Symbol symbol) throws IOException {
+ if (symbol.errorMsg != null && symbol.errorMsg.length() > 0) {
+ generateErrorExpectationFile(symbol);
+ } else {
+ generateCodewordsExpectationFile(symbol);
+ generatePngExpectationFile(symbol);
+ }
+ }
+
+ /**
+ * Generates the error expectation file for the specified symbol.
+ *
+ * @param symbol the symbol to generate the error expectation file for
+ * @throws IOException if there is any I/O error
+ */
+ private void generateErrorExpectationFile(Symbol symbol) throws IOException {
+ if (!errorFile.exists()) {
+ PrintWriter writer = new PrintWriter(errorFile);
+ writer.println(symbol.errorMsg);
+ writer.close();
+ }
+ }
+
+ /**
+ * Generates the codewords expectation file for the specified symbol.
+ *
+ * @param symbol the symbol to generate codewords for
+ * @throws IOException if there is any I/O error
+ */
+ private void generateCodewordsExpectationFile(Symbol symbol) throws IOException {
+ if (!codewordsFile.exists()) {
+ PrintWriter writer = new PrintWriter(codewordsFile);
+ try {
+ int[] codewords = symbol.getCodewords();
+ for (int codeword : codewords) {
+ writer.println(codeword);
+ }
+ } catch (UnsupportedOperationException e) {
+ for (String pattern : symbol.pattern) {
+ writer.println(pattern);
+ }
+ }
+ writer.close();
+ }
+ }
+
+ /**
+ * Generates the image expectation file for the specified symbol.
+ *
+ * @param symbol the symbol to draw
+ * @throws IOException if there is any I/O error
+ */
+ private void generatePngExpectationFile(Symbol symbol) throws IOException {
+ BufferedImage img = draw(symbol, 10.0d);
+ if (img != null) {
+ ImageIO.write(img, "png", pngFile);
+ }
+ }
+
+ /**
+ * Returns the integer contained in the specified string. If the string contains a tab character, it and everything after it
+ * is ignored.
+ *
+ * @param s the string to extract the integer from
+ * @return the integer contained in the specified string
+ */
+ private static int getInt(String s) {
+ int i = s.indexOf('\t');
+ if (i != -1) {
+ s = s.substring(0, i);
+ }
+ return Integer.parseInt(s);
+ }
+
+ /**
+ * Draws the specified symbol and returns the resultant image.
+ *
+ * @param symbol the symbol to draw
+ * @return the resultant image
+ */
+ private static BufferedImage draw(Symbol symbol, double scalingFactor) {
+ int width = (int) (symbol.getWidth() * scalingFactor);
+ int height = (int) (symbol.getHeight() * scalingFactor);
+ if (width > 0 && height > 0) {
+ BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ Graphics2D g2d = img.createGraphics();
+ g2d.setPaint(Color.WHITE);
+ g2d.fillRect(0, 0, width, height);
+ GraphicsRenderer renderer = new GraphicsRenderer(g2d, scalingFactor, Color.WHITE, Color.BLACK, true, true);
+ renderer.render(symbol);
+ g2d.dispose();
+ return img;
+ }
+ return null;
+ }
+
+ /**
+ * Initializes the specified symbol using the specified properties, where keys are attribute names and values are attribute
+ * values.
+ *
+ * @param symbol the symbol to initialize
+ * @param properties the attribute names and values to set
+ * @throws ReflectiveOperationException if there is any reflection error
+ */
+ private static void setProperties(Symbol symbol, Map< String, String > properties) throws ReflectiveOperationException {
+ for (Map.Entry< String, String > entry : properties.entrySet()) {
+ String name = entry.getKey();
+ String value = entry.getValue();
+ String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
+ Method setter = getMethod(symbol.getClass(), setterName);
+ invoke(symbol, setter, value);
+ }
+ }
+
+ /**
+ * Returns the method with the specified name in the specified class, or throws an exception if the specified method cannot be
+ * found.
+ *
+ * @param clazz the class to search in
+ * @param name the name of the method to search for
+ * @return the method with the specified name in the specified class
+ */
+ private static Method getMethod(Class< ? > clazz, String name) {
+ for (Method method : clazz.getMethods()) {
+ if (method.getName().equals(name)) {
+ return method;
+ }
+ }
+ throw new IllegalArgumentException("Unable to find method: " + name);
+ }
+
+ /**
+ * Invokes the specified method on the specified object with the specified parameter.
+ *
+ * @param object the object to invoke the method on
+ * @param setter the method to invoke
+ * @param parameter the parameter to pass to the method
+ * @throws ReflectiveOperationException if there is any reflection error
+ * @throws IllegalArgumentException if the specified parameter is not valid
+ */
+ @SuppressWarnings("unchecked")
+ private static < E extends Enum< E >> void invoke(Object object, Method setter, Object parameter)
+ throws ReflectiveOperationException, IllegalArgumentException {
+ Class< ? > paramType = setter.getParameterTypes()[0];
+ if (String.class.equals(paramType)) {
+ setter.invoke(object, parameter.toString());
+ } else if (boolean.class.equals(paramType)) {
+ setter.invoke(object, Boolean.valueOf(parameter.toString()));
+ } else if (int.class.equals(paramType)) {
+ setter.invoke(object, Integer.parseInt(parameter.toString()));
+ } else if (double.class.equals(paramType)) {
+ setter.invoke(object, Double.parseDouble(parameter.toString()));
+ } else if (Character.class.equals(paramType)) {
+ setter.invoke(object, parameter.toString().charAt(0));
+ } else if (paramType.isEnum()) {
+ Class< E > e = (Class< E >) paramType;
+ setter.invoke(object, Enum.valueOf(e, parameter.toString()));
+ } else {
+ throw new IllegalArgumentException("Unknown setter type: " + paramType);
+ }
+ }
+
+ /**
+ * Returns all .properties files in the specified directory, or an empty array if none are found.
+ *
+ * @param dir the directory to search in
+ * @return all .properties files in the specified directory, or an empty array if none are found
+ */
+ private static File[] getPropertiesFiles(String dir) {
+ File[] files = new File(dir).listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".properties");
+ }
+ });
+ return Objects.requireNonNullElseGet(files, () -> new File[0]);
+ }
+
+ /**
+ * Verifies that the specified images match.
+ *
+ * @param expected the expected image to check against
+ * @param actual the actual image
+ */
+ private static void assertEqualImage(String name, BufferedImage expected, BufferedImage actual) {
+ int w = expected.getWidth();
+ int h = expected.getHeight();
+ assertEquals(w, actual.getWidth(), "width");
+ assertEquals(h, actual.getHeight(), "height");
+ int[] expectedPixels = new int[w * h];
+ expected.getRGB(0, 0, w, h, expectedPixels, 0, w);
+ int[] actualPixels = new int[w * h];
+ actual.getRGB(0, 0, w, h, actualPixels, 0, w);
+ for (int i = 0; i < expectedPixels.length; i++) {
+ int expectedPixel = expectedPixels[i];
+ int actualPixel = actualPixels[i];
+ if (expectedPixel != actualPixel) {
+ int x = i % w;
+ int y = i / w;
+ fail(name + ": pixel mismatch at " + x + ", " + y + " " +
+ Integer.toHexString(expectedPixel) + " " + Integer.toHexString(actualPixel));
+ }
+ }
+ }
+
+ /**
+ * Extracts test configuration properties from the specified properties file. A single properties file can contain
+ * configuration properties for multiple tests.
+ *
+ * @param propertiesFile the properties file to read
+ * @return the test configuration properties in the specified file
+ * @throws IOException if there is an error reading the properties file
+ */
+ private static List> readProperties(File propertiesFile) throws IOException {
+ String content;
+ try {
+ byte[] bytes = Files.readAllBytes(propertiesFile.toPath());
+ content = replacePlaceholders(decode(bytes, StandardCharsets.UTF_8));
+ } catch (CharacterCodingException e) {
+ throw new IOException("Invalid UTF-8 content in file " + propertiesFile.getAbsolutePath(), e);
+ }
+
+ String eol = System.lineSeparator();
+ String[] lines = content.split(eol);
+
+ List< Map< String, String > > allProperties = new ArrayList<>();
+ Map< String, String > properties = new LinkedHashMap<>();
+
+ for (String line : lines) {
+ if (line.isEmpty()) {
+ // an empty line signals the start of a new test configuration within this single file
+ if (!properties.isEmpty()) {
+ allProperties.add(properties);
+ properties = new LinkedHashMap<>();
+ }
+ } else if (!line.startsWith("#")) {
+ int index = line.indexOf('=');
+ if (index != -1) {
+ String name = line.substring(0, index);
+ String value = line.substring(index + 1);
+ properties.put(name, value);
+ } else {
+ throw new IOException(propertiesFile.getAbsolutePath() + ": found line without '=' character; unintentional newline?");
+ }
+ }
+ }
+ if (!properties.isEmpty()) {
+ allProperties.add(properties);
+ }
+ return allProperties;
+ }
+
+ /**
+ * Equivalent to {@link String#String(byte[], Charset)},
+ * except that encoding errors result in exceptions instead of
+ * silent character replacement.
+ *
+ * @param bytes the bytes to decode
+ * @param charset the character set use to decode the bytes
+ * @return the specified bytes, as a string
+ * @throws CharacterCodingException if there is an error decoding the specified bytes
+ */
+ private static String decode(byte[] bytes, Charset charset) throws CharacterCodingException {
+ CharsetDecoder decoder = charset.newDecoder();
+ decoder.onMalformedInput(CodingErrorAction.REPORT);
+ decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
+ CharBuffer chars = decoder.decode(ByteBuffer.wrap(bytes));
+ return chars.toString();
+ }
+
+ /**
+ * Replaces any special placeholders supported in test properties files with their raw values.
+ *
+ * @param s the string to check for placeholders
+ * @return the specified string, with placeholders replaced
+ */
+ private static String replacePlaceholders(String s) {
+ return s.replaceAll("\\\\r", "\r") // "\r" -> CR
+ .replaceAll("\\\\n", "\n"); // "\n" -> LF
+ }
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java
new file mode 100644
index 0000000..c5e38bb
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java
@@ -0,0 +1,59 @@
+package org.xbib.graphics.barcode.output;
+
+import org.junit.jupiter.api.Test;
+import org.xbib.graphics.barcode.Code3Of9;
+import org.xbib.graphics.barcode.HumanReadableLocation;
+import org.xbib.graphics.barcode.render.GraphicsRenderer;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import javax.imageio.ImageIO;
+
+public class Code39Test {
+
+ @Test
+ public void createBarcode1() throws IOException {
+ Code3Of9 code3Of9 = new Code3Of9();
+ code3Of9.setContent("20180123456");
+ code3Of9.setHumanReadableLocation(HumanReadableLocation.BOTTOM);
+ // pixels = (mm * dpi) / 25.4
+ double scalingFactor = (1.0d * 72.0d) / 25.4;
+ int width = (int) (code3Of9.getWidth() * scalingFactor);
+ int height = (int) (code3Of9.getHeight() * scalingFactor);
+ BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ GraphicsRenderer renderer = createRenderer(bufferedImage, scalingFactor);
+ renderer.render(code3Of9);
+ renderer.close();
+ OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode1.png"));
+ ImageIO.write(bufferedImage, "png", outputStream);
+ outputStream.close();
+ }
+
+ @Test
+ public void createBarcode2() throws IOException {
+ int width = 512;
+ int height = 150;
+ Code3Of9 code3Of9 = new Code3Of9();
+ //code3Of9.setContent("20180123456");
+ code3Of9.setContent("11111111111");
+ BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+ GraphicsRenderer renderer = createRenderer(bufferedImage, 3.0d);
+ renderer.render(code3Of9);
+ renderer.close();
+ OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode2.png"));
+ ImageIO.write(bufferedImage, "png", outputStream);
+ outputStream.close();
+ }
+
+ private GraphicsRenderer createRenderer(BufferedImage bufferedImage, double scalingFactor) {
+ Graphics2D g2d = bufferedImage.createGraphics();
+ g2d.setPaint(Color.WHITE);
+ g2d.setBackground(Color.BLACK);
+ g2d.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
+ return new GraphicsRenderer(g2d, scalingFactor, Color.WHITE, Color.BLACK, false, false);
+ }
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java
new file mode 100755
index 0000000..55187e5
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java
@@ -0,0 +1,121 @@
+package org.xbib.graphics.barcode.output;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xbib.graphics.barcode.Code93;
+import org.xbib.graphics.barcode.render.GraphicsRenderer;
+import org.xbib.graphics.barcode.MaxiCode;
+import org.xbib.graphics.barcode.Symbol;
+import org.xbib.graphics.io.vector.eps.EPSGraphics2D;
+import java.awt.Color;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+public class EPSRendererTest {
+
+ private Locale originalDefaultLocale;
+
+ @BeforeEach
+ public void before() {
+ // ensure use of correct decimal separator (period), regardless of default locale
+ originalDefaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.GERMANY);
+ }
+
+ @AfterEach
+ public void after() {
+ Locale.setDefault(originalDefaultLocale);
+ }
+
+ @Test
+ public void testCode93Basic() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.eps");
+ }
+
+ @Test
+ public void testCode93Margin() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.eps");
+ }
+
+ @Test
+ public void testCode93Magnification() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.eps");
+ }
+
+ @Test
+ public void testCode93Colors() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.eps");
+ }
+
+ @Test
+ public void testCode93CustomFont() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setFontName("Arial");
+ code93.setFontSize(26);
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.eps");
+ }
+
+ @Test
+ public void testMaxiCodeBasic() throws IOException {
+ MaxiCode maxicode = new MaxiCode();
+ maxicode.setMode(4);
+ maxicode.setContent("123456789");
+ test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.eps");
+ }
+
+ private void test(Symbol symbol,
+ double magnification,
+ Color paper,
+ Color ink,
+ int margin,
+ String expectationFile) throws IOException {
+ symbol.setQuietZoneHorizontal(margin);
+ symbol.setQuietZoneVertical(margin);
+ int width = (int) (symbol.getWidth() * magnification);
+ int height = (int) (symbol.getHeight() * magnification);
+ EPSGraphics2D epsGraphics2D = new EPSGraphics2D(0, 0, width, height);
+ GraphicsRenderer graphicsRenderer = new GraphicsRenderer(epsGraphics2D, magnification, paper, ink, false, false);
+ graphicsRenderer.render(symbol);
+ graphicsRenderer.close();
+ byte[] actualBytes = epsGraphics2D.getBytes();
+ String actual = new String(actualBytes, StandardCharsets.UTF_8);
+ try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) {
+ bufferedWriter.write(actual);
+ }
+ BufferedReader actualReader = new BufferedReader(new StringReader(actual));
+ InputStream is = getClass().getResourceAsStream(expectationFile);
+ if (is != null) {
+ byte[] expectedBytes = new byte[is.available()];
+ is.read(expectedBytes);
+ String expected = new String(expectedBytes, StandardCharsets.UTF_8);
+ BufferedReader expectedReader = new BufferedReader(new StringReader(expected));
+ int line = 1;
+ String actualLine = actualReader.readLine();
+ String expectedLine = expectedReader.readLine();
+ while (actualLine != null && expectedLine != null) {
+ assertEquals(expectedLine, actualLine, "Line " + line);
+ actualLine = actualReader.readLine();
+ expectedLine = expectedReader.readLine();
+ line++;
+ }
+ }
+ }
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java
new file mode 100755
index 0000000..f0f3833
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java
@@ -0,0 +1,121 @@
+package org.xbib.graphics.barcode.output;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xbib.graphics.barcode.Code93;
+import org.xbib.graphics.barcode.MaxiCode;
+import org.xbib.graphics.barcode.Symbol;
+import org.xbib.graphics.barcode.render.GraphicsRenderer;
+import org.xbib.graphics.io.vector.pdf.PDFGraphics2D;
+import java.awt.Color;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+public class PDFRendererTest {
+
+ private Locale originalDefaultLocale;
+
+ @BeforeEach
+ public void before() {
+ // ensure use of correct decimal separator (period), regardless of default locale
+ originalDefaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.GERMANY);
+ }
+
+ @AfterEach
+ public void after() {
+ Locale.setDefault(originalDefaultLocale);
+ }
+
+ @Test
+ public void testCode93Basic() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.pdf");
+ }
+
+ @Test
+ public void testCode93Margin() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.pdf");
+ }
+
+ @Test
+ public void testCode93Magnification() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.pdf");
+ }
+
+ @Test
+ public void testCode93Colors() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.pdf");
+ }
+
+ @Test
+ public void testCode93CustomFont() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setFontName("Arial");
+ code93.setFontSize(26);
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.pdf");
+ }
+
+ @Test
+ public void testMaxiCodeBasic() throws IOException {
+ MaxiCode maxicode = new MaxiCode();
+ maxicode.setMode(4);
+ maxicode.setContent("123456789");
+ test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.pdf");
+ }
+
+ private void test(Symbol symbol,
+ double magnification,
+ Color paper,
+ Color ink,
+ int margin,
+ String expectationFile) throws IOException {
+ symbol.setQuietZoneHorizontal(margin);
+ symbol.setQuietZoneVertical(margin);
+ int width = (int) (symbol.getWidth() * magnification);
+ int height = (int) (symbol.getHeight() * magnification);
+ PDFGraphics2D pdfGraphics2D = new PDFGraphics2D(0, 0, width, height);
+ GraphicsRenderer graphicsRenderer = new GraphicsRenderer(pdfGraphics2D, magnification, paper, ink, false, false);
+ graphicsRenderer.render(symbol);
+ graphicsRenderer.close();
+ byte[] actualBytes = pdfGraphics2D.getBytes();
+ String actual = new String(actualBytes, StandardCharsets.UTF_8);
+ try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) {
+ bufferedWriter.write(actual);
+ }
+ BufferedReader actualReader = new BufferedReader(new StringReader(actual));
+ InputStream is = getClass().getResourceAsStream(expectationFile);
+ if (is != null) {
+ byte[] expectedBytes = new byte[is.available()];
+ is.read(expectedBytes);
+ String expected = new String(expectedBytes, StandardCharsets.UTF_8);
+ BufferedReader expectedReader = new BufferedReader(new StringReader(expected));
+ int line = 1;
+ String actualLine = actualReader.readLine();
+ String expectedLine = expectedReader.readLine();
+ while (actualLine != null && expectedLine != null) {
+ assertEquals(expectedLine, actualLine, "Line " + line);
+ actualLine = actualReader.readLine();
+ expectedLine = expectedReader.readLine();
+ line++;
+ }
+ }
+ }
+}
diff --git a/barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java b/barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java
new file mode 100755
index 0000000..d97ee80
--- /dev/null
+++ b/barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java
@@ -0,0 +1,121 @@
+package org.xbib.graphics.barcode.output;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xbib.graphics.barcode.Code93;
+import org.xbib.graphics.barcode.MaxiCode;
+import org.xbib.graphics.barcode.Symbol;
+import org.xbib.graphics.barcode.render.GraphicsRenderer;
+import org.xbib.graphics.io.vector.svg.SVGGraphics2D;
+import java.awt.Color;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+public class SvgRendererTest {
+
+ private Locale originalDefaultLocale;
+
+ @BeforeEach
+ public void before() {
+ // ensure use of correct decimal separator (period), regardless of default locale
+ originalDefaultLocale = Locale.getDefault();
+ Locale.setDefault(Locale.GERMANY);
+ }
+
+ @AfterEach
+ public void after() {
+ Locale.setDefault(originalDefaultLocale);
+ }
+
+ @Test
+ public void testCode93Basic() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.svg");
+ }
+
+ @Test
+ public void testCode93Margin() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.svg");
+ }
+
+ @Test
+ public void testCode93Magnification() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.svg");
+ }
+
+ @Test
+ public void testCode93Colors() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setContent("123456789");
+ test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.svg");
+ }
+
+ @Test
+ public void testCode93CustomFont() throws IOException {
+ Code93 code93 = new Code93();
+ code93.setFontName("Arial");
+ code93.setFontSize(26);
+ code93.setContent("123456789");
+ test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.svg");
+ }
+
+ @Test
+ public void testMaxiCodeBasic() throws IOException {
+ MaxiCode maxicode = new MaxiCode();
+ maxicode.setMode(4);
+ maxicode.setContent("123456789");
+ test(maxicode, 1.0, Color.WHITE, Color.BLACK, 5, "maxicode-basic.svg");
+ }
+
+ private void test(Symbol symbol,
+ double magnification,
+ Color paper,
+ Color ink,
+ int margin,
+ String expectationFile) throws IOException {
+ symbol.setQuietZoneHorizontal(margin);
+ symbol.setQuietZoneVertical(margin);
+ int width = (int) (symbol.getWidth() * magnification);
+ int height = (int) (symbol.getHeight() * magnification);
+ SVGGraphics2D svgGraphics2D = new SVGGraphics2D(0, 0, width, height);
+ GraphicsRenderer graphicsRenderer = new GraphicsRenderer(svgGraphics2D, magnification, paper, ink, false, false);
+ graphicsRenderer.render(symbol);
+ graphicsRenderer.close();
+ byte[] actualBytes = svgGraphics2D.getBytes();
+ String actual = new String(actualBytes, StandardCharsets.UTF_8);
+ try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) {
+ bufferedWriter.write(actual);
+ }
+ BufferedReader actualReader = new BufferedReader(new StringReader(actual));
+ InputStream is = getClass().getResourceAsStream(expectationFile);
+ if (is != null) {
+ byte[] expectedBytes = new byte[is.available()];
+ is.read(expectedBytes);
+ String expected = new String(expectedBytes, StandardCharsets.UTF_8);
+ BufferedReader expectedReader = new BufferedReader(new StringReader(expected));
+ int line = 1;
+ String actualLine = actualReader.readLine();
+ String expectedLine = expectedReader.readLine();
+ while (actualLine != null && expectedLine != null) {
+ assertEquals(expectedLine, actualLine, "Line " + line);
+ actualLine = actualReader.readLine();
+ expectedLine = expectedReader.readLine();
+ line++;
+ }
+ }
+ }
+}
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords
new file mode 100755
index 0000000..7a53df3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords
@@ -0,0 +1,18 @@
+12121121
+11112211
+11122111
+11121121
+21112121
+22111111
+21212111
+11211211
+11221111
+21111211
+21211121
+12111121
+11212121
+12112111
+12211111
+21121111
+11111221
+11121221
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.png
new file mode 100644
index 0000000..ec4e4b1
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties
new file mode 100755
index 0000000..e66bd7b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.properties
@@ -0,0 +1 @@
+content=B1-2:3.4$5/6+7890C
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords
new file mode 100755
index 0000000..7a53df3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.codewords
@@ -0,0 +1,18 @@
+12121121
+11112211
+11122111
+11121121
+21112121
+22111111
+21212111
+11211211
+11221111
+21111211
+21211121
+12111121
+11212121
+12112111
+12211111
+21121111
+11111221
+11121221
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.png
new file mode 100644
index 0000000..27d37a2
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.properties
new file mode 100755
index 0000000..e930b0c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/module-width-ratio-2.5.properties
@@ -0,0 +1,2 @@
+moduleWidthRatio=2.5
+content=B1-2:3.4$5/6+7890C
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.error b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.error
new file mode 100755
index 0000000..61fd553
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.error
@@ -0,0 +1 @@
+Invalid characters in input
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.properties
new file mode 100755
index 0000000..21f4bb1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-start-char.properties
@@ -0,0 +1 @@
+content=1234A
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.error b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.error
new file mode 100755
index 0000000..61fd553
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.error
@@ -0,0 +1 @@
+Invalid characters in input
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.properties
new file mode 100755
index 0000000..d51a764
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/codabar/no-stop-char.properties
@@ -0,0 +1 @@
+content=A1234
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.codewords
new file mode 100755
index 0000000..95d25b3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.codewords
@@ -0,0 +1,14 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.png
new file mode 100644
index 0000000..e33390c
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.properties
new file mode 100755
index 0000000..51b6523
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic-one-check-digit.properties
@@ -0,0 +1,2 @@
+checkDigitCount=1
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.codewords
new file mode 100755
index 0000000..6785c36
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.codewords
@@ -0,0 +1,15 @@
+112211 start
+111121 0
+211121 1
+121121 2
+221111 3
+112121 4
+112111 -
+212111 5
+122111 6
+111221 7
+211211 8
+211111 9
+112121 4 (check digit C)
+221111 3 (check digit K)
+112211 stop
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.png
new file mode 100644
index 0000000..6c89f39
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties
new file mode 100755
index 0000000..7ac46f2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.properties
@@ -0,0 +1,4 @@
+content=01234-56789
+
+checkDigitCount=2
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.png
new file mode 100644
index 0000000..e7bad66
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties
new file mode 100755
index 0000000..7d0abb7
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-none.properties
@@ -0,0 +1,2 @@
+humanReadableLocation=NONE
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.png
new file mode 100644
index 0000000..737b500
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties
new file mode 100755
index 0000000..1274d28
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/human-readable-location-top.properties
@@ -0,0 +1,2 @@
+humanReadableLocation=TOP
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error
new file mode 100755
index 0000000..61fd553
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.error
@@ -0,0 +1 @@
+Invalid characters in input
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties
new file mode 100755
index 0000000..6e8967c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/invalid-content.properties
@@ -0,0 +1 @@
+content=01234+56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.png
new file mode 100644
index 0000000..b24b7e6
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties
new file mode 100755
index 0000000..b436aa1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-2.5.properties
@@ -0,0 +1,2 @@
+moduleWidthRatio=2.5
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.png
new file mode 100644
index 0000000..1b07e5c
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties
new file mode 100755
index 0000000..f7e1110
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/module-width-ratio-3.0.properties
@@ -0,0 +1,2 @@
+moduleWidthRatio=3
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.png
new file mode 100644
index 0000000..6c89f39
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties
new file mode 100755
index 0000000..4b140f4
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-delimiter.properties
@@ -0,0 +1,2 @@
+startDelimiter=*
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.png
new file mode 100644
index 0000000..298b55f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties
new file mode 100755
index 0000000..51c3508
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/start-stop-delimiter.properties
@@ -0,0 +1,3 @@
+startDelimiter=$
+stopDelimiter=%
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords
new file mode 100755
index 0000000..5fbc040
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.codewords
@@ -0,0 +1,15 @@
+112211
+111121
+211121
+121121
+221111
+112121
+112111
+212111
+122111
+111221
+211211
+211111
+112121
+221111
+112211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.png
new file mode 100644
index 0000000..62016a8
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties
new file mode 100755
index 0000000..004d4bc
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/stop-delimiter.properties
@@ -0,0 +1,2 @@
+stopDelimiter=+
+content=01234-56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error
new file mode 100755
index 0000000..6bb2fec
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.error
@@ -0,0 +1 @@
+Check digit count must be 1 or 2.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties
new file mode 100755
index 0000000..562d383
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/three-check-digits.properties
@@ -0,0 +1 @@
+checkDigitCount=3
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error
new file mode 100755
index 0000000..6bb2fec
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.error
@@ -0,0 +1 @@
+Check digit count must be 1 or 2.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties
new file mode 100755
index 0000000..9e66f91
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code11/zero-check-digits.properties
@@ -0,0 +1 @@
+checkDigitCount=0
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords
new file mode 100755
index 0000000..61126aa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.codewords
@@ -0,0 +1 @@
+31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.png
new file mode 100644
index 0000000..f376cd3
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties
new file mode 100755
index 0000000..2911d95
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/basic.properties
@@ -0,0 +1 @@
+content=1234567890123
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords
new file mode 100755
index 0000000..61126aa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.codewords
@@ -0,0 +1 @@
+31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.png
new file mode 100644
index 0000000..0e28092
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.properties
new file mode 100755
index 0000000..94de91d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-none.properties
@@ -0,0 +1,2 @@
+humanReadableLocation=NONE
+content=1234567890123
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.codewords
new file mode 100755
index 0000000..61126aa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.codewords
@@ -0,0 +1 @@
+31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.png
new file mode 100644
index 0000000..45e4c47
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties
new file mode 100755
index 0000000..282ee68
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/human-readable-location-top.properties
@@ -0,0 +1,2 @@
+humanReadableLocation=TOP
+content=1234567890123
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords
new file mode 100755
index 0000000..61126aa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.codewords
@@ -0,0 +1 @@
+31111131113113113133111111313131311113311111133131131113131111331131113113113133111131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.png
new file mode 100644
index 0000000..d7e6260
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties
new file mode 100755
index 0000000..687cd75
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code2of5/module-width-ratio-2.5.properties
@@ -0,0 +1,2 @@
+moduleWidthRatio=2.5
+content=1234567890123
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords
new file mode 100755
index 0000000..8bc811b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.codewords
@@ -0,0 +1,12 @@
+1211212111
+2112111121
+1122111121
+2122111111
+1112211121
+2112211111
+1122211111
+1112112121
+2112112111
+1122112111
+1112212111
+121121211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.png
new file mode 100644
index 0000000..1bcc949
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties
new file mode 100755
index 0000000..4507f64
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/basic.properties
@@ -0,0 +1 @@
+content=1234567890
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords
new file mode 100755
index 0000000..8bc811b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.codewords
@@ -0,0 +1,12 @@
+1211212111
+2112111121
+1122111121
+2122111111
+1112211121
+2112211111
+1122211111
+1112112121
+2112112111
+1122112111
+1112212111
+121121211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.png
new file mode 100644
index 0000000..67a6c03
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.properties
new file mode 100755
index 0000000..6d89967
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code3of9/module-width-ratio-3.properties
@@ -0,0 +1,2 @@
+moduleWidthRatio=3
+content=1234567890
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.codewords
new file mode 100755
index 0000000..9532651
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.codewords
@@ -0,0 +1,15 @@
+111141
+131112
+111213
+111312
+111411
+121113
+121212
+121311
+111114
+131211
+141111
+121122
+122211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.png
new file mode 100644
index 0000000..81fb6a8
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.properties
new file mode 100755
index 0000000..b31d857
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/hide-check-digits.properties
@@ -0,0 +1,2 @@
+showCheckDigits=false
+content=0123456789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.codewords
new file mode 100755
index 0000000..51e135a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.codewords
@@ -0,0 +1,23 @@
+111141
+122211
+211113
+122211
+211212
+122211
+211311
+122211
+221112
+122211
+221211
+122211
+231111
+122211
+112113
+122211
+112212
+122211
+112311
+222111
+123111
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.png
new file mode 100644
index 0000000..157d459
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties
new file mode 100755
index 0000000..4995fc9
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-1.properties
@@ -0,0 +1 @@
+content=abcdefghi
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords
new file mode 100755
index 0000000..8c1038f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.codewords
@@ -0,0 +1,23 @@
+111141
+122211
+122112
+122211
+132111
+122211
+111123
+122211
+111222
+122211
+111321
+122211
+121122
+122211
+131121
+122211
+212112
+122211
+212211
+131211
+123111
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.png
new file mode 100644
index 0000000..bd415da
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties
new file mode 100755
index 0000000..f892b9b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-2.properties
@@ -0,0 +1 @@
+content=jklmnopqr
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords
new file mode 100755
index 0000000..e9f6ae4
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.codewords
@@ -0,0 +1,21 @@
+111141
+122211
+211122
+122211
+211221
+122211
+221121
+122211
+222111
+122211
+112122
+122211
+112221
+122211
+122121
+122211
+123111
+212211
+122121
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.png
new file mode 100644
index 0000000..aa911a3
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties
new file mode 100755
index 0000000..e53732a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-lowercase-3.properties
@@ -0,0 +1 @@
+content=stuvwxyz
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords
new file mode 100755
index 0000000..1a37a3f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.codewords
@@ -0,0 +1,14 @@
+111141
+211113
+211212
+211311
+221112
+221211
+231111
+112113
+112212
+112311
+121311
+312111
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.png
new file mode 100644
index 0000000..c2b0467
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties
new file mode 100755
index 0000000..b627ba3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-1.properties
@@ -0,0 +1 @@
+content=ABCDEFGHI
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords
new file mode 100755
index 0000000..624464d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.codewords
@@ -0,0 +1,14 @@
+111141
+122112
+132111
+111123
+111222
+111321
+121122
+131121
+212112
+212211
+123111
+211131
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.png
new file mode 100644
index 0000000..7a28a02
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties
new file mode 100755
index 0000000..05f475e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-2.properties
@@ -0,0 +1 @@
+content=JKLMNOPQR
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords
new file mode 100755
index 0000000..2bfb512
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.codewords
@@ -0,0 +1,13 @@
+111141
+211122
+211221
+221121
+222111
+112122
+112221
+122121
+123111
+211212
+321111
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.png
new file mode 100644
index 0000000..a37ce08
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties
new file mode 100755
index 0000000..1991c87
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/letters-uppercase-3.properties
@@ -0,0 +1 @@
+content=STUVWXYZ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords
new file mode 100755
index 0000000..9532651
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.codewords
@@ -0,0 +1,15 @@
+111141
+131112
+111213
+111312
+111411
+121113
+121212
+121311
+111114
+131211
+141111
+121122
+122211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.png
new file mode 100644
index 0000000..10894b1
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.properties
new file mode 100755
index 0000000..fad6b30
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-bar-height.properties
@@ -0,0 +1,2 @@
+barHeight=100
+content=0123456789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.codewords
new file mode 100755
index 0000000..9532651
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.codewords
@@ -0,0 +1,15 @@
+111141
+131112
+111213
+111312
+111411
+121113
+121212
+121311
+111114
+131211
+141111
+121122
+122211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.png
new file mode 100644
index 0000000..2ac613a
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties
new file mode 100755
index 0000000..875c664
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers-with-custom-module-width.properties
@@ -0,0 +1,2 @@
+moduleWidth=5
+content=0123456789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords
new file mode 100755
index 0000000..9532651
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.codewords
@@ -0,0 +1,15 @@
+111141
+131112
+111213
+111312
+111411
+121113
+121212
+121311
+111114
+131211
+141111
+121122
+122211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.png
new file mode 100644
index 0000000..851ea8f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.properties
new file mode 100755
index 0000000..7b4bbb1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/numbers.properties
@@ -0,0 +1 @@
+content=0123456789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.codewords
new file mode 100755
index 0000000..304c478
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.codewords
@@ -0,0 +1,12 @@
+111141
+121131
+311112
+321111
+311211
+112131
+113121
+211131
+222111
+121311
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.png
new file mode 100644
index 0000000..e51b8ec
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties
new file mode 100755
index 0000000..fcf4d20
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-no-ctrl.properties
@@ -0,0 +1 @@
+content=-.$ /+%
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords
new file mode 100755
index 0000000..71cbb84
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.codewords
@@ -0,0 +1,23 @@
+111141
+312111
+112113
+121221
+221112
+121221
+112311
+121221
+132111
+312111
+211113
+312111
+221112
+312111
+221211
+312111
+112311
+312111
+122112
+112131
+111312
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.png
new file mode 100644
index 0000000..09824dd
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.properties
new file mode 100755
index 0000000..cb5235e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-1.properties
@@ -0,0 +1 @@
+content=< >?
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords
new file mode 100755
index 0000000..a4689c5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.codewords
@@ -0,0 +1,21 @@
+111141
+311121
+211113
+311121
+211212
+311121
+211311
+311121
+231111
+121221
+212112
+121221
+211122
+121221
+112122
+311121
+112113
+231111
+311211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.png
new file mode 100644
index 0000000..f05d1e7
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.properties
new file mode 100755
index 0000000..d403b80
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-2.properties
@@ -0,0 +1 @@
+content=!"#&'
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.codewords
new file mode 100755
index 0000000..f38b861
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.codewords
@@ -0,0 +1,19 @@
+111141
+311121
+112212
+311121
+112311
+311121
+122112
+311121
+111123
+312111
+131121
+312111
+212211
+312111
+211122
+111213
+122211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.png
new file mode 100644
index 0000000..d462510
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.properties
new file mode 100755
index 0000000..26e0d21
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/special-characters-with-ctrl-3.properties
@@ -0,0 +1 @@
+content=()*,{}~
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.codewords
new file mode 100755
index 0000000..9532651
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.codewords
@@ -0,0 +1,15 @@
+111141
+131112
+111213
+111312
+111411
+121113
+121212
+121311
+111114
+131211
+141111
+121122
+122211
+111141
+1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.png b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.png
new file mode 100644
index 0000000..93066b9
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.properties
new file mode 100755
index 0000000..e43c9e3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/code93/start-stop-delimiter.properties
@@ -0,0 +1,2 @@
+startStopDelimiter=*
+content=0123456789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.codewords
new file mode 100755
index 0000000..47fbd8e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.codewords
@@ -0,0 +1 @@
+1112122141123111231411121311111112133112321122212122121311191121222112122111141111132111231
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.png
new file mode 100644
index 0000000..288d934
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.properties
new file mode 100755
index 0000000..fc6e7f5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-13-basic.properties
@@ -0,0 +1,2 @@
+mode=EAN13
+content=123456789012+12345
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.codewords
new file mode 100755
index 0000000..bec10d3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.codewords
@@ -0,0 +1 @@
+111222121221411113211111123111141312321111191121222112122111141111132111231
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.png
new file mode 100644
index 0000000..82d9874
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties
new file mode 100755
index 0000000..bbf49bd
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/ean/ean-8-basic.properties
@@ -0,0 +1,2 @@
+mode=EAN8
+content=1234567+12345
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf b/barcode/src/test/resources/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf
new file mode 100755
index 0000000..9642811
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords
new file mode 100755
index 0000000..659934a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.codewords
@@ -0,0 +1 @@
+FDFFTFTTADFFTTFTTAFDFTFFDATFTFFTDAFTFTDFATDATDATDATDATDATDATDAFFTDF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.png
new file mode 100644
index 0000000..e05f54f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties
new file mode 100755
index 0000000..82ebd55
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/basic.properties
@@ -0,0 +1 @@
+content=10800752-16-3
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords
new file mode 100755
index 0000000..51359d2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.codewords
@@ -0,0 +1 @@
+FDFFTFTTADFFTTFTTAFDFTFFDATFTFFTDAFTFTFADTDATDATDATDATDATDATDAFTTDF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.png b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.png
new file mode 100644
index 0000000..7589003
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties
new file mode 100755
index 0000000..fab9d53
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/japanpost/bug-50.properties
@@ -0,0 +1 @@
+content=10800752-16-4
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords
new file mode 100755
index 0000000..348d970
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.codewords
@@ -0,0 +1 @@
+131131311131111311311131131131313113111111113311313111331111113133111111111331313111133111113113311111113331111111133131131131311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.png
new file mode 100644
index 0000000..5d5c0fc
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.properties
new file mode 100755
index 0000000..ed0e34a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters1.properties
@@ -0,0 +1 @@
+content=ABCDEFGHIJ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.codewords
new file mode 100755
index 0000000..0fca8f6
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.codewords
@@ -0,0 +1 @@
+131131311131111113311131111331313111131111113113313111311311113131131111111133313111113311113111331111113133113311111131131131311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.png
new file mode 100644
index 0000000..048958d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties
new file mode 100755
index 0000000..fb01a6b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters2.properties
@@ -0,0 +1 @@
+content=KLMNOPQRST
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords
new file mode 100755
index 0000000..7747129
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.codewords
@@ -0,0 +1 @@
+13113131113311111131133111113133311111111311311131331131111113313111111111311331131131311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.png
new file mode 100644
index 0000000..53e891f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties
new file mode 100755
index 0000000..943bf42
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/letters3.properties
@@ -0,0 +1 @@
+content=UVWXYZ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords
new file mode 100755
index 0000000..26b4c5b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.codewords
@@ -0,0 +1 @@
+131131311131131111311133111131313311111111133111313113311111113331111111131131313113113111113311311111133131111133111131131131311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.png
new file mode 100644
index 0000000..b0321bd
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties
new file mode 100755
index 0000000..090bd8e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/module-width-ratio-2.properties
@@ -0,0 +1,2 @@
+moduleWidthRatio=2
+content=1234567890
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords
new file mode 100755
index 0000000..26b4c5b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.codewords
@@ -0,0 +1 @@
+131131311131131111311133111131313311111111133111313113311111113331111111131131313113113111113311311111133131111133111131131131311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.png
new file mode 100644
index 0000000..435f272
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.properties
new file mode 100755
index 0000000..4507f64
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/numbers.properties
@@ -0,0 +1 @@
+content=1234567890
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.codewords
new file mode 100755
index 0000000..dfd3585
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.codewords
@@ -0,0 +1 @@
+131131311113111131313311113111133111311113131311111313111311131113131111131313111131331111131131311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.png b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.png
new file mode 100644
index 0000000..ec27120
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties
new file mode 100755
index 0000000..f5ff26d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/logmars/special.properties
@@ -0,0 +1 @@
+content=-. $/+%
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error
new file mode 100755
index 0000000..6bd6b1a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.error
@@ -0,0 +1 @@
+Invalid MaxiCode mode: 0
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties
new file mode 100755
index 0000000..d9e332a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-0.properties
@@ -0,0 +1 @@
+mode=0
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error
new file mode 100755
index 0000000..c8ac80f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.error
@@ -0,0 +1 @@
+Invalid MaxiCode mode: 1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties
new file mode 100755
index 0000000..6999e49
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-1.properties
@@ -0,0 +1 @@
+mode=1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error
new file mode 100755
index 0000000..9c1bf4b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.error
@@ -0,0 +1 @@
+Invalid Primary String
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties
new file mode 100755
index 0000000..650662d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-invalid-primary.properties
@@ -0,0 +1,3 @@
+mode=2
+primary=152382802840X01
+content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPHA DRPITTSBURGHPA
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords
new file mode 100755
index 0000000..50fe863
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.codewords
@@ -0,0 +1,144 @@
+2
+16
+58
+17
+57
+18
+2
+18
+11
+0
+22
+61
+42
+19
+2
+16
+54
+10
+14
+2
+59
+42
+41
+59
+40
+30
+48
+49
+29
+57
+54
+49
+26
+48
+48
+48
+48
+50
+54
+51
+56
+29
+21
+16
+19
+14
+29
+24
+48
+51
+49
+49
+53
+30
+48
+55
+32
+47
+23
+45
+39
+46
+0
+18
+40
+6
+43
+57
+20
+2
+23
+37
+52
+47
+2
+34
+15
+20
+4
+57
+21
+10
+57
+38
+50
+37
+34
+50
+14
+14
+56
+26
+43
+13
+22
+41
+37
+8
+40
+10
+0
+30
+62
+4
+62
+60
+4
+63
+52
+28
+18
+62
+57
+21
+32
+53
+19
+50
+17
+3
+51
+52
+34
+9
+21
+8
+23
+42
+9
+60
+59
+44
+32
+52
+56
+49
+15
+35
+55
+4
+18
+34
+52
+3
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.png
new file mode 100644
index 0000000..f7c37d5
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties
new file mode 100755
index 0000000..aad4dc5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-max-data.properties
@@ -0,0 +1,3 @@
+mode=2
+primary=194280000840002
+content=[)>01961Z00002638UPSNX0311507 /W-'.
R(F+9TBW%4/B"OTD9UJ9&2%"2NN8Z+MV)%H(J
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords
new file mode 100755
index 0000000..911cf43
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.codewords
@@ -0,0 +1,144 @@
+34 mode 2 + part of the postal code
+20
+45
+20
+17
+18
+2
+18
+7
+0 end primary message
+61 start primary message error correction
+53
+12
+1
+38
+55
+55
+6
+31
+40 end primary message error correction
+59 [Shift B]
+42 [
+41 )
+59 [Shift B]
+40 >
+30 RS
+48 0
+49 1
+29 GS
+57 9
+54 6
+49 1
+26 Z
+48 0
+48 0
+48 0
+48 0
+52 4
+57 9
+53 5
+49 1
+29 GS
+21 U
+16 P
+19 S
+14 N
+29 GS
+48 0
+54 6
+24 X
+54 6
+49 1
+48 0
+29 GS
+49 1
+53 5
+57 9
+29 GS
+49 1
+50 2
+51 3
+52 4
+53 5
+54 6
+55 7
+29 GS
+49 1
+47 /
+49 1
+29 GS
+29 GS
+25 Y
+29 GS
+54 6
+51 3
+52 4
+32
+1 A
+12 L
+16 P
+8 H
+1 A
+32
+4 D
+18 R
+29 GS
+16 P
+9 I
+20 T
+20 T
+19 S
+2 B
+21 U
+18 R
+7 G
+8 H
+29 GS
+16 P
+1 A
+30 RS
+62 [Shift E]
+4 EOT
+33 start padding
+33
+44 start secondary message error correction
+40
+52
+23
+54
+59
+30
+31
+49
+1
+41
+53
+21
+35
+8
+47
+10
+11
+32
+26
+60
+53
+5
+40
+39
+32
+8
+44
+38
+51
+43
+8
+55
+14
+23
+53
+40
+17
+49
+31 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.png
new file mode 100644
index 0000000..2baf8d9
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.properties
new file mode 100755
index 0000000..5c691f9
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-standard.properties
@@ -0,0 +1,3 @@
+mode=2
+primary=152382802840001
+content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPHA DRPITTSBURGHPA
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords
new file mode 100755
index 0000000..c5714ed
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.codewords
@@ -0,0 +1,144 @@
+34 mode 2 + part of the postal code
+20
+45
+20
+17
+18
+2
+18
+7
+0 end primary message
+61 start primary message error correction
+53
+12
+1
+38
+55
+55
+6
+31
+40 end primary message error correction
+33 padding
+7 structured append 1 of 8 = 000 111 = 7
+59
+42
+41
+59
+40
+30
+48
+49
+29
+57
+54
+49
+26
+48
+48
+48
+48
+52
+57
+53
+49
+29
+21
+16
+19
+14
+29
+48
+54
+24
+54
+49
+48
+29
+49
+53
+57
+29
+49
+50
+51
+52
+53
+54
+55
+29
+49
+47
+49
+29
+29
+25
+29
+54
+51
+52
+32
+1
+12
+16
+8
+32
+4
+18
+29
+16
+9
+20
+20
+19
+2
+21
+18
+7
+8
+29
+16
+1
+30
+62
+4
+33
+58
+8
+2
+57
+53
+62
+35
+42
+14
+50
+1
+28
+53
+1
+55
+2
+38
+19
+47
+47
+38
+13
+19
+51
+39
+63
+40
+5
+14
+28
+53
+34
+31
+4
+40
+36
+5
+20
+46
+26
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.png
new file mode 100644
index 0000000..2b55ec9
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties
new file mode 100755
index 0000000..0a1662d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-2-structured-append-1-of-8.properties
@@ -0,0 +1,5 @@
+mode=2
+structuredAppendPosition=1
+structuredAppendTotal=8
+primary=152382802840001
+content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPH DRPITTSBURGHPA
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords
new file mode 100755
index 0000000..db2e4c2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.codewords
@@ -0,0 +1,144 @@
+19 mode 3 + part of the postal code
+28
+16
+28
+16
+28
+0
+31
+4
+0
+59 start primary message error correction
+33
+49
+2
+48
+62
+59
+0
+48
+25
+59 start secondary message
+42
+41
+59
+40
+30
+48
+49
+29
+57
+54
+49
+26
+48
+48
+48
+48
+52
+57
+53
+49
+29
+21
+16
+19
+14
+29
+48
+54
+24
+54
+49
+48
+29
+49
+53
+57
+29
+49
+50
+51
+52
+53
+54
+55
+29
+49
+47
+49
+29
+29
+25
+29
+54
+51
+52
+32
+1
+12
+16
+8
+1
+32
+4
+18
+29
+16
+9
+20
+20
+19
+2
+21
+18
+7
+8
+29
+16
+1
+30
+62
+4
+33
+33
+44 start secondary message error correction
+40
+52
+23
+54
+59
+30
+31
+49
+1
+41
+53
+21
+35
+8
+47
+10
+11
+32
+26
+60
+53
+5
+40
+39
+32
+8
+44
+38
+51
+43
+8
+55
+14
+23
+53
+40
+17
+49
+31
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.png
new file mode 100644
index 0000000..36d9932
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties
new file mode 100755
index 0000000..c6cd4cc
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-3-standard.properties
@@ -0,0 +1,3 @@
+mode=3
+primary=A1A1A1 124001
+content=[)>01961Z00004951UPSN06X61015912345671/1Y634 ALPHA DRPITTSBURGHPA
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords
new file mode 100755
index 0000000..cecd045
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+1 A
+2 B
+3 C
+33 start padding
+33
+33
+33
+33
+33
+13 start primary message error correction
+1
+28
+60
+35
+56
+0
+48
+52
+15 end primary message error correction
+33 start secondary message
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+60 start secondary message error correction
+60
+40
+40
+9
+9
+43
+43
+14
+14
+50
+50
+12
+12
+53
+53
+57
+57
+58
+58
+36
+36
+28
+28
+10
+10
+53
+53
+37
+37
+30
+30
+14
+14
+5
+5
+31
+31
+40
+40 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.png
new file mode 100644
index 0000000..050f89a
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.properties
new file mode 100755
index 0000000..fbca35f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic-structured-append-total-1.properties
@@ -0,0 +1,4 @@
+# setting structured append total = 1 should have no impact, and should result in a symbol identical to one where it wasn't set at all
+mode=4
+content=ABC
+structuredAppendTotal=1
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.codewords
new file mode 100755
index 0000000..cecd045
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+1 A
+2 B
+3 C
+33 start padding
+33
+33
+33
+33
+33
+13 start primary message error correction
+1
+28
+60
+35
+56
+0
+48
+52
+15 end primary message error correction
+33 start secondary message
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+60 start secondary message error correction
+60
+40
+40
+9
+9
+43
+43
+14
+14
+50
+50
+12
+12
+53
+53
+57
+57
+58
+58
+36
+36
+28
+28
+10
+10
+53
+53
+37
+37
+30
+30
+14
+14
+5
+5
+31
+31
+40
+40 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.png
new file mode 100644
index 0000000..050f89a
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.properties
new file mode 100755
index 0000000..0fe6268
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-basic.properties
@@ -0,0 +1,2 @@
+mode=4
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.codewords
new file mode 100755
index 0000000..12a96a0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+0 start primary message
+1
+2
+3
+4
+5
+6
+7
+8 end primary message
+52 start primary message error correction
+20
+12
+4
+9
+63
+12
+15
+8
+39 end primary message error correction
+9 start secondary message
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+28
+29
+30
+32
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+58
+53
+54
+55
+56
+57
+33 start padding
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33 end secondary message
+57 start secondary message error correction
+9
+46
+29
+12
+42
+48
+34
+18
+30
+42
+10
+57
+50
+23
+42
+14
+52
+42
+43
+5
+58
+27
+16
+9
+60
+33
+33
+6
+3
+47
+4
+49
+13
+42
+2
+34
+31
+61
+46 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.png
new file mode 100644
index 0000000..c437156
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties
new file mode 100755
index 0000000..a90a529
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-a-all-symbols.properties
@@ -0,0 +1,2 @@
+mode=4
+content=
ABCDEFGHIJKLMNOPQRSTUVWXYZ "#$%&'()*+,-./01234:56789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords
new file mode 100755
index 0000000..fe1d3d2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+63 [Latch B], since next 2+ chars are in Code Set B
+0
+1
+2
+3
+4
+5
+6
+7
+42 start primary message error correction
+34
+1
+61
+21
+61
+59
+47
+38
+7 end primary message error correction
+8 start secondary message
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+28
+29
+30
+32
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+33 start padding
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33 end secondary message
+8 start secondary message error correction
+25
+22
+26
+53
+57
+54
+49
+52
+5
+34
+13
+11
+7
+21
+42
+61
+11
+4
+9
+32
+29
+3
+15
+26
+24
+24
+3
+53
+12
+22
+26
+36
+45
+34
+13
+48
+40
+9
+10 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.png
new file mode 100644
index 0000000..1241727
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.properties
new file mode 100755
index 0000000..77c36db
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-set-b-all-symbols.properties
@@ -0,0 +1,2 @@
+mode=4
+content=`abcdefghijklmnopqrstuvwxyz{}~;<=>?[\]^_ ,./:@!|
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords
new file mode 100755
index 0000000..f19a9b2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+1 A
+59 [Shift B], since only next 1 char is in Code Set B
+2 b
+1 A
+1 A
+1 A
+63 [Latch B], since next 2 chars are in Code Set B
+2 b
+2 b
+11 start primary message error correction
+13
+16
+33
+48
+38
+48
+15
+56
+56 end primary message error correction
+59 [Shift A], since only next 1 char is in Code Set A
+1 A
+2 b
+2 b
+2 b
+63 [Latch A], since next 4 chars are in Code Set A
+1 A
+1 A
+1 A
+1 A
+63 [Latch B], since next 2 chars are in Code Set B
+2 b
+2 b
+57 [3 Shift A], since next 3 chars are in Code Set A
+1 A
+1 A
+1 A
+2 b
+2 b
+56 [2 Shift A], since next 2 chars are in Code Set A
+1 A
+1 A
+2 b
+2 b
+63 [Latch A], since next 4 chars (including padding) are in Code Set A
+1 A
+33 start padding
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+18 start secondary message error correction
+45
+20
+58
+41
+11
+12
+58
+20
+31
+11
+7
+63
+62
+33
+52
+46
+4
+50
+49
+26
+59
+25
+37
+25
+36
+15
+9
+22
+49
+13
+9
+53
+5
+47
+63
+52
+57
+59
+53 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.png
new file mode 100644
index 0000000..6c7f145
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties
new file mode 100755
index 0000000..8145ca5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-a-and-b.properties
@@ -0,0 +1,2 @@
+mode=4
+content=AbAAAbbAbbbAAAAbbAAAbbAAbbA
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords
new file mode 100755
index 0000000..c18a172
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+60 [Shift C]
+7
+61 [Shift D]
+17
+62 [Shift E]
+39
+60 [Shift C]
+7
+60 [Shift C]
+34 start primary message error correction
+18
+16
+28
+24
+58
+1
+47
+51
+8 end primary message error correction
+7 start secondary message
+61 [Shift D]
+17
+61 [Shift D]
+17
+62 [Shift E]
+39
+62 [Shift E]
+39
+60 [Shift C]
+7
+60 [Shift C]
+7
+60 [Shift C]
+7
+61 [Shift D]
+17
+61 [Shift D]
+17
+61 [Shift D]
+17
+62 [Shift E]
+39
+62 [Shift E]
+39
+62 [Shift E]
+39
+60 [Shift C]
+60 [Lock In C]
+7
+7
+7
+7
+61 [Shift D]
+61 [Lock In D]
+17
+17
+17
+17
+62 [Shift E]
+62 [Lock In E]
+39
+39
+39
+39
+60 [Shift C]
+60 [Lock In C]
+7
+7
+7
+7
+7
+61 [Shift D]
+61 [Lock In D]
+17
+17
+17
+17
+17
+62 [Shift E]
+62 [Lock In E]
+39
+39
+39
+39
+39
+63 [Latch B]
+33 start padding
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+5 start secondary message error correction
+43
+15
+62
+60
+32
+13
+5
+41
+10
+1
+22
+12
+60
+42
+63
+51
+22
+35
+25
+6
+58
+46
+47
+43
+33
+44
+26
+17
+46
+35
+62
+1
+39
+11
+54
+29
+16
+61
+5 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.png
new file mode 100644
index 0000000..1bacd8d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.properties
new file mode 100755
index 0000000..b21445f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-code-sets-c-d-e.properties
@@ -0,0 +1,2 @@
+mode=4
+content=Çñ£ÇÇññ££ÇÇÇñññ£££ÇÇÇÇññññ££££ÇÇÇÇÇñññññ£££££
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.codewords
new file mode 100755
index 0000000..2f0b147
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+1
+2
+3
+4
+5
+6
+7
+8
+9
+2 start primary message error correction
+35
+59
+36
+15
+56
+42
+17
+52
+51 end primary message error correction
+10 start secondary message
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+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
+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
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15 end secondary message
+58 start secondary message error correction
+47
+56
+17
+12
+38
+46
+48
+30
+29
+29
+12
+50
+45
+12
+19
+58
+14
+21
+6
+39
+8
+9
+19
+63
+42
+7
+17
+41
+41
+5
+30
+6
+20
+36
+10
+26
+25
+39
+9 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.png
new file mode 100644
index 0000000..f328de4
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties
new file mode 100755
index 0000000..d837bda
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-max-data.properties
@@ -0,0 +1,2 @@
+mode=4
+content=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNO
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords
new file mode 100755
index 0000000..bad69e1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+31 start numeric shift [NS]
+7 numeric compression codeword 1
+22 numeric compression codeword 2
+60 numeric compression codeword 3
+52 numeric compression codeword 4
+21 numeric compression codeword 5
+1 A
+1 A
+1 A
+40 start primary message error correction
+60
+12
+24
+63
+32
+21
+40
+5
+55 end primary message error correction
+1 A
+1 A
+31 start numeric shift [NS]
+58 numeric compression codeword 1
+55 numeric compression codeword 2
+38 numeric compression codeword 3
+34 numeric compression codeword 4
+49 numeric compression codeword 5
+33 start padding
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+47 start secondary message error correction
+46
+50
+54
+58
+45
+5
+4
+48
+55
+23
+48
+19
+32
+9
+28
+54
+12
+49
+18
+27
+62
+35
+59
+56
+59
+0
+38
+4
+50
+14
+21
+8
+19
+19
+16
+21
+26
+43
+47 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.png
new file mode 100644
index 0000000..bd503aa
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.properties
new file mode 100755
index 0000000..8069709
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-numeric-shift.properties
@@ -0,0 +1,2 @@
+mode=4
+content=123456789AAAAA987654321
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.error
new file mode 100755
index 0000000..f3fa885
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.error
@@ -0,0 +1 @@
+Invalid MaxiCode structured append position: 0
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.properties
new file mode 100755
index 0000000..4680198
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-0-of-5.properties
@@ -0,0 +1,4 @@
+mode=4
+structuredAppendTotal=5
+structuredAppendPosition=0
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.codewords
new file mode 100755
index 0000000..cd9a1cc
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+33 padding
+2 structured append 1 of 3 = 000 010 = 2
+1
+2
+3
+33
+33
+33
+33
+45
+22
+35
+13
+18
+36
+47
+1
+32
+15
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+60
+60
+40
+40
+9
+9
+43
+43
+14
+14
+50
+50
+12
+12
+53
+53
+57
+57
+58
+58
+36
+36
+28
+28
+10
+10
+53
+53
+37
+37
+30
+30
+14
+14
+5
+5
+31
+31
+40
+40
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.png
new file mode 100644
index 0000000..740155b
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties
new file mode 100755
index 0000000..d0473d1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-1-of-3.properties
@@ -0,0 +1,3 @@
+mode=4
+structuredAppendTotal=3
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords
new file mode 100755
index 0000000..ab8376d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+33 padding
+10 structured append 2 of 3 = 001 010 = 10
+4
+5
+6
+33
+33
+33
+33
+56
+39
+59
+0
+45
+11
+47
+18
+61
+26
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+60
+60
+40
+40
+9
+9
+43
+43
+14
+14
+50
+50
+12
+12
+53
+53
+57
+57
+58
+58
+36
+36
+28
+28
+10
+10
+53
+53
+37
+37
+30
+30
+14
+14
+5
+5
+31
+31
+40
+40
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.png
new file mode 100644
index 0000000..c53022d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.properties
new file mode 100755
index 0000000..8d1d855
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-2-of-3.properties
@@ -0,0 +1,4 @@
+mode=4
+structuredAppendTotal=3
+structuredAppendPosition=2
+content=DEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.codewords
new file mode 100755
index 0000000..29c6d75
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.codewords
@@ -0,0 +1,144 @@
+4 mode 4
+33 padding
+18 structured append 3 of 3 = 010 010 = 18
+7
+8
+9
+33
+33
+33
+33
+56
+52
+42
+57
+35
+19
+25
+36
+54
+46
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+60
+60
+40
+40
+9
+9
+43
+43
+14
+14
+50
+50
+12
+12
+53
+53
+57
+57
+58
+58
+36
+36
+28
+28
+10
+10
+53
+53
+37
+37
+30
+30
+14
+14
+5
+5
+31
+31
+40
+40
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.png
new file mode 100644
index 0000000..d415e00
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties
new file mode 100755
index 0000000..11ab9eb
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-3-of-3.properties
@@ -0,0 +1,4 @@
+mode=4
+structuredAppendTotal=3
+structuredAppendPosition=3
+content=GHI
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error
new file mode 100755
index 0000000..9fd2ce5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.error
@@ -0,0 +1 @@
+Invalid MaxiCode structured append total: 9
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties
new file mode 100755
index 0000000..709ef05
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-structured-append-5-of-9.properties
@@ -0,0 +1,4 @@
+mode=4
+structuredAppendPosition=5
+structuredAppendTotal=9
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error
new file mode 100755
index 0000000..74ec7c8
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.error
@@ -0,0 +1 @@
+Input data too long
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties
new file mode 100755
index 0000000..b52076d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-4-too-much-data.properties
@@ -0,0 +1,2 @@
+mode=4
+content=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords
new file mode 100755
index 0000000..78bfe26
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.codewords
@@ -0,0 +1,144 @@
+5 mode 5
+1 A
+2 B
+3 C
+33 start padding
+33
+33
+33
+33
+33
+26 start primary message error correction
+33
+12
+53
+26
+49
+21
+37
+45
+59 end primary message error correction
+33 start secondary message
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33
+33 end secondary message
+5 start secondary message error correction
+5
+39
+39
+9
+9
+62
+62
+6
+6
+31
+31
+53
+53
+56
+56
+6
+6
+39
+39
+36
+36
+3
+3
+63
+63
+18
+18
+9
+9
+28
+28
+53
+53
+59
+59
+37
+37
+38
+38
+40
+40
+28
+28
+31
+31
+41
+41
+20
+20
+24
+24
+44
+44
+34
+34 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.png
new file mode 100644
index 0000000..b439a4d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties
new file mode 100755
index 0000000..5302a27
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-basic.properties
@@ -0,0 +1,2 @@
+mode=5
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords
new file mode 100755
index 0000000..f32c2aa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.codewords
@@ -0,0 +1,144 @@
+5 mode 5
+1
+2
+3
+4
+5
+6
+7
+8
+9
+21 start primary message error correction
+3
+43
+45
+54
+49
+63
+4
+45
+7 end primary message error correction
+10 start secondary message
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+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
+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 end secondary message
+5 start secondary message error correction
+60
+20
+38
+7
+14
+59
+19
+52
+60
+54
+12
+17
+37
+62
+32
+43
+39
+16
+22
+3
+13
+0
+23
+20
+36
+47
+0
+17
+59
+4
+21
+39
+16
+38
+31
+63
+42
+32
+22
+3
+26
+16
+28
+37
+59
+13
+42
+59
+45
+60
+18
+36
+53
+20
+11 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.png
new file mode 100644
index 0000000..0d33bb5
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.properties
new file mode 100755
index 0000000..11dec84
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-max-data.properties
@@ -0,0 +1,2 @@
+mode=5
+content=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXY
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.error
new file mode 100755
index 0000000..74ec7c8
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.error
@@ -0,0 +1 @@
+Input data too long
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.properties
new file mode 100755
index 0000000..1c25f21
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-5-too-much-data.properties
@@ -0,0 +1,2 @@
+mode=5
+content=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.codewords
new file mode 100755
index 0000000..0f1d58a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.codewords
@@ -0,0 +1,144 @@
+6 mode 6
+47 start primary message
+47
+47
+47
+47
+47
+47
+47
+47 end primary message
+37 start primary message error correction
+32
+5
+29
+0
+5
+35
+21
+27
+58 end primary message error correction
+47 start secondary message
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47
+47 end secondary message
+49 start secondary message error correction
+49
+31
+31
+48
+48
+56
+56
+32
+32
+17
+17
+26
+26
+1
+1
+27
+27
+60
+60
+5
+5
+3
+3
+23
+23
+1
+1
+24
+24
+57
+57
+32
+32
+42
+42
+36
+36
+31
+31 end secondary message error correction
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.png b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.png
new file mode 100644
index 0000000..71aec56
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties
new file mode 100755
index 0000000..ead3ae3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-max-data.properties
@@ -0,0 +1,2 @@
+mode=6
+content=/////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error
new file mode 100755
index 0000000..74ec7c8
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.error
@@ -0,0 +1 @@
+Input data too long
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties
new file mode 100755
index 0000000..0dae1b1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-6-too-much-data.properties
@@ -0,0 +1,2 @@
+mode=6
+content=//////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error
new file mode 100755
index 0000000..8e1e16d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.error
@@ -0,0 +1 @@
+Invalid MaxiCode mode: 7
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties
new file mode 100755
index 0000000..0b770e5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/maxicode/mode-7.properties
@@ -0,0 +1 @@
+mode=7
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps
new file mode 100755
index 0000000..62bea42
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.eps
@@ -0,0 +1,71 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: OkapiBarcode
+%%Title: 123456789
+%%Pages: 0
+%%BoundingBox: 0 0 128 60
+%%EndComments
+/TL { setlinewidth moveto lineto stroke } bind def
+/TC { moveto 0 360 arc 360 0 arcn fill } bind def
+/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def
+/TB { 2 copy } bind def
+/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def
+/TE { pop pop } bind def
+newpath
+0.00 0.00 0.00 setrgbcolor
+1.00 1.00 1.00 setrgbcolor
+60.00 0.00 TB 0.00 128.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+40.00 15.00 TB 5.00 1.00 TR
+TB 7.00 1.00 TR
+TB 9.00 4.00 TR
+TB 14.00 1.00 TR
+TB 16.00 1.00 TR
+TB 19.00 1.00 TR
+TB 23.00 1.00 TR
+TB 25.00 1.00 TR
+TB 29.00 1.00 TR
+TB 32.00 1.00 TR
+TB 34.00 1.00 TR
+TB 39.00 1.00 TR
+TB 41.00 1.00 TR
+TB 44.00 1.00 TR
+TB 46.00 1.00 TR
+TB 50.00 1.00 TR
+TB 53.00 1.00 TR
+TB 56.00 1.00 TR
+TB 59.00 1.00 TR
+TB 62.00 1.00 TR
+TB 66.00 1.00 TR
+TB 68.00 1.00 TR
+TB 70.00 1.00 TR
+TB 72.00 1.00 TR
+TB 77.00 1.00 TR
+TB 81.00 1.00 TR
+TB 84.00 1.00 TR
+TB 86.00 1.00 TR
+TB 91.00 1.00 TR
+TB 93.00 1.00 TR
+TB 95.00 1.00 TR
+TB 98.00 1.00 TR
+TB 100.00 2.00 TR
+TB 104.00 1.00 TR
+TB 107.00 2.00 TR
+TB 111.00 1.00 TR
+TB 113.00 1.00 TR
+TB 115.00 1.00 TR
+TB 117.00 4.00 TR
+TB 122.00 1.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+matrix currentmatrix
+/Helvetica findfont
+8.00 scalefont setfont
+ 0 0 moveto 64.00 7.00 translate 0.00 rotate 0 0 moveto
+ (123456789Od) stringwidth
+pop
+-2 div 0 rmoveto
+ (123456789Od) show
+setmatrix
+
+showpage
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg
new file mode 100755
index 0000000..9d52bd0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-basic.svg
@@ -0,0 +1,53 @@
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 123456789Od
+
+
+
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps
new file mode 100755
index 0000000..1853e2f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.eps
@@ -0,0 +1,71 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: OkapiBarcode
+%%Title: 123456789
+%%Pages: 0
+%%BoundingBox: 0 0 128 60
+%%EndComments
+/TL { setlinewidth moveto lineto stroke } bind def
+/TC { moveto 0 360 arc 360 0 arcn fill } bind def
+/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def
+/TB { 2 copy } bind def
+/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def
+/TE { pop pop } bind def
+newpath
+1.00 0.00 0.00 setrgbcolor
+0.00 1.00 0.00 setrgbcolor
+60.00 0.00 TB 0.00 128.00 TR
+TE
+1.00 0.00 0.00 setrgbcolor
+40.00 15.00 TB 5.00 1.00 TR
+TB 7.00 1.00 TR
+TB 9.00 4.00 TR
+TB 14.00 1.00 TR
+TB 16.00 1.00 TR
+TB 19.00 1.00 TR
+TB 23.00 1.00 TR
+TB 25.00 1.00 TR
+TB 29.00 1.00 TR
+TB 32.00 1.00 TR
+TB 34.00 1.00 TR
+TB 39.00 1.00 TR
+TB 41.00 1.00 TR
+TB 44.00 1.00 TR
+TB 46.00 1.00 TR
+TB 50.00 1.00 TR
+TB 53.00 1.00 TR
+TB 56.00 1.00 TR
+TB 59.00 1.00 TR
+TB 62.00 1.00 TR
+TB 66.00 1.00 TR
+TB 68.00 1.00 TR
+TB 70.00 1.00 TR
+TB 72.00 1.00 TR
+TB 77.00 1.00 TR
+TB 81.00 1.00 TR
+TB 84.00 1.00 TR
+TB 86.00 1.00 TR
+TB 91.00 1.00 TR
+TB 93.00 1.00 TR
+TB 95.00 1.00 TR
+TB 98.00 1.00 TR
+TB 100.00 2.00 TR
+TB 104.00 1.00 TR
+TB 107.00 2.00 TR
+TB 111.00 1.00 TR
+TB 113.00 1.00 TR
+TB 115.00 1.00 TR
+TB 117.00 4.00 TR
+TB 122.00 1.00 TR
+TE
+1.00 0.00 0.00 setrgbcolor
+matrix currentmatrix
+/Helvetica findfont
+8.00 scalefont setfont
+ 0 0 moveto 64.00 7.00 translate 0.00 rotate 0 0 moveto
+ (123456789Od) stringwidth
+pop
+-2 div 0 rmoveto
+ (123456789Od) show
+setmatrix
+
+showpage
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg
new file mode 100755
index 0000000..cab3f5b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-colors.svg
@@ -0,0 +1,53 @@
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 123456789Od
+
+
+
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps
new file mode 100755
index 0000000..18ba809
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.eps
@@ -0,0 +1,71 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: OkapiBarcode
+%%Title: 123456789
+%%Pages: 0
+%%BoundingBox: 0 0 128 82
+%%EndComments
+/TL { setlinewidth moveto lineto stroke } bind def
+/TC { moveto 0 360 arc 360 0 arcn fill } bind def
+/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def
+/TB { 2 copy } bind def
+/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def
+/TE { pop pop } bind def
+newpath
+0.00 0.00 0.00 setrgbcolor
+1.00 1.00 1.00 setrgbcolor
+82.00 0.00 TB 0.00 128.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+40.00 37.00 TB 5.00 1.00 TR
+TB 7.00 1.00 TR
+TB 9.00 4.00 TR
+TB 14.00 1.00 TR
+TB 16.00 1.00 TR
+TB 19.00 1.00 TR
+TB 23.00 1.00 TR
+TB 25.00 1.00 TR
+TB 29.00 1.00 TR
+TB 32.00 1.00 TR
+TB 34.00 1.00 TR
+TB 39.00 1.00 TR
+TB 41.00 1.00 TR
+TB 44.00 1.00 TR
+TB 46.00 1.00 TR
+TB 50.00 1.00 TR
+TB 53.00 1.00 TR
+TB 56.00 1.00 TR
+TB 59.00 1.00 TR
+TB 62.00 1.00 TR
+TB 66.00 1.00 TR
+TB 68.00 1.00 TR
+TB 70.00 1.00 TR
+TB 72.00 1.00 TR
+TB 77.00 1.00 TR
+TB 81.00 1.00 TR
+TB 84.00 1.00 TR
+TB 86.00 1.00 TR
+TB 91.00 1.00 TR
+TB 93.00 1.00 TR
+TB 95.00 1.00 TR
+TB 98.00 1.00 TR
+TB 100.00 2.00 TR
+TB 104.00 1.00 TR
+TB 107.00 2.00 TR
+TB 111.00 1.00 TR
+TB 113.00 1.00 TR
+TB 115.00 1.00 TR
+TB 117.00 4.00 TR
+TB 122.00 1.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+matrix currentmatrix
+/Arial findfont
+26.00 scalefont setfont
+ 0 0 moveto 64.00 11.00 translate 0.00 rotate 0 0 moveto
+ (123456789Od) stringwidth
+pop
+-2 div 0 rmoveto
+ (123456789Od) show
+setmatrix
+
+showpage
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg
new file mode 100755
index 0000000..5db0be1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-custom-font.svg
@@ -0,0 +1,53 @@
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 123456789Od
+
+
+
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps
new file mode 100755
index 0000000..1d8d66e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.eps
@@ -0,0 +1,71 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: OkapiBarcode
+%%Title: 123456789
+%%Pages: 0
+%%BoundingBox: 0 0 256 120
+%%EndComments
+/TL { setlinewidth moveto lineto stroke } bind def
+/TC { moveto 0 360 arc 360 0 arcn fill } bind def
+/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def
+/TB { 2 copy } bind def
+/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def
+/TE { pop pop } bind def
+newpath
+0.00 0.00 0.00 setrgbcolor
+1.00 1.00 1.00 setrgbcolor
+120.00 0.00 TB 0.00 256.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+80.00 30.00 TB 10.00 2.00 TR
+TB 14.00 2.00 TR
+TB 18.00 8.00 TR
+TB 28.00 2.00 TR
+TB 32.00 2.00 TR
+TB 38.00 2.00 TR
+TB 46.00 2.00 TR
+TB 50.00 2.00 TR
+TB 58.00 2.00 TR
+TB 64.00 2.00 TR
+TB 68.00 2.00 TR
+TB 78.00 2.00 TR
+TB 82.00 2.00 TR
+TB 88.00 2.00 TR
+TB 92.00 2.00 TR
+TB 100.00 2.00 TR
+TB 106.00 2.00 TR
+TB 112.00 2.00 TR
+TB 118.00 2.00 TR
+TB 124.00 2.00 TR
+TB 132.00 2.00 TR
+TB 136.00 2.00 TR
+TB 140.00 2.00 TR
+TB 144.00 2.00 TR
+TB 154.00 2.00 TR
+TB 162.00 2.00 TR
+TB 168.00 2.00 TR
+TB 172.00 2.00 TR
+TB 182.00 2.00 TR
+TB 186.00 2.00 TR
+TB 190.00 2.00 TR
+TB 196.00 2.00 TR
+TB 200.00 4.00 TR
+TB 208.00 2.00 TR
+TB 214.00 4.00 TR
+TB 222.00 2.00 TR
+TB 226.00 2.00 TR
+TB 230.00 2.00 TR
+TB 234.00 8.00 TR
+TB 244.00 2.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+matrix currentmatrix
+/Helvetica findfont
+16.00 scalefont setfont
+ 0 0 moveto 128.00 14.00 translate 0.00 rotate 0 0 moveto
+ (123456789Od) stringwidth
+pop
+-2 div 0 rmoveto
+ (123456789Od) show
+setmatrix
+
+showpage
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg
new file mode 100755
index 0000000..34baea3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-magnification-2.svg
@@ -0,0 +1,53 @@
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 123456789Od
+
+
+
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps
new file mode 100755
index 0000000..2dfa31b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.eps
@@ -0,0 +1,71 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: OkapiBarcode
+%%Title: 123456789
+%%Pages: 0
+%%BoundingBox: 0 0 158 90
+%%EndComments
+/TL { setlinewidth moveto lineto stroke } bind def
+/TC { moveto 0 360 arc 360 0 arcn fill } bind def
+/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def
+/TB { 2 copy } bind def
+/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def
+/TE { pop pop } bind def
+newpath
+0.00 0.00 0.00 setrgbcolor
+1.00 1.00 1.00 setrgbcolor
+90.00 0.00 TB 0.00 158.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+40.00 30.00 TB 20.00 1.00 TR
+TB 22.00 1.00 TR
+TB 24.00 4.00 TR
+TB 29.00 1.00 TR
+TB 31.00 1.00 TR
+TB 34.00 1.00 TR
+TB 38.00 1.00 TR
+TB 40.00 1.00 TR
+TB 44.00 1.00 TR
+TB 47.00 1.00 TR
+TB 49.00 1.00 TR
+TB 54.00 1.00 TR
+TB 56.00 1.00 TR
+TB 59.00 1.00 TR
+TB 61.00 1.00 TR
+TB 65.00 1.00 TR
+TB 68.00 1.00 TR
+TB 71.00 1.00 TR
+TB 74.00 1.00 TR
+TB 77.00 1.00 TR
+TB 81.00 1.00 TR
+TB 83.00 1.00 TR
+TB 85.00 1.00 TR
+TB 87.00 1.00 TR
+TB 92.00 1.00 TR
+TB 96.00 1.00 TR
+TB 99.00 1.00 TR
+TB 101.00 1.00 TR
+TB 106.00 1.00 TR
+TB 108.00 1.00 TR
+TB 110.00 1.00 TR
+TB 113.00 1.00 TR
+TB 115.00 2.00 TR
+TB 119.00 1.00 TR
+TB 122.00 2.00 TR
+TB 126.00 1.00 TR
+TB 128.00 1.00 TR
+TB 130.00 1.00 TR
+TB 132.00 4.00 TR
+TB 137.00 1.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+matrix currentmatrix
+/Helvetica findfont
+8.00 scalefont setfont
+ 0 0 moveto 79.00 22.00 translate 0.00 rotate 0 0 moveto
+ (123456789Od) stringwidth
+pop
+-2 div 0 rmoveto
+ (123456789Od) show
+setmatrix
+
+showpage
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg
new file mode 100755
index 0000000..f27da66
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/code93-margin-size-20.svg
@@ -0,0 +1,53 @@
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 123456789Od
+
+
+
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps
new file mode 100755
index 0000000..c256da2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.eps
@@ -0,0 +1,393 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: OkapiBarcode
+%%Title: 123456789
+%%Pages: 0
+%%BoundingBox: 0 0 420 410
+%%EndComments
+/TL { setlinewidth moveto lineto stroke } bind def
+/TC { moveto 0 360 arc 360 0 arcn fill } bind def
+/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def
+/TB { 2 copy } bind def
+/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def
+/TE { pop pop } bind def
+newpath
+0.00 0.00 0.00 setrgbcolor
+1.00 1.00 1.00 setrgbcolor
+410.00 0.00 TB 0.00 420.00 TR
+TE
+0.00 0.00 0.00 setrgbcolor
+0.00 0.00 0.00 setrgbcolor
+60.76 349.40 10.85 60.76 349.40 8.97 69.73 349.40 TC
+60.76 349.40 7.10 60.76 349.40 5.22 65.98 349.40 TC
+60.76 349.40 3.31 60.76 349.40 1.43 62.19 349.40 TC
+28.69 382.32 29.77 382.95 29.77 384.20 28.69 384.82 27.62 384.20 27.62 382.95 TH
+33.61 382.32 34.69 382.95 34.69 384.20 33.61 384.82 32.54 384.20 32.54 382.95 TH
+38.53 382.32 39.61 382.95 39.61 384.20 38.53 384.82 37.46 384.20 37.46 382.95 TH
+43.45 382.32 44.53 382.95 44.53 384.20 43.45 384.82 42.38 384.20 42.38 382.95 TH
+48.37 382.32 49.45 382.95 49.45 384.20 48.37 384.82 47.30 384.20 47.30 382.95 TH
+53.29 382.32 54.36 382.95 54.36 384.20 53.29 384.82 52.22 384.20 52.22 382.95 TH
+58.21 382.32 59.29 382.95 59.29 384.20 58.21 384.82 57.14 384.20 57.14 382.95 TH
+63.13 382.32 64.21 382.95 64.21 384.20 63.13 384.82 62.05 384.20 62.05 382.95 TH
+68.05 382.32 69.13 382.95 69.13 384.20 68.05 384.82 66.98 384.20 66.98 382.95 TH
+72.97 382.32 74.05 382.95 74.05 384.20 72.97 384.82 71.90 384.20 71.90 382.95 TH
+77.89 382.32 78.97 382.95 78.97 384.20 77.89 384.82 76.82 384.20 76.82 382.95 TH
+82.81 382.32 83.88 382.95 83.88 384.20 82.81 384.82 81.73 384.20 81.73 382.95 TH
+87.73 382.32 88.81 382.95 88.81 384.20 87.73 384.82 86.66 384.20 86.66 382.95 TH
+92.65 382.32 93.73 382.95 93.73 384.20 92.65 384.82 91.58 384.20 91.58 382.95 TH
+95.11 382.32 96.19 382.95 96.19 384.20 95.11 384.82 94.04 384.20 94.04 382.95 TH
+97.57 382.32 98.65 382.95 98.65 384.20 97.57 384.82 96.50 384.20 96.50 382.95 TH
+26.23 378.05 27.31 378.68 27.31 379.93 26.23 380.55 25.16 379.93 25.16 378.68 TH
+31.15 378.05 32.23 378.68 32.23 379.93 31.15 380.55 30.08 379.93 30.08 378.68 TH
+36.07 378.05 37.14 378.68 37.14 379.93 36.07 380.55 35.00 379.93 35.00 378.68 TH
+40.99 378.05 42.07 378.68 42.07 379.93 40.99 380.55 39.92 379.93 39.92 378.68 TH
+45.91 378.05 46.99 378.68 46.99 379.93 45.91 380.55 44.84 379.93 44.84 378.68 TH
+50.83 378.05 51.91 378.68 51.91 379.93 50.83 380.55 49.76 379.93 49.76 378.68 TH
+55.75 378.05 56.83 378.68 56.83 379.93 55.75 380.55 54.68 379.93 54.68 378.68 TH
+60.67 378.05 61.75 378.68 61.75 379.93 60.67 380.55 59.59 379.93 59.59 378.68 TH
+65.59 378.05 66.66 378.68 66.66 379.93 65.59 380.55 64.51 379.93 64.51 378.68 TH
+70.51 378.05 71.59 378.68 71.59 379.93 70.51 380.55 69.44 379.93 69.44 378.68 TH
+75.43 378.05 76.51 378.68 76.51 379.93 75.43 380.55 74.35 379.93 74.35 378.68 TH
+80.35 378.05 81.43 378.68 81.43 379.93 80.35 380.55 79.27 379.93 79.27 378.68 TH
+85.27 378.05 86.35 378.68 86.35 379.93 85.27 380.55 84.20 379.93 84.20 378.68 TH
+90.19 378.05 91.27 378.68 91.27 379.93 90.19 380.55 89.12 379.93 89.12 378.68 TH
+95.11 378.05 96.19 378.68 96.19 379.93 95.11 380.55 94.04 379.93 94.04 378.68 TH
+29.92 375.92 31.00 376.54 31.00 377.79 29.92 378.42 28.85 377.79 28.85 376.54 TH
+34.84 375.92 35.92 376.54 35.92 377.79 34.84 378.42 33.77 377.79 33.77 376.54 TH
+39.76 375.92 40.84 376.54 40.84 377.79 39.76 378.42 38.69 377.79 38.69 376.54 TH
+44.68 375.92 45.75 376.54 45.75 377.79 44.68 378.42 43.61 377.79 43.61 376.54 TH
+49.60 375.92 50.68 376.54 50.68 377.79 49.60 378.42 48.53 377.79 48.53 376.54 TH
+54.52 375.92 55.60 376.54 55.60 377.79 54.52 378.42 53.45 377.79 53.45 376.54 TH
+59.44 375.92 60.52 376.54 60.52 377.79 59.44 378.42 58.36 377.79 58.36 376.54 TH
+64.36 375.92 65.44 376.54 65.44 377.79 64.36 378.42 63.28 377.79 63.28 376.54 TH
+69.28 375.92 70.35 376.54 70.35 377.79 69.28 378.42 68.20 377.79 68.20 376.54 TH
+74.20 375.92 75.28 376.54 75.28 377.79 74.20 378.42 73.13 377.79 73.13 376.54 TH
+79.12 375.92 80.20 376.54 80.20 377.79 79.12 378.42 78.04 377.79 78.04 376.54 TH
+84.04 375.92 85.12 376.54 85.12 377.79 84.04 378.42 82.96 377.79 82.96 376.54 TH
+88.96 375.92 90.04 376.54 90.04 377.79 88.96 378.42 87.88 377.79 87.88 376.54 TH
+93.88 375.92 94.96 376.54 94.96 377.79 93.88 378.42 92.81 377.79 92.81 376.54 TH
+96.34 375.92 97.42 376.54 97.42 377.79 96.34 378.42 95.27 377.79 95.27 376.54 TH
+97.57 373.78 98.65 374.41 98.65 375.66 97.57 376.28 96.50 375.66 96.50 374.41 TH
+27.46 371.65 28.54 372.27 28.54 373.52 27.46 374.15 26.39 373.52 26.39 372.27 TH
+32.38 371.65 33.46 372.27 33.46 373.52 32.38 374.15 31.31 373.52 31.31 372.27 TH
+37.30 371.65 38.38 372.27 38.38 373.52 37.30 374.15 36.23 373.52 36.23 372.27 TH
+42.22 371.65 43.30 372.27 43.30 373.52 42.22 374.15 41.14 373.52 41.14 372.27 TH
+47.14 371.65 48.22 372.27 48.22 373.52 47.14 374.15 46.07 373.52 46.07 372.27 TH
+52.06 371.65 53.14 372.27 53.14 373.52 52.06 374.15 50.99 373.52 50.99 372.27 TH
+56.98 371.65 58.06 372.27 58.06 373.52 56.98 374.15 55.91 373.52 55.91 372.27 TH
+61.90 371.65 62.97 372.27 62.97 373.52 61.90 374.15 60.82 373.52 60.82 372.27 TH
+66.82 371.65 67.90 372.27 67.90 373.52 66.82 374.15 65.74 373.52 65.74 372.27 TH
+71.74 371.65 72.82 372.27 72.82 373.52 71.74 374.15 70.66 373.52 70.66 372.27 TH
+76.66 371.65 77.74 372.27 77.74 373.52 76.66 374.15 75.59 373.52 75.59 372.27 TH
+81.58 371.65 82.66 372.27 82.66 373.52 81.58 374.15 80.51 373.52 80.51 372.27 TH
+86.50 371.65 87.57 372.27 87.57 373.52 86.50 374.15 85.42 373.52 85.42 372.27 TH
+91.42 371.65 92.50 372.27 92.50 373.52 91.42 374.15 90.35 373.52 90.35 372.27 TH
+28.69 369.51 29.77 370.14 29.77 371.39 28.69 372.01 27.62 371.39 27.62 370.14 TH
+33.61 369.51 34.69 370.14 34.69 371.39 33.61 372.01 32.54 371.39 32.54 370.14 TH
+38.53 369.51 39.61 370.14 39.61 371.39 38.53 372.01 37.46 371.39 37.46 370.14 TH
+43.45 369.51 44.53 370.14 44.53 371.39 43.45 372.01 42.38 371.39 42.38 370.14 TH
+48.37 369.51 49.45 370.14 49.45 371.39 48.37 372.01 47.30 371.39 47.30 370.14 TH
+53.29 369.51 54.36 370.14 54.36 371.39 53.29 372.01 52.22 371.39 52.22 370.14 TH
+58.21 369.51 59.29 370.14 59.29 371.39 58.21 372.01 57.14 371.39 57.14 370.14 TH
+63.13 369.51 64.21 370.14 64.21 371.39 63.13 372.01 62.05 371.39 62.05 370.14 TH
+68.05 369.51 69.13 370.14 69.13 371.39 68.05 372.01 66.98 371.39 66.98 370.14 TH
+72.97 369.51 74.05 370.14 74.05 371.39 72.97 372.01 71.90 371.39 71.90 370.14 TH
+77.89 369.51 78.97 370.14 78.97 371.39 77.89 372.01 76.82 371.39 76.82 370.14 TH
+82.81 369.51 83.88 370.14 83.88 371.39 82.81 372.01 81.73 371.39 81.73 370.14 TH
+87.73 369.51 88.81 370.14 88.81 371.39 87.73 372.01 86.66 371.39 86.66 370.14 TH
+92.65 369.51 93.73 370.14 93.73 371.39 92.65 372.01 91.58 371.39 91.58 370.14 TH
+95.11 369.51 96.19 370.14 96.19 371.39 95.11 372.01 94.04 371.39 94.04 370.14 TH
+96.34 367.38 97.42 368.00 97.42 369.25 96.34 369.88 95.27 369.25 95.27 368.00 TH
+26.23 365.24 27.31 365.87 27.31 367.12 26.23 367.74 25.16 367.12 25.16 365.87 TH
+31.15 365.24 32.23 365.87 32.23 367.12 31.15 367.74 30.08 367.12 30.08 365.87 TH
+36.07 365.24 37.14 365.87 37.14 367.12 36.07 367.74 35.00 367.12 35.00 365.87 TH
+40.99 365.24 42.07 365.87 42.07 367.12 40.99 367.74 39.92 367.12 39.92 365.87 TH
+45.91 365.24 46.99 365.87 46.99 367.12 45.91 367.74 44.84 367.12 44.84 365.87 TH
+50.83 365.24 51.91 365.87 51.91 367.12 50.83 367.74 49.76 367.12 49.76 365.87 TH
+55.75 365.24 56.83 365.87 56.83 367.12 55.75 367.74 54.68 367.12 54.68 365.87 TH
+60.67 365.24 61.75 365.87 61.75 367.12 60.67 367.74 59.59 367.12 59.59 365.87 TH
+65.59 365.24 66.66 365.87 66.66 367.12 65.59 367.74 64.51 367.12 64.51 365.87 TH
+70.51 365.24 71.59 365.87 71.59 367.12 70.51 367.74 69.44 367.12 69.44 365.87 TH
+75.43 365.24 76.51 365.87 76.51 367.12 75.43 367.74 74.35 367.12 74.35 365.87 TH
+80.35 365.24 81.43 365.87 81.43 367.12 80.35 367.74 79.27 367.12 79.27 365.87 TH
+85.27 365.24 86.35 365.87 86.35 367.12 85.27 367.74 84.20 367.12 84.20 365.87 TH
+90.19 365.24 91.27 365.87 91.27 367.12 90.19 367.74 89.12 367.12 89.12 365.87 TH
+97.57 365.24 98.65 365.87 98.65 367.12 97.57 367.74 96.50 367.12 96.50 365.87 TH
+29.92 363.11 31.00 363.73 31.00 364.98 29.92 365.61 28.85 364.98 28.85 363.73 TH
+34.84 363.11 35.92 363.73 35.92 364.98 34.84 365.61 33.77 364.98 33.77 363.73 TH
+39.76 363.11 40.84 363.73 40.84 364.98 39.76 365.61 38.69 364.98 38.69 363.73 TH
+44.68 363.11 45.75 363.73 45.75 364.98 44.68 365.61 43.61 364.98 43.61 363.73 TH
+47.14 363.11 48.22 363.73 48.22 364.98 47.14 365.61 46.07 364.98 46.07 363.73 TH
+49.60 363.11 50.68 363.73 50.68 364.98 49.60 365.61 48.53 364.98 48.53 363.73 TH
+52.06 363.11 53.14 363.73 53.14 364.98 52.06 365.61 50.99 364.98 50.99 363.73 TH
+54.52 363.11 55.60 363.73 55.60 364.98 54.52 365.61 53.45 364.98 53.45 363.73 TH
+61.90 363.11 62.97 363.73 62.97 364.98 61.90 365.61 60.82 364.98 60.82 363.73 TH
+76.66 363.11 77.74 363.73 77.74 364.98 76.66 365.61 75.59 364.98 75.59 363.73 TH
+79.12 363.11 80.20 363.73 80.20 364.98 79.12 365.61 78.04 364.98 78.04 363.73 TH
+84.04 363.11 85.12 363.73 85.12 364.98 84.04 365.61 82.96 364.98 82.96 363.73 TH
+88.96 363.11 90.04 363.73 90.04 364.98 88.96 365.61 87.88 364.98 87.88 363.73 TH
+93.88 363.11 94.96 363.73 94.96 364.98 93.88 365.61 92.81 364.98 92.81 363.73 TH
+53.29 360.97 54.36 361.60 54.36 362.85 53.29 363.47 52.22 362.85 52.22 361.60 TH
+55.75 360.97 56.83 361.60 56.83 362.85 55.75 363.47 54.68 362.85 54.68 361.60 TH
+60.67 360.97 61.75 361.60 61.75 362.85 60.67 363.47 59.59 362.85 59.59 361.60 TH
+65.59 360.97 66.66 361.60 66.66 362.85 65.59 363.47 64.51 362.85 64.51 361.60 TH
+75.43 360.97 76.51 361.60 76.51 362.85 75.43 363.47 74.35 362.85 74.35 361.60 TH
+77.89 360.97 78.97 361.60 78.97 362.85 77.89 363.47 76.82 362.85 76.82 361.60 TH
+27.46 358.84 28.54 359.46 28.54 360.71 27.46 361.34 26.39 360.71 26.39 359.46 TH
+32.38 358.84 33.46 359.46 33.46 360.71 32.38 361.34 31.31 360.71 31.31 359.46 TH
+37.30 358.84 38.38 359.46 38.38 360.71 37.30 361.34 36.23 360.71 36.23 359.46 TH
+42.22 358.84 43.30 359.46 43.30 360.71 42.22 361.34 41.14 360.71 41.14 359.46 TH
+52.06 358.84 53.14 359.46 53.14 360.71 52.06 361.34 50.99 360.71 50.99 359.46 TH
+71.74 358.84 72.82 359.46 72.82 360.71 71.74 361.34 70.66 360.71 70.66 359.46 TH
+76.66 358.84 77.74 359.46 77.74 360.71 76.66 361.34 75.59 360.71 75.59 359.46 TH
+81.58 358.84 82.66 359.46 82.66 360.71 81.58 361.34 80.51 360.71 80.51 359.46 TH
+86.50 358.84 87.57 359.46 87.57 360.71 86.50 361.34 85.42 360.71 85.42 359.46 TH
+91.42 358.84 92.50 359.46 92.50 360.71 91.42 361.34 90.35 360.71 90.35 359.46 TH
+96.34 358.84 97.42 359.46 97.42 360.71 96.34 361.34 95.27 360.71 95.27 359.46 TH
+28.69 356.70 29.77 357.33 29.77 358.58 28.69 359.20 27.62 358.58 27.62 357.33 TH
+33.61 356.70 34.69 357.33 34.69 358.58 33.61 359.20 32.54 358.58 32.54 357.33 TH
+38.53 356.70 39.61 357.33 39.61 358.58 38.53 359.20 37.46 358.58 37.46 357.33 TH
+43.45 356.70 44.53 357.33 44.53 358.58 43.45 359.20 42.38 358.58 42.38 357.33 TH
+48.37 356.70 49.45 357.33 49.45 358.58 48.37 359.20 47.30 358.58 47.30 357.33 TH
+50.83 356.70 51.91 357.33 51.91 358.58 50.83 359.20 49.76 358.58 49.76 357.33 TH
+72.97 356.70 74.05 357.33 74.05 358.58 72.97 359.20 71.90 358.58 71.90 357.33 TH
+75.43 356.70 76.51 357.33 76.51 358.58 75.43 359.20 74.35 358.58 74.35 357.33 TH
+82.81 356.70 83.88 357.33 83.88 358.58 82.81 359.20 81.73 358.58 81.73 357.33 TH
+87.73 356.70 88.81 357.33 88.81 358.58 87.73 359.20 86.66 358.58 86.66 357.33 TH
+92.65 356.70 93.73 357.33 93.73 358.58 92.65 359.20 91.58 358.58 91.58 357.33 TH
+95.11 356.70 96.19 357.33 96.19 358.58 95.11 359.20 94.04 358.58 94.04 357.33 TH
+42.22 354.57 43.30 355.19 43.30 356.44 42.22 357.07 41.14 356.44 41.14 355.19 TH
+71.74 354.57 72.82 355.19 72.82 356.44 71.74 357.07 70.66 356.44 70.66 355.19 TH
+74.20 354.57 75.28 355.19 75.28 356.44 74.20 357.07 73.13 356.44 73.13 355.19 TH
+76.66 354.57 77.74 355.19 77.74 356.44 76.66 357.07 75.59 356.44 75.59 355.19 TH
+79.12 354.57 80.20 355.19 80.20 356.44 79.12 357.07 78.04 356.44 78.04 355.19 TH
+26.23 352.43 27.31 353.06 27.31 354.31 26.23 354.93 25.16 354.31 25.16 353.06 TH
+31.15 352.43 32.23 353.06 32.23 354.31 31.15 354.93 30.08 354.31 30.08 353.06 TH
+36.07 352.43 37.14 353.06 37.14 354.31 36.07 354.93 35.00 354.31 35.00 353.06 TH
+40.99 352.43 42.07 353.06 42.07 354.31 40.99 354.93 39.92 354.31 39.92 353.06 TH
+45.91 352.43 46.99 353.06 46.99 354.31 45.91 354.93 44.84 354.31 44.84 353.06 TH
+75.43 352.43 76.51 353.06 76.51 354.31 75.43 354.93 74.35 354.31 74.35 353.06 TH
+80.35 352.43 81.43 353.06 81.43 354.31 80.35 354.93 79.27 354.31 79.27 353.06 TH
+85.27 352.43 86.35 353.06 86.35 354.31 85.27 354.93 84.20 354.31 84.20 353.06 TH
+90.19 352.43 91.27 353.06 91.27 354.31 90.19 354.93 89.12 354.31 89.12 353.06 TH
+29.92 350.30 31.00 350.92 31.00 352.17 29.92 352.80 28.85 352.17 28.85 350.92 TH
+34.84 350.30 35.92 350.92 35.92 352.17 34.84 352.80 33.77 352.17 33.77 350.92 TH
+39.76 350.30 40.84 350.92 40.84 352.17 39.76 352.80 38.69 352.17 38.69 350.92 TH
+42.22 350.30 43.30 350.92 43.30 352.17 42.22 352.80 41.14 352.17 41.14 350.92 TH
+44.68 350.30 45.75 350.92 45.75 352.17 44.68 352.80 43.61 352.17 43.61 350.92 TH
+47.14 350.30 48.22 350.92 48.22 352.17 47.14 352.80 46.07 352.17 46.07 350.92 TH
+76.66 350.30 77.74 350.92 77.74 352.17 76.66 352.80 75.59 352.17 75.59 350.92 TH
+84.04 350.30 85.12 350.92 85.12 352.17 84.04 352.80 82.96 352.17 82.96 350.92 TH
+88.96 350.30 90.04 350.92 90.04 352.17 88.96 352.80 87.88 352.17 87.88 350.92 TH
+93.88 350.30 94.96 350.92 94.96 352.17 93.88 352.80 92.81 352.17 92.81 350.92 TH
+96.34 350.30 97.42 350.92 97.42 352.17 96.34 352.80 95.27 352.17 95.27 350.92 TH
+45.91 348.16 46.99 348.79 46.99 350.04 45.91 350.66 44.84 350.04 44.84 348.79 TH
+75.43 348.16 76.51 348.79 76.51 350.04 75.43 350.66 74.35 350.04 74.35 348.79 TH
+95.11 348.16 96.19 348.79 96.19 350.04 95.11 350.66 94.04 350.04 94.04 348.79 TH
+27.46 346.03 28.54 346.65 28.54 347.90 27.46 348.53 26.39 347.90 26.39 346.65 TH
+32.38 346.03 33.46 346.65 33.46 347.90 32.38 348.53 31.31 347.90 31.31 346.65 TH
+37.30 346.03 38.38 346.65 38.38 347.90 37.30 348.53 36.23 347.90 36.23 346.65 TH
+47.14 346.03 48.22 346.65 48.22 347.90 47.14 348.53 46.07 347.90 46.07 346.65 TH
+76.66 346.03 77.74 346.65 77.74 347.90 76.66 348.53 75.59 347.90 75.59 346.65 TH
+79.12 346.03 80.20 346.65 80.20 347.90 79.12 348.53 78.04 347.90 78.04 346.65 TH
+81.58 346.03 82.66 346.65 82.66 347.90 81.58 348.53 80.51 347.90 80.51 346.65 TH
+86.50 346.03 87.57 346.65 87.57 347.90 86.50 348.53 85.42 347.90 85.42 346.65 TH
+91.42 346.03 92.50 346.65 92.50 347.90 91.42 348.53 90.35 347.90 90.35 346.65 TH
+28.69 343.89 29.77 344.52 29.77 345.77 28.69 346.39 27.62 345.77 27.62 344.52 TH
+33.61 343.89 34.69 344.52 34.69 345.77 33.61 346.39 32.54 345.77 32.54 344.52 TH
+38.53 343.89 39.61 344.52 39.61 345.77 38.53 346.39 37.46 345.77 37.46 344.52 TH
+43.45 343.89 44.53 344.52 44.53 345.77 43.45 346.39 42.38 345.77 42.38 344.52 TH
+48.37 343.89 49.45 344.52 49.45 345.77 48.37 346.39 47.30 345.77 47.30 344.52 TH
+72.97 343.89 74.05 344.52 74.05 345.77 72.97 346.39 71.90 345.77 71.90 344.52 TH
+75.43 343.89 76.51 344.52 76.51 345.77 75.43 346.39 74.35 345.77 74.35 344.52 TH
+77.89 343.89 78.97 344.52 78.97 345.77 77.89 346.39 76.82 345.77 76.82 344.52 TH
+82.81 343.89 83.88 344.52 83.88 345.77 82.81 346.39 81.73 345.77 81.73 344.52 TH
+87.73 343.89 88.81 344.52 88.81 345.77 87.73 346.39 86.66 345.77 86.66 344.52 TH
+92.65 343.89 93.73 344.52 93.73 345.77 92.65 346.39 91.58 345.77 91.58 344.52 TH
+95.11 343.89 96.19 344.52 96.19 345.77 95.11 346.39 94.04 345.77 94.04 344.52 TH
+97.57 343.89 98.65 344.52 98.65 345.77 97.57 346.39 96.50 345.77 96.50 344.52 TH
+42.22 341.76 43.30 342.38 43.30 343.63 42.22 344.26 41.14 343.63 41.14 342.38 TH
+47.14 341.76 48.22 342.38 48.22 343.63 47.14 344.26 46.07 343.63 46.07 342.38 TH
+49.60 341.76 50.68 342.38 50.68 343.63 49.60 344.26 48.53 343.63 48.53 342.38 TH
+74.20 341.76 75.28 342.38 75.28 343.63 74.20 344.26 73.13 343.63 73.13 342.38 TH
+96.34 341.76 97.42 342.38 97.42 343.63 96.34 344.26 95.27 343.63 95.27 342.38 TH
+26.23 339.62 27.31 340.25 27.31 341.50 26.23 342.12 25.16 341.50 25.16 340.25 TH
+31.15 339.62 32.23 340.25 32.23 341.50 31.15 342.12 30.08 341.50 30.08 340.25 TH
+36.07 339.62 37.14 340.25 37.14 341.50 36.07 342.12 35.00 341.50 35.00 340.25 TH
+43.45 339.62 44.53 340.25 44.53 341.50 43.45 342.12 42.38 341.50 42.38 340.25 TH
+48.37 339.62 49.45 340.25 49.45 341.50 48.37 342.12 47.30 341.50 47.30 340.25 TH
+50.83 339.62 51.91 340.25 51.91 341.50 50.83 342.12 49.76 341.50 49.76 340.25 TH
+75.43 339.62 76.51 340.25 76.51 341.50 75.43 342.12 74.35 341.50 74.35 340.25 TH
+77.89 339.62 78.97 340.25 78.97 341.50 77.89 342.12 76.82 341.50 76.82 340.25 TH
+80.35 339.62 81.43 340.25 81.43 341.50 80.35 342.12 79.27 341.50 79.27 340.25 TH
+85.27 339.62 86.35 340.25 86.35 341.50 85.27 342.12 84.20 341.50 84.20 340.25 TH
+90.19 339.62 91.27 340.25 91.27 341.50 90.19 342.12 89.12 341.50 89.12 340.25 TH
+95.11 339.62 96.19 340.25 96.19 341.50 95.11 342.12 94.04 341.50 94.04 340.25 TH
+97.57 339.62 98.65 340.25 98.65 341.50 97.57 342.12 96.50 341.50 96.50 340.25 TH
+29.92 337.49 31.00 338.11 31.00 339.36 29.92 339.99 28.85 339.36 28.85 338.11 TH
+34.84 337.49 35.92 338.11 35.92 339.36 34.84 339.99 33.77 339.36 33.77 338.11 TH
+39.76 337.49 40.84 338.11 40.84 339.36 39.76 339.99 38.69 339.36 38.69 338.11 TH
+44.68 337.49 45.75 338.11 45.75 339.36 44.68 339.99 43.61 339.36 43.61 338.11 TH
+49.60 337.49 50.68 338.11 50.68 339.36 49.60 339.99 48.53 339.36 48.53 338.11 TH
+69.28 337.49 70.35 338.11 70.35 339.36 69.28 339.99 68.20 339.36 68.20 338.11 TH
+71.74 337.49 72.82 338.11 72.82 339.36 71.74 339.99 70.66 339.36 70.66 338.11 TH
+74.20 337.49 75.28 338.11 75.28 339.36 74.20 339.99 73.13 339.36 73.13 338.11 TH
+76.66 337.49 77.74 338.11 77.74 339.36 76.66 339.99 75.59 339.36 75.59 338.11 TH
+79.12 337.49 80.20 338.11 80.20 339.36 79.12 339.99 78.04 339.36 78.04 338.11 TH
+84.04 337.49 85.12 338.11 85.12 339.36 84.04 339.99 82.96 339.36 82.96 338.11 TH
+88.96 337.49 90.04 338.11 90.04 339.36 88.96 339.99 87.88 339.36 87.88 338.11 TH
+93.88 337.49 94.96 338.11 94.96 339.36 93.88 339.99 92.81 339.36 92.81 338.11 TH
+45.91 335.35 46.99 335.98 46.99 337.23 45.91 337.85 44.84 337.23 44.84 335.98 TH
+48.37 335.35 49.45 335.98 49.45 337.23 48.37 337.85 47.30 337.23 47.30 335.98 TH
+50.83 335.35 51.91 335.98 51.91 337.23 50.83 337.85 49.76 337.23 49.76 335.98 TH
+55.75 335.35 56.83 335.98 56.83 337.23 55.75 337.85 54.68 337.23 54.68 335.98 TH
+65.59 335.35 66.66 335.98 66.66 337.23 65.59 337.85 64.51 337.23 64.51 335.98 TH
+68.05 335.35 69.13 335.98 69.13 337.23 68.05 337.85 66.98 337.23 66.98 335.98 TH
+70.51 335.35 71.59 335.98 71.59 337.23 70.51 337.85 69.44 337.23 69.44 335.98 TH
+75.43 335.35 76.51 335.98 76.51 337.23 75.43 337.85 74.35 337.23 74.35 335.98 TH
+95.11 335.35 96.19 335.98 96.19 337.23 95.11 337.85 94.04 337.23 94.04 335.98 TH
+97.57 335.35 98.65 335.98 98.65 337.23 97.57 337.85 96.50 337.23 96.50 335.98 TH
+27.46 333.22 28.54 333.84 28.54 335.09 27.46 335.72 26.39 335.09 26.39 333.84 TH
+32.38 333.22 33.46 333.84 33.46 335.09 32.38 335.72 31.31 335.09 31.31 333.84 TH
+37.30 333.22 38.38 333.84 38.38 335.09 37.30 335.72 36.23 335.09 36.23 333.84 TH
+42.22 333.22 43.30 333.84 43.30 335.09 42.22 335.72 41.14 335.09 41.14 333.84 TH
+47.14 333.22 48.22 333.84 48.22 335.09 47.14 335.72 46.07 335.09 46.07 333.84 TH
+52.06 333.22 53.14 333.84 53.14 335.09 52.06 335.72 50.99 335.09 50.99 333.84 TH
+54.52 333.22 55.60 333.84 55.60 335.09 54.52 335.72 53.45 335.09 53.45 333.84 TH
+56.98 333.22 58.06 333.84 58.06 335.09 56.98 335.72 55.91 335.09 55.91 333.84 TH
+59.44 333.22 60.52 333.84 60.52 335.09 59.44 335.72 58.36 335.09 58.36 333.84 TH
+61.90 333.22 62.97 333.84 62.97 335.09 61.90 335.72 60.82 335.09 60.82 333.84 TH
+64.36 333.22 65.44 333.84 65.44 335.09 64.36 335.72 63.28 335.09 63.28 333.84 TH
+69.28 333.22 70.35 333.84 70.35 335.09 69.28 335.72 68.20 335.09 68.20 333.84 TH
+71.74 333.22 72.82 333.84 72.82 335.09 71.74 335.72 70.66 335.09 70.66 333.84 TH
+81.58 333.22 82.66 333.84 82.66 335.09 81.58 335.72 80.51 335.09 80.51 333.84 TH
+86.50 333.22 87.57 333.84 87.57 335.09 86.50 335.72 85.42 335.09 85.42 333.84 TH
+91.42 333.22 92.50 333.84 92.50 335.09 91.42 335.72 90.35 335.09 90.35 333.84 TH
+96.34 333.22 97.42 333.84 97.42 335.09 96.34 335.72 95.27 335.09 95.27 333.84 TH
+28.69 331.08 29.77 331.71 29.77 332.96 28.69 333.58 27.62 332.96 27.62 331.71 TH
+33.61 331.08 34.69 331.71 34.69 332.96 33.61 333.58 32.54 332.96 32.54 331.71 TH
+38.53 331.08 39.61 331.71 39.61 332.96 38.53 333.58 37.46 332.96 37.46 331.71 TH
+43.45 331.08 44.53 331.71 44.53 332.96 43.45 333.58 42.38 332.96 42.38 331.71 TH
+48.37 331.08 49.45 331.71 49.45 332.96 48.37 333.58 47.30 332.96 47.30 331.71 TH
+53.29 331.08 54.36 331.71 54.36 332.96 53.29 333.58 52.22 332.96 52.22 331.71 TH
+58.21 331.08 59.29 331.71 59.29 332.96 58.21 333.58 57.14 332.96 57.14 331.71 TH
+63.13 331.08 64.21 331.71 64.21 332.96 63.13 333.58 62.05 332.96 62.05 331.71 TH
+68.05 331.08 69.13 331.71 69.13 332.96 68.05 333.58 66.98 332.96 66.98 331.71 TH
+72.97 331.08 74.05 331.71 74.05 332.96 72.97 333.58 71.90 332.96 71.90 331.71 TH
+75.43 331.08 76.51 331.71 76.51 332.96 75.43 333.58 74.35 332.96 74.35 331.71 TH
+77.89 331.08 78.97 331.71 78.97 332.96 77.89 333.58 76.82 332.96 76.82 331.71 TH
+80.35 331.08 81.43 331.71 81.43 332.96 80.35 333.58 79.27 332.96 79.27 331.71 TH
+82.81 331.08 83.88 331.71 83.88 332.96 82.81 333.58 81.73 332.96 81.73 331.71 TH
+87.73 331.08 88.81 331.71 88.81 332.96 87.73 333.58 86.66 332.96 86.66 331.71 TH
+92.65 331.08 93.73 331.71 93.73 332.96 92.65 333.58 91.58 332.96 91.58 331.71 TH
+95.11 331.08 96.19 331.71 96.19 332.96 95.11 333.58 94.04 332.96 94.04 331.71 TH
+97.57 331.08 98.65 331.71 98.65 332.96 97.57 333.58 96.50 332.96 96.50 331.71 TH
+76.66 328.95 77.74 329.57 77.74 330.82 76.66 331.45 75.59 330.82 75.59 329.57 TH
+79.12 328.95 80.20 329.57 80.20 330.82 79.12 331.45 78.04 330.82 78.04 329.57 TH
+81.58 328.95 82.66 329.57 82.66 330.82 81.58 331.45 80.51 330.82 80.51 329.57 TH
+84.04 328.95 85.12 329.57 85.12 330.82 84.04 331.45 82.96 330.82 82.96 329.57 TH
+88.96 328.95 90.04 329.57 90.04 330.82 88.96 331.45 87.88 330.82 87.88 329.57 TH
+93.88 328.95 94.96 329.57 94.96 330.82 93.88 331.45 92.81 330.82 92.81 329.57 TH
+96.34 328.95 97.42 329.57 97.42 330.82 96.34 331.45 95.27 330.82 95.27 329.57 TH
+26.23 326.81 27.31 327.44 27.31 328.69 26.23 329.31 25.16 328.69 25.16 327.44 TH
+31.15 326.81 32.23 327.44 32.23 328.69 31.15 329.31 30.08 328.69 30.08 327.44 TH
+36.07 326.81 37.14 327.44 37.14 328.69 36.07 329.31 35.00 328.69 35.00 327.44 TH
+40.99 326.81 42.07 327.44 42.07 328.69 40.99 329.31 39.92 328.69 39.92 327.44 TH
+45.91 326.81 46.99 327.44 46.99 328.69 45.91 329.31 44.84 328.69 44.84 327.44 TH
+50.83 326.81 51.91 327.44 51.91 328.69 50.83 329.31 49.76 328.69 49.76 327.44 TH
+55.75 326.81 56.83 327.44 56.83 328.69 55.75 329.31 54.68 328.69 54.68 327.44 TH
+60.67 326.81 61.75 327.44 61.75 328.69 60.67 329.31 59.59 328.69 59.59 327.44 TH
+65.59 326.81 66.66 327.44 66.66 328.69 65.59 329.31 64.51 328.69 64.51 327.44 TH
+70.51 326.81 71.59 327.44 71.59 328.69 70.51 329.31 69.44 328.69 69.44 327.44 TH
+95.11 326.81 96.19 327.44 96.19 328.69 95.11 329.31 94.04 328.69 94.04 327.44 TH
+27.46 324.68 28.54 325.30 28.54 326.55 27.46 327.18 26.39 326.55 26.39 325.30 TH
+29.92 324.68 31.00 325.30 31.00 326.55 29.92 327.18 28.85 326.55 28.85 325.30 TH
+32.38 324.68 33.46 325.30 33.46 326.55 32.38 327.18 31.31 326.55 31.31 325.30 TH
+34.84 324.68 35.92 325.30 35.92 326.55 34.84 327.18 33.77 326.55 33.77 325.30 TH
+37.30 324.68 38.38 325.30 38.38 326.55 37.30 327.18 36.23 326.55 36.23 325.30 TH
+39.76 324.68 40.84 325.30 40.84 326.55 39.76 327.18 38.69 326.55 38.69 325.30 TH
+42.22 324.68 43.30 325.30 43.30 326.55 42.22 327.18 41.14 326.55 41.14 325.30 TH
+44.68 324.68 45.75 325.30 45.75 326.55 44.68 327.18 43.61 326.55 43.61 325.30 TH
+56.98 324.68 58.06 325.30 58.06 326.55 56.98 327.18 55.91 326.55 55.91 325.30 TH
+59.44 324.68 60.52 325.30 60.52 326.55 59.44 327.18 58.36 326.55 58.36 325.30 TH
+61.90 324.68 62.97 325.30 62.97 326.55 61.90 327.18 60.82 326.55 60.82 325.30 TH
+64.36 324.68 65.44 325.30 65.44 326.55 64.36 327.18 63.28 326.55 63.28 325.30 TH
+79.12 324.68 80.20 325.30 80.20 326.55 79.12 327.18 78.04 326.55 78.04 325.30 TH
+84.04 324.68 85.12 325.30 85.12 326.55 84.04 327.18 82.96 326.55 82.96 325.30 TH
+28.69 322.54 29.77 323.17 29.77 324.42 28.69 325.04 27.62 324.42 27.62 323.17 TH
+33.61 322.54 34.69 323.17 34.69 324.42 33.61 325.04 32.54 324.42 32.54 323.17 TH
+36.07 322.54 37.14 323.17 37.14 324.42 36.07 325.04 35.00 324.42 35.00 323.17 TH
+40.99 322.54 42.07 323.17 42.07 324.42 40.99 325.04 39.92 324.42 39.92 323.17 TH
+45.91 322.54 46.99 323.17 46.99 324.42 45.91 325.04 44.84 324.42 44.84 323.17 TH
+48.37 322.54 49.45 323.17 49.45 324.42 48.37 325.04 47.30 324.42 47.30 323.17 TH
+50.83 322.54 51.91 323.17 51.91 324.42 50.83 325.04 49.76 324.42 49.76 323.17 TH
+53.29 322.54 54.36 323.17 54.36 324.42 53.29 325.04 52.22 324.42 52.22 323.17 TH
+65.59 322.54 66.66 323.17 66.66 324.42 65.59 325.04 64.51 324.42 64.51 323.17 TH
+68.05 322.54 69.13 323.17 69.13 324.42 68.05 325.04 66.98 324.42 66.98 323.17 TH
+70.51 322.54 71.59 323.17 71.59 324.42 70.51 325.04 69.44 324.42 69.44 323.17 TH
+72.97 322.54 74.05 323.17 74.05 324.42 72.97 325.04 71.90 324.42 71.90 323.17 TH
+77.89 322.54 78.97 323.17 78.97 324.42 77.89 325.04 76.82 324.42 76.82 323.17 TH
+82.81 322.54 83.88 323.17 83.88 324.42 82.81 325.04 81.73 324.42 81.73 323.17 TH
+87.73 322.54 88.81 323.17 88.81 324.42 87.73 325.04 86.66 324.42 86.66 323.17 TH
+92.65 322.54 93.73 323.17 93.73 324.42 92.65 325.04 91.58 324.42 91.58 323.17 TH
+27.46 320.41 28.54 321.03 28.54 322.28 27.46 322.91 26.39 322.28 26.39 321.03 TH
+32.38 320.41 33.46 321.03 33.46 322.28 32.38 322.91 31.31 322.28 31.31 321.03 TH
+37.30 320.41 38.38 321.03 38.38 322.28 37.30 322.91 36.23 322.28 36.23 321.03 TH
+42.22 320.41 43.30 321.03 43.30 322.28 42.22 322.91 41.14 322.28 41.14 321.03 TH
+59.44 320.41 60.52 321.03 60.52 322.28 59.44 322.91 58.36 322.28 58.36 321.03 TH
+64.36 320.41 65.44 321.03 65.44 322.28 64.36 322.91 63.28 322.28 63.28 321.03 TH
+69.28 320.41 70.35 321.03 70.35 322.28 69.28 322.91 68.20 322.28 68.20 321.03 TH
+74.20 320.41 75.28 321.03 75.28 322.28 74.20 322.91 73.13 322.28 73.13 321.03 TH
+76.66 320.41 77.74 321.03 77.74 322.28 76.66 322.91 75.59 322.28 75.59 321.03 TH
+79.12 320.41 80.20 321.03 80.20 322.28 79.12 322.91 78.04 322.28 78.04 321.03 TH
+81.58 320.41 82.66 321.03 82.66 322.28 81.58 322.91 80.51 322.28 80.51 321.03 TH
+84.04 320.41 85.12 321.03 85.12 322.28 84.04 322.91 82.96 322.28 82.96 321.03 TH
+86.50 320.41 87.57 321.03 87.57 322.28 86.50 322.91 85.42 322.28 85.42 321.03 TH
+91.42 320.41 92.50 321.03 92.50 322.28 91.42 322.91 90.35 322.28 90.35 321.03 TH
+96.34 320.41 97.42 321.03 97.42 322.28 96.34 322.91 95.27 322.28 95.27 321.03 TH
+26.23 318.27 27.31 318.90 27.31 320.15 26.23 320.77 25.16 320.15 25.16 318.90 TH
+28.69 318.27 29.77 318.90 29.77 320.15 28.69 320.77 27.62 320.15 27.62 318.90 TH
+31.15 318.27 32.23 318.90 32.23 320.15 31.15 320.77 30.08 320.15 30.08 318.90 TH
+33.61 318.27 34.69 318.90 34.69 320.15 33.61 320.77 32.54 320.15 32.54 318.90 TH
+38.53 318.27 39.61 318.90 39.61 320.15 38.53 320.77 37.46 320.15 37.46 318.90 TH
+43.45 318.27 44.53 318.90 44.53 320.15 43.45 320.77 42.38 320.15 42.38 318.90 TH
+45.91 318.27 46.99 318.90 46.99 320.15 45.91 320.77 44.84 320.15 44.84 318.90 TH
+50.83 318.27 51.91 318.90 51.91 320.15 50.83 320.77 49.76 320.15 49.76 318.90 TH
+65.59 318.27 66.66 318.90 66.66 320.15 65.59 320.77 64.51 320.15 64.51 318.90 TH
+68.05 318.27 69.13 318.90 69.13 320.15 68.05 320.77 66.98 320.15 66.98 318.90 TH
+70.51 318.27 71.59 318.90 71.59 320.15 70.51 320.77 69.44 320.15 69.44 318.90 TH
+72.97 318.27 74.05 318.90 74.05 320.15 72.97 320.77 71.90 320.15 71.90 318.90 TH
+77.89 318.27 78.97 318.90 78.97 320.15 77.89 320.77 76.82 320.15 76.82 318.90 TH
+82.81 318.27 83.88 318.90 83.88 320.15 82.81 320.77 81.73 320.15 81.73 318.90 TH
+85.27 318.27 86.35 318.90 86.35 320.15 85.27 320.77 84.20 320.15 84.20 318.90 TH
+90.19 318.27 91.27 318.90 91.27 320.15 90.19 320.77 89.12 320.15 89.12 318.90 TH
+95.11 318.27 96.19 318.90 96.19 320.15 95.11 320.77 94.04 320.15 94.04 318.90 TH
+29.92 316.14 31.00 316.76 31.00 318.01 29.92 318.64 28.85 318.01 28.85 316.76 TH
+34.84 316.14 35.92 316.76 35.92 318.01 34.84 318.64 33.77 318.01 33.77 316.76 TH
+37.30 316.14 38.38 316.76 38.38 318.01 37.30 318.64 36.23 318.01 36.23 316.76 TH
+42.22 316.14 43.30 316.76 43.30 318.01 42.22 318.64 41.14 318.01 41.14 316.76 TH
+47.14 316.14 48.22 316.76 48.22 318.01 47.14 318.64 46.07 318.01 46.07 316.76 TH
+49.60 316.14 50.68 316.76 50.68 318.01 49.60 318.64 48.53 318.01 48.53 316.76 TH
+52.06 316.14 53.14 316.76 53.14 318.01 52.06 318.64 50.99 318.01 50.99 316.76 TH
+54.52 316.14 55.60 316.76 55.60 318.01 54.52 318.64 53.45 318.01 53.45 316.76 TH
+59.44 316.14 60.52 316.76 60.52 318.01 59.44 318.64 58.36 318.01 58.36 316.76 TH
+64.36 316.14 65.44 316.76 65.44 318.01 64.36 318.64 63.28 318.01 63.28 316.76 TH
+66.82 316.14 67.90 316.76 67.90 318.01 66.82 318.64 65.74 318.01 65.74 316.76 TH
+71.74 316.14 72.82 316.76 72.82 318.01 71.74 318.64 70.66 318.01 70.66 316.76 TH
+76.66 316.14 77.74 316.76 77.74 318.01 76.66 318.64 75.59 318.01 75.59 316.76 TH
+81.58 316.14 82.66 316.76 82.66 318.01 81.58 318.64 80.51 318.01 80.51 316.76 TH
+86.50 316.14 87.57 316.76 87.57 318.01 86.50 318.64 85.42 318.01 85.42 316.76 TH
+88.96 316.14 90.04 316.76 90.04 318.01 88.96 318.64 87.88 318.01 87.88 316.76 TH
+91.42 316.14 92.50 316.76 92.50 318.01 91.42 318.64 90.35 318.01 90.35 316.76 TH
+93.88 316.14 94.96 316.76 94.96 318.01 93.88 318.64 92.81 318.01 92.81 316.76 TH
+28.69 314.00 29.77 314.63 29.77 315.88 28.69 316.50 27.62 315.88 27.62 314.63 TH
+33.61 314.00 34.69 314.63 34.69 315.88 33.61 316.50 32.54 315.88 32.54 314.63 TH
+58.21 314.00 59.29 314.63 59.29 315.88 58.21 316.50 57.14 315.88 57.14 314.63 TH
+63.13 314.00 64.21 314.63 64.21 315.88 63.13 316.50 62.05 315.88 62.05 314.63 TH
+65.59 314.00 66.66 314.63 66.66 315.88 65.59 316.50 64.51 315.88 64.51 314.63 TH
+70.51 314.00 71.59 314.63 71.59 315.88 70.51 316.50 69.44 315.88 69.44 314.63 TH
+75.43 314.00 76.51 314.63 76.51 315.88 75.43 316.50 74.35 315.88 74.35 314.63 TH
+80.35 314.00 81.43 314.63 81.43 315.88 80.35 316.50 79.27 315.88 79.27 314.63 TH
+87.73 314.00 88.81 314.63 88.81 315.88 87.73 316.50 86.66 315.88 86.66 314.63 TH
+92.65 314.00 93.73 314.63 93.73 315.88 92.65 316.50 91.58 315.88 91.58 314.63 TH
+
+showpage
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg
new file mode 100755
index 0000000..40be49c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/output/maxicode-basic.svg
@@ -0,0 +1,384 @@
+
+
+
+ 123456789
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords
new file mode 100755
index 0000000..869cf40
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.codewords
@@ -0,0 +1,11 @@
+22131114232212341113313221111
+31131142111413113122614121111
+31221121231143143211234211111
+22221143211132241113233311111
+21321131112612431313112411111
+21411121421124244211212321111
+22311114232212122133232312111
+31311143141112511212233212111
+32211121311531326111124112111
+41211121211316121221264111211
+42111111521133131153214111121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.png
new file mode 100644
index 0000000..6c7137c
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties
new file mode 100755
index 0000000..d67a107
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-2-columns.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords
new file mode 100755
index 0000000..9dca55e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.codewords
@@ -0,0 +1,6 @@
+221311142322123411133111223112122423231511222213111
+311311221151321414411112123151322112222243113113111
+312211611132212233123112213121421124244211213122111
+222211112141342122242213113121231611212232322222111
+213211331421121213522113122111411342312242212132111
+214111123212241116124113212115411311215311132141111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.png
new file mode 100644
index 0000000..8a60ca8
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties
new file mode 100755
index 0000000..bd91f93
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic-ecc-level-7-ignored.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+preferredEccLevel=7
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords
new file mode 100755
index 0000000..9dca55e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.codewords
@@ -0,0 +1,6 @@
+221311142322123411133111223112122423231511222213111
+311311221151321414411112123151322112222243113113111
+312211611132212233123112213121421124244211213122111
+222211112141342122242213113121231611212232322222111
+213211331421121213522113122111411342312242212132111
+214111123212241116124113212115411311215311132141111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.png
new file mode 100644
index 0000000..8a60ca8
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties
new file mode 100755
index 0000000..aaaa744
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-basic.properties
@@ -0,0 +1,2 @@
+mode=MICRO
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords
new file mode 100755
index 0000000..4165dd4
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.codewords
@@ -0,0 +1,17 @@
+22211223521211223312312221121
+21311232241221312222322131121
+21221231162112116112232122121
+21222151214211113113252122211
+21213111252312112323142121311
+21212261211222223151122121221
+21211311212343235212112121131
+21121325111223213332212112131
+21112312221162221143222111231
+21113252123211111422512111321
+21114114222114111611152111411
+21123133132113112312612112311
+21122222211144132312322112221
+21131221143141241221322113121
+21132143111115512213122113211
+21141112241331221113522114111
+21231112112325114243112123111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.png
new file mode 100644
index 0000000..6bb628a
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.properties
new file mode 100755
index 0000000..818cc95
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-data-variety.properties
@@ -0,0 +1,2 @@
+mode=MICRO
+content=TESTING 12345678901234567890 testing@TESTING
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.codewords
new file mode 100755
index 0000000..87ecbae
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.codewords
@@ -0,0 +1,15 @@
+2131121423221211231214212214121224232131121
+2122121131226111232112621221211244212122121
+2122211113235111142121151151235212112122211
+2121311423221211133114232212142322122121311
+2121221161112411132211611124116111242121221
+2121132352121111123223521211235212112121131
+2112131423221211122314232212142322122112131
+2111231161112411113311611124116111242111231
+2111321112134411112431121261211111552111321
+2111411212222511121412222116214233112111411
+2112312214412111211422234211321222142112311
+2112223231114212111411421413314113312112221
+2113123134131112112322122134113134132113121
+2113211122521312113211114216511212232113211
+2114111163311111213214121242611223112114111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.png
new file mode 100644
index 0000000..4a18fda
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.properties
new file mode 100755
index 0000000..30bf693
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-1-of-3.properties
@@ -0,0 +1,6 @@
+mode=MICRO
+variant=18
+structuredAppendFileId=123
+structuredAppendPosition=1
+structuredAppendTotal=3
+content=this
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.codewords
new file mode 100755
index 0000000..1543e8d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.codewords
@@ -0,0 +1,15 @@
+2131121423221211231224211214231511222131121
+2122123111261211232112621221211244212122121
+2122215113311211142121151151235212112122211
+2121311423221211133114232212142322122121311
+2121221161112411132211611124116111242121221
+2121132352121111123223521211235212112121131
+2112131423221211122314232212142322122112131
+2111231161112411113311611124116111242111231
+2111323262111111112412421412235112122111321
+2111413131242111121421211415233222122111411
+2112311241215111211423234111211331152112311
+2112221213124312111463122111123413212112221
+2113121231422212112311213423111115162113121
+2113214112212412113231162112512311132113211
+2114111324113211213213222241335111212114111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.png
new file mode 100644
index 0000000..3a77743
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties
new file mode 100755
index 0000000..4b81899
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-2-of-3.properties
@@ -0,0 +1,6 @@
+mode=MICRO
+variant=18
+structuredAppendFileId=123
+structuredAppendPosition=2
+structuredAppendTotal=3
+content=is a
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords
new file mode 100755
index 0000000..52cf6cb
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.codewords
@@ -0,0 +1,15 @@
+2131121423221211231214212214111611152131121
+2122122313412111232112621221211244212122121
+2122211113316111142121151151245111122122211
+2121311423221211133114232212142322122121311
+2121221161112411132211611124116111242121221
+2121132352121111123223521211235212112121131
+2112131423221211122314232212142322122112131
+2111231161112411113311611124116111242111231
+2111322141152111112411423132511431112111321
+2111412213213311121412324122141232312111411
+2112313212332111211411235113611113312112311
+2112221123143212111423331131121112452112221
+2113123312133112112315122123212213152113121
+2113213214231112113211322161623112112113211
+2114116111322111213221331232132123412114111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.png
new file mode 100644
index 0000000..da1bed4
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties
new file mode 100755
index 0000000..8c88464
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-structured-append-3-of-3.properties
@@ -0,0 +1,6 @@
+mode=MICRO
+variant=18
+structuredAppendFileId=123
+structuredAppendPosition=3
+structuredAppendTotal=3
+content=test
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords
new file mode 100755
index 0000000..4328727
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.codewords
@@ -0,0 +1,26 @@
+2213112333221114211131113251124141134112111
+3113111112451213311121611132121244124111211
+3122116111241113221141215112112323414111121
+2222113324112113131112151412212122253211121
+2132111312522112231126113211612112223121121
+2141112122124312321111161241121212443112121
+2231111423221212411115123131151115123112211
+3131111221541111511121143411116112233111311
+3221114121116111421123116112115131233111221
+4121112111213611412114232212151115123111131
+4211111221541112312121143312121142152211131
+3311113111116312311221131153111123532211221
+2411112113314212221233311411112321162211311
+2321115111223212222141123231411113152212211
+2312111434112112132123521211235212112221211
+3212111423221212141111215142211123343121211
+4112115422111111241152211114211152323211211
+4111211122143311331131231151162111322311211
+4111121142512111322113141412413311222311121
+3211126131111311321232112314451212112221121
+3121121231213411312213411511541131112131121
+3112125221114112212221423113111116152122121
+3112214123211313112221214115322131142122211
+3111312422114113111312221531111232612121311
+3111224232112212211312131315231131422121221
+3111135112223111311331223411512311132121131
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.png
new file mode 100644
index 0000000..e6a3e6e
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties
new file mode 100755
index 0000000..8b139ee
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-utf-8.properties
@@ -0,0 +1,2 @@
+mode=MICRO
+content=12üƒœ˜Ë±‹3asdf23456789012asdf89012asdf89asdfaf2
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords
new file mode 100755
index 0000000..6dd72b5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.codewords
@@ -0,0 +1,11 @@
+221311142322123221111
+311311611111334121111
+312211111222534211111
+222211211511243311111
+213211421231312411111
+214111114311152321111
+223111321412312312111
+313111412132223212111
+322111245111124112111
+412111221222334111211
+421111211231164111121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.png
new file mode 100644
index 0000000..af7cb2e
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties
new file mode 100755
index 0000000..8ae7d73
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-01.properties
@@ -0,0 +1,16 @@
+mode=MICRO
+dataColumns=1
+content=ABCDEF
+
+mode=MICRO
+rows=11
+content=ABCDEF
+
+mode=MICRO
+dataColumns=1
+rows=11
+content=ABCDEF
+
+mode=MICRO
+variant=1
+content=ABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords
new file mode 100755
index 0000000..f6b90a0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.codewords
@@ -0,0 +1,14 @@
+313111116111243131111
+322111311111633221111
+412111111232344121111
+421111511312224211111
+331111311111633311111
+241111111232342411111
+232111511312222321111
+231211311351122312111
+321211232215113212111
+411211215211414112111
+411121125322114111211
+411112222411144111121
+321112121344113211121
+312112163112213121121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.png
new file mode 100644
index 0000000..f5060c3
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties
new file mode 100755
index 0000000..7dd4840
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-02.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=1
+content=ABCDEFABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords
new file mode 100755
index 0000000..abc7f2e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.codewords
@@ -0,0 +1,17 @@
+222112235212112221121
+213112411111442131121
+212212211161412122121
+212221111513412122211
+212131411111442121311
+212122211161412121221
+212113111513412121131
+211213411111442112131
+211123211161412111231
+211132111513412111321
+211141111133342111411
+211231411213142112311
+211222114132322112221
+211312221611222113121
+211321121251232113211
+211411316111132114111
+212311223125112123111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.png
new file mode 100644
index 0000000..cf1e179
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties
new file mode 100755
index 0000000..a646f44
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-03.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=1
+content=ABCDEFABCDEFABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords
new file mode 100755
index 0000000..63e2297
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.codewords
@@ -0,0 +1,20 @@
+411112142322124111121
+321112611111333211121
+312112111222533121121
+311212211511243112121
+311221611111333112211
+311131111222533111311
+311122211511243111221
+311113611111333111131
+221113111222532211131
+221122211511242211221
+221131611111332211311
+221221111222532212211
+222121242313112221211
+312121212241143121211
+321121223213313211211
+231121424121212311211
+231112331231222311121
+222112115331212221121
+213112132121162131121
+212212141251212122121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.png
new file mode 100644
index 0000000..c468ea0
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties
new file mode 100755
index 0000000..d0e533a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-04.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=1
+content=ABCDEFABCDEFABCDEFABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords
new file mode 100755
index 0000000..34ed307
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.codewords
@@ -0,0 +1,24 @@
+322111235212114112111
+412111411111444111211
+421111211161414111121
+331111111513413211121
+241111412114313121121
+232111121142153112121
+231211311111633112211
+321211111232343111311
+411211511312223111221
+411121112122443111131
+411112221312242211131
+321112611111332211221
+312112111222532211311
+311212211511242212211
+311221111452212221211
+311131112232513121211
+311122112332233211211
+311113331122142311211
+221113312211522311121
+221122314321122221121
+221131421123222131121
+221221114115132122121
+222121131213152122211
+312121141242122121311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.png
new file mode 100644
index 0000000..580113a
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties
new file mode 100755
index 0000000..743ccf4
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-05.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=1
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords
new file mode 100755
index 0000000..846384f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.codewords
@@ -0,0 +1,28 @@
+311122142322123211211
+311113611111332311211
+221113111222532311121
+221122211511242221121
+221131111452212131121
+221221112232512122121
+222121411111442122211
+312121211161412121311
+321121111513412121221
+231121412114312121131
+231112121142152112131
+222112311111632111231
+213112111232342111321
+212212511312222111411
+212221112122442112311
+212131221312242112221
+212122611111332113121
+212113111222532113211
+211213211511242114111
+211123111452212123111
+211132124215112213111
+211141141331313113111
+211231412124123122111
+211222135222112222111
+211312121224232132111
+211321511114222141111
+211411142211332231111
+212311333112133131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.png
new file mode 100644
index 0000000..dbfe35f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties
new file mode 100755
index 0000000..b9ecc98
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-06.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=1
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGH
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords
new file mode 100755
index 0000000..cb0fffb
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.codewords
@@ -0,0 +1,8 @@
+22131114232212411111442213111
+31131121116141511312223113111
+31221111212244112232513122111
+22221141111144111232342222111
+21321113114214532113112132111
+21411111321621132113332141111
+22311111343113111231352231111
+31311123143112512212133131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.png
new file mode 100644
index 0000000..0593a09
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties
new file mode 100755
index 0000000..8342fe7
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-07.properties
@@ -0,0 +1,16 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCD
+
+mode=MICRO
+rows=8
+content=ABCDEFGHIJABCD
+
+mode=MICRO
+dataColumns=2
+rows=8
+content=ABCDEFGHIJABCD
+
+mode=MICRO
+variant=7
+content=ABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords
new file mode 100755
index 0000000..600684e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.codewords
@@ -0,0 +1,11 @@
+22131114232212411111443221111
+31131121116141511312224121111
+31221111212244112232514211111
+22221141111144111232343311111
+21321151131222111452212411111
+21411111223251311111632321111
+22311111123234111433312312111
+31311121115331114111443212111
+32211113231331216222114112111
+41211112142223322111254111211
+42111141141213351321114111121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.png
new file mode 100644
index 0000000..6ce5622
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.properties
new file mode 100755
index 0000000..85963b5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-08.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.codewords
new file mode 100755
index 0000000..7ae1834
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.codewords
@@ -0,0 +1,14 @@
+31311111611124611111333131111
+32211111122253111513413221111
+41211141211431221312244121111
+42111161111133211161414211111
+33111111151341112122443311111
+24111122131224411111442411111
+23211121116141511312222321111
+23121111212244112232512312111
+32121141111144111232343212111
+41121151131222226111314112111
+41112111421611231261114111211
+41111242411311211241514111121
+32111221511241134112413211121
+31211233125111113133413121121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.png
new file mode 100644
index 0000000..ea430d5
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.properties
new file mode 100755
index 0000000..b5f2718
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-09.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.codewords
new file mode 100755
index 0000000..d9c05d7
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.codewords
@@ -0,0 +1,17 @@
+22211223521211311111632221121
+21311211123234211511242131121
+21221211145221121142152122121
+21222131111163111222532122211
+21213121151124412114312121311
+21212212114215611111332121221
+21211311122253111513412121131
+21121341211431221312242112131
+21112361111133211161412111231
+21113211151341112122442111321
+21114122131224411111442111411
+21123121116141511312222112311
+21122211351123115131232112221
+21131213112423211311262113121
+21132132122115112212622113211
+21141121111452113113252114111
+21231123212421132431212123111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.png
new file mode 100644
index 0000000..78d4cec
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties
new file mode 100755
index 0000000..7549948
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-10.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords
new file mode 100755
index 0000000..bbf4856
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.codewords
@@ -0,0 +1,20 @@
+41111214232212411111444111121
+32111221116141511312223211121
+31211211212244112232513121121
+31121241111144111232343112121
+31122151131222111452213112211
+31113111223251311111633111311
+31112211123234211511243111221
+31111311145221121142153111131
+22111331111163111222532211131
+22112221151124412114312211221
+22113112114215611111332211311
+22122111122253111513412212211
+22212141211431221312242221211
+31212161111133211161413121211
+32112111151341111121553211211
+23112131131134123132142311211
+23111211134115311126122311121
+22211231621211231161122221121
+21311221241511113242222131121
+21221222133411142111612122121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.png
new file mode 100644
index 0000000..5607dba
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.properties
new file mode 100755
index 0000000..c17db9e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-11.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.codewords
new file mode 100755
index 0000000..4fa1300
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.codewords
@@ -0,0 +1,23 @@
+32211123521211311111634112111
+41211111123234211511244111211
+42111111145221121142154111121
+33111131111163111222533211121
+24111121151124412114313121121
+23211112114215611111333112121
+23121111122253111513413112211
+32121141211431221312243111311
+41121161111133211161413111221
+41112111151341112122443111131
+41111222131224411111442211131
+32111221116141511312222211221
+31211211212244112232512211311
+31121241111144111232342212211
+31122151131222111452212221211
+31113111223251311111633121211
+31112211123234112224143211211
+31111321315311331421122311211
+22111312512411212211442311121
+22112214121215321313312221121
+22113121144122113122612131121
+22122115421211222112432122121
+22212121312215211513222122211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.png
new file mode 100644
index 0000000..d21de5a
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.properties
new file mode 100755
index 0000000..0f77b9f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-12.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.codewords
new file mode 100755
index 0000000..de35405
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.codewords
@@ -0,0 +1,26 @@
+22111323521211311111632311121
+22112211123234211511242221121
+22113111145221121142152131121
+22122131111163111222532122121
+22212121151124412114312122211
+31212112114215611111332121311
+32112111122253111513412121221
+23112141211431221312242121131
+23111261111133211161412112131
+22211211151341112122442111231
+21311222131224411111442111321
+21221221116141511312222111411
+21222111212244112232512112311
+21213141111144111232342112221
+21212251131222111452212113121
+21211311223251311111632113211
+21121311123234211511242114111
+21112311145221121142152123111
+21113231111163112124422213111
+21114131513112131423213113111
+21123111231162411211163122111
+21122215311222131121532222111
+21131241422121151111162132111
+21132151221312215211412141111
+21141112512411114412132231111
+21231114151311313111163131111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.png
new file mode 100644
index 0000000..c2f3d81
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.properties
new file mode 100755
index 0000000..27f3f66
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-13.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=2
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJAB
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.codewords
new file mode 100755
index 0000000..75481f5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.codewords
@@ -0,0 +1,6 @@
+2213111423221211223141111144111232342213111
+3113115113122212123111145221121142153113111
+3122112113135112213124111251121312433122111
+2222114121123313113112133133114243112222111
+2132113112423113122131324211311232232132111
+2141111441141113212112211631221262112141111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.png
new file mode 100644
index 0000000..2cac563
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.properties
new file mode 100755
index 0000000..2f30cd2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-14.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.codewords
new file mode 100755
index 0000000..2c37a97
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.codewords
@@ -0,0 +1,8 @@
+2231111423221214112141111144111232342231111
+3131115113122214121111145221121142153131111
+3221113111116314211111122253111513413221111
+4121114121143113311123112332123142224121111
+4211114112121513221143112321411414114211111
+3311112311216113131124511211114224213311111
+2411112311112612231112414212221131432411111
+2321111111542212321132113421411216112321111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.png
new file mode 100644
index 0000000..7302423
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.properties
new file mode 100755
index 0000000..2e65cf1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-15.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGH
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.codewords
new file mode 100755
index 0000000..c868e6b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.codewords
@@ -0,0 +1,10 @@
+2312112352121112411131111163111222532312111
+3212112115112411511141211431221312243212111
+4112116111113311421121116141511312224112111
+4111211121224411412111223251311111634111211
+4111121112323412312121151124214142214111121
+3211121111522412311212211361211251323211121
+3121121131223412221222112162115323113121121
+3112123131323112222112222116111512153112121
+3112213113322212132142111314321522113112211
+3111311252211312141113231331132113333111311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.png
new file mode 100644
index 0000000..25696cb
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties
new file mode 100755
index 0000000..8d67ecd
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-16.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords
new file mode 100755
index 0000000..d1a6982
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.codewords
@@ -0,0 +1,12 @@
+3111221423221211241141111144111232343111221
+3111135113122211331111145221121142153111131
+2211133111116311322111122253111513412211131
+2211224121143111321222131224411111442211221
+2211312111614111312251131222111452212211311
+2212211122325112212231111163111222532212211
+2221214231112313112231231223211214242221211
+3121212212412313111351211511122153123121211
+3211211112134412211312441212611123123211211
+2311212411223211311321312116142133212311211
+2311122124411211221352131122331222132311121
+2221123361111111222222421123122132512221121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.png
new file mode 100644
index 0000000..6ade8cf
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.properties
new file mode 100755
index 0000000..c5da84c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-17.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.codewords
new file mode 100755
index 0000000..8c93351
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.codewords
@@ -0,0 +1,15 @@
+2131121423221211231241111144111232342131121
+2122125113122211232111145221121142152122121
+2122213111116311142111122253111513412122211
+2121314121143111133122131224411111442121311
+2121222111614111132251131222111452212121221
+2121131122325111123231111163111222532121131
+2112132115112411122341211431221312242112131
+2111236111113311113321116141511312222111231
+2111323261111211112442124211214311232111321
+2111413133222111121411142323113233132111411
+2112312123431111211424123113124212412112311
+2112222142223112111411451212123421312112221
+2113122231251112112321122234221114242113121
+2113213116221112113221153212441112132113211
+2114113131124211213211212343142312312114111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.png
new file mode 100644
index 0000000..2c6ea3c
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.properties
new file mode 100755
index 0000000..00d0274
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-18.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.codewords
new file mode 100755
index 0000000..63478c5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.codewords
@@ -0,0 +1,20 @@
+2213111423221211421141111144111232343211211
+3113115113122211412111145221121142152311211
+3122113111116312312111122253111513412311121
+2222114121143112311222131224411111442221121
+2132112111614112221251131222111452212131121
+2141111122325112222131111163111222532122121
+2231112115112412132141211431221312242122211
+3131116111113312141121116141511312222121311
+3221111121224411241111223251311111632121221
+4121111112323411331121151124412114312121131
+4211111211421511322161111133211161412112131
+3311111115134111321214312231121421512111231
+2411113312123211312221131423132124132111321
+2321114214131112212241232212112253122111411
+2312111113215313112221121451221113522112311
+3212112142331113111312112325241112242112221
+4112111212451112211336112112321126112113121
+4111216112221211311311122154232113412113211
+4111121141411411221322223231413311222114111
+3211122313321211222233112115121154212123111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.png
new file mode 100644
index 0000000..2348999
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.properties
new file mode 100755
index 0000000..2d5eb6f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-19.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.codewords
new file mode 100755
index 0000000..a3f544d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.codewords
@@ -0,0 +1,26 @@
+2213111423221214211141111144111232344112111
+3113115113122213311111145221121142154111211
+3122113111116313221111122253111513414111121
+2222114121143113131122131224411111443211121
+2132112111614112231151131222111452213121121
+2141111122325112321131111163111222533112121
+2231112115112412411141211431221312243112211
+3131116111113311511121116141511312223111311
+3221111121224411421111223251311111633111221
+4121111112323411412121151124412114313111131
+4211111211421512312161111133211161412211131
+3311111115134112311211212244112232512211221
+2411114111114412221211123234211511242211311
+2321111114522112222112114215611111332212211
+2312111112225312132111151341112122442221211
+3212112213122412141123132132424113113121211
+4112112331511111241111211164522122213211211
+4111211313114311331122121153213113332311211
+4111122111152411322131321214411211432311121
+3211123115221211321242313211512222212221121
+3121122233123111312221121451223311322131121
+3112121514212112212212232115333211132122121
+3112216311113113112253121122112253122122211
+3111311221234213111311532311142121422121311
+3111223116113112211323232221311512312121221
+3111135214112111311324133112311131252121131
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.png
new file mode 100644
index 0000000..b0ec757
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties
new file mode 100755
index 0000000..de7515a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-20.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords
new file mode 100755
index 0000000..ae08d50
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.codewords
@@ -0,0 +1,32 @@
+3121122352121111312231111163111222532131121
+3112122115112412212241211431221312242122121
+3112216111113313112221116141511312222122211
+3111311121224413111311223251311111632121311
+3111221112323412211321151124412114312121221
+3111131211421511311361111133211161412121131
+2211131115134111221311212244112232512112131
+2211224111114411222211123234211511242111231
+2211311114522111231212114215611111332111321
+2212211112225311232111151341112122442111411
+2221212213122411142141111144111232342112311
+3121215113122211133111145221121142152112221
+3211213111116311132211122253111513412113121
+2311214121143111123222131224411111442113211
+2311122111614111122351131222111452212114111
+2221121122325111113331111163111222532123111
+2131122115112411112441211431221312242213111
+2122126111113311121421116141511312223113111
+2122211121224411211411223251311111633122111
+2121311112323412111415112421211431412222111
+2121221162213112112351221213412123132132111
+2121131121416112113212341222134114122141111
+2112132513122111213211222414122331232231111
+2111233611211211214144111114111341153131111
+2111321154211211223111333141121412423221111
+2111411513131212123114133131121313154121111
+2112311231125212213143111214142111614211111
+2112223121135113113111111543115232213311111
+2113123321213213122142111143223323112411111
+2113214412131113212123123213115113322321111
+2114111431122314112122431122123321322312111
+2123112111314414121122231412221212253212111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.png
new file mode 100644
index 0000000..6aef18f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties
new file mode 100755
index 0000000..9267baa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-21.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords
new file mode 100755
index 0000000..16326e0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.codewords
@@ -0,0 +1,38 @@
+2312112352121113112231111163111222532112311
+3212112115112413111341211431221312242112221
+4112116111113312211321116141511312222113121
+4111211121224411311311223251311111632113211
+4111121112323411221321151124412114312114111
+3211121211421511222261111133211161412123111
+3121121115134111231211212244112232512213111
+3112124111114411232111123234211511243113111
+3112211114522111142112114215611111333122111
+3111311112225311133111151341112122442222111
+3111222213122411132241111144111232342132111
+3111135113122211123211145221121142152141111
+2211133111116311122311122253111513412231111
+2211224121143111113322131224411111443131111
+2211312111614111112451131222111452213221111
+2212211122325111121431111163111222534121111
+2221212115112411211441211431221312244211111
+3121216111113312111421116141511312223311111
+3211211121224412112311223251311111632411111
+2311211112323412113221151124412114312321111
+2311121211421511213261111133211161412312111
+2221121115134111214111212244112232513212111
+2131124111114411223111123234211511244112111
+2122121114522112123121224213211335114111211
+2122212411115212213113222142214412214111121
+2121314221133113113111143331222414113211121
+2121223211261113122143212212131161313121121
+2121132232133113212111542112144211133112121
+2112131313161114112112112226222512123112211
+2111233112322314121112311252111353213111311
+2111322353111114211112131441112223423111221
+2111411214121513311134121132222322223111131
+2112311111532313221142132122321124132211131
+2112222121154113131111222144135321112211221
+2113121332412112231121332312112331242211311
+2113211142124212321124114221431115112212211
+2114111244111312411121412232111313432221211
+2123112111142511511141431211342311213121211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.png
new file mode 100644
index 0000000..3b17df8
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.properties
new file mode 100755
index 0000000..8a90341
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-22.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGH
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.codewords
new file mode 100755
index 0000000..15d9667
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.codewords
@@ -0,0 +1,44 @@
+2213111423221211241141111144111232342113121
+3113115113122211331111145221121142152113211
+3122113111116311322111122253111513412114111
+2222114121143111321222131224411111442123111
+2132112111614111312251131222111452212213111
+2141111122325112212231111163111222533113111
+2231112115112413112241211431221312243122111
+3131116111113313111321116141511312222222111
+3221111121224412211311223251311111632132111
+4121111112323411311321151124412114312141111
+4211111211421511221361111133211161412231111
+3311111115134111222211212244112232513131111
+2411114111114411231211123234211511243221111
+2321111114522111232112114215611111334121111
+2312111112225311142111151341112122444211111
+3212112213122411133141111144111232343311111
+4112115113122211132211145221121142152411111
+4111213111116311123211122253111513412321111
+4111124121143111122322131224411111442312111
+3211122111614111113351131222111452213212111
+3121121122325111112431111163111222534112111
+3112122115112411121441211431221312244111211
+3112216111113311211421116141511312224111121
+3111311121224412111411223251311111633211121
+3111221112323412112321151124412114313121121
+3111131211421512113261111133211161413112121
+2211131115134111213211212244112232513112211
+2211224111114411214122123241321211343111311
+2211314215121111223161321211135112313111221
+2212211232233112123111352131225212123111131
+2221213222142112213141311421313313122211131
+3121211311451113113121134222611311312211221
+3211211225123113122131421132114331312211311
+2311213121132413212132211521321311332212211
+2311123111433114112141141411213153112221211
+2221125213311114121113111145132321413121211
+2131122324121214211113141511332312213211211
+2122126232111113311143111313321331222311211
+2122211142323113221121312242113513212311121
+2121312212314213131114212313512112412221121
+2121225213112212231131133123411224212131121
+2121132311115312321115311123124211152122121
+2112133321132212411151511211421121512122211
+2111231352113111511152111124123212512121311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.png
new file mode 100644
index 0000000..8d82ebc
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties
new file mode 100755
index 0000000..07f777f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-23.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=3
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJAB
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords
new file mode 100755
index 0000000..c44f734
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.codewords
@@ -0,0 +1,4 @@
+211231116111246111113312312121116141511312222112131
+211222112122441122325112311231111163111222532111231
+211312131322231322312312221212212216231314212111321
+211321331331212111533112222123143112512212132111411
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.png
new file mode 100644
index 0000000..a5c360b
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties
new file mode 100755
index 0000000..cdfdb08
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-24.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords
new file mode 100755
index 0000000..bcbb88f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.codewords
@@ -0,0 +1,6 @@
+221311142322124111114411223111123234211511242213111
+311311111452211211421512123161111133211161413113111
+312211111513411121224412213111223251311111633122111
+222211122324123321213213113112122423131241412222111
+213211114113422322411213122112611222122112622132111
+214111234211221434112113212123411123621122122141111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.png
new file mode 100644
index 0000000..ad50566
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties
new file mode 100755
index 0000000..f5b81f6
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-25.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJAB
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords
new file mode 100755
index 0000000..216395b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.codewords
@@ -0,0 +1,8 @@
+223111142322124111114414112111123234211511242231111
+313111111452211211421514121161111133211161413131111
+322111111513411121224414211111223251311111633221111
+412111111232342115112413311141211431221312244121111
+421111611111332111614113221111511332125113314211111
+331111151211422112135213131121421223142112333311111
+241111321312323115123112231123332211122224132411111
+232111123212511161213212321111145122211133152321111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.png
new file mode 100644
index 0000000..4f95b03
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties
new file mode 100755
index 0000000..27a35b7
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-26.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords
new file mode 100755
index 0000000..902c6f2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.codewords
@@ -0,0 +1,10 @@
+231211235212113111116312411111122253111513412312111
+321211412114312213122411511141111144111232343212111
+411211511312221114522111421112114215611111334112111
+411121111222531115134111412111212244112232514111211
+411112411111441112323412312121151124412114314111121
+321112121142156111113312311221116141511312223211121
+312112412151121151251112221222311233116331113121121
+311212311132512221141412222111215142333311123112121
+311221322231134215121112132154111122412224113112211
+311131114214132531113112141122231241116231123111311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.png
new file mode 100644
index 0000000..ca6a625
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties
new file mode 100755
index 0000000..8de0a13
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-27.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords
new file mode 100755
index 0000000..585e723
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.codewords
@@ -0,0 +1,12 @@
+311122142322124111114411241111123234211511243111221
+311113111452211211421511331161111133211161413111131
+221113111513411121224411322111223251311111632211131
+221122111232342115112411321241211431221312242211221
+221131611111332111614111312251131222111452212211311
+221221112232513111116312212211122253111513412212211
+222121412114312213122413112241111144111232342221211
+312121511312221114522113111344111114221531123121211
+321121221121622353111112211322412231122113343211211
+231121121411162414122111311312151214421121512311211
+231112431122223111221611221322134122413133112311121
+222112113116222351121211222231131161411241132221121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.png
new file mode 100644
index 0000000..f6d139e
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.properties
new file mode 100755
index 0000000..9981d7e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-28.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGH
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.codewords
new file mode 100755
index 0000000..2051705
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.codewords
@@ -0,0 +1,15 @@
+213112142322124111114411231211123234211511242131121
+212212111452211211421511232161111133211161412122121
+212221111513411121224411142111223251311111632122211
+212131111232342115112411133141211431221312242121311
+212122611111332111614111132251131222111452212121221
+212113112232513111116311123211122253111513412121131
+211213412114312213122411122341111144111232342112131
+211123511312221114522111113312114215611111332111231
+211132111222531115134111112411212244112232512111321
+211141411111441112323411121421151124133132132111411
+211231441221212123421211211432233112212252212112311
+211222131412411241232212111412121244132411322112221
+211312411122513333111212112333121232212232322113121
+211321222243113112221512113233213113522411112113211
+211411521133111121244211213231611212124311142114111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.png
new file mode 100644
index 0000000..0e945e2
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties
new file mode 100755
index 0000000..69382ea
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-29.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords
new file mode 100755
index 0000000..906f0f0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.codewords
@@ -0,0 +1,20 @@
+221311142322124111114411421111123234211511243211211
+311311111452211211421511412161111133211161412311211
+312211111513411121224412312111223251311111632311121
+222211111232342115112412311241211431221312242221121
+213211611111332111614112221251131222111452212131121
+214111112232513111116312222111122253111513412122121
+223111412114312213122412132141111144111232342122211
+313111511312221114522112141112114215611111332121311
+322111111222531115134111241111212244112232512121221
+412111411111441112323411331121151124412114312121131
+421111121142156111113311322121116141511312222112131
+331111112122441122325111321231111163111222532111231
+241111211511244121143111312222131224411111442111321
+232111211161415113122212212242151211221234122111411
+231211143211233111531213112211314151122421412112311
+321211213221152131211613111321112235111116152112221
+411211511121331312512212211312311252511114222113121
+411121123116213123115111311323126111411242122113211
+411112422212311316111311221321231413322313212114111
+321112411122242123431111222231152311512112142123111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.png
new file mode 100644
index 0000000..74e5681
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties
new file mode 100755
index 0000000..87be2bd
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-30.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords
new file mode 100755
index 0000000..4a51535
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.codewords
@@ -0,0 +1,26 @@
+221311142322124111114414211111123234211511244112111
+311311111452211211421513311161111133211161414111211
+312211111513411121224413221111223251311111634111121
+222211111232342115112413131141211431221312243211121
+213211611111332111614112231151131222111452213121121
+214111112232513111116312321111122253111513413112121
+223111412114312213122412411141111144111232343112211
+313111511312221114522111511112114215611111333111311
+322111111222531115134111421111212244112232513111221
+412111411111441112323411412121151124412114313111131
+421111121142156111113312312121116141511312222211131
+331111112122441122325112311231111163111222532211221
+241111211511244121143112221222131224411111442211311
+232111211161415113122212222111145221121142152212211
+231211311111631112225312132111151341112122442221211
+321211221312244111114412141111123234211511243121211
+411211111452211211421511241161111133211161413211211
+411121111513411121224411331111223251311111632311211
+411112141131331322312311322141121242222113152311121
+321112341223112151124111321211144411121161322221121
+312112611121141132324111312263112211111512422131121
+311212411411412411213312212223131421151421212122121
+311221213251122323411113112241114141421314112122211
+311131113521312231143113111311222243122215312121311
+311122151223211314212312211312222413424211122121221
+311113431511111131135211311311125421622111222121131
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.png
new file mode 100644
index 0000000..08177e4
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties
new file mode 100755
index 0000000..b6aa007
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-31.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJAB
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords
new file mode 100755
index 0000000..fdd6cb0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.codewords
@@ -0,0 +1,32 @@
+312112235212113111116311312211122253111513412131121
+311212412114312213122412212241111144111232342122121
+311221511312221114522113112212114215611111332122211
+311131111222531115134113111311212244112232512121311
+311122411111441112323412211321151124412114312121221
+311113121142156111113311311321116141511312222121131
+221113112122441122325111221331111163111222532112131
+221122211511244121143111222222131224411111442111231
+221131211161415113122211231211145221121142152111321
+221221311111631112225311232111151341112122442111411
+222121221312244111114411142111123234211511242112311
+312121111452211211421511133161111133211161412112221
+321121111513411121224411132211223251311111632113121
+231121111232342115112411123241211431221312242113211
+231112611111332111614111122351131222111452212114111
+222112112232513111116311113311122253111513412123111
+213112412114312213122411112441111144111232342213111
+212212511312221114522111121412114215611111333113111
+212221111222531115134111211411212244112232513122111
+212131411111441112323412111421151124412114312222111
+212122121142156111113312112321116141511312222132111
+212113112122441122325112113231111163111222532141111
+211213211511244121143111213211242313151411132231111
+211123322231132131511311214131112414131351213131111
+211132313111432141152111223112242141142212323221111
+211141141124224151211212123113324121112423134121111
+211231221234124121251112213113225112441114114211111
+211222113132421114134213113121112361214411223311111
+211312133241214131142113122111113136212213152411111
+211321411232314212212313212124123113521111242321111
+211411312112521122214414112114212142121111462312111
+212311321214313222142114121114132222141414113212111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.png
new file mode 100644
index 0000000..7d5c608
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties
new file mode 100755
index 0000000..0f93657
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-32.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGH
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords
new file mode 100755
index 0000000..c29b0d7
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.codewords
@@ -0,0 +1,38 @@
+231211235212113111116313112211122253111513412112311
+321211412114312213122413111341111144111232342112221
+411211511312221114522112211312114215611111332113121
+411121111222531115134111311311212244112232512113211
+411112411111441112323411221321151124412114312114111
+321112121142156111113311222221116141511312222123111
+312112112122441122325111231231111163111222532213111
+311212211511244121143111232122131224411111443113111
+311221211161415113122211142111145221121142153122111
+311131311111631112225311133111151341112122442222111
+311122221312244111114411132211123234211511242132111
+311113111452211211421511123261111133211161412141111
+221113111513411121224411122311223251311111632231111
+221122111232342115112411113341211431221312243131111
+221131611111332111614111112451131222111452213221111
+221221112232513111116311121411122253111513414121111
+222121412114312213122411211441111144111232344211111
+312121511312221114522112111412114215611111333311111
+321121111222531115134112112311212244112232512411111
+231121411111441112323412113221151124412114312321111
+231112121142156111113311213221116141511312222312111
+222112112122441122325111214131111163111222533212111
+213112211511244121143111223122131224411111444112111
+212212211161415113122212123111145221121142154111211
+212221311111631112225312213111151341112122444111121
+212131221312244111114413113111123234211511243211121
+212122111452211211421513122161111133211161413121121
+212113125123123131124213212112412124121113443112121
+211213311611312113223314112133311411211321343112211
+211123116111243212312314121111211164431214113111311
+211132221261122252111314211122341131111131633111221
+211141142124124141222113311121161222123232133111131
+211231111441146211123113221125133111231131152211131
+211222315311211441222113131111221136134313112211221
+211312122232231141522112231114123132221421322211311
+211321612211222211422312321142132122412221142212211
+211411111212451132243112411113231232134511112221211
+212311213221152213314111511132112242142133213121211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.png
new file mode 100644
index 0000000..2fc64eb
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties
new file mode 100755
index 0000000..13d9338
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-33.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords
new file mode 100755
index 0000000..835c6e8
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.codewords
@@ -0,0 +1,44 @@
+221311142322124111114411241111123234211511242113121
+311311111452211211421511331161111133211161412113211
+312211111513411121224411322111223251311111632114111
+222211111232342115112411321241211431221312242123111
+213211611111332111614111312251131222111452212213111
+214111112232513111116312212211122253111513413113111
+223111412114312213122413112241111144111232343122111
+313111511312221114522113111312114215611111332222111
+322111111222531115134112211311212244112232512132111
+412111411111441112323411311321151124412114312141111
+421111121142156111113311221321116141511312222231111
+331111112122441122325111222231111163111222533131111
+241111211511244121143111231222131224411111443221111
+232111211161415113122211232111145221121142154121111
+231211311111631112225311142111151341112122444211111
+321211221312244111114411133111123234211511243311111
+411211111452211211421511132261111133211161412411111
+411121111513411121224411123211223251311111632321111
+411112111232342115112411122341211431221312242312111
+321112611111332111614111113351131222111452213212111
+312112112232513111116311112411122253111513414112111
+311212412114312213122411121441111144111232344111211
+311221511312221114522111211412114215611111334111121
+311131111222531115134112111411212244112232513211121
+311122411111441112323412112321151124412114313121121
+311113121142156111113312113221116141511312223112121
+221113112122441122325111213231111163111222533112211
+221122211511244121143111214122131224411111443111311
+221131211161415113122211223111145221121142153111221
+221221311111631112225312123111151341112122443111131
+222121221312244111114412213111123234211511242211131
+312121111452211211421513113152111223421411132211221
+321121311261211314114213122111441411612131122211311
+231121113133144321113213212115121511142131232212211
+231112112211633221341114112112125123612311212221211
+222112123312232131133314121111111246612132113121211
+213112231111261133412214211112333311112431233211211
+212212251331113411312213311114134112321422122311211
+212221212115412154111213221121441122314212312311121
+212131133231131222322313131141331221131421232221121
+212122141143121111633112231112316112331124122131121
+212113111512421521123212321111322431242112412122121
+211213112243312214213212411133112142122521132122211
+211123212142144231321111511134132211321332212121311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.png
new file mode 100644
index 0000000..0b4cb27
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.properties
new file mode 100755
index 0000000..3436584
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/micro-variant-34.properties
@@ -0,0 +1,3 @@
+mode=MICRO
+dataColumns=4
+content=ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.codewords
new file mode 100755
index 0000000..85b16cf
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.codewords
@@ -0,0 +1,10 @@
+8111111331111235211121363411133141111144711311121
+8111111351111323421114131131226151111224711311121
+8111111331111163212311431432112321111353711311121
+8111111311115152432111322411132311114243711311121
+8111111321113414311126124313131121113315711311121
+8111111341114114214211242442112141114411711311121
+8111111311123234121124242314112321123143711311121
+8111111341121116222143124111242221116141711311121
+8111111321122162134222212241142111122352711311121
+8111111311132522121332321322322221132332711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.png
new file mode 100644
index 0000000..55aff2e
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.properties
new file mode 100755
index 0000000..14c3974
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-10-rows.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+rows=10
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.error
new file mode 100755
index 0000000..400405b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.error
@@ -0,0 +1 @@
+Too many codewords required (2700, but max is 929)
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.properties
new file mode 100755
index 0000000..8bbae3e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-30-columns-90-rows.properties
@@ -0,0 +1,4 @@
+mode=NORMAL
+dataColumns=30
+rows=90
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.error
new file mode 100755
index 0000000..fb8779f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.error
@@ -0,0 +1 @@
+Too many columns (31)
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.properties
new file mode 100755
index 0000000..b7fd6d9
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-31-columns.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+dataColumns=31
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.codewords
new file mode 100755
index 0000000..498473f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.codewords
@@ -0,0 +1,4 @@
+8111111341111144211121363411133112122423231511221122241441111243711311121
+8111111351111323141441115132211222224311311126124313131161111133711311121
+8111111331111262214211242442112122116113114113152232113321111353711311121
+8111111311114243311131523421122223221115121332321322322251116111711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.png
new file mode 100644
index 0000000..f02b2dc
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.properties
new file mode 100755
index 0000000..439dc05
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-5-columns.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+dataColumns=5
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.codewords
new file mode 100755
index 0000000..4bfb3c9
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.codewords
@@ -0,0 +1,6 @@
+811111134111114421113342341113311212242323151122112224141324231151111251711311121
+811111134111141451322112222243113111261243131311122211622612311161111133711311121
+811111131111134523521211235212112352121123521211235212112352121111111444711311121
+811111131111424314232212142322121423221214232212142322121423221231121135711311121
+811111132111351311611124116111241161112411611124311142321121612331113224711311121
+811111134111431212341222124215111521133112313241241111521521113341115122711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.png
new file mode 100644
index 0000000..cb1245e
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties
new file mode 100755
index 0000000..e4a3941
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-6-rows-6-columns.properties
@@ -0,0 +1,4 @@
+mode=NORMAL
+dataColumns=6
+rows=6
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords
new file mode 100755
index 0000000..0d4a9a2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.codewords
@@ -0,0 +1,7 @@
+8111111351111152211121363411133141111144711311121
+8111111351111125421114131131226141111216711311121
+8111111331111163212311431432112321111155711311121
+8111111321114251432111322411132311114243711311121
+8111111321113216311126124313131141113232711311121
+8111111341114114214211242442112151113411711311121
+8111111331123151322511212321131421123143711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.png
new file mode 100644
index 0000000..260a6a0
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties
new file mode 100755
index 0000000..14c709a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-0.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=0
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords
new file mode 100755
index 0000000..9fd5987
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.codewords
@@ -0,0 +1,8 @@
+8111111351111152211121363411133141111144711311121
+8111111361111232421114131131226141111216711311121
+8111111331111163212311431432112331111262711311121
+8111111321114251432111322411132311114243711311121
+8111111331113323311126124313131141113232711311121
+8111111341114114214211242442112151114221711311121
+8111111331123151151131322233231121123143711311121
+8111111311116232542111121112542111116133711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.png
new file mode 100644
index 0000000..52aa836
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties
new file mode 100755
index 0000000..17e8082
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-1.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=1
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords
new file mode 100755
index 0000000..7f552e2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.codewords
@@ -0,0 +1,7 @@
+811111135111115231112144341113311212242351111152711311121
+811111135111132311312261221151321414411141111216711311121
+811111131111124611451311223411316111322121111353711311121
+811111132111425121333221322412212332211321114251711311121
+811111132111341411611124331141313511231141113232711311121
+811111135111412212232241133114222224114141114411711311121
+811111133112315123112332133142212331241131123151711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.png
new file mode 100644
index 0000000..3e20ce2
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties
new file mode 100755
index 0000000..80c2ab1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-2.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=2
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords
new file mode 100755
index 0000000..5fc1060
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.codewords
@@ -0,0 +1,10 @@
+811111133111123541112152341113311212242351111152711311121
+811111135111142211312261221151321414411151111224711311121
+811111131111124611451311223411316111322121111452711311121
+811111131111515221333221322412212332211321114251711311121
+811111133111352111611124116111243121341221113315711311121
+811111135111412211211533314111336311211231115213711311121
+811111131112323415121511131423212414122131123151711311121
+811111134112121511124116333141111314421121116141711311121
+811111136112221211411216121211451211134461123121711311121
+811111131113252223212421111214163115123111132423711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.png
new file mode 100644
index 0000000..fc614e6
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.properties
new file mode 100755
index 0000000..754178a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-3.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=3
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.codewords
new file mode 100755
index 0000000..a2f9d85
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.codewords
@@ -0,0 +1,11 @@
+81111113311112352111213634111331121224232315112231111235711311121
+81111113411121252211513214144111513221122222431151111224711311121
+81111113211112546111322122331231214211242442112121112163711311121
+81111113111151522115132233121331112321161324231111115152711311121
+81111113411141411411411413154111522213111122521321113315711311121
+81111113411142132142132211332331112213341332142121116114711311121
+81111113111232341412313211323214211214241225231111123234711311121
+81111113511213223411231244222111521411211152214121116141711311121
+81111113111222532311125224211241531231111122214451123311711311121
+81111113111325221311252211142521331212324142121211132522711311121
+81111113211241243121422213154111221144212311331321123215711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.png
new file mode 100644
index 0000000..0ce9078
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.properties
new file mode 100755
index 0000000..bb5ee89
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-4.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=4
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.codewords
new file mode 100755
index 0000000..45cbc12
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.codewords
@@ -0,0 +1,16 @@
+8111111351111251311122433411133112122423231511221122241441111243711311121
+8111111361112141141441115132211222224311311126124313131141111315711311121
+8111111331111262214211242442112123521211235212112352121111112254711311121
+8111111331121135142322124132132113124141151314113314113151116111711311121
+8111111321114224521312211222612124214211112111642212351141113331711311121
+8111111351114221144114112321134121136211412111611324123121116213711311121
+8111111311123333132221152231251111313413331113321224241121123242711311121
+8111111351121421211442213313211311124413121451211123116211116331711311121
+8111111321122261421241121423113221331133612231111122214451124121711311121
+8111111321133142211215232215112315131213312512214232122111133134711311121
+8111111311124215332231121131135222163111131151233414211141123231711311121
+8111111311131541113124323312511111132351132123411333122241134211711311121
+8111111321151124412112331211515121231314423111232114142211151116711311121
+8111111331132313132161214221221351131123331141312222411351131222711311121
+8111111311151242611123124112411323511311112132523112116212111344711311121
+8111111322112135313215112421151111142521132421133411214112111515711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.png
new file mode 100644
index 0000000..2cc69e8
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties
new file mode 100755
index 0000000..1b760b0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-5.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=5
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords
new file mode 100755
index 0000000..d7c9a49
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.codewords
@@ -0,0 +1,20 @@
+81111113211113262111213634111331121224232315112211222414132423114321113221111326711311121
+81111113311123152222431131112612431313111222116226123111422322115122111451111323711311121
+81111113211113531312144122411322112215321145212115331121241211511223214221112361711311121
+81111113411211431421211522111424232215111112424214113331332114213322213141121143711311121
+81111113311143312611321122325111112262214321211314215211221152311511421221113414711311121
+81111113411144111151231352133111521131132541112113432121145121121542121121121154711311121
+81111113211233413112224215112223332411211514111321141521314122131114333121123341711311121
+81111113411221242512321131132412124112421222511342111512412132221113613141121116711311121
+81111113111223521232142211132153224113221341111511111246131412412211611341125121711311121
+81111113111332333112234113151114151113141121312641221232211121361211252311133233711311121
+81111113211243226231111232112215251131134212212322325111522211134112313221123314711311121
+81111113111321535213311151214112211113531123153132621111213122421422123221136211711311121
+81111113311511321413222251511211112332233113214223141222112424122142321231151132711311121
+81111113211331153114312221153311514131111211542141131115412321135115112141131313711311121
+81111113111521511222143212142151152211321334112214512112115133213211531112111542711311121
+81111113321121432222323121123341512112411232321342321221131131341312131532112143711311121
+81111113411421221113532144212211121253214113121461412111422223111512411211134511711311121
+81111113521141216113211214311223123322311231314221311135114311153131134141211161711311121
+81111113312114233114123242111341413213211316131111132324333121222122242231211423711311121
+81111113211533113111352132213114211241241122612232123321112261225214112111145122711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.png
new file mode 100644
index 0000000..bb2a8a7
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.properties
new file mode 100755
index 0000000..45701ab
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-6.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=6
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.codewords
new file mode 100755
index 0000000..6bd2dea
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.codewords
@@ -0,0 +1,30 @@
+811111131111151641112152341113311212242323151122112224141324231143211132241113233111325121111425711311121
+811111134111242243131311122211622612311111611124116111244212313112531131412221143323311151111422711311121
+811111131111144412111443115322121341232122126112114232311112124512512411235311116111322161113122711311121
+811111133112123423322113121521232411233124211214214134113212143121413411322211241112161421121226711311121
+811111132111452131143122411231324122231231132115311333211321511311611223342231114212313131113521711311121
+811111134111512215412121331111611131253133115112122411331312114411341223631121122231133231121261711311121
+811111131112424224112331212216121123322323312411222311151314151121123143141422212121212621124151711311121
+811111135112223153312111611113311154113112226121513321115112223141112521521122314111414141121215711311121
+811111135112311323116211213212332114115223126111131111451121153313312133144411112122225131126121711311121
+811111131113414221333221112424122311233222122134133133122114314114112125121221264123113211133332711311121
+811111131112512412611321211236113112322342323111161241113123412131142114111345111133611131123421711311121
+811111136113221141134211421241121211316213111343111122542161221262213111223411312311135121141251711311121
+811111133115123133131132311132512332231121423113114151221124312321141521322411223231121421151223711311121
+811111133113322262311211411331313215221142111215211531131311421453211311311126121241114341131511711311121
+811111131116124123431121114412131312124312211334223411312151121431111163112212353121115362112212711311121
+811111132211223424241211233222123225112111252411413113222112243213114241221131431112141612112226711311121
+811111133114241152121123512313114312212212225113121352211121511511116133211233144121241211135222711311121
+811111134211431111332232252111411351211311613212111215424211411321126113122114331151211511211335711311121
+811111133121152221111425221121351115111621131126113332132121323331221323331113322332221221211514711311121
+811111131115431161221122421421212161113232113223341322112151124123113214221143226112113251151121711311121
+811111132121225211312333122214322142122352123112113411241211154213231331114413125113321111213252711311121
+811111131121342325131221313111162115142113142321131224221223241222151321321214312231221412122423711311121
+811111133211261135122112313143114512121133123221221331142113321415134111221235113214221252111421711311121
+811111133212521114121143113114241322224151114122611124112332113212111146111313432342112211221631711311121
+811111132213122413242212331111343132121421423311341212313423112151211241132222141211252321222125711311121
+811111131211613221416111114221514111313341333111341223111112542122113314332131132161113212114215711311121
+811111131213225162132111314112321531132114231132511332111211124533111161124411131133223221231341711311121
+811111131213333133221222323112142211142414151113331311321221231511114243324123112214213211224232711311121
+811111133121311562121131311122164211323141122124113112531231216123113214341221133311221441212115711311121
+811111132124124111412125233111331131152332115212134113133121621114311223145122111242212313111145711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.png
new file mode 100644
index 0000000..4c674c7
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties
new file mode 100755
index 0000000..40bf5d6
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-7.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=7
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords
new file mode 100755
index 0000000..15bd547
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.codewords
@@ -0,0 +1,41 @@
+81111113311121441111313634111331121224232315112211222414132423114321113224111323311132512133322132241221233221131423221221112136711311121
+81111113411125211161112411611124116111241161112411611124116111241161112411611124442221112115311312215312442121121121116441112125711311121
+81111113111121553161121211414141331251111113225221141152215412113112126112523121143122311211326161223111113122341126123151113213711311121
+81111113111214161212222523212421211511242222141314222411112222161222342123212223121512144122133113313312123132143332131131121333711311121
+81111113211151331112451212412151221342211421116111412251131351214112242111311352421111165212132122124123121451211134115141114141711311121
+81111113311154115111331211421116111421521113215312613112122122431313215116121141111411442232123221231242141312413241113221121352711311121
+81111113411311422324131114151113121331333231131314112323221422311114222413212413142131232132251133112142131412143231151131131134711311121
+81111113411223225121131353121221131341135312122112312161114411414422211111311451411414111161112441252111422221131262112251121322711311121
+81111113111232613251112223111252254111212111621312242141121115421451221111433131123111262212621111141441221312512232113321126311711311121
+81111113111412161411121622221215314122132222141323211314424112122114142242111143512112411125221341221232222422212323222141141141711311121
+81111113111252231241114322113116111542121211512412145121221144213315211131114133361121123211221541142122241133121111613321124124711311121
+81111113111331611422133132421131133112241112215422441121134122221132223311151143113132423111531213232141144113121232213311141441711311121
+81111113211514211214212413232213212115141132422215113231121411163221152131141133111131361112323411151116132232221423211311151413711311121
+81111113311333211115411312114314313242114213121332114231111351234311151114411141442221112131116221125132411112166131111331132214711311121
+81111113121112451321133311331422223311323311511212321224411144113114511131216112116231121241161122311134231111532132143122112261711311121
+81111113121124244141111432422112112331241212212632321411132132231115141331311314333312111115232212132125231411231211151522112333711311121
+81111113311431223412312151312311121442122211523141222213421111165322111232122511421212143111322411411243331521114121211551141122711311121
+81111113321153114112512112221234114231321143131315211331123115223411511111633111152311311432213122311233112112362162221131211351711311121
+81111113212121261413212323131223232113142122161211113235223125113333121132131331113331142122141412224231242211141223312342121142711311121
+81111113411612114115131112231161211532125122141152141121113111541321612111631122511321311222521223124221111143151113512341152121711311121
+81111113112124421441121311432222335111211312124314512211221162122353111132421131116132121211326114212241115141311422214111213351711311121
+81111113121232331321241314122124121134323131242111243321312211252512213112314222222115131133422121241511122232232321242111214134711311121
+81111113321131243111342252211213332132121212441232233112521221311161122326123111411412131212522241212412511122321142215142112124711311121
+81111113112211364122511122126211124411132351212121211343211261133222115132321141221111542341122221116312224114211163311122131152711311121
+81111113312222324131132222112333341211323221112514112422111611151215111524231113111333322222161124232121112432221321211612131315711311121
+81111113512111151151123321135131331131231112542111531132124211424211141311521133231131154321221214311151311424113315211122114322711311121
+81111113421341111423123142114311142213311212215313422122243211312142122332121161123311241621113231331141163211211223214211231531711311121
+81111113312311242112223423151122311322413141221342412121151215113125122122123241212314131312242251211241122222151323221321231116711311121
+81111113421222223114211413411241322133121152123242212114451311113132421131122611111245124323211121123413412421122411311451212222711311121
+81111113112421424311411212112451214412212112621221521312121122531113154111211236414111412111621312422222125121142431123133111161711311121
+81111113121422231241411312131612131113162214122321112136221311255121124123231114132221151324312114121116211122351142431111233124711311121
+81111113212152223114221334132112411224211213522131133321215112411122511424123311312333112512321152322111112111643111231521214313711311121
+81111113431142114311411211342231234211222124124111442122311162212351212111313143112214332132143111441411531131123213511112211433711311121
+81111113121515114142212111115152212115141412323131241321113151323125112214151113214232121513222114111513222222232311314211242412711311121
+81111113412223124111222411411243211251323114322111125322123161122141611153121221542111124212232111134313211335114212313161221221711311121
+81111113122131521441111413212341125121141421123313431113134214111332223124511211122321421132233214211134122114333161131111311226711311121
+81111113331122413116113111252411221315211221241423213231314124112412112432121431112232242411112521514112333112131514131123112233711311121
+81111113112253125421111233113123161241113611211242112322111352221111522424113312111353214212151113216121321231231253113121224213711311121
+81111113113122342112611341124311111215421131314312413132222112431522123122622111123132411143222251123113541131111341111512222242711311121
+81111113331212321113323334231121321312323211214313131116214232123211224213213124142223121221332331412213311222423323112223121224711311121
+81111113312341216121112323134121131153214322211225113113312331131113411541121512411322221131135231224221421413114111232342141311711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.png
new file mode 100644
index 0000000..d0a33a1
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties
new file mode 100755
index 0000000..f670abe
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-8.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=8
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error
new file mode 100755
index 0000000..3aba9d2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.error
@@ -0,0 +1 @@
+ECC level must be between 0 and 8.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties
new file mode 100755
index 0000000..c5bf86a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-ecc-level-9.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+preferredEccLevel=9
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords
new file mode 100755
index 0000000..7f552e2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.codewords
@@ -0,0 +1,7 @@
+811111135111115231112144341113311212242351111152711311121
+811111135111132311312261221151321414411141111216711311121
+811111131111124611451311223411316111322121111353711311121
+811111132111425121333221322412212332211321114251711311121
+811111132111341411611124331141313511231141113232711311121
+811111135111412212232241133114222224114141114411711311121
+811111133112315123112332133142212331241131123151711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.png
new file mode 100644
index 0000000..26c0290
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties
new file mode 100755
index 0000000..e74dd56
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic-row-height-10.properties
@@ -0,0 +1,3 @@
+mode=NORMAL
+barHeight=10
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords
new file mode 100755
index 0000000..7f552e2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.codewords
@@ -0,0 +1,7 @@
+811111135111115231112144341113311212242351111152711311121
+811111135111132311312261221151321414411141111216711311121
+811111131111124611451311223411316111322121111353711311121
+811111132111425121333221322412212332211321114251711311121
+811111132111341411611124331141313511231141113232711311121
+811111135111412212232241133114222224114141114411711311121
+811111133112315123112332133142212331241131123151711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.png
new file mode 100644
index 0000000..3e20ce2
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties
new file mode 100755
index 0000000..358dc74
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-basic.properties
@@ -0,0 +1,2 @@
+mode=NORMAL
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords
new file mode 100755
index 0000000..8320923
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.codewords
@@ -0,0 +1,11 @@
+811111133111123521113243213332213224122151111152711311121
+811111136111133122114322311621121161122351111224711311121
+811111131111124651214211113113254131115131111361711311121
+811111131111515211232314212232321414222121114251711311121
+811111133111342241151212116111243511231121113315711311121
+811111135111412222331231214211244122511131115114711311121
+811111131112323432122142111423231422211431123151711311121
+811111135112112431132511331321131123126121116141711311121
+811111136112221223521211133312222161221211122451711311121
+811111131113252232113151222414113332131111132423711311121
+811111133112332241131511131152222161113221123215711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.png
new file mode 100644
index 0000000..db3bb8f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties
new file mode 100755
index 0000000..08f5eb4
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-data-variety.properties
@@ -0,0 +1,2 @@
+mode=NORMAL
+content=TESTING 12345678901234567890 testing@TESTING
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords
new file mode 100755
index 0000000..e11d41e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.codewords
@@ -0,0 +1,5 @@
+81111113411111441111232614212214121224232315112231111235711311121
+81111113611111331262122121124421211235126113113161111133711311121
+81111113211112542352121123521211235212112352121131111163711311121
+81111113111142431423221214232212142322121423221211115152711311121
+81111113311132241161112411611124522411113213322131113224711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.png
new file mode 100644
index 0000000..7e700db
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties
new file mode 100755
index 0000000..d993bc2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-1-of-3.properties
@@ -0,0 +1,8 @@
+mode=NORMAL
+rows=5
+dataColumns=4
+preferredEccLevel=0
+structuredAppendFileId=123
+structuredAppendPosition=1
+structuredAppendTotal=3
+content=this
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords
new file mode 100755
index 0000000..cba3df1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.codewords
@@ -0,0 +1,5 @@
+81111113411111441111232624211214231511223111325131111235711311121
+81111113611111331262122121124421211236116113113161111133711311121
+81111113211112542352121123521211235212112352121131111163711311121
+81111113111142431423221214232212142322121423221211115152711311121
+81111113311132241161112411611124311622113112413231113224711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.png
new file mode 100644
index 0000000..bc4bf46
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties
new file mode 100755
index 0000000..c6cc5d0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-2-of-3.properties
@@ -0,0 +1,8 @@
+mode=NORMAL
+rows=5
+dataColumns=4
+preferredEccLevel=0
+structuredAppendFileId=123
+structuredAppendPosition=2
+structuredAppendTotal=3
+content=is a
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords
new file mode 100755
index 0000000..631eccc
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.codewords
@@ -0,0 +1,5 @@
+81111113411111441111232614212214111611152411142231111235711311121
+81111113611111331262122121124421111241166113113161111133711311121
+81111113211112542451111223521211235212112352121131111163711311121
+81111113111142431423221214232212142322121423221211115152711311121
+81111113311132241161112411611124411115136131212131113224711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.png
new file mode 100644
index 0000000..d1c4dcc
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties
new file mode 100755
index 0000000..09c7239
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-structured-append-3-of-3.properties
@@ -0,0 +1,8 @@
+mode=NORMAL
+rows=5
+dataColumns=4
+preferredEccLevel=0
+structuredAppendFileId=123
+structuredAppendPosition=3
+structuredAppendTotal=3
+content=test
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error
new file mode 100755
index 0000000..eff26c5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.error
@@ -0,0 +1 @@
+Too few rows (3) and columns (3) to hold codewords (20)
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties
new file mode 100755
index 0000000..550513b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-too-few-columns-and-rows.properties
@@ -0,0 +1,4 @@
+mode=NORMAL
+dataColumns=3
+rows=3
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords
new file mode 100755
index 0000000..0453447
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.codewords
@@ -0,0 +1,28 @@
+81111113111115162122212513313213421111433113224115141311211141521112161421122135111141441423221211111516711311121
+81111113411116125331211131122512222441112111523243232111211152322116311221143411216111322111432351111422711311121
+81111113211114522252121223512121144113121153231131121162111213441111336114431112211112542212216111112155711311121
+81111113311212342131322341311124232312132213314123332112211221352112223431142141221141511114252131121234711311121
+81111113311141331161122341112422121442121151224121611132211143232421411231132313311135213112221531113521711311121
+81111113311152132112115421116312211211541111336123521211116133112121114551113312122121442261221131115411711311121
+81111113111242421111414421131324111141441423221233311312312212242331241111143331211421332324212111124242711311121
+81111113411213144312222123143112116213215111314111611124412232211212613141112125511131412161113241121215711311121
+81111113611231212112115411113361132121431321113521321134111213445111341111121146111213441111336111123261711311121
+81111113111341421423221215122321211133421141512211223224311111363212214242421112111141441423221211134142711311121
+81111113111241164513111121115232114114414131311311431241311325112313412111621321511131411161112431123421711311121
+81111113111322521161331131131161143122311443111261113221111232614312411111522411154211121112114611133161711311121
+81111113311512311512151115113231212114153112234111132126311413311331422114232113223232211423221231151231711311121
+81111113511321314422211141113133432321114111222451111125216111324111313331114331211145211111512541131511711311121
+81111113121111461111336123521211134321211112154215221132234211222342112223421122234211222342112212111245711311121
+81111113221122341511121515111215151112151511121515111215151112151511121515111215151112151511121522112234711311121
+81111113411411143511221235113121216111325111314111115224511214211161122361111133123211524113313111135222711311121
+81111113321151132261221111113361235212112162211213111343215411121122234211232143111112462261221132115311711311121
+81111113312115222112152312132125311132512123111633222131311322412211213531422311133141221113141531211522711311121
+81111113311521134111313311611124231133134111141431233212221143222116321111621321411131331161112451151121711311121
+81111113221213511132162123411222111612412251212221131252123131421122214415331121143311221442212111212442711311121
+81111113112134231511121515141311211215231121322512323213332213212411142221113342124141135111115211213423711311121
+81111113321121166211113223113412132152121261132111114315512211145123212134223111541111221161112452111421711311121
+81111113312162111434112151214112143121321112154221121451111422511122224322612211511133121111336111221136711311121
+81111113221312242111415241111243142322121423221214232212142322121423221214232212111232343323122122131224711311121
+81111113121143143311241241323211411311153112211621315311126121314513111141151113113311524115212112114215711311121
+81111113112232511242232111231531231311512242122261223111116132123133114114221331221361113163111142134111711311121
+81111113121333311132412313151213222414111114121611232512321112343121324112233222123242213241211312133331711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.png
new file mode 100644
index 0000000..5ce7edd
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.properties
new file mode 100755
index 0000000..1e16bbc
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample-2.properties
@@ -0,0 +1,2 @@
+mode=NORMAL
+content=[)>0102V1Z1G5124026901066870001875FDEB90106681951/115.0LBN77 REDDY DRIVEMELIANABC0260610ZGI00611ZRTC TEST12Z901263303523ZN22ZN20Z0.0015631Z 9K50224199ZGIBI02840156CADPaintingNO EEI 30.37 (f) 010S630151821EXN26Zb1d7
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords
new file mode 100755
index 0000000..9355b96
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.codewords
@@ -0,0 +1,27 @@
+8111111321111425412113321331321342111143311322411514131121114152111216142112213531111334711311121
+8111111351112133511131411161122341111612122162212211422321611132211143232152114141111414711311121
+8111111331111361121322516112312112232142211214511111336114431112511134111222113561112213711311121
+8111111321121226111414143121122514214131232113141514131111114144111243412112415151121151711311121
+8111111311114216411212155111314111611223411112161111623251211412122154112161113221113513711311121
+8111111331115114111211462252121215311321111312442153131121121154211163122112115431116122711311121
+8111111321124151111141441423221233311114213221152111334212151115151413111111414411124143711311121
+8111111341121413311221165111314111611124433131113111261223315111112361214214131161121132711311121
+8111111311122451124411133113521121231242521131131262311111113361235212111132162141124113711311121
+8111111311133332241115213321112411133134151413111111414421131126111233331111414421133241711311121
+8111111331124132116111241312421321133115122311613214221235112311311232233216211121123413711311121
+8111111321132161114116122131153122231241226122115111341111121146111213441111336141134112711311121
+8111111321151223142322121512232121113342123241221114232331111136321221424242111211151215711311121
+8111111341132222511131411161112445131111211152321231611225113212312242212123411341131412711311121
+8111111311161142125121141322224131231151114216111262311111113361235212111161331132111261711311121
+8111111312112226211322332142331112414113221122341413141214131115132131241514131142112151711311121
+8111111341141213511131411161112445131111211433124232311111611223611111331311411521135131711311121
+8111111342114212511233111231324122612211111211461252211312442121311162211243121332116121711311121
+8111111321211514111216141112161411114144142322121512313111122424214233111241411341211431711311121
+8111111331152212411133311111512552122131122352115421111231213511341231214122322141151113711311121
+8111111311212244325112211232122421541112511241212261221111113361235212111343212111212541711311121
+8111111312122423421112422233231151111152424211121111414414232212151231315121114211213324711311121
+8111111352112132432321116111113321611132511131412111321661111232116111241161112442111413711311121
+8111111331216112115223123211531123126111113212254221511131621112115224112323114131221152711311121
+8111111321222125222212151513141124213131311611312132231314232113112125141313111642131141711311121
+8111111312114413412124126221122161121231312232133112413251322211421231312112523132114132711311121
+8111111311223152114511132531113121126113143213211422133131135112214211245113321132135111711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.png
new file mode 100644
index 0000000..ea785c9
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties
new file mode 100755
index 0000000..6d6c2f1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-tracking-sample.properties
@@ -0,0 +1,2 @@
+mode=NORMAL
+content=[)>010212550840497954912206750221FDE5100871431121/15.00LBN30 JOHNNY DRIVENOUBURDEGAReturns Department0610ZED00611ZJamony Reits12Z1646888994415Z11867621520Z10031Z100231290331000125400079549122047534Z0235Z01
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords
new file mode 100755
index 0000000..01d5f69
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.codewords
@@ -0,0 +1,15 @@
+81111113411112432112142423332211311132511241411331111235711311121
+81111113511115211112451221611132121244124111232361111232711311121
+81111113211112544121511211232341243111326221311161112114711311121
+81111113511161112121222512233321424121212122323211115152711311121
+81111113211141254211222341131412111444111161112431113323711311121
+81111113411142131343212112512213114125212121154131115312711311121
+81111113211232421241411322113242112332234141141111123234711311121
+81111113611212314111161211611124251132121221541111116232711311121
+81111113111222531121153311223251311111632113115351123212711311121
+81111113111331341111232621133142333114111123211611132522711311121
+81111113211236115111223241123231411113154422211131123223711311121
+81111113111314422142142113532111312161121123153151133112711311121
+81111113111511161313222333241121323111153322132111143331711311121
+81111113411321232322411221416111311331231221612241131214711311121
+81111113211511511432132121116213113113251121234332111162711311121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.png
new file mode 100644
index 0000000..39fbc9c
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties
new file mode 100755
index 0000000..2e24968
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/normal-utf-8.properties
@@ -0,0 +1,2 @@
+mode=NORMAL
+content=12üƒœ˜Ë±‹3asdf23456789012asdf89012asdf89asdfaf2
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords
new file mode 100755
index 0000000..6fcc10b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.codewords
@@ -0,0 +1,7 @@
+811111135111115221112136341113311
+811111135111112542111413113122611
+811111133111116321231143143211231
+811111132111425143211132241113231
+811111132111321631112612431313111
+811111134111411421421124244211211
+811111133112315132251121232113141
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.png
new file mode 100644
index 0000000..2d68807
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties
new file mode 100755
index 0000000..180375e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-0.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=0
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords
new file mode 100755
index 0000000..9638ffb
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.codewords
@@ -0,0 +1,8 @@
+811111135111115221112136341113311
+811111136111123242111413113122611
+811111133111116321231143143211231
+811111132111425143211132241113231
+811111133111332331112612431313111
+811111134111411421421124244211211
+811111133112315115113132223323111
+811111131111623254211112111254211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.png
new file mode 100644
index 0000000..8ae69fe
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties
new file mode 100755
index 0000000..e5e16ba
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-1.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=1
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords
new file mode 100755
index 0000000..a138e32
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.codewords
@@ -0,0 +1,7 @@
+81111113511111523111214434111331121224231
+81111113511113231131226122115132141441111
+81111113111112461145131122341131611132211
+81111113211142512133322132241221233221131
+81111113211134141161112433114131351123111
+81111113511141221223224113311422222411411
+81111113311231512311233213314221233124111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.png
new file mode 100644
index 0000000..034e3d5
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.properties
new file mode 100755
index 0000000..9b6621e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-2.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=2
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.codewords
new file mode 100755
index 0000000..990623f
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.codewords
@@ -0,0 +1,10 @@
+81111113311112354111215234111331121224231
+81111113511114221131226122115132141441111
+81111113111112461145131122341131611132211
+81111113111151522133322132241221233221131
+81111113311135211161112411611124312134121
+81111113511141221121153331411133631121121
+81111113111232341512151113142321241412211
+81111113411212151112411633314111131442111
+81111113611222121141121612121145121113441
+81111113111325222321242111121416311512311
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.png
new file mode 100644
index 0000000..a26ca8d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties
new file mode 100755
index 0000000..330d750
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-3.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=3
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords
new file mode 100755
index 0000000..766397b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.codewords
@@ -0,0 +1,11 @@
+8111111331111235211121363411133112122423231511221
+8111111341112125221151321414411151322112222243111
+8111111321111254611132212233123121421124244211211
+8111111311115152211513223312133111232116132423111
+8111111341114141141141141315411152221311112252131
+8111111341114213214213221133233111221334133214211
+8111111311123234141231321132321421121424122523111
+8111111351121322341123124422211152141121115221411
+8111111311122253231112522421124153123111112221441
+8111111311132522131125221114252133121232414212121
+8111111321124124312142221315411122114421231133131
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.png
new file mode 100644
index 0000000..578b40d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties
new file mode 100755
index 0000000..ee031fa
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-4.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=4
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords
new file mode 100755
index 0000000..a20be05
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.codewords
@@ -0,0 +1,16 @@
+811111135111125131112243341113311212242323151122112224141
+811111136111214114144111513221122222431131112612431313111
+811111133111126221421124244211212352121123521211235212111
+811111133112113514232212413213211312414115131411331411311
+811111132111422452131221122261212421421111211164221235111
+811111135111422114411411232113412113621141211161132412311
+811111131112333313222115223125111131341333111332122424111
+811111135112142121144221331321131112441312145121112311621
+811111132112226142124112142311322133113361223111112221441
+811111132113314221121523221511231513121331251221423212211
+811111131112421533223112113113522216311113115123341421111
+811111131113154111312432331251111113235113212341133312221
+811111132115112441211233121151512123131442311123211414221
+811111133113231313216121422122135113112333114131222241131
+811111131115124261112312411241132351131111213252311211621
+811111132211213531321511242115111114252113242113341121411
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.png
new file mode 100644
index 0000000..a6f089d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.properties
new file mode 100755
index 0000000..0e4880c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-5.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=5
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.codewords
new file mode 100755
index 0000000..864221a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.codewords
@@ -0,0 +1,20 @@
+8111111321111326211121363411133112122423231511221122241413242311432111321
+8111111331112315222243113111261243131311122211622612311142232211512211141
+8111111321111353131214412241132211221532114521211533112124121151122321421
+8111111341121143142121152211142423221511111242421411333133211421332221311
+8111111331114331261132112232511111226221432121131421521122115231151142121
+8111111341114411115123135213311152113113254111211343212114512112154212111
+8111111321123341311222421511222333241121151411132114152131412213111433311
+8111111341122124251232113113241212411242122251134211151241213222111361311
+8111111311122352123214221113215322411322134111151111124613141241221161131
+8111111311133233311223411315111415111314112131264122123221112136121125231
+8111111321124322623111123211221525113113421221232232511152221113411231321
+8111111311132153521331115121411221111353112315313262111121312242142212321
+8111111331151132141322225151121111233223311321422314122211242412214232121
+8111111321133115311431222115331151413111121154214113111541232113511511211
+8111111311152151122214321214215115221132133411221451211211513321321153111
+8111111332112143222232312112334151211241123232134232122113113134131213151
+8111111341142122111353214421221112125321411312146141211142222311151241121
+8111111352114121611321121431122312332231123131422131113511431115313113411
+8111111331211423311412324211134141321321131613111113232433312122212224221
+8111111321153311311135213221311421124124112261223212332111226122521411211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.png
new file mode 100644
index 0000000..eb1da21
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties
new file mode 100755
index 0000000..d95ba6d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-6.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=6
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords
new file mode 100755
index 0000000..2d20b49
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.codewords
@@ -0,0 +1,30 @@
+81111113111115164111215234111331121224232315112211222414132423114321113224111323311132511
+81111113411124224313131112221162261231111161112411611124421231311253113141222114332331111
+81111113111114441211144311532212134123212212611211423231111212451251241123531111611132211
+81111113311212342332211312152123241123312421121421413411321214312141341132221124111216141
+81111113211145213114312241123132412223123113211531133321132151131161122334223111421231311
+81111113411151221541212133111161113125313311511212241133131211441134122363112112223113321
+81111113111242422411233121221612112332232331241122231115131415112112314314142221212121261
+81111113511222315331211161111331115411311222612151332111511222314111252152112231411141411
+81111113511231132311621121321233211411522312611113111145112115331331213314441111212222511
+81111113111341422133322111242412231123322212213413313312211431411411212512122126412311321
+81111113111251241261132121123611311232234232311116124111312341213114211411134511113361111
+81111113611322114113421142124112121131621311134311112254216122126221311122341131231113511
+81111113311512313313113231113251233223112142311311415122112431232114152132241122323112141
+81111113311332226231121141133131321522114211121521153113131142145321131131112612124111431
+81111113111612412343112111441213131212431221133422341131215112143111116311221235312111531
+81111113221122342424121123322212322511211125241141311322211224321311424122113143111214161
+81111113311424115212112351231311431221221222511312135221112151151111613321123314412124121
+81111113421143111133223225211141135121131161321211121542421141132112611312211433115121151
+81111113312115222111142522112135111511162113112611333213212132333122132333111332233222121
+81111113111543116122112242142121216111323211322334132211215112412311321422114322611211321
+81111113212122521131233312221432214212235212311211341124121115421323133111441312511332111
+81111113112134232513122131311116211514211314232113122422122324122215132132121431223122141
+81111113321126113512211231314311451212113312322122133114211332141513411122123511321422121
+81111113321252111412114311311424132222415111412261112411233211321211114611131343234211221
+81111113221312241324221233111134313212142142331134121231342311215121124113222214121125231
+81111113121161322141611111422151411131334133311134122311111254212211331433213113216111321
+81111113121322516213211131411232153113211423113251133211121112453311116112441113113322321
+81111113121333313322122232311214221114241415111333131132122123151111424332412311221421321
+81111113312131156212113131112216421132314112212411311253123121612311321434122113331122141
+81111113212412411141212523311133113115233211521213411313312162111431122314512211124221231
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.png
new file mode 100644
index 0000000..47bbb88
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties
new file mode 100755
index 0000000..03f4f8b
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-7.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=7
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords
new file mode 100755
index 0000000..d82f42e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.codewords
@@ -0,0 +1,41 @@
+8111111331112144111131363411133112122423231511221122241413242311432111322411132331113251213332213224122123322113142322121
+8111111341112521116111241161112411611124116111241161112411611124116111241161112444222111211531131221531244212112112111641
+8111111311112155316112121141414133125111111322522114115221541211311212611252312114312231121132616122311111312234112612311
+8111111311121416121222252321242121151124222214131422241111222216122234212321222312151214412213311331331212313214333213111
+8111111321115133111245121241215122134221142111611141225113135121411224211131135242111116521213212212412312145121113411511
+8111111331115411511133121142111611142152111321531261311212212243131321511612114111141144223212322123124214131241324111321
+8111111341131142232413111415111312133133323113131411232322142231111422241321241314213123213225113311214213141214323115111
+8111111341122322512113135312122113134113531212211231216111441141442221111131145141141411116111244125211142222113126211221
+8111111311123261325111222311125225411121211162131224214112111542145122111143313112311126221262111114144122131251223211331
+8111111311141216141112162222121531412213222214132321131442411212211414224211114351211241112522134122123222242221232322211
+8111111311125223124111432211311611154212121151241214512122114421331521113111413336112112321122154114212224113312111161331
+8111111311133161142213313242113113311224111221542244112113412222113222331115114311313242311153121323214114411312123221331
+8111111321151421121421241323221321211514113242221511323112141116322115213114113311113136111232341115111613223222142321131
+8111111331133321111541131211431431324211421312133211423111135123431115111441114144222111213111622112513241111216613111131
+8111111312111245132113331133142222331132331151121232122441114411311451113121611211623112124116112231113423111153213214311
+8111111312112424414111143242211211233124121221263232141113213223111514133131131433331211111523221213212523141123121115151
+8111111331143122341231215131231112144212221152314122221342111116532211123212251142121214311132241141124333152111412121151
+8111111332115311411251211222123411423132114313131521133112311522341151111163311115231131143221312231123311211236216222111
+8111111321212126141321232313122323211314212216121111323522312511333312113213133111333114212214141222423124221114122331231
+8111111341161211411513111223116121153212512214115214112111311154132161211163112251132131122252122312422111114315111351231
+8111111311212442144112131143222233511121131212431451221122116212235311113242113111613212121132611421224111514131142221411
+8111111312123233132124131412212412113432313124211124332131221125251221311231422222211513113342212124151112223223232124211
+8111111332113124311134225221121333213212121244123223311252122131116112232612311141141213121252224121241251112232114221511
+8111111311221136412251112212621112441113235121212121134321126113322211513232114122111154234112222111631222411421116331111
+8111111331222232413113222211233334121132322111251411242211161115121511152423111311133332222216112423212111243222132121161
+8111111351211115115112332113513133113123111254211153113212421142421114131152113323113115432122121431115131142411331521111
+8111111342134111142312314211431114221331121221531342212224321131214212233212116112331124162111323133114116321121122321421
+8111111331231124211222342315112231132241314122134241212115121511312512212212324121231413131224225121124112222215132322131
+8111111342122222311421141341124132213312115212324221211445131111313242113112261111124512432321112112341341242112241131141
+8111111311242142431141121211245121441221211262122152131212112253111315411121123641411141211162131242222212512114243112311
+8111111312142223124141131213161213111316221412232111213622131125512112412323111413222115132431211412111621112235114243111
+8111111321215222311422133413211241122421121352213113332121511241112251142412331131233311251232115232211111211164311123151
+8111111343114211431141121134223123421122212412411144212231116221235121211131314311221433213214311144141153113112321351111
+8111111312151511414221211111515221211514141232313124132111315132312511221415111321423212151322211411151322222223231131421
+8111111341222312411122241141124321125132311432211112532212316112214161115312122154211112421223211113431321133511421231311
+8111111312213152144111141321234112512114142112331343111313421411133222312451121112232142113223321421113412211433316113111
+8111111333112241311611311125241122131521122124142321323131412411241211243212143111223224241111252151411233311213151413111
+8111111311225312542111123311312316124111361121124211232211135222111152242411331211135321421215111321612132123123125311311
+8111111311312234211261134112431111121542113131431241313222211243152212312262211112313241114322225112311354113111134111151
+8111111333121232111332333423112132131232321121431313111621423212321122421321312414222312122133233141221331122242332311221
+8111111331234121612111232313412113115321432221122511311331233113111341154112151241132222113113523122422142141311411123231
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.png
new file mode 100644
index 0000000..c800f58
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties
new file mode 100755
index 0000000..cae02cc
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-8.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=8
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error
new file mode 100755
index 0000000..3aba9d2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.error
@@ -0,0 +1 @@
+ECC level must be between 0 and 8.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties
new file mode 100755
index 0000000..0bbb11c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic-ecc-level-9.properties
@@ -0,0 +1,3 @@
+mode=TRUNCATED
+preferredEccLevel=9
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords
new file mode 100755
index 0000000..a138e32
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.codewords
@@ -0,0 +1,7 @@
+81111113511111523111214434111331121224231
+81111113511113231131226122115132141441111
+81111113111112461145131122341131611132211
+81111113211142512133322132241221233221131
+81111113211134141161112433114131351123111
+81111113511141221223224113311422222411411
+81111113311231512311233213314221233124111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.png
new file mode 100644
index 0000000..034e3d5
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.properties
new file mode 100755
index 0000000..8ddc7b3
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-basic.properties
@@ -0,0 +1,2 @@
+mode=TRUNCATED
+content=This is just a test.
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.codewords
new file mode 100755
index 0000000..02b95b2
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.codewords
@@ -0,0 +1,11 @@
+81111113311112352111324321333221322412211
+81111113611113312211432231162112116112231
+81111113111112465121421111311325413111511
+81111113111151521123231421223232141422211
+81111113311134224115121211611124351123111
+81111113511141222233123121421124412251111
+81111113111232343212214211142323142221141
+81111113511211243113251133132113112312611
+81111113611222122352121113331222216122121
+81111113111325223211315122241411333213111
+81111113311233224113151113115222216111321
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.png b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.png
new file mode 100644
index 0000000..a749e09
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.properties
new file mode 100755
index 0000000..27036a6
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/pdf417/truncated-data-variety.properties
@@ -0,0 +1,2 @@
+mode=TRUNCATED
+content=TESTING 12345678901234567890 testing@TESTING
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.codewords
new file mode 100755
index 0000000..60d5e70
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.codewords
@@ -0,0 +1 @@
+LLLSSLLLSSLLSSLLLSSLLSLSLLSLSLLSLLSLSLLSLLSLSLLSLSLLLSLSLSSLLSSLLLL
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.png
new file mode 100644
index 0000000..90f2f66
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties
new file mode 100755
index 0000000..482a436
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/planet-basic.properties
@@ -0,0 +1,2 @@
+mode=PLANET
+content=336699885526
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords
new file mode 100755
index 0000000..6ff6604
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.codewords
@@ -0,0 +1 @@
+LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.png
new file mode 100644
index 0000000..883c586
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties
new file mode 100755
index 0000000..390cba9
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-basic.properties
@@ -0,0 +1,2 @@
+mode=POSTNET
+content=012345678
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords
new file mode 100755
index 0000000..6ff6604
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.codewords
@@ -0,0 +1 @@
+LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.png
new file mode 100644
index 0000000..f4a9a12
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties
new file mode 100755
index 0000000..0866492
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-module-width-2.properties
@@ -0,0 +1,4 @@
+mode=POSTNET
+moduleWidth=2
+humanReadableLocation=BOTTOM
+content=012345678
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords
new file mode 100755
index 0000000..6ff6604
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.codewords
@@ -0,0 +1 @@
+LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.png
new file mode 100644
index 0000000..cbd4f0f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties
new file mode 100755
index 0000000..2c0e188
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-above.properties
@@ -0,0 +1,3 @@
+mode=POSTNET
+humanReadableLocation=TOP
+content=012345678
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords
new file mode 100755
index 0000000..6ff6604
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.codewords
@@ -0,0 +1 @@
+LLLSSSSSSLLSSLSLSSLLSSLSSLSLSLSSLLSSLSSSLLSSLSSLSSLL
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.png b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.png
new file mode 100644
index 0000000..6f0380c
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties
new file mode 100755
index 0000000..b5bb3c4
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/postnet/postnet-readable-below.properties
@@ -0,0 +1,3 @@
+mode=POSTNET
+humanReadableLocation=BOTTOM
+content=012345678
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords
new file mode 100755
index 0000000..374c6d5
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.codewords
@@ -0,0 +1,21 @@
+711111117
+151322151
+1131124111311
+1131111511311
+1131142111311
+15122111151
+711111117
+0:29
+0211313213121
+312113112213
+02114211312111
+0113114111121111
+02111141141121
+0811132113
+72142212
+1511131233
+113111132112111
+1131144321
+1131112222131
+151222233
+72122211111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.png
new file mode 100644
index 0000000..8a0b798
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties
new file mode 100755
index 0000000..25f91eb
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-01.properties
@@ -0,0 +1,2 @@
+preferredVersion=1
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords
new file mode 100755
index 0000000..006ee30
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.codewords
@@ -0,0 +1,25 @@
+7121347
+15121521151
+11311212221111311
+1131112811311
+11311231211111311
+15122232151
+7111111111117
+094138
+02113114111113121
+012211223212111111
+021325221121111
+12111111421131121
+01222111233211111
+03111132621112
+1131211221211411
+01112211111112131121
+1112529121
+0812321323
+7612111132
+1511522323
+113111113111711
+11311322121421
+11311122111131131
+15143112133
+75112411111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.png
new file mode 100644
index 0000000..7f95190
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties
new file mode 100755
index 0000000..bec8317
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-02.properties
@@ -0,0 +1,2 @@
+preferredVersion=2
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords
new file mode 100755
index 0000000..e354478
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.codewords
@@ -0,0 +1,29 @@
+7214357
+15125134151
+11311211612211311
+113112111121111311311
+1131111211411311311
+15122263151
+71111111111111117
+0811331219
+02224611122114
+1124116121211122
+01224343622
+0211211241231221111
+01142941331
+114211122131121221
+1112413211112122111
+011212111111122614
+03121211511812
+0131113112132122112
+12123112114121312
+02419112211221
+012141322111631
+08121431131211
+7115151111113
+15141311211323
+1131152212631
+1131112221351113
+113111321231711
+15126313124
+75211132111112
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.png
new file mode 100644
index 0000000..6943407
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.properties
new file mode 100755
index 0000000..c570f77
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-03.properties
@@ -0,0 +1,2 @@
+preferredVersion=3
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.codewords
new file mode 100755
index 0000000..4c985ef
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.codewords
@@ -0,0 +1,33 @@
+71343211137
+151321212224151
+1131122331321211311
+1131113215111411311
+1131121331122411311
+151521113213151
+711111111111111111117
+0;51211318
+0211314341121113121
+0155353111111212
+01144611131212131
+132124131111313212
+0141111221113111112121111
+31144341511113
+01111213221122113152
+011224612131121113
+2111423132112211311
+033231222211241121
+013251211212131421
+1321112112531121113
+0111211112111212241151
+0126184111111212
+1211251121231211231
+0112235411413212
+134121221111111151111
+0823222111332111
+723111121122111141
+151152114313212
+113111121111222291
+1131121841212221
+11311122112122111211131
+151352212312113
+7212221311113111111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.png
new file mode 100644
index 0000000..226d847
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties
new file mode 100755
index 0000000..00c42d0
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-04.properties
@@ -0,0 +1,2 @@
+preferredVersion=4
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords
new file mode 100755
index 0000000..f1b4015
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.codewords
@@ -0,0 +1,37 @@
+7211121221111221117
+1511339142151
+11311212211211241311311
+1131123136111511311
+11311211121221114411311
+15113332512111151
+7111111111111111111111117
+08132132211238
+04412114111113232311
+132142223134711
+311111313232511121121
+212231311311111113242
+112141211341121111121211
+04211112111212211121117
+11111151232423211131
+0324133311321:
+1121232212312241421
+01122316121324224
+01121121113113333211212
+01113112132111421164
+0342321111224311231
+0513143123211111322
+01211121544141111321
+01131111142261132222
+02212232111212112131321
+13221421213323142
+062211211211111411121121
+0211131113232111113242
+2121221122311151621
+081421212623131
+7111111122164111131
+15111111461121235
+1131111112213321125211
+11311921112142331
+1131141132111222222121
+1512121212411231115
+723111341244121
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.png
new file mode 100644
index 0000000..3a802ba
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties
new file mode 100755
index 0000000..64b8035
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-05.properties
@@ -0,0 +1,2 @@
+preferredVersion=5
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords
new file mode 100755
index 0000000..1b43c1e
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.codewords
@@ -0,0 +1,41 @@
+716112145211117
+15111313436122151
+1131116111412111132111311
+11311;3113223111311
+11311332111331112511311
+15111112133111161111151
+71111111111111111111111111117
+08116311611<
+0231111112222411121112323
+0221121111112311412112211122
+06211211311111121131111511
+0121273123111211421122
+11111112214211126211113111
+21221152251132111341
+11135223121521211121111
+6712513213235
+021311112117511242131
+0422112211111121214515
+02215211131311231211121111
+021215:111133235
+1113122133324115112111
+02155325122121211122
+1112262622113111143
+111112121521312211421122
+1211241113143123312211
+0112212322142151151211
+0523112313541111511
+03111241116212111421122
+1521231212112213111111131
+111524116221331115
+11113312132212231211121111
+1111211241111114313235
+1241211121231312136111
+0811112212511112111341
+73112223512111111111111
+15122121451123235
+113111331211212222731
+113111142153122311111211
+11311127222124422111
+151251113422121112122
+7321121112121311152111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.png
new file mode 100644
index 0000000..0234605
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties
new file mode 100755
index 0000000..6b96e12
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-06.properties
@@ -0,0 +1,2 @@
+preferredVersion=6
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords
new file mode 100755
index 0000000..eef57ef
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.codewords
@@ -0,0 +1,45 @@
+712111111:3142117
+151212154243111112151
+113113131113123132211211311
+1131112211392121122111311
+11311221112472143111311
+151811121311211224151
+711111111111111111111111111111117
+0=21532111131218
+021131121224722163121
+01154212311111213311111412
+0411221211211233112171131
+1132111221122121212122222212
+02114241325132141212111
+0112122212213124123111113121
+13112116211121111322132221
+01212121222215114312243
+11113321322243217141
+01122115111211111112111311111412
+24311211414111112171131
+014241224121122122222212
+127111141151321451111
+0413121213332112313323
+212111326111113322311132
+0112131434131233121323
+13731121712331711
+051123121211152512311112
+32213111113312312141161
+01221131332122134221121112
+11313212134432161111111
+021121212114121132212123111111
+1431:1112121121211111141
+0111132211111125211122112211112
+137212231331126241
+1132141111442124123123
+041132223324414113121
+0142231612321132211223
+122132113281131451111
+08121111312321312111132111
+7362311112221211111141
+1511112114332112121123212
+113111123111;43181
+1131171311326211111421
+113111121111114312414121131
+15134111312112111112443
+776122111223112111111
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.png
new file mode 100644
index 0000000..55415b1
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties
new file mode 100755
index 0000000..a16b69c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-07.properties
@@ -0,0 +1,2 @@
+preferredVersion=7
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords
new file mode 100755
index 0000000..eb83f45
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.codewords
@@ -0,0 +1,49 @@
+7212111631111165117
+1511421143123111121141151
+113115111141344311112111311
+11311225111122121422211211311
+11311212111792221411311
+1511112211211213111111121123151
+7111111111111111111111111111111111117
+081322121213211113112:
+0448112162111221142311
+0211121221511121211131242111311
+111231232132211121511215121
+01241221221122113111152211132
+11111111131212222113111211234211
+23111123313111121131422111131
+01211121111411111121416316111
+41111161116131111127214
+01222123421111111311111211143111
+16111132331112113372311
+02123111622121413121111113211
+02211121111212517227142
+3313212411113111122211233111
+0412111221342312324211222
+02116342;1212112513
+02331115111313111112242332
+31111111212412111111321211111111221
+02332221341323134113131
+0283428241211251111
+02261511121321211124334
+71112124121231521111222111
+02242221371112111244222
+131111111331312212142111122221
+01221232211422112121243322
+0211322211112141111332111121321
+16151111325512482
+111111211323112264211112111121
+0311126415122112333142
+044111441212111511121121321
+02231235621415264
+01132231311211125121112112112111
+0134223113252114115124
+33211111311121662112531
+0811121221211314112423122
+71242211311141411221111211
+151134134336114332
+1131111114212261232112531
+11311321131121134111241111411
+1131121112111112212361127111
+15124822213411211111122
+7314212331162114151
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.png
new file mode 100644
index 0000000..2c6545f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties
new file mode 100755
index 0000000..3e67e39
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-08.properties
@@ -0,0 +1,2 @@
+preferredVersion=8
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords
new file mode 100755
index 0000000..b8f04d1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.codewords
@@ -0,0 +1,53 @@
+711131123111131111111341137
+151122282111323542151
+113111111123381223431211311
+113114111131512242121111111111311
+1131142111221111611113411311311
+1511312111134322211433151
+71111111111111111111111111111111111111117
+083121343325521:
+02311141111132117113111141323
+12131231611114231161131231
+24121211112322171213411112111
+2311121121111112111443121212116
+3311134144211123111244131
+0232231211211124231521121151
+1111112135131311421111124411111
+0221133111341113218312125
+03125141213231222111212211141
+12253154112122115347
+1311212311111123291211212111112
+0511142114111114413211113214
+02131132312135112121221213111111
+11326211241112231122331331
+1111112112142121822122112312111
+0221153141114221411512125
+04511171:331111121531
+122312213713131117111314
+0411112512111131111611111141112111
+011123131111133113211234112314
+225112118151122121146111
+03312221722124212411221131
+1332121121211121322121211221161
+213113211131121131111121141212114
+1311131121712212132111131112221
+1223132123111918112215
+04414121142111122131111221131111
+01112211321121117111111214121215
+21112521152121121321111311122111
+03322221411221151116112314
+116321111221222221111111122111111111
+6112221113121111115214121314
+113115211241212621111312112111
+111113111121211111122511161155
+2151753311311111111222121111
+012411314212311111121232122115
+031211121613118222237111
+0821112311311312211222121314
+7531123211112311131231112111
+15131135122351111211112314
+113111123112211163121112127111
+11311111412116241124331151
+113111413131121441211211331121
+15123156131112123311216
+7415311112622221431211
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.png
new file mode 100644
index 0000000..037d618
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.properties
new file mode 100755
index 0000000..b51d4f1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-09.properties
@@ -0,0 +1,2 @@
+preferredVersion=9
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.codewords
new file mode 100755
index 0000000..6f5d702
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.codewords
@@ -0,0 +1,57 @@
+711141311111123211111244227
+15132233131121233211121212151
+11311321212124211321111211312211311
+1131111211112122511331311211111211311
+113113332512721114321211311
+1512212331634132121213151
+711111111111111111111111111111111111111111117
+09213111311223411213211119
+0211312141312111633312112123121
+111213112113311311111122111342113112
+01121122211361155311112123241
+312325124112146111111211211212
+0114212111121221212111121231334111111
+14111113:1133111112131212111111111
+037112121212191331325141
+22211112415132121111121312111221112
+421157216212331331121131
+21111342215111422211131212113112
+21522212421421112311111133241
+02162233312222115111111211211212
+24212112322121321212313352111
+011221111782111211112131212111213
+2121411311121111141111133132632
+0112211112129112312112121311121321
+11212151321112823331311311111
+2124214224123222111213121116
+216111111144115223113131621
+021113512331211331112422111323
+221111514311411113212112132111221
+01121321121222634111143111131112
+216122251294211122731
+0112133174113211112221131111111212
+131131314312143211121121331111111
+61311221231111121221124311142111
+211231221117312512212131111231
+0221114111212211217211142233212
+24223143115212213112132161
+0111223211122172112121143111121212
+2111411111211312255121112231331
+221321721211111431211114321212
+041123122212132231115121123211111
+2131231121211123111212313411312111
+21123111112413111126412111211331
+231215212124212131111312111112212
+031214211213122112221113133361
+3112411124341111111112131211131212
+11124111113113311;1111113111331
+521111233132163111211112321212
+06411113132111521122113361111
+081312112133131112214121332111
+731111331131111124112132311141
+151117313313113115121213212
+1131111231334153112213<1
+11311221131353132112131214211111
+1131111111111316121125211111311211211
+1512113141211141512112221321112
+721313111412223122131411511
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.png b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.png
new file mode 100644
index 0000000..ed8f325
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.properties
new file mode 100755
index 0000000..087e9a7
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/qrcode/version-10.properties
@@ -0,0 +1,2 @@
+preferredVersion=10
+content=ABC
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.codewords
new file mode 100755
index 0000000..4f37112
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.codewords
@@ -0,0 +1 @@
+1112221212214111132123111141111113121213311232112221212211191123211111222112212111411111132
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.png
new file mode 100644
index 0000000..7910d59
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties
new file mode 100755
index 0000000..470bc68
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-basic.properties
@@ -0,0 +1,2 @@
+mode=UPCA
+content=12345678901+1234
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords
new file mode 100755
index 0000000..4f37112
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.codewords
@@ -0,0 +1 @@
+1112221212214111132123111141111113121213311232112221212211191123211111222112212111411111132
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.png b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.png
new file mode 100644
index 0000000..7e99f7d
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties
new file mode 100755
index 0000000..3a37f4d
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-a-font.properties
@@ -0,0 +1,3 @@
+mode=UPCA
+fontSize=4
+content=12345678901+1234
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords
new file mode 100755
index 0000000..5f8ccfe
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.codewords
@@ -0,0 +1 @@
+11121221411113213214111213111111191123211111222112212111411111132
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.png b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.png
new file mode 100644
index 0000000..27032bf
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties
new file mode 100755
index 0000000..f0d5dcf
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/upc/upc-e-basic.properties
@@ -0,0 +1,2 @@
+mode=UPCE
+content=1234567+1234
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords
new file mode 100755
index 0000000..1534787
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.codewords
@@ -0,0 +1 @@
+AFDADFAAAFTDDTDDTATADDFAADFDDFTAFTDTAFTFTDTATFTFAFDFDFFFFTFFFATDD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.png
new file mode 100644
index 0000000..761f00b
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties
new file mode 100755
index 0000000..f48958a
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-20.properties
@@ -0,0 +1 @@
+content=12345678901234567890
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords
new file mode 100755
index 0000000..6b3fced
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.codewords
@@ -0,0 +1 @@
+FTTDTTDTTDAFFTTAADDFFFAADAAAADTDFFAFFAATFADDFDDTTDADADDDATTTFDFDF
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.png
new file mode 100644
index 0000000..0386710
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties
new file mode 100755
index 0000000..0814523
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-25.properties
@@ -0,0 +1 @@
+content=12345678901234567890-12345
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords
new file mode 100755
index 0000000..0448f41
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.codewords
@@ -0,0 +1 @@
+TADDDDATDAAADFDDAFDFDDTFADAADAADFAFFFDAFAFTDTADADFTFFATFFATDFTATD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.png
new file mode 100644
index 0000000..49c94f6
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties
new file mode 100755
index 0000000..f95b880
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-29.properties
@@ -0,0 +1 @@
+content=12345678901234567890-123456789
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords
new file mode 100755
index 0000000..5058a4c
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.codewords
@@ -0,0 +1 @@
+AAFDFTDTADDTATTTDTDTDTTDTDDDADTFTTAFFTDFAFAAAFDFATDFTATTFDDFDAAFD
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.png b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.png
new file mode 100644
index 0000000..19ed30f
Binary files /dev/null and b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.png differ
diff --git a/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.properties b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.properties
new file mode 100755
index 0000000..52973d1
--- /dev/null
+++ b/barcode/src/test/resources/org/xbib/graphics/barcode/uspsonecode/usps-one-code-length-31.properties
@@ -0,0 +1 @@
+content=12345678901234567890-12345678901
diff --git a/build.gradle b/build.gradle
index 79391f9..8379f09 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,51 +1,35 @@
-
plugins {
- id "io.codearte.nexus-staging" version "0.11.0"
+ id "de.marcphilipp.nexus-publish" version "0.4.0"
+ id "io.codearte.nexus-staging" version "0.21.1"
}
-apply plugin: 'java'
-apply plugin: 'maven'
-apply plugin: 'signing'
-apply plugin: "io.codearte.nexus-staging"
-
-configurations {
- wagon
+wrapper {
+ gradleVersion = "${rootProject.property('gradle.wrapper.version')}"
+ distributionType = Wrapper.DistributionType.ALL
}
-dependencies {
- testCompile "junit:junit:4.12"
- wagon "org.apache.maven.wagon:wagon-ssh:3.0.0"
+ext {
+ user = 'jprante'
+ name = 'graphics'
+ description = 'A collection of graphics libraries for Java'
+ inceptionYear = '2020'
+ url = 'https://github.com/' + user + '/' + name
+ scmUrl = 'https://github.com/' + user + '/' + name
+ scmConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git'
+ scmDeveloperConnection = 'scm:git:ssh://git@github.com:' + user + '/' + name + '.git'
+ issueManagementSystem = 'Github'
+ issueManagementUrl = ext.scmUrl + '/issues'
+ licenseName = 'The Apache License, Version 2.0'
+ licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
-sourceCompatibility = JavaVersion.VERSION_1_8
-targetCompatibility = JavaVersion.VERSION_1_8
+subprojects {
+ apply plugin: 'java-library'
-[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
-tasks.withType(JavaCompile) {
- options.compilerArgs << "-Xlint:all"
+ apply from: rootProject.file('gradle/ide/idea.gradle')
+ apply from: rootProject.file('gradle/compile/java.gradle')
+ apply from: rootProject.file('gradle/test/junit5.gradle')
+ apply from: rootProject.file('gradle/publishing/publication.gradle')
}
-task javadocJar(type: Jar, dependsOn: classes) {
- from javadoc
- into "build/tmp"
- classifier 'javadoc'
-}
-
-task sourcesJar(type: Jar, dependsOn: classes) {
- from sourceSets.main.allSource
- into "build/tmp"
- classifier 'sources'
-}
-
-artifacts {
- archives javadocJar, sourcesJar
-}
-
-if (project.hasProperty('signing.keyId')) {
- signing {
- sign configurations.archives
- }
-}
-
-apply from: 'gradle/ext.gradle'
-apply from: 'gradle/publish.gradle'
+apply from: rootProject.file('gradle/publishing/sonatype.gradle')
diff --git a/chart/NOTICE.txt b/chart/NOTICE.txt
new file mode 100644
index 0000000..e301852
--- /dev/null
+++ b/chart/NOTICE.txt
@@ -0,0 +1,6 @@
+This work is derived from XChart by Tim Molter
+
+http://knowm.org/open-source/xchart/
+https://github.com/timmolter/xchart
+
+Apache License 2.0
diff --git a/chart/src/main/java/module-info.java b/chart/src/main/java/module-info.java
new file mode 100644
index 0000000..f256da4
--- /dev/null
+++ b/chart/src/main/java/module-info.java
@@ -0,0 +1,17 @@
+module org.xbib.graphics.chart {
+ exports org.xbib.graphics.chart;
+ exports org.xbib.graphics.chart.axis;
+ exports org.xbib.graphics.chart.bubble;
+ exports org.xbib.graphics.chart.category;
+ exports org.xbib.graphics.chart.formatter;
+ exports org.xbib.graphics.chart.io;
+ exports org.xbib.graphics.chart.legend;
+ exports org.xbib.graphics.chart.ohlc;
+ exports org.xbib.graphics.chart.pie;
+ exports org.xbib.graphics.chart.plot;
+ exports org.xbib.graphics.chart.series;
+ exports org.xbib.graphics.chart.style;
+ exports org.xbib.graphics.chart.theme;
+ exports org.xbib.graphics.chart.xy;
+ requires transitive java.desktop;
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/Chart.java b/chart/src/main/java/org/xbib/graphics/chart/Chart.java
new file mode 100644
index 0000000..4f26cd2
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/Chart.java
@@ -0,0 +1,391 @@
+package org.xbib.graphics.chart;
+
+import org.xbib.graphics.chart.axis.Direction;
+import org.xbib.graphics.chart.axis.Axis;
+import org.xbib.graphics.chart.axis.AxisPair;
+import org.xbib.graphics.chart.io.BitmapFormat;
+import org.xbib.graphics.chart.io.VectorGraphicsFormat;
+import org.xbib.graphics.chart.legend.Legend;
+import org.xbib.graphics.chart.plot.Plot;
+import org.xbib.graphics.chart.series.Series;
+import org.xbib.graphics.chart.style.Styler;
+import org.xbib.graphics.chart.io.vector.EPSGraphics2D;
+import org.xbib.graphics.chart.io.vector.PDFGraphics2D;
+import org.xbib.graphics.chart.io.vector.ProcessingPipeline;
+import org.xbib.graphics.chart.io.vector.SVGGraphics2D;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.Format;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+
+public abstract class Chart {
+
+ protected final ST styler;
+
+ protected final ChartTitle chartTitle;
+
+ protected final Map seriesMap;
+
+ protected AxisPair, ?> axisPair;
+
+ protected Plot plot;
+
+ protected Legend legend;
+
+ private int width;
+
+ private int height;
+
+ private String title = "";
+
+ private String xAxisTitle = "";
+
+ private String yAxisTitle = "";
+
+ private final Map yAxisGroupTitleMap;
+
+ protected Chart(int width, int height, ST styler) {
+ this.styler = styler;
+ this.width = width;
+ this.height = height;
+ this.chartTitle = new ChartTitle<>(this);
+ this.seriesMap = new LinkedHashMap<>();
+ this.yAxisGroupTitleMap = new HashMap<>();
+ }
+
+ public abstract void paint(Graphics2D g, int width, int height);
+
+ protected void paintBackground(Graphics2D g) {
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, styler.getAntiAlias()
+ ? RenderingHints.VALUE_ANTIALIAS_ON
+ : RenderingHints.VALUE_ANTIALIAS_OFF);
+ g.setColor(styler.getChartBackgroundColor());
+ Shape rect = new Rectangle2D.Double(0, 0, getWidth(), getHeight());
+ g.fill(rect);
+ }
+
+ public List listFromDoubleArray(double[] data) {
+ if (data == null) {
+ return null;
+ }
+ List dataNumber;
+ dataNumber = new ArrayList<>();
+ for (double d : data) {
+ dataNumber.add(d);
+ }
+ return dataNumber;
+ }
+
+ public List listFromFloatArray(float[] data) {
+ if (data == null) {
+ return null;
+ }
+ List dataNumber;
+ dataNumber = new ArrayList<>();
+ for (float f : data) {
+ dataNumber.add((double) f);
+ }
+ return dataNumber;
+ }
+
+ public List listFromIntArray(int[] data) {
+ if (data == null) {
+ return null;
+ }
+ List dataNumber;
+ dataNumber = new ArrayList<>();
+ for (double d : data) {
+ dataNumber.add(d);
+ }
+ return dataNumber;
+ }
+
+ public List getGeneratedData(int length) {
+ List generatedData = new ArrayList<>();
+ for (int i = 1; i < length + 1; i++) {
+ generatedData.add((double) i);
+ }
+ return generatedData;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getXAxisTitle() {
+ return xAxisTitle;
+ }
+
+ public void setXAxisTitle(String xAxisTitle) {
+ this.xAxisTitle = xAxisTitle;
+ }
+
+ public String getyYAxisTitle() {
+ return yAxisTitle;
+ }
+
+ public void setYAxisTitle(String yAxisTitle) {
+ this.yAxisTitle = yAxisTitle;
+ }
+
+ public String getYAxisGroupTitle(int yAxisGroup) {
+ String title = yAxisGroupTitleMap.get(yAxisGroup);
+ if (title == null) {
+ return yAxisTitle;
+ }
+ return title;
+ }
+
+ public void setYAxisGroupTitle(int yAxisGroup, String yAxisTitle) {
+ yAxisGroupTitleMap.put(yAxisGroup, yAxisTitle);
+ }
+
+ public void setXAxisLabelOverrideMap(Map overrideMap) {
+ axisPair.getAxisLabelOverrideMap().put("X0", overrideMap);
+ }
+
+ public void setYAxisLabelOverrideMap(Map overrideMap) {
+ axisPair.getAxisLabelOverrideMap().put("Y0", overrideMap);
+ }
+
+ public void setYAxisLabelOverrideMap(Map overrideMap, int yAxisGroup) {
+ axisPair.getAxisLabelOverrideMap().put(("Y" + yAxisGroup), overrideMap);
+ }
+
+ public Map getYAxisLabelOverrideMap(Direction direction, int yIndex) {
+ Map> axisLabelOverrideMap = axisPair.getAxisLabelOverrideMap();
+ return axisLabelOverrideMap.get((direction.name() + yIndex));
+ }
+
+ public ChartTitle getChartTitle() {
+ return chartTitle;
+ }
+
+ public Legend getLegend() {
+ return legend;
+ }
+
+ public Plot getPlot() {
+ return plot;
+ }
+
+ public Axis, ?> getXAxis() {
+ return axisPair.getXAxis();
+ }
+
+ public Axis, ?> getYAxis() {
+ return axisPair.getYAxis();
+ }
+
+ public Axis, ?> getYAxis(int yIndex) {
+ return axisPair.getYAxis(yIndex);
+ }
+
+ public AxisPair, ?> getAxisPair() {
+ return axisPair;
+ }
+
+ public Map getSeriesMap() {
+ return seriesMap;
+ }
+
+ public S removeSeries(String seriesName) {
+ return seriesMap.remove(seriesName);
+ }
+
+ public ST getStyler() {
+ return styler;
+ }
+
+ public Format getXAxisFormat() {
+ return axisPair.getXAxis().getAxisTickCalculator().getAxisFormat();
+ }
+
+ public Format getYAxisFormat() {
+ return axisPair.getYAxis().getAxisTickCalculator().getAxisFormat();
+ }
+
+ /**
+ * Save chart as an image file.
+ *
+ * @param outputStream output stream
+ * @param bitmapFormat bitmap format
+ * @throws IOException if save fails
+ */
+ public void saveBitmap(OutputStream outputStream, BitmapFormat bitmapFormat) throws IOException {
+ BufferedImage bufferedImage = getBufferedImage();
+ ImageIO.write(bufferedImage, bitmapFormat.toString().toLowerCase(), outputStream);
+ outputStream.close();
+ }
+
+ /**
+ * Save a chart as a PNG with a custom DPI. The default DPI is 72, which is fine for displaying charts on a
+ * computer
+ * monitor, but for printing
+ * charts, a DPI of around 300 is much better.
+ *
+ * @param outputStream output stream
+ * @param dpi dot sper inch
+ * @throws IOException if save fails
+ */
+ public void saveBitmapWithDPI(OutputStream outputStream, BitmapFormat bitmapFormat, int dpi) throws IOException {
+ double scaleFactor = dpi / 72.0;
+ BufferedImage bufferedImage = new BufferedImage((int) (getWidth() * scaleFactor), (int) (getHeight() * scaleFactor), BufferedImage.TYPE_INT_RGB);
+ Graphics2D graphics2D = bufferedImage.createGraphics();
+ AffineTransform at = graphics2D.getTransform();
+ at.scale(scaleFactor, scaleFactor);
+ graphics2D.setTransform(at);
+ paint(graphics2D, getWidth(), getHeight());
+ Iterator writers = ImageIO.getImageWritersByFormatName(bitmapFormat.toString().toLowerCase());
+ if (writers.hasNext()) {
+ ImageWriter writer = writers.next();
+ // instantiate an ImageWriteParam object with default compression options
+ ImageWriteParam iwp = writer.getDefaultWriteParam();
+ ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
+ IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, iwp);
+ if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
+ throw new IllegalArgumentException("It is not possible to set the DPI on a bitmap with " + bitmapFormat + " format!! Try another format.");
+ }
+ setDPI(metadata, dpi);
+ try {
+ writer.setOutput(outputStream);
+ IIOImage image = new IIOImage(bufferedImage, null, metadata);
+ writer.write(null, image, iwp);
+ } finally {
+ writer.dispose();
+ }
+ }
+ }
+
+ /**
+ * Sets the metadata.
+ *
+ * @param metadata metadata
+ * @param DPI dots per inch
+ * @throws IIOInvalidTreeException if setting fails
+ */
+ private static void setDPI(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException {
+ // for PNG, it's dots per millimeter
+ double dotsPerMilli = 1.0 * DPI / 10 / 2.54;
+ IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
+ horiz.setAttribute("value", Double.toString(dotsPerMilli));
+ IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
+ vert.setAttribute("value", Double.toString(dotsPerMilli));
+ IIOMetadataNode dim = new IIOMetadataNode("Dimension");
+ dim.appendChild(horiz);
+ dim.appendChild(vert);
+ IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
+ root.appendChild(dim);
+ metadata.mergeTree("javax_imageio_1.0", root);
+ }
+
+ /**
+ * Save a Chart as a JPEG file
+ *
+ * @param outputStream output stream
+ * @param quality - a float between 0 and 1 (1 = maximum quality)
+ * @throws IOException if save fails
+ */
+ public void saveJPGWithQuality(OutputStream outputStream, float quality) throws IOException {
+ BufferedImage bufferedImage = getBufferedImage();
+ Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
+ ImageWriter writer = iter.next();
+ ImageWriteParam iwp = writer.getDefaultWriteParam();
+ iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+ iwp.setCompressionQuality(quality);
+ try (outputStream) {
+ writer.setOutput(outputStream);
+ IIOImage image = new IIOImage(bufferedImage, null, null);
+ writer.write(null, image, iwp);
+ writer.dispose();
+ }
+ }
+
+ /**
+ * Generates a byte[] for a given chart.
+ *
+ * @param bitmapFormat bitmap format
+ * @return a byte[] for a given chart
+ * @throws IOException if byte array fails
+ */
+ public byte[] getBitmapBytes(BitmapFormat bitmapFormat) throws IOException {
+ BufferedImage bufferedImage = getBufferedImage();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ImageIO.write(bufferedImage, bitmapFormat.toString().toLowerCase(), baos);
+ baos.flush();
+ byte[] imageInBytes = baos.toByteArray();
+ baos.close();
+ return imageInBytes;
+ }
+
+ public BufferedImage getBufferedImage() {
+ BufferedImage bufferedImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
+ Graphics2D graphics2D = bufferedImage.createGraphics();
+ paint(graphics2D, getWidth(), getHeight());
+ return bufferedImage;
+ }
+
+ public void write(OutputStream outputStream, VectorGraphicsFormat vectorGraphicsFormat)
+ throws IOException {
+ ProcessingPipeline g = null;
+ switch (vectorGraphicsFormat) {
+ case EPS:
+ g = new EPSGraphics2D(0.0, 0.0, getWidth(), getHeight());
+ break;
+ case PDF:
+ g = new PDFGraphics2D(0.0, 0.0, getWidth(), getHeight());
+ break;
+ case SVG:
+ g = new SVGGraphics2D(0.0, 0.0, getWidth(), getHeight());
+ break;
+
+ default:
+ break;
+ }
+ paint(g, getWidth(), getHeight());
+ if (outputStream != null) {
+ outputStream.write(g.getBytes());
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java
new file mode 100644
index 0000000..3626c56
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/ChartBuilder.java
@@ -0,0 +1,66 @@
+package org.xbib.graphics.chart;
+
+import org.xbib.graphics.chart.theme.DefaultTheme;
+import org.xbib.graphics.chart.theme.Theme;
+
+public abstract class ChartBuilder, C extends Chart, ?>> {
+
+ private int width;
+
+ private int height;
+
+ private String title;
+
+ private Theme theme;
+
+ public ChartBuilder() {
+ this.width = 800;
+ this.height = 600;
+ this.title = "";
+ this.theme = new DefaultTheme();
+ }
+
+ @SuppressWarnings("unchecked")
+ public T width(int width) {
+ this.width = width;
+ return (T) this;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T height(int height) {
+ this.height = height;
+ return (T) this;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T title(String title) {
+ this.title = title;
+ return (T) this;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T theme(Theme theme) {
+ if (theme != null) {
+ this.theme = theme;
+ }
+ return (T) this;
+ }
+
+ public Theme getTheme() {
+ return theme;
+ }
+
+ public abstract C build();
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java b/chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java
new file mode 100644
index 0000000..e52f790
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/ChartComponent.java
@@ -0,0 +1,14 @@
+package org.xbib.graphics.chart;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * A components of a chart that need to be painted.
+ */
+public interface ChartComponent {
+
+ Rectangle2D getBounds();
+
+ void paint(Graphics2D g);
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java b/chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java
new file mode 100644
index 0000000..2789e68
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/ChartTitle.java
@@ -0,0 +1,87 @@
+package org.xbib.graphics.chart;
+
+import org.xbib.graphics.chart.series.Series;
+import org.xbib.graphics.chart.style.Styler;
+import org.xbib.graphics.chart.theme.Theme;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Chart Title.
+ */
+public class ChartTitle implements ChartComponent {
+
+ private final Chart chart;
+ private Rectangle2D bounds;
+
+ public ChartTitle(Chart chart) {
+ this.chart = chart;
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ if (bounds == null) {
+ bounds = getBoundsHint();
+ }
+ return bounds;
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ g.setFont(chart.getStyler().getChartTitleFont());
+ if (!chart.getStyler().isChartTitleVisible() || chart.getTitle().length() == 0) {
+ return;
+ }
+ Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout textLayout = new TextLayout(chart.getTitle(), chart.getStyler().getChartTitleFont(), frc);
+ Rectangle2D textBounds = textLayout.getBounds();
+ double xOffset = chart.getPlot().getBounds().getX();
+ double yOffset = chart.getStyler().getChartPadding();
+ if (chart.getStyler().isChartTitleBoxVisible()) {
+ double chartTitleBoxWidth = chart.getPlot().getBounds().getWidth();
+ double chartTitleBoxHeight = textBounds.getHeight() + 2 * chart.getStyler().getChartTitlePadding();
+ g.setStroke(Theme.Strokes.TITLE);
+ Shape rect = new Rectangle2D.Double(xOffset, yOffset, chartTitleBoxWidth, chartTitleBoxHeight);
+ g.setColor(chart.getStyler().getChartTitleBoxBackgroundColor());
+ g.fill(rect);
+ g.setColor(chart.getStyler().getChartTitleBoxBorderColor());
+ g.draw(rect);
+ }
+ xOffset = chart.getPlot().getBounds().getX() + (chart.getPlot().getBounds().getWidth() - textBounds.getWidth()) / 2.0;
+ yOffset = chart.getStyler().getChartPadding() + textBounds.getHeight() + chart.getStyler().getChartTitlePadding();
+ g.setColor(chart.getStyler().getChartFontColor());
+ Shape shape = textLayout.getOutline(null);
+ AffineTransform orig = g.getTransform();
+ AffineTransform at = new AffineTransform();
+ at.translate(xOffset, yOffset);
+ g.transform(at);
+ g.fill(shape);
+ g.setTransform(orig);
+ double width = 2 * chart.getStyler().getChartTitlePadding() + textBounds.getWidth();
+ double height = 2 * chart.getStyler().getChartTitlePadding() + textBounds.getHeight();
+ bounds = new Rectangle2D.Double(xOffset - chart.getStyler().getChartTitlePadding(),
+ yOffset - textBounds.getHeight() - chart.getStyler().getChartTitlePadding(), width, height);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint);
+ }
+
+ private Rectangle2D getBoundsHint() {
+ if (chart.getStyler().isChartTitleVisible() && chart.getTitle().length() > 0) {
+ TextLayout textLayout = new TextLayout(chart.getTitle(), chart.getStyler().getChartTitleFont(),
+ new FontRenderContext(null, true, false));
+ Rectangle2D rectangle = textLayout.getBounds();
+ double width = 2 * chart.getStyler().getChartTitlePadding() + rectangle.getWidth();
+ double height = 2 * chart.getStyler().getChartTitlePadding() + rectangle.getHeight();
+ return new Rectangle2D.Double(Double.NaN, Double.NaN, width, height);
+ } else {
+ return new Rectangle2D.Double();
+ }
+ }
+}
\ No newline at end of file
diff --git a/chart/src/main/java/org/xbib/graphics/chart/Histogram.java b/chart/src/main/java/org/xbib/graphics/chart/Histogram.java
new file mode 100644
index 0000000..2a4912e
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/Histogram.java
@@ -0,0 +1,96 @@
+package org.xbib.graphics.chart;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class Histogram {
+
+ private final Collection extends Number> originalData;
+
+ private final int numBins;
+
+ private final double min;
+
+ private final double max;
+
+ private List xAxisData;
+
+ private List yAxisData;
+
+ public Histogram(Collection extends Number> data, int numBins) {
+ this.numBins = numBins;
+ this.originalData = data;
+ double tempMax = -Double.MAX_VALUE;
+ double tempMin = Double.MAX_VALUE;
+ for (Number number : data) {
+ double value = number.doubleValue();
+ if (value > tempMax) {
+ tempMax = value;
+ }
+ if (value < tempMin) {
+ tempMin = value;
+ }
+ }
+ max = tempMax;
+ min = tempMin;
+ init();
+ }
+
+ public Histogram(Collection extends Number> data, int numBins, double min, double max) {
+ this.numBins = numBins;
+ this.originalData = data;
+ this.min = min;
+ this.max = max;
+ init();
+ }
+
+ private void init() {
+ double[] tempYAxisData = new double[numBins];
+ final double binSize = (max - min) / numBins;
+ for (Number anOriginalData : originalData) {
+ double doubleValue = (anOriginalData).doubleValue();
+ int bin = (int) ((doubleValue - min) / binSize);
+ if (bin >= 0) {
+ if (doubleValue == max) {
+ tempYAxisData[bin - 1] += 1;
+ } else if (bin <= numBins && bin != numBins) {
+ tempYAxisData[bin] += 1;
+ }
+ }
+ }
+ yAxisData = new ArrayList<>(numBins);
+ for (double d : tempYAxisData) {
+ yAxisData.add(d);
+ }
+ xAxisData = new ArrayList<>(numBins);
+ for (int i = 0; i < numBins; i++) {
+ xAxisData.add(((i * (max - min)) / numBins + min) + binSize / 2);
+ }
+ }
+
+ public List getxAxisData() {
+ return xAxisData;
+ }
+
+ public List getyAxisData() {
+ return yAxisData;
+ }
+
+ public Collection extends Number> getOriginalData() {
+ return originalData;
+ }
+
+ public int getNumBins() {
+ return numBins;
+ }
+
+ public double getMin() {
+ return min;
+ }
+
+ public double getMax() {
+ return max;
+ }
+
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/QuickChart.java b/chart/src/main/java/org/xbib/graphics/chart/QuickChart.java
new file mode 100644
index 0000000..bc2ca8d
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/QuickChart.java
@@ -0,0 +1,59 @@
+package org.xbib.graphics.chart;
+
+import org.xbib.graphics.chart.theme.Theme;
+import org.xbib.graphics.chart.xy.XYChart;
+import org.xbib.graphics.chart.xy.XYSeries;
+
+import java.util.List;
+
+/**
+ * A convenience class for making Charts with one line of code.
+ */
+public final class QuickChart {
+
+ private final static int WIDTH = 600;
+ private final static int HEIGHT = 400;
+
+ private QuickChart() {
+ }
+
+ public static XYChart getChart(String chartTitle, String xTitle, String yTitle,
+ String seriesName, double[] xData, double[] yData) {
+ double[][] yData2d = {yData};
+ if (seriesName == null) {
+ return getChart(chartTitle, xTitle, yTitle, null, xData, yData2d);
+ } else {
+ return getChart(chartTitle, xTitle, yTitle, new String[]{seriesName}, xData, yData2d);
+ }
+ }
+
+ public static XYChart getChart(String chartTitle, String xTitle, String yTitle,
+ String[] seriesNames, double[] xData, double[][] yData) {
+ XYChart chart = new XYChart(WIDTH, HEIGHT);
+ chart.setTitle(chartTitle);
+ chart.setXAxisTitle(xTitle);
+ chart.setYAxisTitle(yTitle);
+ for (int i = 0; i < yData.length; i++) {
+ XYSeries series;
+ if (seriesNames != null) {
+ series = chart.addSeries(seriesNames[i], xData, yData[i]);
+ } else {
+ chart.getStyler().setLegendVisible(false);
+ series = chart.addSeries(" " + i, xData, yData[i]);
+ }
+ series.setMarker(Theme.Series.NONE_MARKER);
+ }
+ return chart;
+ }
+
+ public static XYChart getChart(String chartTitle, String xTitle, String yTitle,
+ String seriesName, List extends Double> xData, List extends Double> yData) {
+ XYChart chart = new XYChart(WIDTH, HEIGHT);
+ chart.setTitle(chartTitle);
+ chart.setXAxisTitle(xTitle);
+ chart.setYAxisTitle(yTitle);
+ XYSeries series = chart.addSeries(seriesName, xData, yData);
+ series.setMarker(Theme.Series.NONE_MARKER);
+ return chart;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java b/chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java
new file mode 100644
index 0000000..cec0821
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/Axis.java
@@ -0,0 +1,293 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.category.CategoryStyler;
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.ChartComponent;
+import org.xbib.graphics.chart.series.AxesChartSeries;
+import org.xbib.graphics.chart.series.AxesChartSeriesCategory;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+import org.xbib.graphics.chart.legend.LegendPosition;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.List;
+import java.util.Map;
+
+public class Axis implements ChartComponent {
+
+ private final Chart chart;
+ private final Rectangle2D.Double bounds;
+ private final ST axesChartStyler;
+ private final AxisTitle axisTitle;
+ private final AxisTick axisTick;
+ private final Direction direction;
+ private final int yIndex;
+ private DataType dataType;
+ private AxisTickCalculator axisTickCalculator;
+ private double min;
+ private double max;
+
+ public Axis(Chart chart, Direction direction, int yIndex) {
+ this.chart = chart;
+ this.axesChartStyler = chart.getStyler();
+ this.direction = direction;
+ this.yIndex = yIndex;
+ bounds = new Rectangle2D.Double();
+ axisTitle = new AxisTitle<>(chart, direction, direction == Direction.Y ? this : null, yIndex);
+ axisTick = new AxisTick<>(chart, direction, direction == Direction.Y ? this : null);
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ return bounds;
+ }
+
+ public void resetMinMax() {
+ min = Double.MAX_VALUE;
+ max = -Double.MAX_VALUE;
+ }
+
+ public void addMinMax(double min, double max) {
+ if (Double.isNaN(this.min) || min < this.min) {
+ this.min = min;
+ }
+ if (max > this.max) {
+ this.max = max;
+ }
+ }
+
+ public void preparePaint() {
+ if (direction == Direction.Y) {
+ double xOffset = 0;
+ double yOffset = chart.getChartTitle().getBounds().getHeight() + axesChartStyler.getChartPadding();
+ int i = 1;
+ double width = 60;
+ double height;
+ do {
+ double approximateXAxisWidth = chart.getWidth() - width
+ - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE
+ ? chart.getLegend().getBounds().getWidth()
+ : 0)
+ - 2 * axesChartStyler.getChartPadding()
+ - (axesChartStyler.isYAxisTicksVisible() ? (axesChartStyler.getPlotMargin()) : 0)
+ - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE
+ && axesChartStyler.isLegendVisible()
+ ? axesChartStyler.getChartPadding()
+ : 0);
+
+ height = chart.getHeight() - yOffset
+ - chart.getXAxis().getXAxisHeightHint(approximateXAxisWidth)
+ - axesChartStyler.getPlotMargin()
+ - axesChartStyler.getChartPadding()
+ - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideS
+ ? chart.getLegend().getBounds().getHeight()
+ : 0);
+
+ width = getYAxisWidthHint(height);
+ } while (i-- > 0);
+ bounds.setRect(xOffset, yOffset, width, height);
+ } else {
+ Rectangle2D leftYAxisBounds = chart.getAxisPair().getLeftYAxisBounds();
+ Rectangle2D rightYAxisBounds = chart.getAxisPair().getRightYAxisBounds();
+ double maxYAxisY = Math.max(leftYAxisBounds.getY() + leftYAxisBounds.getHeight(),
+ rightYAxisBounds.getY() + rightYAxisBounds.getHeight());
+ double xOffset = leftYAxisBounds.getWidth() + axesChartStyler.getChartPadding();
+ double yOffset = maxYAxisY + axesChartStyler.getPlotMargin()
+ - (axesChartStyler.getLegendPosition() == LegendPosition.OutsideS
+ ? chart.getLegend().getBounds().getHeight()
+ : 0);
+ double legendWidth = 0;
+ if (axesChartStyler.getLegendPosition() == LegendPosition.OutsideE
+ && axesChartStyler.isLegendVisible()) {
+ legendWidth = chart.getLegend().getBounds().getWidth() + axesChartStyler.getChartPadding();
+ }
+ double width = chart.getWidth() - leftYAxisBounds.getWidth()
+ - rightYAxisBounds.getWidth()
+ - 2 * axesChartStyler.getChartPadding()
+ - legendWidth;
+ double height = chart.getHeight() - maxYAxisY
+ - axesChartStyler.getChartPadding()
+ - axesChartStyler.getPlotMargin();
+ bounds.setRect(xOffset, yOffset, width, height);
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ if (direction == Direction.Y) {
+ boolean onRight = axesChartStyler.getYAxisGroupPosistion(yIndex) == YAxisPosition.Right;
+ if (onRight) {
+ axisTick.paint(g);
+ axisTitle.paint(g);
+ } else {
+ axisTitle.paint(g);
+ axisTick.paint(g);
+ }
+ bounds.width = (axesChartStyler.isYAxisTitleVisible() ? axisTitle.getBounds().getWidth() : 0)
+ + axisTick.getBounds().getWidth();
+
+ } else {
+ this.axisTickCalculator = getAxisTickCalculator(bounds.getWidth());
+ axisTitle.paint(g);
+ axisTick.paint(g);
+ }
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint);
+ }
+
+ private double getXAxisHeightHint(double workingSpace) {
+ double titleHeight = 0.0;
+ if (chart.getXAxisTitle() != null &&
+ !chart.getXAxisTitle().trim().equalsIgnoreCase("") &&
+ axesChartStyler.isXAxisTitleVisible()) {
+ TextLayout textLayout = new TextLayout(chart.getXAxisTitle(),
+ axesChartStyler.getAxisTitleFont(), new FontRenderContext(null, true, false));
+ Rectangle2D rectangle = textLayout.getBounds();
+ titleHeight = rectangle.getHeight() + chart.getStyler().getAxisTitlePadding();
+ }
+ this.axisTickCalculator = getAxisTickCalculator(workingSpace);
+ double axisTickLabelsHeight = 0.0;
+ if (chart.getStyler().isXAxisTicksVisible()) {
+ String sampleLabel = "";
+ for (int i = 0; i < axisTickCalculator.getTickLabels().size(); i++) {
+ if (axisTickCalculator.getTickLabels().get(i) != null &&
+ axisTickCalculator.getTickLabels().get(i).length() > sampleLabel.length()) {
+ sampleLabel = axisTickCalculator.getTickLabels().get(i);
+ }
+ }
+ TextLayout textLayout = new TextLayout(sampleLabel.length() == 0 ? " " : sampleLabel,
+ axesChartStyler.getAxisTickLabelsFont(),
+ new FontRenderContext(null, true, false));
+ AffineTransform rot = axesChartStyler.getXAxisLabelRotation() == 0 ? null :
+ AffineTransform.getRotateInstance(-1 * Math.toRadians(axesChartStyler.getXAxisLabelRotation()));
+ Shape shape = textLayout.getOutline(rot);
+ Rectangle2D rectangle = shape.getBounds();
+ axisTickLabelsHeight = rectangle.getHeight() + axesChartStyler.getAxisTickPadding() + axesChartStyler.getAxisTickMarkLength();
+ }
+ return titleHeight + axisTickLabelsHeight;
+ }
+
+ private double getYAxisWidthHint(double workingSpace) {
+ double titleHeight = 0.0;
+ if (chart.getyYAxisTitle() != null &&
+ !chart.getyYAxisTitle().trim().equalsIgnoreCase("") &&
+ axesChartStyler.isYAxisTitleVisible()) {
+ TextLayout textLayout = new TextLayout(chart.getyYAxisTitle(),
+ axesChartStyler.getAxisTitleFont(), new FontRenderContext(null, true, false));
+ Rectangle2D rectangle = textLayout.getBounds();
+ titleHeight = rectangle.getHeight() + axesChartStyler.getAxisTitlePadding();
+ }
+ double axisTickLabelsHeight = 0.0;
+ if (axesChartStyler.isYAxisTicksVisible()) {
+ this.axisTickCalculator = getAxisTickCalculator(workingSpace);
+ String sampleLabel = "";
+ for (int i = 0; i < axisTickCalculator.getTickLabels().size(); i++) {
+ if (axisTickCalculator.getTickLabels().get(i) != null
+ && axisTickCalculator.getTickLabels().get(i).length() > sampleLabel.length()) {
+ sampleLabel = axisTickCalculator.getTickLabels().get(i);
+ }
+ }
+ TextLayout textLayout = new TextLayout(sampleLabel.length() == 0 ? " " : sampleLabel,
+ axesChartStyler.getAxisTickLabelsFont(), new FontRenderContext(null, true, false));
+ Rectangle2D rectangle = textLayout.getBounds();
+ axisTickLabelsHeight = rectangle.getWidth() + axesChartStyler.getAxisTickPadding() + axesChartStyler.getAxisTickMarkLength();
+ }
+ return titleHeight + axisTickLabelsHeight;
+ }
+
+ private AxisTickCalculator getAxisTickCalculator(double workingSpace) {
+ Map labelOverrideMap = chart.getYAxisLabelOverrideMap(getDirection(), yIndex);
+ if (labelOverrideMap != null) {
+ if (getDirection() == Direction.X && axesChartStyler instanceof CategoryStyler) {
+ AxesChartSeriesCategory axesChartSeries =
+ (AxesChartSeriesCategory) chart.getSeriesMap().values().iterator().next();
+ List> categories = (List>) axesChartSeries.getXData();
+ DataType axisType = chart.getAxisPair().getXAxis().getDataType();
+ return new AxisTickCalculatorOverride(getDirection(),
+ workingSpace,
+ axesChartStyler,
+ labelOverrideMap,
+ axisType,
+ categories.size());
+ }
+ return new AxisTickCalculatorOverride(getDirection(), workingSpace, min, max, axesChartStyler, labelOverrideMap);
+ }
+ if (getDirection() == Direction.X) {
+ if (axesChartStyler instanceof CategoryStyler) {
+ AxesChartSeriesCategory axesChartSeries =
+ (AxesChartSeriesCategory) chart.getSeriesMap().values().iterator().next();
+ List> categories = (List>) axesChartSeries.getXData();
+ DataType axisType = chart.getAxisPair().getXAxis().getDataType();
+ return new AxisTickCalculatorCategory(
+ getDirection(), workingSpace, categories, axisType, axesChartStyler);
+ } else if (getDataType() == DataType.Instant) {
+ return new AxisTickCalculatorInstant(getDirection(), workingSpace, min, max, axesChartStyler);
+ } else if (axesChartStyler.isXAxisLogarithmic()) {
+ return new AxisTickCalculatorLogarithmic(
+ getDirection(), workingSpace, min, max, axesChartStyler);
+ } else {
+ return new AxisTickCalculatorNumber(getDirection(), workingSpace, min, max, axesChartStyler);
+ }
+ } else {
+ if (axesChartStyler.isYAxisLogarithmic() && getDataType() != DataType.Instant) {
+ return new AxisTickCalculatorLogarithmic(getDirection(), workingSpace, min, max, axesChartStyler);
+ } else {
+ return new AxisTickCalculatorNumber(getDirection(), workingSpace, min, max, axesChartStyler);
+ }
+ }
+ }
+
+ public DataType getDataType() {
+ return dataType;
+ }
+
+ public void setDataType(DataType dataType) {
+
+ if (dataType != null && this.dataType != null && this.dataType != dataType) {
+ throw new IllegalArgumentException("Different Axes (e.g. Date, Number, String) cannot be mixed on the same chart!!");
+ }
+ this.dataType = dataType;
+ }
+
+ public double getMin() {
+ return min;
+ }
+
+ public void setMin(double min) {
+ this.min = min;
+ }
+
+ public double getMax() {
+ return max;
+ }
+
+ public void setMax(double max) {
+ this.max = max;
+ }
+
+ public AxisTick getAxisTick() {
+ return axisTick;
+ }
+
+ public Direction getDirection() {
+ return direction;
+ }
+
+ public AxisTitle getAxisTitle() {
+ return axisTitle;
+ }
+
+ public AxisTickCalculator getAxisTickCalculator() {
+ return this.axisTickCalculator;
+ }
+
+ public int getYIndex() {
+ return yIndex;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java
new file mode 100644
index 0000000..20a2a84
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisPair.java
@@ -0,0 +1,363 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.category.CategorySeriesRenderStyle;
+import org.xbib.graphics.chart.category.CategoryStyler;
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.ChartComponent;
+import org.xbib.graphics.chart.series.AxesChartSeries;
+import org.xbib.graphics.chart.series.AxesChartSeriesCategory;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+import org.xbib.graphics.chart.legend.LegendPosition;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class AxisPair implements ChartComponent {
+
+ private final Chart chart;
+
+ private final Axis xAxis;
+
+ private final Axis yAxis;
+
+ private final TreeMap> yAxisMap;
+
+ private final Rectangle2D.Double leftYAxisBounds;
+
+ private final Rectangle2D.Double rightYAxisBounds;
+
+ private Axis leftMainYAxis;
+
+ private Axis rightMainYAxis;
+
+ private final Map> axisLabelOverrideMap;
+
+ public AxisPair(Chart chart) {
+ this.chart = chart;
+ this.xAxis = new Axis<>(chart, Direction.X, 0);
+ this.yAxis = new Axis<>(chart, Direction.Y, 0);
+ this.yAxisMap = new TreeMap<>();
+ yAxisMap.put(0, yAxis);
+ leftYAxisBounds = new Rectangle2D.Double();
+ rightYAxisBounds = new Rectangle2D.Double();
+ axisLabelOverrideMap = new HashMap<>();
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ return null; // should never be called
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ prepareForPaint();
+ leftMainYAxis = null;
+ rightMainYAxis = null;
+ ST styler = chart.getStyler();
+ final int chartPadding = styler.getChartPadding();
+ int tickMargin = (styler.isYAxisTicksVisible() ? (styler.getPlotMargin()) : 0);
+ leftYAxisBounds.width = 0;
+ int leftCount = 0;
+ double leftStart = chartPadding;
+ for (Map.Entry> e : yAxisMap.entrySet()) {
+ Axis ya = e.getValue();
+ if (styler.getYAxisGroupPosistion(e.getKey()) == YAxisPosition.Right) {
+ continue;
+ }
+ if (e.getKey() == 0) {
+ continue;
+ }
+ ya.preparePaint();
+ Rectangle2D.Double bounds = (Rectangle2D.Double) ya.getBounds();
+ bounds.x = leftStart;
+ ya.paint(g);
+ double width = bounds.getWidth();
+ leftStart += chartPadding + width + tickMargin;
+ leftYAxisBounds.width += width;
+ leftCount++;
+ leftMainYAxis = ya;
+ }
+ if (styler.getYAxisGroupPosistion(0) != YAxisPosition.Right) {
+ yAxis.preparePaint();
+ Rectangle2D.Double bounds = (Rectangle2D.Double) yAxis.getBounds();
+ bounds.x = leftStart;
+ yAxis.paint(g);
+ double width = bounds.getWidth();
+ //leftStart += chartPadding + width + tickMargin;
+ leftYAxisBounds.width += width;
+ leftCount++;
+ leftMainYAxis = yAxis;
+ }
+
+ if (leftCount > 1) {
+ leftYAxisBounds.width += (leftCount - 1) * chartPadding;
+ }
+ leftYAxisBounds.width += leftCount * tickMargin;
+ rightYAxisBounds.width = 0;
+ double legendWidth = 0;
+ if (styler.getLegendPosition() == LegendPosition.OutsideE && styler.isLegendVisible()) {
+ legendWidth = chart.getLegend().getBounds().getWidth() + styler.getChartPadding();
+ }
+ double rightEnd = chart.getWidth() - legendWidth - chartPadding;
+ rightYAxisBounds.x = rightEnd;
+ int rightCount = 0;
+ for (Map.Entry> e : yAxisMap.descendingMap().entrySet()) {
+ Axis ya = e.getValue();
+ if (styler.getYAxisGroupPosistion(e.getKey()) != YAxisPosition.Right) {
+ continue;
+ }
+ if (e.getKey() == 0) {
+ continue;
+ }
+ ya.preparePaint();
+ Rectangle2D.Double bounds = (Rectangle2D.Double) ya.getBounds();
+ double aproxWidth = bounds.getWidth();
+ double xOffset = rightEnd - aproxWidth;
+ bounds.x = xOffset;
+ rightYAxisBounds.x = xOffset;
+ ya.paint(g);
+ rightYAxisBounds.width += aproxWidth;
+ rightEnd -= chartPadding + aproxWidth + tickMargin;
+ rightCount++;
+ rightMainYAxis = ya;
+ }
+ if (styler.getYAxisGroupPosistion(0) == YAxisPosition.Right) {
+ yAxis.preparePaint();
+ Rectangle2D.Double bounds = (Rectangle2D.Double) yAxis.getBounds();
+ double aproxWidth = bounds.getWidth();
+ double xOffset = rightEnd - aproxWidth;
+ bounds.x = xOffset;
+ rightYAxisBounds.x = xOffset;
+ yAxis.paint(g);
+ rightYAxisBounds.width += aproxWidth;
+ //rightEnd -= chartPadding + aproxWidth + tickMargin;
+ rightCount++;
+ rightMainYAxis = yAxis;
+ }
+ if (leftMainYAxis == null) {
+ leftMainYAxis = yAxis;
+ }
+ if (rightMainYAxis == null) {
+ rightMainYAxis = yAxis;
+ }
+
+ if (rightCount > 1) {
+ rightYAxisBounds.width += (rightCount - 1) * chartPadding;
+ }
+ rightYAxisBounds.width += rightCount * tickMargin;
+ Rectangle2D.Double bounds = (java.awt.geom.Rectangle2D.Double) yAxis.getBounds();
+ leftYAxisBounds.x = chartPadding;
+ leftYAxisBounds.y = bounds.y;
+ leftYAxisBounds.height = bounds.height;
+ rightYAxisBounds.y = bounds.y;
+ rightYAxisBounds.height = bounds.height;
+ xAxis.preparePaint();
+ xAxis.paint(g);
+ }
+
+ public Axis getYAxis(int yIndex) {
+ return yAxisMap.get(yIndex);
+ }
+
+ public Axis getXAxis() {
+ return xAxis;
+ }
+
+ public Axis getYAxis() {
+ return yAxis;
+ }
+
+ public Rectangle2D.Double getLeftYAxisBounds() {
+ return leftYAxisBounds;
+ }
+
+ public Rectangle2D.Double getRightYAxisBounds() {
+ return rightYAxisBounds;
+ }
+
+ public Axis getLeftMainYAxis() {
+ return leftMainYAxis;
+ }
+
+ public Axis getRightMainYAxis() {
+ return rightMainYAxis;
+ }
+
+ public Map> getAxisLabelOverrideMap() {
+ return axisLabelOverrideMap;
+ }
+
+ private void prepareForPaint() {
+ boolean mainYAxisUsed = false;
+ if (chart.getSeriesMap() != null) {
+ for (S series : chart.getSeriesMap().values()) {
+ int yIndex = series.getYAxisGroup();
+ if (!mainYAxisUsed && yIndex == 0) {
+ mainYAxisUsed = true;
+ }
+ if (yAxisMap.containsKey(yIndex)) {
+ continue;
+ }
+ yAxisMap.put(yIndex, new Axis<>(chart, Direction.Y, yIndex));
+ }
+ }
+ xAxis.setDataType(null);
+ yAxis.setDataType(null);
+ for (S series : chart.getSeriesMap().values()) {
+ xAxis.setDataType(series.getxAxisDataType());
+ yAxis.setDataType(series.getyAxisDataType());
+ getYAxis(series.getYAxisGroup()).setDataType(series.getyAxisDataType());
+ if (!mainYAxisUsed) {
+ yAxis.setDataType(series.getyAxisDataType());
+ }
+ }
+ xAxis.resetMinMax();
+ for (Axis ya : yAxisMap.values()) {
+ ya.resetMinMax();
+ }
+ if (chart.getSeriesMap() == null || chart.getSeriesMap().size() < 1) {
+ xAxis.addMinMax(-1, 1);
+ for (Axis ya : yAxisMap.values()) {
+ ya.addMinMax(-1, 1);
+ }
+ } else {
+ int disabledCount = 0;
+ for (S series : chart.getSeriesMap().values()) {
+ if (!series.isEnabled()) {
+ disabledCount++;
+ continue;
+ }
+ xAxis.addMinMax(series.getXMin(), series.getXMax());
+ getYAxis(series.getYAxisGroup()).addMinMax(series.getYMin(), series.getYMax());
+ if (!mainYAxisUsed) {
+ yAxis.addMinMax(series.getYMin(), series.getYMax());
+ }
+ }
+ if (disabledCount == chart.getSeriesMap().values().size()) {
+ xAxis.addMinMax(-1, 1);
+ for (Axis ya : yAxisMap.values()) {
+ ya.addMinMax(-1, 1);
+ }
+ }
+ }
+ overrideMinMaxForXAxis();
+ for (Axis ya : yAxisMap.values()) {
+ overrideMinMaxForYAxis(ya);
+ }
+ if (chart.getStyler().isXAxisLogarithmic() && xAxis.getMin() <= 0.0) {
+ throw new IllegalArgumentException("Series data (accounting for error bars too) cannot be less or equal to zero for a logarithmic X-Axis");
+ }
+ if (chart.getStyler().isYAxisLogarithmic() && yAxis.getMin() <= 0.0) {
+ for (Axis ya : yAxisMap.values()) {
+ if (ya.getMin() <= 0.0) {
+ throw new IllegalArgumentException("Series data (accounting for error bars too) cannot be less or equal to zero for a logarithmic Y-Axis");
+ }
+ }
+ }
+ if (xAxis.getMin() == Double.POSITIVE_INFINITY || xAxis.getMax() == Double.POSITIVE_INFINITY) {
+ throw new IllegalArgumentException(
+ "Series data (accounting for error bars too) cannot be equal to Double.POSITIVE_INFINITY!!!");
+ }
+ for (Axis ya : yAxisMap.values()) {
+ if (ya.getMin() == Double.POSITIVE_INFINITY || ya.getMax() == Double.POSITIVE_INFINITY) {
+ throw new IllegalArgumentException(
+ "Series data (accounting for error bars too) cannot be equal to Double.POSITIVE_INFINITY!!!");
+ }
+ if (ya.getMin() == Double.NEGATIVE_INFINITY || ya.getMax() == Double.NEGATIVE_INFINITY) {
+ throw new IllegalArgumentException(
+ "Series data (accounting for error bars too) cannot be equal to Double.NEGATIVE_INFINITY!!!");
+ }
+ }
+
+ if (xAxis.getMin() == Double.NEGATIVE_INFINITY || xAxis.getMax() == Double.NEGATIVE_INFINITY) {
+ throw new IllegalArgumentException(
+ "Series data (accounting for error bars too) cannot be equal to Double.NEGATIVE_INFINITY!!!");
+ }
+ }
+
+ private void overrideMinMaxForXAxis() {
+ double overrideXAxisMinValue = xAxis.getMin();
+ double overrideXAxisMaxValue = xAxis.getMax();
+ if (chart.getStyler().getXAxisMin() != null) {
+ overrideXAxisMinValue = chart.getStyler().getXAxisMin();
+ }
+ if (chart.getStyler().getXAxisMax() != null) {
+ overrideXAxisMaxValue = chart.getStyler().getXAxisMax();
+ }
+ xAxis.setMin(overrideXAxisMinValue);
+ xAxis.setMax(overrideXAxisMaxValue);
+ }
+
+ private void overrideMinMaxForYAxis(Axis, ?> yAxis) {
+ double overrideYAxisMinValue = yAxis.getMin();
+ double overrideYAxisMaxValue = yAxis.getMax();
+ if (chart.getStyler() instanceof CategoryStyler) {
+ CategoryStyler categoryStyler = (CategoryStyler) chart.getStyler();
+ if (categoryStyler.getDefaultSeriesRenderStyle() == CategorySeriesRenderStyle.Bar) {
+ if (categoryStyler.isStacked()) {
+ AxesChartSeriesCategory axesChartSeries =
+ (AxesChartSeriesCategory) chart.getSeriesMap().values().iterator().next();
+ List> categories = (List>) axesChartSeries.getXData();
+ int numCategories = categories.size();
+ double[] accumulatedStackOffsetPos = new double[numCategories];
+ double[] accumulatedStackOffsetNeg = new double[numCategories];
+ for (S series : chart.getSeriesMap().values()) {
+ AxesChartSeriesCategory axesChartSeriesCategory = (AxesChartSeriesCategory) series;
+ if (!series.isEnabled()) {
+ continue;
+ }
+ int categoryCounter = 0;
+ for (Number next : axesChartSeriesCategory.getYData()) {
+ if (next == null) {
+ categoryCounter++;
+ continue;
+ }
+ if (next.doubleValue() > 0) {
+ accumulatedStackOffsetPos[categoryCounter] += next.doubleValue();
+ } else if (next.doubleValue() < 0) {
+ accumulatedStackOffsetNeg[categoryCounter] += next.doubleValue();
+ }
+ categoryCounter++;
+ }
+ }
+ double max = accumulatedStackOffsetPos[0];
+ for (int i = 1; i < accumulatedStackOffsetPos.length; i++) {
+ if (accumulatedStackOffsetPos[i] > max) {
+ max = accumulatedStackOffsetPos[i];
+ }
+ }
+ double min = accumulatedStackOffsetNeg[0];
+ for (int i = 1; i < accumulatedStackOffsetNeg.length; i++) {
+ if (accumulatedStackOffsetNeg[i] < min) {
+ min = accumulatedStackOffsetNeg[i];
+ }
+ }
+ overrideYAxisMaxValue = max;
+ overrideYAxisMinValue = min;
+ }
+ if (yAxis.getMin() > 0.0) {
+ overrideYAxisMinValue = 0.0;
+ }
+ if (yAxis.getMax() < 0.0) {
+ overrideYAxisMaxValue = 0.0;
+ }
+ }
+ }
+ if (chart.getStyler().getYAxisMin(yAxis.getYIndex()) != null) {
+ overrideYAxisMinValue = chart.getStyler().getYAxisMin(yAxis.getYIndex());
+ } else if (chart.getStyler().getYAxisMin() != null) {
+ overrideYAxisMinValue = chart.getStyler().getYAxisMin();
+ }
+ if (chart.getStyler().getYAxisMax(yAxis.getYIndex()) != null) {
+ overrideYAxisMaxValue = chart.getStyler().getYAxisMax(yAxis.getYIndex());
+ } else if (chart.getStyler().getYAxisMax() != null) {
+ overrideYAxisMaxValue = chart.getStyler().getYAxisMax();
+ }
+ yAxis.setMin(overrideYAxisMinValue);
+ yAxis.setMax(overrideYAxisMaxValue);
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java
new file mode 100644
index 0000000..ac19962
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTick.java
@@ -0,0 +1,62 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.ChartComponent;
+import org.xbib.graphics.chart.series.AxesChartSeries;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+public class AxisTick implements ChartComponent {
+
+ private final Chart chart;
+
+ private final Direction direction;
+
+ private Rectangle2D bounds;
+
+ private final AxisTickLabels axisTickLabels;
+
+ private final AxisTickMarks axisTickMarks;
+
+ protected AxisTick(Chart chart, Direction direction, Axis yAxis) {
+ this.chart = chart;
+ this.direction = direction;
+ axisTickLabels = new AxisTickLabels<>(chart, direction, yAxis);
+ axisTickMarks = new AxisTickMarks<>(chart, direction, yAxis);
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ return bounds;
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ if (direction == Direction.Y && chart.getStyler().isYAxisTicksVisible()) {
+ axisTickLabels.paint(g);
+ axisTickMarks.paint(g);
+ bounds = new Rectangle2D.Double(axisTickLabels.getBounds().getX(),
+ axisTickLabels.getBounds().getY(),
+ axisTickLabels.getBounds().getWidth() + chart.getStyler().getAxisTickPadding() + axisTickMarks.getBounds().getWidth(),
+ axisTickMarks.getBounds().getHeight()
+ );
+ } else if (direction == Direction.X && chart.getStyler().isXAxisTicksVisible()) {
+ axisTickLabels.paint(g);
+ axisTickMarks.paint(g);
+ bounds = new Rectangle2D.Double(axisTickMarks.getBounds().getX(),
+ axisTickMarks.getBounds().getY(),
+ axisTickLabels.getBounds().getWidth(),
+ axisTickMarks.getBounds().getHeight() + chart.getStyler().getAxisTickPadding() + axisTickLabels.getBounds().getHeight()
+ );
+ } else {
+ bounds = new Rectangle2D.Double();
+ }
+ }
+
+ protected AxisTickLabels getAxisTickLabels() {
+ return axisTickLabels;
+ }
+
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java
new file mode 100644
index 0000000..16a2701
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculator.java
@@ -0,0 +1,87 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.text.Format;
+import java.util.LinkedList;
+import java.util.List;
+
+public abstract class AxisTickCalculator {
+
+ protected final Direction axisDirection;
+
+ protected final double workingSpace;
+
+ protected final double minValue;
+
+ protected final double maxValue;
+
+ protected final AxesChartStyler styler;
+
+ protected Format axisFormat;
+
+ protected List tickLocations = new LinkedList<>();
+
+ protected List tickLabels = new LinkedList<>();
+
+ public AxisTickCalculator(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) {
+ this.axisDirection = axisDirection;
+ this.workingSpace = workingSpace;
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+ this.styler = styler;
+ }
+
+ /**
+ * Gets the first position
+ *
+ * @param gridStep grid step
+ * @return first posiition
+ */
+ public double getFirstPosition(double gridStep) {
+ return minValue - (minValue % gridStep) - gridStep;
+ }
+
+ public List getTickLocations() {
+ return tickLocations;
+ }
+
+ public List getTickLabels() {
+ return tickLabels;
+ }
+
+ /**
+ * Given the generated tickLabels, will they fit side-by-side without overlapping each other and looking bad?
+ * Sometimes the given tickSpacingHint is simply too small.
+ *
+ * @param tickLabels tick lables
+ * @param tickSpacingHint tick psacing hint
+ * @return true if it fits
+ */
+ public boolean willLabelsFitInTickSpaceHint(List tickLabels, int tickSpacingHint) {
+ if (this.axisDirection == Direction.Y) {
+ return true;
+ }
+ String sampleLabel = " ";
+ for (String tickLabel : tickLabels) {
+ if (tickLabel != null && tickLabel.length() > sampleLabel.length()) {
+ sampleLabel = tickLabel;
+ }
+ }
+ TextLayout textLayout = new TextLayout(sampleLabel, styler.getAxisTickLabelsFont(), new FontRenderContext(null, true, false));
+ AffineTransform rot = styler.getXAxisLabelRotation() == 0 ? null : AffineTransform.getRotateInstance(-1 * Math.toRadians(styler.getXAxisLabelRotation()));
+ Shape shape = textLayout.getOutline(rot);
+ Rectangle2D rectangle = shape.getBounds();
+ double largestLabelWidth = rectangle.getWidth();
+ return (largestLabelWidth * 1.1 < tickSpacingHint);
+ }
+
+ public Format getAxisFormat() {
+ return axisFormat;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java
new file mode 100644
index 0000000..92ad620
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorCategory.java
@@ -0,0 +1,51 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.formatter.DateFormatter;
+import org.xbib.graphics.chart.formatter.NumberFormatter;
+import org.xbib.graphics.chart.formatter.StringFormatter;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis
+ * ticks for String axes
+ */
+public class AxisTickCalculatorCategory extends AxisTickCalculator {
+
+ public AxisTickCalculatorCategory(Direction axisDirection, double workingSpace, List> categories, DataType axisType, AxesChartStyler styler) {
+ super(axisDirection, workingSpace, Double.NaN, Double.NaN, styler);
+ calculate(categories, axisType);
+ }
+
+ private void calculate(List> categories, DataType axisType) {
+ // tick space - a percentage of the working space available for ticks
+ double tickSpace = styler.getPlotContentSize() * workingSpace; // in plot space
+ // where the tick should begin in the working space in pixels
+ double margin = (workingSpace - tickSpace) / 2.0;
+ // generate all tickLabels and tickLocations from the first to last position
+ double gridStep = (tickSpace / (double) categories.size());
+ double firstPosition = gridStep / 2.0;
+ // set up String formatters that may be encountered
+ if (axisType == DataType.String) {
+ axisFormat = new StringFormatter();
+ } else if (axisType == DataType.Number) {
+ axisFormat = new NumberFormatter(styler, axisDirection, minValue, maxValue);
+ } else if (axisType == DataType.Instant) {
+ axisFormat = new DateFormatter(styler.getDatePattern(), styler.getLocale());
+ }
+ int counter = 0;
+ for (Object category : categories) {
+ if (axisType == DataType.String) {
+ tickLabels.add(category.toString());
+ } else if (axisType == DataType.Number) {
+ tickLabels.add(axisFormat.format(new BigDecimal(category.toString()).doubleValue()));
+ } else if (axisType == DataType.Instant) {
+ tickLabels.add(axisFormat.format(category));
+ }
+ double tickLabelPosition = (int) (margin + firstPosition + gridStep * counter++);
+ tickLocations.add(tickLabelPosition);
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java
new file mode 100644
index 0000000..8b749e2
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorInstant.java
@@ -0,0 +1,161 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis
+ * ticks for date axes
+ */
+public class AxisTickCalculatorInstant extends AxisTickCalculator {
+
+ private static final long MILLIS_SCALE = TimeUnit.MILLISECONDS.toMillis(1L);
+ private static final long SEC_SCALE = TimeUnit.SECONDS.toMillis(1L);
+ private static final long MIN_SCALE = TimeUnit.MINUTES.toMillis(1L);
+ private static final long HOUR_SCALE = TimeUnit.HOURS.toMillis(1L);
+ private static final long DAY_SCALE = TimeUnit.DAYS.toMillis(1L);
+ private static final long MONTH_SCALE = TimeUnit.DAYS.toMillis(1L) * 30;
+ private static final long YEAR_SCALE = TimeUnit.DAYS.toMillis(1L) * 365;
+
+ private static final List timeSpans = new ArrayList<>();
+
+ static {
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 1, "ss.SSS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 2, "ss.SSS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 5, "ss.SSS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 10, "ss.SSS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 50, "ss.SS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 100, "ss.SS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 200, "ss.SS"));
+ timeSpans.add(new TimeSpan(MILLIS_SCALE, 500, "ss.SS"));
+
+ timeSpans.add(new TimeSpan(SEC_SCALE, 1, "ss.SS"));
+ timeSpans.add(new TimeSpan(SEC_SCALE, 2, "ss.S"));
+ timeSpans.add(new TimeSpan(SEC_SCALE, 5, "ss.S"));
+ timeSpans.add(new TimeSpan(SEC_SCALE, 10, "HH:mm:ss"));
+ timeSpans.add(new TimeSpan(SEC_SCALE, 15, "HH:mm:ss"));
+ timeSpans.add(new TimeSpan(SEC_SCALE, 20, "HH:mm:ss"));
+ timeSpans.add(new TimeSpan(SEC_SCALE, 30, "HH:mm:ss"));
+
+ timeSpans.add(new TimeSpan(MIN_SCALE, 1, "HH:mm:ss"));
+ timeSpans.add(new TimeSpan(MIN_SCALE, 2, "HH:mm:ss"));
+ timeSpans.add(new TimeSpan(MIN_SCALE, 5, "HH:mm:ss"));
+ timeSpans.add(new TimeSpan(MIN_SCALE, 10, "HH:mm"));
+ timeSpans.add(new TimeSpan(MIN_SCALE, 15, "HH:mm"));
+ timeSpans.add(new TimeSpan(MIN_SCALE, 20, "HH:mm"));
+ timeSpans.add(new TimeSpan(MIN_SCALE, 30, "HH:mm"));
+
+ timeSpans.add(new TimeSpan(HOUR_SCALE, 1, "HH:mm"));
+ timeSpans.add(new TimeSpan(HOUR_SCALE, 2, "HH:mm"));
+ timeSpans.add(new TimeSpan(HOUR_SCALE, 4, "HH:mm"));
+ timeSpans.add(new TimeSpan(HOUR_SCALE, 8, "HH:mm"));
+ timeSpans.add(new TimeSpan(HOUR_SCALE, 12, "HH:mm"));
+
+ timeSpans.add(new TimeSpan(DAY_SCALE, 1, "EEE HH:mm"));
+ timeSpans.add(new TimeSpan(DAY_SCALE, 2, "EEE HH:mm"));
+ timeSpans.add(new TimeSpan(DAY_SCALE, 3, "EEE HH:mm"));
+ timeSpans.add(new TimeSpan(DAY_SCALE, 5, "MM-dd"));
+ timeSpans.add(new TimeSpan(DAY_SCALE, 10, "MM-dd"));
+ timeSpans.add(new TimeSpan(DAY_SCALE, 15, "MM-dd"));
+
+ timeSpans.add(new TimeSpan(MONTH_SCALE, 1, "MM-dd"));
+ timeSpans.add(new TimeSpan(MONTH_SCALE, 2, "MM-dd"));
+ timeSpans.add(new TimeSpan(MONTH_SCALE, 3, "MM-dd"));
+ timeSpans.add(new TimeSpan(MONTH_SCALE, 4, "MM-dd"));
+ timeSpans.add(new TimeSpan(MONTH_SCALE, 6, "yyyy-MM"));
+
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 1, "yyyy-MM"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 2, "yyyy-MM"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 5, "yyyy"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 10, "yyyy"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 20, "yyyy"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 100, "yyyy"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 500, "yyyy"));
+ timeSpans.add(new TimeSpan(YEAR_SCALE, 1000, "yyyy"));
+ }
+
+ public AxisTickCalculatorInstant(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) {
+ super(axisDirection, workingSpace, minValue, maxValue, styler);
+ calculate();
+ }
+
+ private void calculate() {
+ double tickSpace = styler.getPlotContentSize() * workingSpace;
+ if (tickSpace < styler.getXAxisTickMarkSpacingHint()) {
+ return;
+ }
+ double margin = (workingSpace - tickSpace) / 2.0;
+ long span = (long) Math.abs(maxValue - minValue);
+ int tickSpacingHint = styler.getXAxisTickMarkSpacingHint();
+ int gridStepInChartSpace;
+ long gridStepHint = (long) (span / tickSpace * tickSpacingHint);
+ int index = 0;
+ for (int i = 0; i < timeSpans.size() - 1; i++) {
+ if (span < ((timeSpans.get(i).getUnitAmount() * timeSpans.get(i).getMagnitude() + timeSpans.get(i + 1).getUnitAmount() * timeSpans.get(i + 1).getMagnitude()) / 2.0)) {
+ index = i;
+ break;
+ }
+ }
+ String datePattern = timeSpans.get(index).getDatePattern();
+ for (int i = index - 1; i > 0; i--) {
+ if (gridStepHint > timeSpans.get(i).getUnitAmount() * timeSpans.get(i).getMagnitude()) {
+ index = i;
+ break;
+ }
+ }
+ index--;
+ do {
+ tickLabels.clear();
+ tickLocations.clear();
+ double gridStep = timeSpans.get(++index).getUnitAmount() * timeSpans.get(index).getMagnitude(); // in time units (ms)
+ gridStepInChartSpace = (int) (gridStep / span * tickSpace);
+ double firstPosition = getFirstPosition(gridStep);
+ if (styler.getDatePattern() != null) {
+ datePattern = styler.getDatePattern();
+ }
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(datePattern)
+ .withZone(styler.getZoneId());
+ for (double value = firstPosition; value <= maxValue + 2 * gridStep; value = value + gridStep) {
+ long l = Math.round(value);
+ tickLabels.add(dateTimeFormatter.format(Instant.ofEpochMilli(l)));
+ double tickLabelPosition = margin + ((value - minValue) / (maxValue - minValue) * tickSpace);
+ tickLocations.add(tickLabelPosition);
+ }
+ } while (!willLabelsFitInTickSpaceHint(tickLabels, gridStepInChartSpace));
+ }
+
+ static class TimeSpan {
+
+ private final long unitAmount;
+ private final int magnitude;
+ private final String datePattern;
+
+ TimeSpan(long unitAmount, int magnitude, String datePattern) {
+ this.unitAmount = unitAmount;
+ this.magnitude = magnitude;
+ this.datePattern = datePattern;
+ }
+
+ long getUnitAmount() {
+ return unitAmount;
+ }
+
+ int getMagnitude() {
+ return magnitude;
+ }
+
+ String getDatePattern() {
+ return datePattern;
+ }
+
+ @Override
+ public String toString() {
+ return "TimeSpan [unitAmount=" + unitAmount + ", magnitude=" + magnitude + ", datePattern=" + datePattern + "]";
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java
new file mode 100644
index 0000000..ae97bf4
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorLogarithmic.java
@@ -0,0 +1,62 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.formatter.NumberLogFormatter;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.math.BigDecimal;
+
+/**
+ * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis
+ * ticks for logarithmic axes
+ */
+public class AxisTickCalculatorLogarithmic extends AxisTickCalculator {
+
+ private final NumberLogFormatter numberLogFormatter;
+
+ public AxisTickCalculatorLogarithmic(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) {
+ super(axisDirection, workingSpace, minValue, maxValue, styler);
+ numberLogFormatter = new NumberLogFormatter(styler, axisDirection);
+ axisFormat = numberLogFormatter;
+ calculate();
+ }
+
+ private void calculate() {
+ if (minValue == maxValue) {
+ tickLabels.add(numberLogFormatter.format(BigDecimal.valueOf(maxValue)));
+ tickLocations.add(workingSpace / 2.0);
+ return;
+ }
+ double tickSpace = styler.getPlotContentSize() * workingSpace;
+ if (tickSpace < styler.getXAxisTickMarkSpacingHint()) {
+ return;
+ }
+ double margin = (workingSpace - tickSpace) / 2.0;
+ int logMin = (int) Math.floor(Math.log10(minValue));
+ int logMax = (int) Math.ceil(Math.log10(maxValue));
+ double firstPosition = pow(logMin);
+ double tickStep = pow(logMin - 1);
+ for (int i = logMin; i <= logMax; i++) {
+ for (double j = firstPosition; j <= pow(i) + .00000001; j = j + tickStep) {
+ if (j < minValue - tickStep) {
+ continue;
+ }
+ if (j > maxValue + tickStep) {
+ break;
+ }
+ if (Math.abs(Math.log10(j) % 1) < 0.00000001) {
+ tickLabels.add(numberLogFormatter.format(j));
+ } else {
+ tickLabels.add(null);
+ }
+ double tickLabelPosition = (int) (margin + (Math.log10(j) - Math.log10(minValue)) / (Math.log10(maxValue) - Math.log10(minValue)) * tickSpace);
+ tickLocations.add(tickLabelPosition);
+ }
+ tickStep = tickStep * pow(1);
+ firstPosition = tickStep + pow(i);
+ }
+ }
+
+ private static double pow(int exponent) {
+ return exponent > 0 ? Math.pow(10, exponent) : 1.0 / Math.pow(10, -1 * exponent);
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java
new file mode 100644
index 0000000..dbd95b3
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorNumber.java
@@ -0,0 +1,89 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.formatter.NumberFormatter;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * This class encapsulates the logic to generate the axis tick mark and axis tick label data for rendering the axis
+ * ticks for decimal axes
+ */
+public class AxisTickCalculatorNumber extends AxisTickCalculator {
+
+ private final NumberFormatter numberFormatter;
+
+ public AxisTickCalculatorNumber(Direction axisDirection, double workingSpace, double minValue, double maxValue, AxesChartStyler styler) {
+ super(axisDirection, workingSpace, minValue, maxValue, styler);
+ numberFormatter = new NumberFormatter(styler, axisDirection, minValue, maxValue);
+ axisFormat = numberFormatter;
+ calculate();
+ }
+
+ private void calculate() {
+ if (minValue == maxValue) {
+ tickLabels.add(numberFormatter.format(BigDecimal.valueOf(maxValue)));
+ tickLocations.add(workingSpace / 2.0);
+ return;
+ }
+ double tickSpace = styler.getPlotContentSize() * workingSpace;
+ if (tickSpace < styler.getXAxisTickMarkSpacingHint()) {
+ return;
+ }
+ double margin = (workingSpace - tickSpace) / 2.0;
+ double span = Math.abs(Math.min((maxValue - minValue), Double.MAX_VALUE - 1));
+ int tickSpacingHint = (axisDirection == Direction.X ? styler.getXAxisTickMarkSpacingHint() : styler.getYAxisTickMarkSpacingHint()) - 5;
+ if (axisDirection == Direction.Y && tickSpace < 160) {
+ tickSpacingHint = 25 - 5;
+ }
+ int gridStepInChartSpace;
+ do {
+ tickLabels.clear();
+ tickLocations.clear();
+ tickSpacingHint += 5;
+ double significand = span / tickSpace * tickSpacingHint;
+ int exponent = 0;
+ if (significand == 0) {
+ exponent = 1;
+ } else if (significand < 1) {
+ while (significand < 1) {
+ significand *= 10.0;
+ exponent--;
+ }
+ } else {
+ while (significand >= 10 || significand == Double.NEGATIVE_INFINITY) {
+ significand /= 10.0;
+ exponent++;
+ }
+ }
+ double gridStep;
+ if (significand > 7.5) {
+ gridStep = 10.0 * pow(exponent);
+ } else if (significand > 3.5) {
+ gridStep = 5.0 * pow(exponent);
+ } else if (significand > 1.5) {
+ gridStep = 2.0 * pow(exponent);
+ } else {
+ gridStep = pow(exponent);
+ }
+ gridStepInChartSpace = (int) (gridStep / span * tickSpace);
+ BigDecimal gridStepBigDecimal = BigDecimal.valueOf(gridStep);
+ BigDecimal cleanedGridStep = gridStepBigDecimal.setScale(10, RoundingMode.HALF_UP).stripTrailingZeros();
+ BigDecimal firstPosition = BigDecimal.valueOf(getFirstPosition(cleanedGridStep.doubleValue()));
+ BigDecimal cleanedFirstPosition = firstPosition.setScale(10, RoundingMode.HALF_UP).stripTrailingZeros();
+ for (BigDecimal value = cleanedFirstPosition;
+ value.compareTo(BigDecimal.valueOf(maxValue + 2 * cleanedGridStep.doubleValue())) < 0;
+ value = value.add(cleanedGridStep)) {
+ String tickLabel = numberFormatter.format(value);
+ tickLabels.add(tickLabel);
+ double tickLabelPosition = margin + ((value.doubleValue() - minValue) / (maxValue - minValue) * tickSpace);
+ tickLocations.add(tickLabelPosition);
+ }
+ } while (!willLabelsFitInTickSpaceHint(tickLabels, gridStepInChartSpace));
+ }
+
+ private static double pow(int exponent) {
+ return exponent > 0 ? Math.pow(10, exponent) : 1.0 / Math.pow(10, -1 * exponent);
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java
new file mode 100644
index 0000000..467f4f3
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickCalculatorOverride.java
@@ -0,0 +1,79 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.formatter.DateFormatter;
+import org.xbib.graphics.chart.formatter.NumberFormatter;
+import org.xbib.graphics.chart.formatter.StringFormatter;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.util.Map;
+
+/**
+ * This class encapsulates the logic to generate the axis tick mark and axis tick label data for
+ * rendering the axis ticks for given values and labels.
+ */
+class AxisTickCalculatorOverride extends AxisTickCalculator {
+
+ public AxisTickCalculatorOverride(Direction axisDirection,
+ double workingSpace,
+ double minValue,
+ double maxValue,
+ AxesChartStyler styler,
+ Map labelOverrideMap) {
+ super(axisDirection, workingSpace, minValue, maxValue, styler);
+ axisFormat = new NumberFormatter(styler, axisDirection, minValue, maxValue);
+ calculate(labelOverrideMap);
+ }
+
+ public AxisTickCalculatorOverride(Direction axisDirection,
+ double workingSpace,
+ AxesChartStyler styler,
+ Map markMap,
+ DataType axisType,
+ int categoryCount) {
+ super(axisDirection, workingSpace, Double.NaN, Double.NaN, styler);
+ if (axisType == DataType.String) {
+ axisFormat = new StringFormatter();
+ } else if (axisType == DataType.Number) {
+ axisFormat = new NumberFormatter(styler, axisDirection, minValue, maxValue);
+ } else if (axisType == DataType.Instant) {
+ axisFormat = new DateFormatter(styler.getDatePattern(), styler.getLocale());
+ }
+ calculateForCategory(markMap, categoryCount);
+ }
+
+ private void calculate(Map labelOverrideMap) {
+ if (minValue == maxValue) {
+ String label = labelOverrideMap.isEmpty() ? " " : labelOverrideMap.values().iterator().next().toString();
+ tickLabels.add(label);
+ tickLocations.add(workingSpace / 2.0);
+ return;
+ }
+ double tickSpace = styler.getPlotContentSize() * workingSpace;
+ if (tickSpace < styler.getXAxisTickMarkSpacingHint()) {
+ return;
+ }
+ double margin = (workingSpace - tickSpace) / 2.0;
+ for (Map.Entry entry : labelOverrideMap.entrySet()) {
+ Object value = entry.getValue();
+ String tickLabel = value == null ? " " : value.toString();
+ tickLabels.add(tickLabel);
+ double tickLabelPosition =
+ margin + ((entry.getKey() - minValue) / (maxValue - minValue) * tickSpace);
+ tickLocations.add(tickLabelPosition);
+ }
+ }
+
+ private void calculateForCategory(Map locationLabelMap, int categoryCount) {
+ double tickSpace = styler.getPlotContentSize() * workingSpace;
+ double margin = (workingSpace - tickSpace) / 2.0;
+ double gridStep = (tickSpace / categoryCount);
+ double firstPosition = gridStep / 2.0;
+ for (Map.Entry entry : locationLabelMap.entrySet()) {
+ Object value = entry.getValue();
+ String tickLabel = value == null ? " " : value.toString();
+ tickLabels.add(tickLabel);
+ double tickLabelPosition = margin + firstPosition + gridStep * entry.getKey();
+ tickLocations.add(tickLabelPosition);
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java
new file mode 100644
index 0000000..352a49e
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickLabels.java
@@ -0,0 +1,168 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.ChartComponent;
+import org.xbib.graphics.chart.series.AxesChartSeries;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Axis tick labels.
+ */
+public class AxisTickLabels implements ChartComponent {
+
+ private final Chart chart;
+
+ private final Direction direction;
+
+ private final Axis yAxis;
+
+ private Rectangle2D bounds;
+
+ protected AxisTickLabels(Chart chart, Direction direction, Axis yAxis) {
+ this.chart = chart;
+ this.direction = direction;
+ this.yAxis = yAxis;
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ return bounds;
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ ST styler = chart.getStyler();
+ g.setFont(styler.getAxisTickLabelsFont());
+ g.setColor(styler.getAxisTickLabelsColor());
+ if (direction == Direction.Y && chart.getStyler().isYAxisTicksVisible()) {
+ boolean onRight = styler.getYAxisGroupPosistion(yAxis.getYIndex()) == YAxisPosition.Right;
+ double xOffset;
+ if (onRight) {
+ xOffset = yAxis.getBounds().getX() + (styler.isYAxisTicksVisible() ?
+ styler.getAxisTickMarkLength() + styler.getAxisTickPadding() : 0);
+ } else {
+ double xWidth = yAxis.getAxisTitle().getBounds().getWidth();
+ xOffset = yAxis.getAxisTitle().getBounds().getX() + xWidth;
+ }
+ double yOffset = yAxis.getBounds().getY();
+ double height = yAxis.getBounds().getHeight();
+ double maxTickLabelWidth = 0;
+ Map axisLabelTextLayouts = new HashMap<>();
+ for (int i = 0; i < chart.getYAxis().getAxisTickCalculator().getTickLabels().size(); i++) {
+ String tickLabel = chart.getYAxis().getAxisTickCalculator().getTickLabels().get(i);
+ double tickLocation = chart.getYAxis().getAxisTickCalculator().getTickLocations().get(i);
+ double flippedTickLocation = yOffset + height - tickLocation;
+ if (tickLabel != null && !tickLabel.isEmpty() &&
+ flippedTickLocation > yOffset &&
+ flippedTickLocation < yOffset + height) {
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout axisLabelTextLayout = new TextLayout(tickLabel, styler.getAxisTickLabelsFont(), frc);
+ Rectangle2D tickLabelBounds = axisLabelTextLayout.getBounds();
+ double boundWidth = tickLabelBounds.getWidth();
+ if (boundWidth > maxTickLabelWidth) {
+ maxTickLabelWidth = boundWidth;
+ }
+ axisLabelTextLayouts.put(tickLocation, axisLabelTextLayout);
+ }
+ }
+ for (Map.Entry entry : axisLabelTextLayouts.entrySet()) {
+ Double tickLocation = entry.getKey();
+ TextLayout axisLabelTextLayout = axisLabelTextLayouts.get(tickLocation);
+ Shape shape = axisLabelTextLayout.getOutline(null);
+ Rectangle2D tickLabelBounds = shape.getBounds();
+ double flippedTickLocation = yOffset + height - tickLocation;
+ AffineTransform orig = g.getTransform();
+ AffineTransform at = new AffineTransform();
+ double boundWidth = tickLabelBounds.getWidth();
+ double xPos;
+ switch (chart.getStyler().getYAxisLabelAlignment()) {
+ case Right:
+ xPos = xOffset + maxTickLabelWidth - boundWidth;
+ break;
+ case Centre:
+ xPos = xOffset + (maxTickLabelWidth - boundWidth) / 2;
+ break;
+ case Left:
+ default:
+ xPos = xOffset;
+ }
+ at.translate(xPos, flippedTickLocation + tickLabelBounds.getHeight() / 2.0);
+ g.transform(at);
+ g.fill(shape);
+ g.setTransform(orig);
+ }
+ bounds = new Rectangle2D.Double(xOffset, yOffset, maxTickLabelWidth, height);
+ } else if (direction == Direction.X && chart.getStyler().isXAxisTicksVisible()) {
+ double xOffset = chart.getXAxis().getBounds().getX();
+ double yOffset = chart.getXAxis().getAxisTitle().getBounds().getY();
+ double width = chart.getXAxis().getBounds().getWidth();
+ double maxTickLabelHeight = 0;
+ int maxTickLabelY = 0;
+ for (int i = 0; i < chart.getXAxis().getAxisTickCalculator().getTickLabels().size(); i++) {
+ String tickLabel = chart.getXAxis().getAxisTickCalculator().getTickLabels().get(i);
+ double tickLocation = chart.getXAxis().getAxisTickCalculator().getTickLocations().get(i);
+ double shiftedTickLocation = xOffset + tickLocation;
+ if (tickLabel != null && !tickLabel.isEmpty() &&
+ shiftedTickLocation > xOffset &&
+ shiftedTickLocation < xOffset + width) {
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout textLayout = new TextLayout(tickLabel, chart.getStyler().getAxisTickLabelsFont(), frc);
+ AffineTransform rot = AffineTransform.getRotateInstance(-1 * Math.toRadians(chart.getStyler().getXAxisLabelRotation()), 0, 0);
+ Shape shape = textLayout.getOutline(rot);
+ Rectangle2D tickLabelBounds = shape.getBounds2D();
+ if (tickLabelBounds.getBounds().height > maxTickLabelY) {
+ maxTickLabelY = tickLabelBounds.getBounds().height;
+ }
+ }
+ }
+ for (int i = 0; i < chart.getXAxis().getAxisTickCalculator().getTickLabels().size(); i++) {
+ String tickLabel = chart.getXAxis().getAxisTickCalculator().getTickLabels().get(i);
+ double tickLocation = chart.getXAxis().getAxisTickCalculator().getTickLocations().get(i);
+ double shiftedTickLocation = xOffset + tickLocation;
+ if (tickLabel != null && shiftedTickLocation > xOffset && shiftedTickLocation < xOffset + width) {
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout textLayout = new TextLayout(tickLabel, styler.getAxisTickLabelsFont(), frc);
+ AffineTransform rot = AffineTransform.getRotateInstance(
+ -1 * Math.toRadians(styler.getXAxisLabelRotation()), 0, 0);
+ Shape shape = textLayout.getOutline(rot);
+ Rectangle2D tickLabelBounds = shape.getBounds2D();
+ AffineTransform orig = g.getTransform();
+ AffineTransform at = new AffineTransform();
+ double xPos;
+ switch (styler.getXAxisLabelAlignment()) {
+ case Left:
+ xPos = shiftedTickLocation;
+ break;
+ case Right:
+ xPos = shiftedTickLocation - tickLabelBounds.getWidth();
+ break;
+ case Centre:
+ default:
+ xPos = shiftedTickLocation - tickLabelBounds.getWidth() / 2.0;
+ }
+ double shiftX = -1 * tickLabelBounds.getX() * Math.sin(Math.toRadians(chart.getStyler().getXAxisLabelRotation()));
+ double shiftY = -1 * (tickLabelBounds.getY() + tickLabelBounds.getHeight());
+ at.translate(xPos + shiftX, yOffset + shiftY);
+ g.transform(at);
+ g.fill(shape);
+ g.setTransform(orig);
+ if (tickLabelBounds.getHeight() > maxTickLabelHeight) {
+ maxTickLabelHeight = tickLabelBounds.getHeight();
+ }
+ }
+ }
+ bounds = new Rectangle2D.Double(xOffset, yOffset - maxTickLabelHeight, width, maxTickLabelHeight);
+ } else {
+ bounds = new Rectangle2D.Double();
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java
new file mode 100644
index 0000000..d0ce02e
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTickMarks.java
@@ -0,0 +1,101 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.ChartComponent;
+import org.xbib.graphics.chart.series.AxesChartSeries;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Axis tick marks. This includes the little tick marks and the line that hugs the plot area.
+ */
+public class AxisTickMarks implements ChartComponent {
+
+ private final Chart chart;
+
+ private final Direction direction;
+
+ private final Axis yAxis;
+
+ private Rectangle2D bounds;
+
+ protected AxisTickMarks(Chart chart, Direction direction, Axis yAxis) {
+ this.chart = chart;
+ this.direction = direction;
+ this.yAxis = yAxis;
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ return bounds;
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ ST styler = chart.getStyler();
+ g.setColor(styler.getAxisTickMarksColor());
+ g.setStroke(styler.getAxisTickMarksStroke());
+ if (direction == Direction.Y && chart.getStyler().isYAxisTicksVisible()) {
+ int axisTickMarkLength = styler.getAxisTickMarkLength();
+ boolean onRight = styler.getYAxisGroupPosistion(yAxis.getYIndex()) == YAxisPosition.Right;
+ Rectangle2D yAxisBounds = yAxis.getBounds();
+ Rectangle2D axisTickLabelBounds = yAxis.getAxisTick().getAxisTickLabels().getBounds();
+ double xOffset;
+ double lineXOffset;
+ if (onRight) {
+ xOffset = axisTickLabelBounds.getX() - styler.getAxisTickPadding() - axisTickMarkLength;
+ lineXOffset = xOffset;
+ } else {
+ xOffset = axisTickLabelBounds.getX() + axisTickLabelBounds.getWidth() + styler.getAxisTickPadding();
+ lineXOffset = xOffset + axisTickMarkLength;
+ }
+ double yOffset = yAxisBounds.getY();
+ bounds = new Rectangle2D.Double(xOffset, yOffset, styler.getAxisTickMarkLength(), yAxis.getBounds().getHeight());
+ if (styler.isAxisTicksMarksVisible()) {
+ for (int i = 0; i < chart.getYAxis().getAxisTickCalculator().getTickLabels().size(); i++) {
+ double tickLocation = yAxis.getAxisTickCalculator().getTickLocations().get(i);
+ double flippedTickLocation = yOffset + yAxisBounds.getHeight() - tickLocation;
+ if (flippedTickLocation > bounds.getY() &&
+ flippedTickLocation < bounds.getY() + bounds.getHeight()) {
+ Shape line = new Line2D.Double(xOffset, flippedTickLocation, xOffset + axisTickMarkLength, flippedTickLocation);
+ g.draw(line);
+ }
+ }
+ }
+ if (styler.isAxisTicksLineVisible()) {
+ Shape line = new Line2D.Double(lineXOffset, yOffset, lineXOffset, yOffset + yAxisBounds.getHeight());
+ g.draw(line);
+ }
+ } else if (direction == Direction.X && styler.isXAxisTicksVisible()) {
+ int axisTickMarkLength = styler.getAxisTickMarkLength();
+ double xOffset = chart.getXAxis().getBounds().getX();
+ double yOffset = chart.getXAxis().getAxisTick().getAxisTickLabels().getBounds().getY() - styler.getAxisTickPadding();
+ bounds = new Rectangle2D.Double(xOffset, yOffset - axisTickMarkLength,
+ chart.getXAxis().getBounds().getWidth(), axisTickMarkLength);
+ if (styler.isAxisTicksMarksVisible()) {
+ for (int i = 0; i < chart.getXAxis().getAxisTickCalculator().getTickLabels().size(); i++) {
+ double tickLocation = chart.getXAxis().getAxisTickCalculator().getTickLocations().get(i);
+ double shiftedTickLocation = xOffset + tickLocation;
+ if (shiftedTickLocation > bounds.getX() &&
+ shiftedTickLocation < bounds.getX() + bounds.getWidth()) {
+ Shape line = new Line2D.Double(shiftedTickLocation, yOffset, xOffset + tickLocation, yOffset - chart.getStyler().getAxisTickMarkLength());
+ g.draw(line);
+ }
+ }
+ }
+ if (styler.isAxisTicksLineVisible()) {
+ g.setStroke(styler.getAxisTickMarksStroke());
+ g.drawLine((int) xOffset,
+ (int) (yOffset - axisTickMarkLength),
+ (int) (xOffset + chart.getXAxis().getBounds().getWidth()),
+ (int) (yOffset - axisTickMarkLength));
+ } else {
+ bounds = new Rectangle2D.Double();
+ }
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java
new file mode 100644
index 0000000..0010256
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/AxisTitle.java
@@ -0,0 +1,108 @@
+package org.xbib.graphics.chart.axis;
+
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.ChartComponent;
+import org.xbib.graphics.chart.series.Series;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * Axis title.
+ */
+public class AxisTitle implements ChartComponent {
+
+ private final Chart chart;
+ private final Direction direction;
+ private final Axis yAxis;
+ private final int yIndex;
+ private Rectangle2D bounds;
+
+ public AxisTitle(Chart chart, Direction direction, Axis yAxis, int yIndex) {
+ this.chart = chart;
+ this.direction = direction;
+ this.yAxis = yAxis;
+ this.yIndex = yIndex;
+ }
+
+ @Override
+ public Rectangle2D getBounds() {
+ return bounds;
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ bounds = new Rectangle2D.Double();
+ g.setColor(chart.getStyler().getChartFontColor());
+ g.setFont(chart.getStyler().getAxisTitleFont());
+ if (direction == Direction.Y) {
+ String yAxisTitle = chart.getYAxisGroupTitle(yIndex);
+ if (yAxisTitle != null && !yAxisTitle.trim().equalsIgnoreCase("") &&
+ chart.getStyler().isYAxisTitleVisible()) {
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout nonRotatedTextLayout = new TextLayout(chart.getyYAxisTitle(), chart.getStyler().getAxisTitleFont(), frc);
+ Rectangle2D nonRotatedRectangle = nonRotatedTextLayout.getBounds();
+ boolean onRight = chart.getStyler().getYAxisGroupPosistion(yAxis.getYIndex()) == YAxisPosition.Right;
+ int xOffset;
+ if (onRight) {
+ xOffset = (int) (yAxis.getAxisTick().getBounds().getX()
+ + yAxis.getAxisTick().getBounds().getWidth()
+ + nonRotatedRectangle.getHeight());
+ } else {
+ xOffset = (int) (yAxis.getBounds().getX() + nonRotatedRectangle.getHeight());
+ }
+ int yOffset = (int) ((yAxis.getBounds().getHeight() + nonRotatedRectangle.getWidth()) / 2.0
+ + yAxis.getBounds().getY());
+ AffineTransform rot = AffineTransform.getRotateInstance(-1 * Math.PI / 2, 0, 0);
+ Shape shape = nonRotatedTextLayout.getOutline(rot);
+ AffineTransform orig = g.getTransform();
+ AffineTransform at = new AffineTransform();
+ at.translate(xOffset, yOffset);
+ g.transform(at);
+ g.fill(shape);
+ g.setTransform(orig);
+ bounds = new Rectangle2D.Double(xOffset - nonRotatedRectangle.getHeight(),
+ yOffset - nonRotatedRectangle.getWidth(),
+ nonRotatedRectangle.getHeight() + chart.getStyler().getAxisTitlePadding(),
+ nonRotatedRectangle.getWidth());
+ } else {
+ bounds = new Rectangle2D.Double(yAxis.getBounds().getX(), yAxis.getBounds().getY(),0, yAxis.getBounds().getHeight());
+ }
+ } else {
+
+ if (chart.getXAxisTitle() != null &&
+ !chart.getXAxisTitle().trim().equalsIgnoreCase("") &&
+ chart.getStyler().isXAxisTitleVisible()) {
+
+ FontRenderContext frc = g.getFontRenderContext();
+ TextLayout textLayout = new TextLayout(chart.getXAxisTitle(), chart.getStyler().getAxisTitleFont(), frc);
+ Rectangle2D rectangle = textLayout.getBounds();
+
+ double xOffset = chart.getXAxis().getBounds().getX()
+ + (chart.getXAxis().getBounds().getWidth() - rectangle.getWidth()) / 2.0;
+ double yOffset = chart.getXAxis().getBounds().getY()
+ + chart.getXAxis().getBounds().getHeight()
+ - rectangle.getHeight();
+
+ Shape shape = textLayout.getOutline(null);
+ AffineTransform orig = g.getTransform();
+ AffineTransform at = new AffineTransform();
+ at.translate((float) xOffset, (float) (yOffset - rectangle.getY()));
+ g.transform(at);
+ g.fill(shape);
+ g.setTransform(orig);
+ bounds = new Rectangle2D.Double(xOffset, yOffset - chart.getStyler().getAxisTitlePadding(),
+ rectangle.getWidth(), rectangle.getHeight() + chart.getStyler().getAxisTitlePadding());
+ } else {
+ bounds = new Rectangle2D.Double(chart.getXAxis().getBounds().getX(),
+ chart.getXAxis().getBounds().getY() + chart.getXAxis().getBounds().getHeight(),
+ chart.getXAxis().getBounds().getWidth(), 0);
+ }
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java b/chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java
new file mode 100644
index 0000000..e4b0bea
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/DataType.java
@@ -0,0 +1,5 @@
+package org.xbib.graphics.chart.axis;
+
+public enum DataType {
+ Number, Instant, String;
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java b/chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java
new file mode 100644
index 0000000..e886b47
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/Direction.java
@@ -0,0 +1,5 @@
+package org.xbib.graphics.chart.axis;
+
+public enum Direction {
+ X, Y
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java b/chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java
new file mode 100644
index 0000000..8e3b8a1
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/TextAlignment.java
@@ -0,0 +1,5 @@
+package org.xbib.graphics.chart.axis;
+
+public enum TextAlignment {
+ Left, Centre, Right;
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java b/chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java
new file mode 100644
index 0000000..1e9ed0d
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/axis/YAxisPosition.java
@@ -0,0 +1,5 @@
+package org.xbib.graphics.chart.axis;
+
+public enum YAxisPosition {
+ Left, Right
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java
new file mode 100644
index 0000000..0af3128
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChart.java
@@ -0,0 +1,204 @@
+package org.xbib.graphics.chart.bubble;
+
+import org.xbib.graphics.chart.axis.AxisPair;
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.plot.AxesChartPlot;
+import org.xbib.graphics.chart.plot.ContentPlot;
+import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle;
+import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler;
+import org.xbib.graphics.chart.theme.Theme;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Ellipse2D;
+import java.util.List;
+import java.util.Map;
+
+public class BubbleChart extends Chart {
+
+ public BubbleChart(int width, int height) {
+ super(width, height, new BubbleStyler());
+ axisPair = new AxisPair<>(this);
+ plot = new BubblePlot<>(this);
+ legend = new BubbleLegend<>(this);
+ }
+
+ public BubbleChart(int width, int height, Theme theme) {
+ this(width, height);
+ styler.setTheme(theme);
+ }
+
+ public BubbleChart(BubbleChartBuilder chartBuilder) {
+ this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme());
+ setTitle(chartBuilder.getTitle());
+ setXAxisTitle(chartBuilder.xAxisTitle);
+ setYAxisTitle(chartBuilder.yAxisTitle);
+ }
+
+ public BubbleSeries addSeries(String seriesName,
+ List> xData,
+ List extends Number> yData,
+ List extends Number> bubbleData) {
+ sanityCheck(seriesName, xData, yData, bubbleData);
+ BubbleSeries series;
+ if (xData != null) {
+ if (xData.size() != yData.size()) {
+ throw new IllegalArgumentException("X and Y-Axis sizes are not the same");
+ }
+ series = new BubbleSeries(seriesName, xData, yData, bubbleData);
+ } else { // generate xData
+ series = new BubbleSeries(seriesName, getGeneratedData(yData.size()), yData, bubbleData);
+ }
+ seriesMap.put(seriesName, series);
+ return series;
+ }
+
+ private void sanityCheck(String seriesName, List> xData,
+ List extends Number> yData,
+ List extends Number> bubbleData) {
+ if (seriesMap.containsKey(seriesName)) {
+ throw new IllegalArgumentException("Series name >"
+ + seriesName
+ + "< has already been used. Use unique names for each series");
+ }
+ if (yData == null) {
+ throw new IllegalArgumentException("Y-Axis data cannot be null >" + seriesName);
+ }
+ if (yData.size() == 0) {
+ throw new IllegalArgumentException("Y-Axis data cannot be empty >" + seriesName);
+ }
+ if (bubbleData == null) {
+ throw new IllegalArgumentException("Bubble data cannot be null >" + seriesName);
+ }
+ if (bubbleData.size() == 0) {
+ throw new IllegalArgumentException("Bubble data cannot be empty >" + seriesName);
+ }
+ if (xData != null && xData.size() == 0) {
+ throw new IllegalArgumentException("X-Axis data cannot be empty >" + seriesName);
+ }
+ if (bubbleData.size() != yData.size()) {
+ throw new IllegalArgumentException("Bubble Data and Y-Axis sizes are not the same >" + seriesName);
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D g, int width, int height) {
+ setWidth(width);
+ setHeight(height);
+ for (BubbleSeries bubbleSeries : getSeriesMap().values()) {
+ BubbleSeriesRenderStyle seriesType =
+ bubbleSeries.getBubbleSeriesRenderStyle();
+ if (seriesType == null) {
+ bubbleSeries.setBubbleSeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle());
+ }
+ }
+ setSeriesStyles();
+ paintBackground(g);
+ axisPair.paint(g);
+ plot.paint(g);
+ chartTitle.paint(g);
+ legend.paint(g);
+ }
+
+ private void setSeriesStyles() {
+ SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler =
+ new SeriesColorMarkerLineStyleCycler(
+ getStyler().getSeriesColors(),
+ getStyler().getSeriesMarkers(),
+ getStyler().getSeriesLines());
+ for (BubbleSeries series : getSeriesMap().values()) {
+ SeriesColorMarkerLineStyle seriesColorMarkerLineStyle =
+ seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle();
+ if (series.getLineStyle() == null) {
+ series.setLineStyle(seriesColorMarkerLineStyle.getStroke());
+ }
+ if (series.getLineColor() == null) {
+ series.setLineColor(seriesColorMarkerLineStyle.getColor());
+ }
+ if (series.getFillColor() == null) {
+ series.setFillColor(seriesColorMarkerLineStyle.getColor());
+ }
+ }
+ }
+
+ private static class BubblePlot extends AxesChartPlot {
+
+ private BubblePlot(Chart chart) {
+ super(chart);
+ this.contentPlot = new BubbleContentPlot<>(chart);
+ }
+ }
+
+ private static class BubbleContentPlot extends ContentPlot {
+
+ private final ST stylerBubble;
+
+ private BubbleContentPlot(Chart chart) {
+ super(chart);
+ stylerBubble = chart.getStyler();
+ }
+
+ @Override
+ public void doPaint(Graphics2D g) {
+ double xTickSpace = stylerBubble.getPlotContentSize() * getBounds().getWidth();
+ double xLeftMargin = ((int) getBounds().getWidth() - xTickSpace) / 2.0;
+ double yTickSpace = stylerBubble.getPlotContentSize() * getBounds().getHeight();
+ double yTopMargin = ((int) getBounds().getHeight() - yTickSpace) / 2.0;
+ double xMin = chart.getXAxis().getMin();
+ double xMax = chart.getXAxis().getMax();
+ if (stylerBubble.isXAxisLogarithmic()) {
+ xMin = Math.log10(xMin);
+ xMax = Math.log10(xMax);
+ }
+ Map map = chart.getSeriesMap();
+ for (S series : map.values()) {
+ if (!series.isEnabled()) {
+ continue;
+ }
+ double yMin = chart.getYAxis(series.getYAxisGroup()).getMin();
+ double yMax = chart.getYAxis(series.getYAxisGroup()).getMax();
+ if (stylerBubble.isYAxisLogarithmic()) {
+ yMin = Math.log10(yMin);
+ yMax = Math.log10(yMax);
+ }
+ for (int i = 0; i < series.getXData().size(); i++) {
+ Double x = (Double) series.getXData().get(i);
+ if (stylerBubble.isXAxisLogarithmic()) {
+ x = Math.log10(x);
+ }
+ if (Double.isNaN((Double)series.getYData().get(i))) {
+ continue;
+ }
+ Double yOrig = (Double) series.getYData().get(i);
+ double y;
+ if (stylerBubble.isYAxisLogarithmic()) {
+ y = Math.log10(yOrig);
+ } else {
+ y = yOrig;
+ }
+ double xTransform = xLeftMargin + ((x - xMin) / (xMax - xMin) * xTickSpace);
+ double yTransform =
+ getBounds().getHeight() - (yTopMargin + (y - yMin) / (yMax - yMin) * yTickSpace);
+ if (Math.abs(xMax - xMin) / 5 == 0.0) {
+ xTransform = getBounds().getWidth() / 2.0;
+ }
+ if (Math.abs(yMax - yMin) / 5 == 0.0) {
+ yTransform = getBounds().getHeight() / 2.0;
+ }
+ double xOffset = getBounds().getX() + xTransform;
+ double yOffset = getBounds().getY() + yTransform;
+ if (series.getExtraValues() != null) {
+ Double bubbleSize = (Double) series.getExtraValues().get(i);
+ Shape bubble;
+ bubble = new Ellipse2D.Double(xOffset - bubbleSize / 2, yOffset - bubbleSize / 2, bubbleSize, bubbleSize);
+ g.setColor(series.getFillColor());
+ g.fill(bubble);
+ g.setColor(series.getLineColor());
+ g.setStroke(series.getLineStyle());
+ g.draw(bubble);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java
new file mode 100644
index 0000000..0cee777
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleChartBuilder.java
@@ -0,0 +1,27 @@
+package org.xbib.graphics.chart.bubble;
+
+import org.xbib.graphics.chart.ChartBuilder;
+
+public class BubbleChartBuilder extends ChartBuilder {
+
+ String xAxisTitle = "";
+ String yAxisTitle = "";
+
+ public BubbleChartBuilder() {
+ }
+
+ public BubbleChartBuilder xAxisTitle(String xAxisTitle) {
+ this.xAxisTitle = xAxisTitle;
+ return this;
+ }
+
+ public BubbleChartBuilder yAxisTitle(String yAxisTitle) {
+ this.yAxisTitle = yAxisTitle;
+ return this;
+ }
+
+ @Override
+ public BubbleChart build() {
+ return new BubbleChart(this);
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java
new file mode 100644
index 0000000..2074f44
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleLegend.java
@@ -0,0 +1,68 @@
+package org.xbib.graphics.chart.bubble;
+
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.legend.AbstractLegend;
+import org.xbib.graphics.chart.series.AxesChartSeries;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+import org.xbib.graphics.chart.legend.LegendLayout;
+import org.xbib.graphics.chart.legend.LegendRenderType;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Map;
+
+public class BubbleLegend extends AbstractLegend {
+
+ private final ST axesChartStyler;
+
+ public BubbleLegend(Chart chart) {
+ super(chart);
+ axesChartStyler = chart.getStyler();
+ }
+
+ @Override
+ public void doPaint(Graphics2D g) {
+ double startx = xOffset + chart.getStyler().getLegendPadding();
+ double starty = yOffset + chart.getStyler().getLegendPadding();
+ Object oldHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ Map map = chart.getSeriesMap();
+ for (S series : map.values()) {
+ if (series.isNotShownInLegend()) {
+ continue;
+ }
+ if (!series.isEnabled()) {
+ continue;
+ }
+ Map seriesTextBounds = getSeriesTextBounds(series);
+ float legendEntryHeight = getLegendEntryHeight(seriesTextBounds, BOX_SIZE);
+ Shape rectSmall = new Ellipse2D.Double(startx, starty, BOX_SIZE, BOX_SIZE);
+ g.setColor(series.getFillColor());
+ g.fill(rectSmall);
+ g.setStroke(series.getLineStyle());
+ g.setColor(series.getLineColor());
+ g.draw(rectSmall);
+ final double x = startx + BOX_SIZE + chart.getStyler().getLegendPadding();
+ paintSeriesText(g, seriesTextBounds, BOX_SIZE, x, starty);
+ if (chart.getStyler().getLegendLayout() == LegendLayout.Vertical) {
+ starty += legendEntryHeight + chart.getStyler().getLegendPadding();
+ } else {
+ int markerWidth = BOX_SIZE;
+ if (series.getLegendRenderType() == LegendRenderType.Line) {
+ markerWidth = chart.getStyler().getLegendSeriesLineLength();
+ }
+ float legendEntryWidth = getLegendEntryWidth(seriesTextBounds, markerWidth);
+ startx += legendEntryWidth + chart.getStyler().getLegendPadding();
+ }
+ }
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldHint);
+ }
+
+ @Override
+ public double getSeriesLegendRenderGraphicHeight(S series) {
+ return series.getLegendRenderType() == LegendRenderType.Box ? BOX_SIZE : axesChartStyler.getMarkerSize();
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java
new file mode 100644
index 0000000..808a476
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeries.java
@@ -0,0 +1,33 @@
+package org.xbib.graphics.chart.bubble;
+
+import org.xbib.graphics.chart.axis.DataType;
+import org.xbib.graphics.chart.series.NoMarkersSeries;
+import org.xbib.graphics.chart.legend.LegendRenderType;
+
+import java.util.List;
+
+/**
+ * A Series containing X, Y and bubble size data to be plotted on a Chart.
+ */
+public class BubbleSeries extends NoMarkersSeries {
+
+ private BubbleSeriesRenderStyle bubbleSeriesRenderStyle = null;
+
+ public BubbleSeries(String name, List> xData, List extends Number> yData, List extends Number> bubbleSizes) {
+ super(name, xData, yData, bubbleSizes, DataType.Number);
+ }
+
+ public BubbleSeriesRenderStyle getBubbleSeriesRenderStyle() {
+ return bubbleSeriesRenderStyle;
+ }
+
+ public void setBubbleSeriesRenderStyle(BubbleSeriesRenderStyle bubbleSeriesRenderStyle) {
+ this.bubbleSeriesRenderStyle = bubbleSeriesRenderStyle;
+ }
+
+ @Override
+ public LegendRenderType getLegendRenderType() {
+ return bubbleSeriesRenderStyle.getLegendRenderType();
+ }
+
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java
new file mode 100644
index 0000000..359d4ff
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleSeriesRenderStyle.java
@@ -0,0 +1,19 @@
+package org.xbib.graphics.chart.bubble;
+
+import org.xbib.graphics.chart.legend.LegendRenderable;
+import org.xbib.graphics.chart.legend.LegendRenderType;
+
+public enum BubbleSeriesRenderStyle implements LegendRenderable {
+ Round(LegendRenderType.Box);
+
+ private final LegendRenderType legendRenderType;
+
+ BubbleSeriesRenderStyle(LegendRenderType legendRenderType) {
+ this.legendRenderType = legendRenderType;
+ }
+
+ @Override
+ public LegendRenderType getLegendRenderType() {
+ return legendRenderType;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java
new file mode 100644
index 0000000..dbd4bd3
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/bubble/BubbleStyler.java
@@ -0,0 +1,44 @@
+package org.xbib.graphics.chart.bubble;
+
+import org.xbib.graphics.chart.style.AxesChartStyler;
+import org.xbib.graphics.chart.theme.Theme;
+
+public class BubbleStyler extends AxesChartStyler {
+
+ private BubbleSeriesRenderStyle bubbleChartSeriesRenderStyle;
+
+ public BubbleStyler() {
+ this.setAllStyles();
+ super.setAllStyles();
+ }
+
+ @Override
+ protected void setAllStyles() {
+ bubbleChartSeriesRenderStyle = BubbleSeriesRenderStyle.Round; // set default to Round
+ }
+
+ public BubbleSeriesRenderStyle getDefaultSeriesRenderStyle() {
+ return bubbleChartSeriesRenderStyle;
+ }
+
+ /**
+ * Sets the default series render style for the chart (Round is the only one for now) You can
+ * override the series render style individually on each Series object.
+ *
+ * @param bubbleChartSeriesRenderStyle render style
+ */
+ public BubbleStyler setDefaultSeriesRenderStyle(BubbleSeriesRenderStyle bubbleChartSeriesRenderStyle) {
+ this.bubbleChartSeriesRenderStyle = bubbleChartSeriesRenderStyle;
+ return this;
+ }
+
+ /**
+ * Set the theme the styler should use
+ *
+ * @param theme theme
+ */
+ public void setTheme(Theme theme) {
+ this.theme = theme;
+ super.setAllStyles();
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java
new file mode 100644
index 0000000..113db2d
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChart.java
@@ -0,0 +1,706 @@
+package org.xbib.graphics.chart.category;
+
+import org.xbib.graphics.chart.axis.DataType;
+import org.xbib.graphics.chart.axis.Axis;
+import org.xbib.graphics.chart.axis.AxisPair;
+import org.xbib.graphics.chart.Chart;
+import org.xbib.graphics.chart.legend.MarkerLegend;
+import org.xbib.graphics.chart.plot.AxesChartPlot;
+import org.xbib.graphics.chart.plot.ContentPlot;
+import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyle;
+import org.xbib.graphics.chart.style.SeriesColorMarkerLineStyleCycler;
+import org.xbib.graphics.chart.theme.Theme;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.xbib.graphics.chart.category.CategorySeriesRenderStyle.SteppedBar;
+
+public class CategoryChart extends Chart {
+
+ public CategoryChart(int width, int height) {
+ super(width, height, new CategoryStyler());
+ axisPair = new AxisPair<>(this);
+ plot = new CategoryPlot<>(this);
+ legend = new MarkerLegend<>(this);
+ }
+
+ public CategoryChart(int width, int height, Theme theme) {
+ this(width, height);
+ styler.setTheme(theme);
+ }
+
+ public CategoryChart(CategoryChartBuilder chartBuilder) {
+ this(chartBuilder.getWidth(), chartBuilder.getHeight(), chartBuilder.getTheme());
+ setTitle(chartBuilder.getTitle());
+ setXAxisTitle(chartBuilder.getxAxisTitle());
+ setYAxisTitle(chartBuilder.getyAxisTitle());
+ }
+
+ /**
+ * Add a series for a Category type chart using using double arrays
+ *
+ * @param seriesName series name
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @return A Series object that you can set properties on
+ */
+ public CategorySeries addSeries(String seriesName, double[] xData, double[] yData) {
+ return addSeries(seriesName, xData, yData, null);
+ }
+
+ /**
+ * Add a series for a Category type chart using using double arrays with error bars
+ *
+ * @param seriesName series name
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @param errorBars the error bar data
+ * @return A Series object that you can set properties on
+ */
+ public CategorySeries addSeries(String seriesName, double[] xData, double[] yData, double[] errorBars) {
+ return addSeries(seriesName, listFromDoubleArray(xData), listFromDoubleArray(yData),
+ listFromDoubleArray(errorBars));
+ }
+
+ /**
+ * Add a series for a X-Y type chart using using int arrays
+ *
+ * @param seriesName series name
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @return A Series object that you can set properties on
+ */
+ public CategorySeries addSeries(String seriesName, int[] xData, int[] yData) {
+ return addSeries(seriesName, xData, yData, null);
+ }
+
+ /**
+ * Add a series for a X-Y type chart using using int arrays with error bars
+ *
+ * @param seriesName series name
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @param errorBars the error bar data
+ * @return A Series object that you can set properties on
+ */
+ public CategorySeries addSeries(String seriesName, int[] xData, int[] yData, int[] errorBars) {
+ return addSeries(seriesName, listFromIntArray(xData), listFromIntArray(yData),
+ listFromIntArray(errorBars));
+ }
+
+ /**
+ * Add a series for a Category type chart using Lists
+ *
+ * @param seriesName series name
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @return A Series object that you can set properties on
+ */
+ public CategorySeries addSeries(String seriesName, List> xData, List extends Number> yData) {
+ return addSeries(seriesName, xData, yData, null);
+ }
+
+ /**
+ * Add a series for a Category type chart using Lists with error bars
+ *
+ * @param seriesName series name
+ * @param xData the X-Axis data
+ * @param yData the Y-Axis data
+ * @param errorBars the error bar data
+ * @return A Series object that you can set properties on
+ */
+ public CategorySeries addSeries(String seriesName,
+ List> xData,
+ List extends Number> yData,
+ List extends Number> errorBars) {
+ sanityCheck(seriesName, xData, yData, errorBars);
+ CategorySeries series;
+ if (xData != null) {
+ if (xData.size() != yData.size()) {
+ throw new IllegalArgumentException("X and Y-Axis sizes are not the same");
+ }
+ series = new CategorySeries(seriesName, xData, yData, errorBars, getDataType(xData));
+ } else {
+ series = new CategorySeries(seriesName, getGeneratedData(yData.size()), yData, errorBars, DataType.String);
+ }
+ seriesMap.put(seriesName, series);
+ return series;
+ }
+
+ private void sanityCheck(String seriesName, List> xData, List extends Number> yData,
+ List extends Number> errorBars) {
+ if (seriesMap.containsKey(seriesName)) {
+ throw new IllegalArgumentException("Series name >" + seriesName + "< has already been used");
+ }
+ if (yData == null) {
+ throw new IllegalArgumentException("Y-Axis data cannot be null");
+ }
+ if (yData.size() == 0) {
+ throw new IllegalArgumentException("Y-Axis data cannot be empty");
+ }
+ if (xData != null && xData.size() == 0) {
+ throw new IllegalArgumentException("X-Axis data cannot be empty");
+ }
+ if (errorBars != null && errorBars.size() != yData.size()) {
+ throw new IllegalArgumentException("Error bars and Y-Axis sizes are not the same");
+ }
+ }
+
+ @Override
+ public void paint(Graphics2D g, int width, int height) {
+ setWidth(width);
+ setHeight(height);
+ for (CategorySeries categorySeries : getSeriesMap().values()) {
+ CategorySeriesRenderStyle seriesType = categorySeries.getCategorySeriesRenderStyle();
+ if (seriesType == null) {
+ categorySeries.setCategorySeriesRenderStyle(getStyler().getDefaultSeriesRenderStyle());
+ }
+ }
+ setSeriesStyles();
+ paintBackground(g);
+ axisPair.paint(g);
+ plot.paint(g);
+ chartTitle.paint(g);
+ legend.paint(g);
+ }
+
+ public void setSeriesStyles() {
+ SeriesColorMarkerLineStyleCycler seriesColorMarkerLineStyleCycler =
+ new SeriesColorMarkerLineStyleCycler(getStyler().getSeriesColors(),
+ getStyler().getSeriesMarkers(), getStyler().getSeriesLines());
+ for (CategorySeries series : getSeriesMap().values()) {
+ SeriesColorMarkerLineStyle seriesColorMarkerLineStyle =
+ seriesColorMarkerLineStyleCycler.getNextSeriesColorMarkerLineStyle();
+ if (series.getLineStyle() == null) {
+ series.setLineStyle(seriesColorMarkerLineStyle.getStroke());
+ }
+ if (series.getLineColor() == null) {
+ series.setLineColor(seriesColorMarkerLineStyle.getColor());
+ }
+ if (series.getFillColor() == null) {
+ series.setFillColor(seriesColorMarkerLineStyle.getColor());
+ }
+ if (series.getMarker() == null) {
+ series.setMarker(seriesColorMarkerLineStyle.getMarker());
+ }
+ if (series.getMarkerColor() == null) {
+ series.setMarkerColor(seriesColorMarkerLineStyle.getColor());
+ }
+ }
+ }
+
+ private DataType getDataType(List> data) {
+ DataType axisType;
+ Iterator> itr = data.iterator();
+ Object dataPoint = itr.next();
+ if (dataPoint instanceof Number) {
+ axisType = DataType.Number;
+ } else if (dataPoint instanceof Instant) {
+ axisType = DataType.Instant;
+ } else if (dataPoint instanceof String) {
+ axisType = DataType.String;
+ } else {
+ throw new IllegalArgumentException("Series data must be either Number, Instant or String type");
+ }
+ return axisType;
+ }
+
+ private static class CategoryPlot extends AxesChartPlot {
+
+ private final ST categoryStyler;
+
+ private CategoryPlot(Chart chart) {
+ super(chart);
+ categoryStyler = chart.getStyler();
+ }
+
+ @Override
+ public void paint(Graphics2D g) {
+ if (CategorySeriesRenderStyle.Bar.equals(categoryStyler.getDefaultSeriesRenderStyle()) || CategorySeriesRenderStyle.Stick.equals(categoryStyler.getDefaultSeriesRenderStyle())) {
+ this.contentPlot = new ContentPlotCategoryBar<>(chart);
+ } else {
+ this.contentPlot = new ContentPlotCategoryLineAreaScatter<>(chart);
+ }
+ super.paint(g);
+ }
+ }
+
+ private static class ContentPlotCategoryBar extends ContentPlot {
+
+ private final ST stylerCategory;
+
+ ContentPlotCategoryBar(Chart chart) {
+ super(chart);
+ this.stylerCategory = chart.getStyler();
+ }
+
+ @Override
+ public void doPaint(Graphics2D g) {
+ double xTickSpace = stylerCategory.getPlotContentSize() * getBounds().getWidth();
+ double xLeftMargin = (getBounds().getWidth() - xTickSpace) / 2.0;
+ Map seriesMap = chart.getSeriesMap();
+ int numCategories = seriesMap.values().iterator().next().getXData().size();
+ double gridStep = xTickSpace / numCategories;
+ double yMin = chart.getYAxis().getMin();
+ double yMax = chart.getYAxis().getMax();
+ int chartForm; // 1=positive, -1=negative, 0=span
+ if (yMin > 0.0 && yMax > 0.0) {
+ chartForm = 1; // positive chart
+ } else if (yMin < 0.0 && yMax < 0.0) {
+ chartForm = -1; // negative chart
+ } else {
+ chartForm = 0;// span chart
+ }
+ double yTickSpace = stylerCategory.getPlotContentSize() * getBounds().getHeight();
+ double yTopMargin = (getBounds().getHeight() - yTickSpace) / 2.0;
+ int seriesCounter = 0;
+ double[] accumulatedStackOffsetPos = new double[numCategories];
+ double[] accumulatedStackOffsetNeg = new double[numCategories];
+ for (S series : seriesMap.values()) {
+ double previousX = -Double.MAX_VALUE;
+ double previousY = -Double.MAX_VALUE;
+ Iterator extends Number> yItr = series.getYData().iterator();
+ Iterator extends Number> ebItr = null;
+ Collection extends Number> errorBars = series.getExtraValues();
+ if (errorBars != null) {
+ ebItr = errorBars.iterator();
+ }
+ List steppedPath = null;
+ List steppedReturnPath = null;
+ int categoryCounter = 0;
+ while (yItr.hasNext()) {
+ Number next = yItr.next();
+ if (next == null) {
+ previousX = -Double.MAX_VALUE;
+ previousY = -Double.MAX_VALUE;
+ categoryCounter++;
+ continue;
+ }
+ double y = next.doubleValue();
+ double yTop = 0.0;
+ double yBottom = 0.0;
+ switch (chartForm) {
+ case 1: // positive chart
+ // check for points off the chart draw area due to a custom yMin
+ if (y < yMin) {
+ categoryCounter++;
+ continue;
+ }
+ yTop = y;
+ yBottom = yMin;
+ break;
+ case -1: // negative chart
+ // check for points off the chart draw area due to a custom yMin
+ if (y > yMax) {
+ categoryCounter++;
+ continue;
+ }
+ yTop = yMax;
+ yBottom = y;
+ break;
+ case 0: // span chart
+ if (y >= 0.0) { // positive
+ yTop = y;
+ if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Bar
+ || series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Stick
+ || series.getCategorySeriesRenderStyle()
+ == SteppedBar) {
+ yBottom = 0.0;
+ } else {
+ yBottom = y;
+ }
+ if (stylerCategory.isStacked()) {
+ yTop += accumulatedStackOffsetPos[categoryCounter];
+ yBottom += accumulatedStackOffsetPos[categoryCounter];
+ accumulatedStackOffsetPos[categoryCounter] += (yTop - yBottom);
+ }
+ } else {
+ if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Bar
+ || series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Stick
+ || series.getCategorySeriesRenderStyle()
+ == SteppedBar) {
+ yTop = 0.0;
+ } else {
+ yTop = y; // yTransform uses yTop, and for non-bars and stick, it's the same as
+ // yBottom.
+ }
+ yBottom = y;
+ if (stylerCategory.isStacked()) {
+ yTop -= accumulatedStackOffsetNeg[categoryCounter];
+ yBottom -= accumulatedStackOffsetNeg[categoryCounter];
+ accumulatedStackOffsetNeg[categoryCounter] += (yTop - yBottom);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ double yTransform = getBounds().getHeight() - (yTopMargin + (yTop - yMin) / (yMax - yMin) * yTickSpace);
+ double yOffset = getBounds().getY() + yTransform;
+
+ double zeroTransform = getBounds().getHeight() - (yTopMargin + (yBottom - yMin) / (yMax - yMin) * yTickSpace);
+ double zeroOffset = getBounds().getY() + zeroTransform;
+ double xOffset;
+ double barWidth;
+
+ {
+ double barWidthPercentage = stylerCategory.getAvailableSpaceFill();
+ // SteppedBars can not have any space between them
+ if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.SteppedBar)
+ barWidthPercentage = 1;
+
+ if (stylerCategory.isOverlapped() || stylerCategory.isStacked()) {
+
+ barWidth = gridStep * barWidthPercentage;
+ double barMargin = gridStep * (1 - barWidthPercentage) / 2;
+ xOffset = getBounds().getX() + xLeftMargin + gridStep * categoryCounter++ + barMargin;
+ } else {
+
+ barWidth = gridStep / chart.getSeriesMap().size() * barWidthPercentage;
+ double barMargin = gridStep * (1 - barWidthPercentage) / 2;
+ xOffset =
+ getBounds().getX()
+ + xLeftMargin
+ + gridStep * categoryCounter++
+ + seriesCounter * barWidth
+ + barMargin;
+ }
+ }
+
+ // SteppedBar. Partially drawn in loop, partially after loop.
+ if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.SteppedBar) {
+
+ double yCenter = zeroOffset;
+ double yTip = yOffset;
+ double stepLength = gridStep;
+
+ // yTip should be the value end, yCenter the center (0) end.
+ if (y < 0) {
+
+ yTip = zeroOffset;
+ yCenter = yOffset;
+ }
+
+ // Init in first iteration
+ if (steppedPath == null) {
+ steppedPath = new ArrayList<>();
+ steppedReturnPath = new ArrayList<>();
+ steppedPath.add(new Point2D.Double(xOffset, yCenter));
+ } else if (stylerCategory.isStacked()) {
+ // If a section of a stacked graph has changed from positive
+ // to negative or vice-versa, draw what we've stored up so far
+ // and resume with a blank slate.
+ if ((previousY > 0 && y < 0) || (previousY < 0 && y > 0)) {
+ drawStepBar(g, series, steppedPath, steppedReturnPath);
+
+ steppedPath.clear();
+ steppedReturnPath.clear();
+ steppedPath.add(new Point2D.Double(xOffset, yCenter));
+ }
+ }
+
+ if (!yItr.hasNext()) {
+
+ // Shift the far point of the final bar backwards
+ // by the same amount its start was shifted forward.
+ if (!(stylerCategory.isOverlapped() || stylerCategory.isStacked())) {
+
+ double singleBarStep = stepLength / (double) chart.getSeriesMap().size();
+ stepLength -= (seriesCounter * singleBarStep);
+ }
+ }
+
+ // Draw the vertical line to the new y position, and the horizontal flat of the bar.
+ steppedPath.add(new Point2D.Double(xOffset, yTip));
+ steppedPath.add(new Point2D.Double(xOffset + stepLength, yTip));
+
+ // Add the corresponding centerline (or equivalent) to the return path
+ // Could be simplfied and removed for non-stacked graphs
+ steppedReturnPath.add(new Point2D.Double(xOffset, yCenter));
+ steppedReturnPath.add(new Point2D.Double(xOffset + stepLength, yCenter));
+
+ previousY = y;
+ }
+
+ // paint series
+ if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Bar) {
+
+ // paint bar
+ Path2D.Double path = new Path2D.Double();
+ path.moveTo(xOffset, yOffset);
+ path.lineTo(xOffset + barWidth, yOffset);
+ path.lineTo(xOffset + barWidth, zeroOffset);
+ path.lineTo(xOffset, zeroOffset);
+ path.closePath();
+
+ g.setColor(series.getFillColor());
+ g.fill(path);
+ } else if (CategorySeriesRenderStyle.Stick.equals(series.getCategorySeriesRenderStyle())) {
+ if (series.getLineStyle() != Theme.Series.NONE_STROKE) {
+ g.setColor(series.getLineColor());
+ g.setStroke(series.getLineStyle());
+ Shape line = new Line2D.Double(xOffset, zeroOffset, xOffset, yOffset);
+ g.draw(line);
+ }
+
+ // paint marker
+ if (series.getMarker() != null) {
+ g.setColor(series.getMarkerColor());
+
+ if (y <= 0) {
+ series.getMarker().paint(g, xOffset, zeroOffset, stylerCategory.getMarkerSize());
+ } else {
+ series.getMarker().paint(g, xOffset, yOffset, stylerCategory.getMarkerSize());
+ }
+ }
+ } else {
+ if (series.getCategorySeriesRenderStyle() == CategorySeriesRenderStyle.Line) {
+ if (series.getLineStyle() != Theme.Series.NONE_STROKE) {
+ if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) {
+ g.setColor(series.getLineColor());
+ g.setStroke(series.getLineStyle());
+ Shape line = new Line2D.Double(previousX, previousY, xOffset + barWidth / 2, yOffset);
+ g.draw(line);
+ }
+ }
+ }
+ previousX = xOffset + barWidth / 2;
+ previousY = yOffset;
+
+ // paint marker
+ if (series.getMarker() != null) {
+ g.setColor(series.getMarkerColor());
+ series.getMarker().paint(g, previousX, previousY, stylerCategory.getMarkerSize());
+ }
+
+ }
+ // paint error bars
+ if (errorBars != null) {
+ double eb = ebItr.next().doubleValue();
+ // set error bar style
+ if (stylerCategory.isErrorBarsColorSeriesColor()) {
+ g.setColor(series.getLineColor());
+ } else {
+ g.setColor(stylerCategory.getErrorBarsColor());
+ }
+ g.setStroke(Theme.Strokes.ERROR_BARS);
+
+ // Top value
+ double errorBarLength = ((eb) / (yMax - yMin) * yTickSpace);
+ double topEBOffset = yOffset - errorBarLength;
+
+ // Bottom value
+ double bottomEBOffset = yOffset + errorBarLength;
+
+ // Draw it
+ double errorBarOffset = xOffset + barWidth / 2;
+ Shape line = new Line2D.Double(errorBarOffset, topEBOffset, errorBarOffset, bottomEBOffset);
+ g.draw(line);
+ line = new Line2D.Double(errorBarOffset - 3, bottomEBOffset, errorBarOffset + 3, bottomEBOffset);
+ g.draw(line);
+ line = new Line2D.Double(errorBarOffset - 3, topEBOffset, errorBarOffset + 3, topEBOffset);
+ g.draw(line);
+ }
+
+ }
+ // Final drawing of a steppedBar is done after the main loop,
+ // as it continues on null and we may end up missing the final iteration.
+ if (steppedPath != null && !steppedReturnPath.isEmpty()) {
+ drawStepBar(g, series, steppedPath, steppedReturnPath);
+ }
+ seriesCounter++;
+ }
+ }
+
+ private void drawStepBarLine(Graphics2D g, S series, Path2D.Double path) {
+ if (series.getLineColor() != null) {
+ g.setColor(series.getLineColor());
+ g.setStroke(series.getLineStyle());
+ g.draw(path);
+ }
+ }
+
+ private void drawStepBarFill(Graphics2D g, S series, Path2D.Double path) {
+ if (series.getFillColor() != null) {
+ g.setColor(series.getFillColor());
+ g.fill(path);
+ }
+ }
+
+ private void drawStepBar(Graphics2D g, S series, List path, List returnPath) {
+ Collections.reverse(returnPath);
+ returnPath.remove(returnPath.size() - 1);
+ path.addAll(returnPath);
+ Path2D.Double drawPath = new Path2D.Double();
+ Point2D.Double startPoint = path.remove(0);
+ drawPath.moveTo(startPoint.getX(), startPoint.getY());
+ for (Point2D.Double currentPoint : path) {
+ drawPath.lineTo(currentPoint.getX(), currentPoint.getY());
+ }
+ drawStepBarFill(g, series, drawPath);
+ drawPath.reset();
+ drawPath.moveTo(startPoint.getX(), startPoint.getY());
+ List linePath = path.subList(0, path.size() - returnPath.size() + 1);
+ for (Point2D.Double currentPoint : linePath) {
+ drawPath.lineTo(currentPoint.getX(), currentPoint.getY());
+ }
+ drawStepBarLine(g, series, drawPath);
+ }
+ }
+
+ private static class ContentPlotCategoryLineAreaScatter extends ContentPlot {
+
+ private final ST categoryStyler;
+
+ protected ContentPlotCategoryLineAreaScatter(Chart chart) {
+ super(chart);
+ this.categoryStyler = chart.getStyler();
+ }
+
+ @Override
+ public void doPaint(Graphics2D g) {
+ double xTickSpace = categoryStyler.getPlotContentSize() * getBounds().getWidth();
+ double xLeftMargin = ((int) getBounds().getWidth() - xTickSpace) / 2.0;
+ double yTickSpace = categoryStyler.getPlotContentSize() * getBounds().getHeight();
+ double yTopMargin =((int) getBounds().getHeight() - yTickSpace) / 2.0;
+ Map seriesMap = chart.getSeriesMap();
+ int numCategories = seriesMap.values().iterator().next().getXData().size();
+ double gridStep = xTickSpace / numCategories;
+ for (S series : seriesMap.values()) {
+ if (!series.isEnabled()) {
+ continue;
+ }
+ Axis, ?> yAxis = chart.getYAxis(series.getYAxisGroup());
+ double yMin = yAxis.getMin();
+ double yMax = yAxis.getMax();
+ if (categoryStyler.isYAxisLogarithmic()) {
+ yMin = Math.log10(yMin);
+ yMax = Math.log10(yMax);
+ }
+ Collection extends Number> yData = series.getYData();
+ double previousX = -Double.MAX_VALUE;
+ double previousY = -Double.MAX_VALUE;
+ Iterator extends Number> yItr = yData.iterator();
+ Iterator extends Number> ebItr = null;
+ Collection extends Number> errorBars = series.getExtraValues();
+ if (errorBars != null) {
+ ebItr = errorBars.iterator();
+ }
+ Path2D.Double path = null;
+ int categoryCounter = 0;
+ while (yItr.hasNext()) {
+ Number next = yItr.next();
+ if (next == null) {
+ // for area charts
+ closePath(g, path, previousX, yTopMargin);
+ path = null;
+ previousX = -Double.MAX_VALUE;
+ previousY = -Double.MAX_VALUE;
+ continue;
+ }
+ double yOrig = next.doubleValue();
+ double y;
+ if (categoryStyler.isYAxisLogarithmic()) {
+ y = Math.log10(yOrig);
+ } else {
+ y = yOrig;
+ }
+ double yTransform = getBounds().getHeight() - (yTopMargin + (y - yMin) / (yMax - yMin) * yTickSpace);
+ if (Math.abs(yMax - yMin) / 5 == 0.0) {
+ yTransform = getBounds().getHeight() / 2.0;
+ }
+ double xOffset = getBounds().getX() + xLeftMargin + categoryCounter++ * gridStep + gridStep / 2;
+ double yOffset = getBounds().getY() + yTransform;
+ if (CategorySeriesRenderStyle.Line.equals(series.getCategorySeriesRenderStyle()) ||
+ CategorySeriesRenderStyle.Area.equals(series.getCategorySeriesRenderStyle())) {
+ if (series.getLineStyle() != Theme.Series.NONE_STROKE) {
+ if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) {
+ g.setColor(series.getLineColor());
+ g.setStroke(series.getLineStyle());
+ Shape line = new Line2D.Double(previousX, previousY, xOffset, yOffset);
+ g.draw(line);
+ }
+ }
+ }
+ if (CategorySeriesRenderStyle.Area.equals(series.getCategorySeriesRenderStyle())) {
+ if (previousX != -Double.MAX_VALUE && previousY != -Double.MAX_VALUE) {
+ g.setColor(series.getFillColor());
+ double yBottomOfArea = getBounds().getY() + getBounds().getHeight() - yTopMargin;
+ if (path == null) {
+ path = new Path2D.Double();
+ path.moveTo(previousX, yBottomOfArea);
+ path.lineTo(previousX, previousY);
+ }
+ path.lineTo(xOffset, yOffset);
+ }
+ if (xOffset < previousX) {
+ throw new RuntimeException("X-Data must be in ascending order for Area Charts");
+ }
+ }
+ if (CategorySeriesRenderStyle.Stick.equals(series.getCategorySeriesRenderStyle())) {
+ if (series.getLineStyle() != Theme.Series.NONE_STROKE) {
+ double yBottomOfArea = getBounds().getY() + getBounds().getHeight() - yTopMargin;
+ g.setColor(series.getLineColor());
+ g.setStroke(series.getLineStyle());
+ Shape line = new Line2D.Double(xOffset, yBottomOfArea, xOffset, yOffset);
+ g.draw(line);
+ }
+ }
+ previousX = xOffset;
+ previousY = yOffset;
+ if (series.getMarker() != null) {
+ g.setColor(series.getMarkerColor());
+ series.getMarker().paint(g, xOffset, yOffset, categoryStyler.getMarkerSize());
+ }
+ if (errorBars != null) {
+ double eb = ebItr.next().doubleValue();
+ if (categoryStyler.isErrorBarsColorSeriesColor()) {
+ g.setColor(series.getLineColor());
+ } else {
+ g.setColor(categoryStyler.getErrorBarsColor());
+ }
+ g.setStroke(Theme.Strokes.ERROR_BARS);
+ double topValue;
+ if (categoryStyler.isYAxisLogarithmic()) {
+ topValue = yOrig + eb;
+ topValue = Math.log10(topValue);
+ } else {
+ topValue = y + eb;
+ }
+ double topEBTransform = getBounds().getHeight() - (yTopMargin + (topValue - yMin) / (yMax - yMin) * yTickSpace);
+ double topEBOffset = getBounds().getY() + topEBTransform;
+ double bottomValue;
+ if (categoryStyler.isYAxisLogarithmic()) {
+ bottomValue = yOrig - eb;
+ bottomValue = Math.log10(bottomValue);
+ } else {
+ bottomValue = y - eb;
+ }
+ double bottomEBTransform = getBounds().getHeight() - (yTopMargin + (bottomValue - yMin) / (yMax - yMin) * yTickSpace);
+ double bottomEBOffset = getBounds().getY() + bottomEBTransform;
+ Shape line = new Line2D.Double(xOffset, topEBOffset, xOffset, bottomEBOffset);
+ g.draw(line);
+ line = new Line2D.Double(xOffset - 3, bottomEBOffset, xOffset + 3, bottomEBOffset);
+ g.draw(line);
+ line = new Line2D.Double(xOffset - 3, topEBOffset, xOffset + 3, topEBOffset);
+ g.draw(line);
+ }
+ }
+ g.setColor(series.getFillColor());
+ closePath(g, path, previousX, yTopMargin);
+ }
+ }
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java
new file mode 100644
index 0000000..f36b48c
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryChartBuilder.java
@@ -0,0 +1,40 @@
+package org.xbib.graphics.chart.category;
+
+import org.xbib.graphics.chart.ChartBuilder;
+
+public class CategoryChartBuilder extends ChartBuilder {
+
+ private String xAxisTitle = "";
+ private String yAxisTitle = "";
+
+ public CategoryChartBuilder() {
+ }
+
+ public CategoryChartBuilder xAxisTitle(String xAxisTitle) {
+ this.xAxisTitle = xAxisTitle;
+ return this;
+ }
+
+ public CategoryChartBuilder yAxisTitle(String yAxisTitle) {
+ this.yAxisTitle = yAxisTitle;
+ return this;
+ }
+
+ public String getxAxisTitle() {
+ return xAxisTitle;
+ }
+
+ public String getyAxisTitle() {
+ return yAxisTitle;
+ }
+
+ /**
+ * return fully built Chart_Category
+ *
+ * @return a Chart_Category
+ */
+ @Override
+ public CategoryChart build() {
+ return new CategoryChart(this);
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java
new file mode 100644
index 0000000..71d3bb4
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeries.java
@@ -0,0 +1,36 @@
+package org.xbib.graphics.chart.category;
+
+import org.xbib.graphics.chart.axis.DataType;
+import org.xbib.graphics.chart.series.AxesChartSeriesCategory;
+import org.xbib.graphics.chart.legend.LegendRenderType;
+
+import java.util.List;
+
+/**
+ * A Series containing category data to be plotted on a Chart.
+ */
+public class CategorySeries extends AxesChartSeriesCategory {
+
+ private CategorySeriesRenderStyle categorySeriesRenderStyle = null;
+
+ public CategorySeries(String name, List> xData,
+ List extends Number> yData,
+ List extends Number> errorBars,
+ DataType axisType) {
+ super(name, xData, yData, errorBars, axisType);
+ }
+
+ public CategorySeriesRenderStyle getCategorySeriesRenderStyle() {
+ return categorySeriesRenderStyle;
+ }
+
+ public void setCategorySeriesRenderStyle(CategorySeriesRenderStyle chartXYSeriesRenderStyle) {
+ this.categorySeriesRenderStyle = chartXYSeriesRenderStyle;
+ }
+
+ @Override
+ public LegendRenderType getLegendRenderType() {
+ return categorySeriesRenderStyle.getLegendRenderType();
+ }
+
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java
new file mode 100644
index 0000000..4ef08f6
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategorySeriesRenderStyle.java
@@ -0,0 +1,30 @@
+package org.xbib.graphics.chart.category;
+
+import org.xbib.graphics.chart.legend.LegendRenderable;
+import org.xbib.graphics.chart.legend.LegendRenderType;
+
+public enum CategorySeriesRenderStyle implements LegendRenderable {
+
+ Line(LegendRenderType.Line),
+
+ Area(LegendRenderType.Line),
+
+ Scatter(LegendRenderType.Scatter),
+
+ Bar(LegendRenderType.BoxNoOutline),
+
+ SteppedBar(LegendRenderType.Box),
+
+ Stick(LegendRenderType.Line);
+
+ private final LegendRenderType legendRenderType;
+
+ CategorySeriesRenderStyle(LegendRenderType legendRenderType) {
+ this.legendRenderType = legendRenderType;
+ }
+
+ @Override
+ public LegendRenderType getLegendRenderType() {
+ return legendRenderType;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java
new file mode 100644
index 0000000..31183f8
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/category/CategoryStyler.java
@@ -0,0 +1,96 @@
+package org.xbib.graphics.chart.category;
+
+import org.xbib.graphics.chart.style.AxesChartStyler;
+import org.xbib.graphics.chart.theme.Theme;
+
+public class CategoryStyler extends AxesChartStyler {
+
+ private CategorySeriesRenderStyle categorySeriesRenderStyle;
+
+ private double availableSpaceFill;
+ private boolean isOverlapped;
+ private boolean isStacked;
+
+ public CategoryStyler() {
+
+ this.setAllStyles();
+ super.setAllStyles();
+ }
+
+ @Override
+ protected void setAllStyles() {
+
+ this.categorySeriesRenderStyle = CategorySeriesRenderStyle.Bar; // set default to bar
+
+ availableSpaceFill = theme.getAvailableSpaceFill();
+ isOverlapped = theme.isOverlapped();
+ }
+
+ /**
+ * Sets the available space for rendering each category as a percentage. For a bar chart with one
+ * series, it will be the width of the bar as a percentage of the maximum space alloted for the
+ * bar. If there are three series and three bars, the three bars will share the available space.
+ * This affects all category series render types, not only bar charts. Full width is 100%, i.e.
+ * 1.0
+ *
+ * @param availableSpaceFill space fill
+ */
+ public void setAvailableSpaceFill(double availableSpaceFill) {
+ this.availableSpaceFill = availableSpaceFill;
+ }
+
+ public double getAvailableSpaceFill() {
+
+ return availableSpaceFill;
+ }
+
+ /**
+ * Sets the default series render style for the chart (bar, stick, line, scatter, area, etc.) You can override the
+ * series render style individually on each Series object.
+ *
+ * @param categorySeriesRenderStyle render style
+ */
+ public void setDefaultSeriesRenderStyle(CategorySeriesRenderStyle categorySeriesRenderStyle) {
+ this.categorySeriesRenderStyle = categorySeriesRenderStyle;
+ }
+
+ public CategorySeriesRenderStyle getDefaultSeriesRenderStyle() {
+ return categorySeriesRenderStyle;
+ }
+
+ public boolean isOverlapped() {
+ return isOverlapped;
+ }
+
+ /**
+ * set whether or no bars are overlapped. Otherwise they are places side-by-side
+ *
+ * @param isOverlapped overlapped
+ */
+ public void setOverlapped(boolean isOverlapped) {
+ this.isOverlapped = isOverlapped;
+ }
+
+ public boolean isStacked() {
+ return isStacked;
+ }
+
+ /**
+ * Set whether or not series renderings (i.e. bars, stick, etc.) are stacked.
+ *
+ * @param isStacked stacked
+ */
+ public void setStacked(boolean isStacked) {
+ this.isStacked = isStacked;
+ }
+
+ /**
+ * Set the theme the styler should use
+ *
+ * @param theme theme
+ */
+ protected void setTheme(Theme theme) {
+ this.theme = theme;
+ super.setAllStyles();
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java
new file mode 100644
index 0000000..9308f22
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/DateFormatter.java
@@ -0,0 +1,37 @@
+package org.xbib.graphics.chart.formatter;
+
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+@SuppressWarnings("serial")
+public class DateFormatter extends Format {
+
+ private final String pattern;
+
+ private final Locale locale;
+
+ public DateFormatter(String pattern, Locale locale) {
+ this.pattern = pattern;
+ this.locale = locale;
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ if (obj instanceof ZonedDateTime) {
+ ZonedDateTime zdt = (ZonedDateTime) obj;
+ DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern)
+ .withLocale(locale);
+ toAppendTo.append(zdt.format(dtf));
+ }
+ return toAppendTo;
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java
new file mode 100644
index 0000000..96ebf6b
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberFormatter.java
@@ -0,0 +1,102 @@
+package org.xbib.graphics.chart.formatter;
+
+import org.xbib.graphics.chart.axis.Direction;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+@SuppressWarnings("serial")
+public class NumberFormatter extends Format {
+
+ private final AxesChartStyler styler;
+ private final Direction axisDirection;
+ private final double min;
+ private final double max;
+ private final NumberFormat numberFormat;
+
+ public NumberFormatter(AxesChartStyler styler, Direction axisDirection, double min, double max) {
+ this.styler = styler;
+ this.axisDirection = axisDirection;
+ this.min = min;
+ this.max = max;
+ numberFormat = NumberFormat.getNumberInstance(styler.getLocale());
+ }
+
+ public String getFormatPattern(BigDecimal value) {
+ if (value.compareTo(BigDecimal.ZERO) == 0) {
+ return "0";
+ }
+ double difference = max - min;
+ int placeOfDifference;
+ if (difference == 0.0) {
+ placeOfDifference = 0;
+ } else {
+ placeOfDifference = (int) Math.floor(Math.log(difference) / Math.log(10));
+ }
+ int placeOfValue;
+ if (value.doubleValue() == 0.0) {
+ placeOfValue = 0;
+ } else {
+ placeOfValue = (int) Math.floor(Math.log(value.doubleValue()) / Math.log(10));
+ }
+ if (placeOfDifference <= 4 && placeOfDifference >= -4) {
+ return getNormalDecimalPatternPositive(placeOfValue);
+ } else {
+ return getScientificDecimalPattern();
+ }
+ }
+
+ private String getNormalDecimalPatternPositive(int placeOfValue) {
+ int maxNumPlaces = 15;
+ StringBuilder sb = new StringBuilder();
+ for (int i = maxNumPlaces - 1; i >= -1 * maxNumPlaces; i--) {
+ if (i >= 0 && (i < placeOfValue)) {
+ sb.append("0");
+ } else if (i < 0 && (i > placeOfValue)) {
+ sb.append("0");
+ } else {
+ sb.append("#");
+ }
+ if (i % 3 == 0 && i > 0) {
+ sb.append(",");
+ }
+ if (i == 0) {
+ sb.append(".");
+ }
+ }
+ return sb.toString();
+ }
+
+ private String getScientificDecimalPattern() {
+ return "0.###############E0";
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ Number value = (Number) obj;
+ String decimalPattern;
+ if (axisDirection == Direction.X && styler.getXAxisDecimalPattern() != null) {
+ decimalPattern = styler.getXAxisDecimalPattern();
+ } else if (axisDirection == Direction.Y && styler.getYAxisDecimalPattern() != null) {
+ decimalPattern = styler.getYAxisDecimalPattern();
+ } else if (styler.getDecimalPattern() != null) {
+ decimalPattern = styler.getDecimalPattern();
+ } else {
+ decimalPattern = getFormatPattern(BigDecimal.valueOf(value.doubleValue()));
+ }
+ DecimalFormat normalFormat = (DecimalFormat) numberFormat;
+ normalFormat.applyPattern(decimalPattern);
+ toAppendTo.append(normalFormat.format(value));
+ return toAppendTo;
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java
new file mode 100644
index 0000000..827cbd8
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/NumberLogFormatter.java
@@ -0,0 +1,54 @@
+package org.xbib.graphics.chart.formatter;
+
+import org.xbib.graphics.chart.axis.Direction;
+import org.xbib.graphics.chart.style.AxesChartStyler;
+
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+@SuppressWarnings("serial")
+public class NumberLogFormatter extends Format {
+
+ private final AxesChartStyler styler;
+
+ private final Direction axisDirection;
+
+ private final NumberFormat numberFormat;
+
+ public NumberLogFormatter(AxesChartStyler styler, Direction axisDirection) {
+ this.styler = styler;
+ this.axisDirection = axisDirection;
+ numberFormat = NumberFormat.getNumberInstance(styler.getLocale());
+ }
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ double number = (Double) obj;
+ String decimalPattern;
+ if (axisDirection == Direction.X && styler.getXAxisDecimalPattern() != null) {
+ decimalPattern = styler.getXAxisDecimalPattern();
+ } else if (axisDirection == Direction.Y && styler.getYAxisDecimalPattern() != null) {
+ decimalPattern = styler.getYAxisDecimalPattern();
+ } else if (styler.getDecimalPattern() != null) {
+ decimalPattern = styler.getDecimalPattern();
+ } else {
+ if (Math.abs(number) > 1000.0 || Math.abs(number) < 0.001) {
+ decimalPattern = "0E0";
+ } else {
+ decimalPattern = "0.###";
+ }
+ }
+ DecimalFormat normalFormat = (DecimalFormat) numberFormat;
+ normalFormat.applyPattern(decimalPattern);
+ toAppendTo.append(normalFormat.format(number));
+ return toAppendTo;
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java b/chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java
new file mode 100644
index 0000000..91a6203
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/formatter/StringFormatter.java
@@ -0,0 +1,21 @@
+package org.xbib.graphics.chart.formatter;
+
+import java.text.FieldPosition;
+import java.text.Format;
+import java.text.ParsePosition;
+
+@SuppressWarnings("serial")
+public class StringFormatter extends Format {
+
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ String string = obj.toString();
+ toAppendTo.append(string);
+ return toAppendTo;
+ }
+
+ @Override
+ public Object parseObject(String source, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java b/chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java
new file mode 100644
index 0000000..d4481a8
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/BitmapFormat.java
@@ -0,0 +1,5 @@
+package org.xbib.graphics.chart.io;
+
+public enum BitmapFormat {
+ PNG, JPG, BMP, GIF
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java b/chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java
new file mode 100644
index 0000000..b2c1e06
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/CSVExporter.java
@@ -0,0 +1,91 @@
+package org.xbib.graphics.chart.io;
+
+import org.xbib.graphics.chart.xy.XYSeries;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * This class is used to export Chart data to a path.
+ */
+public class CSVExporter {
+
+ private final static String LF = System.getProperty("line.separator");
+
+ public static void writeCSVRows(XYSeries series, Path path) throws IOException {
+ try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(path),
+ StandardCharsets.UTF_8))) {
+ String csv = join(series.getXData()) + LF;
+ bufferedWriter.write(csv);
+ csv = join(series.getYData()) + LF;
+ bufferedWriter.write(csv);
+ if (series.getExtraValues() != null) {
+ csv = join(series.getExtraValues()) + LF;
+ bufferedWriter.write(csv);
+ }
+ }
+ }
+
+ public static void writeCSVColumns(XYSeries series, Path path) throws IOException {
+ try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(path),
+ StandardCharsets.UTF_8))) {
+ Collection> xData = series.getXData();
+ Collection extends Number> yData = series.getYData();
+ Collection extends Number> errorBarData = series.getExtraValues();
+ Iterator> itrx = xData.iterator();
+ Iterator extends Number> itry = yData.iterator();
+ Iterator extends Number> itrErrorBar = null;
+ if (errorBarData != null) {
+ itrErrorBar = errorBarData.iterator();
+ }
+ while (itrx.hasNext()) {
+ Number xDataPoint = (Number) itrx.next();
+ Number yDataPoint = itry.next();
+ Number errorBarValue = null;
+ if (itrErrorBar != null) {
+ errorBarValue = itrErrorBar.next();
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(xDataPoint).append(",");
+ sb.append(yDataPoint).append(",");
+ if (errorBarValue != null) {
+ sb.append(errorBarValue).append(",");
+ }
+ sb.append(LF);
+ bufferedWriter.write(sb.toString());
+ }
+ }
+ }
+
+ private static String join(Collection> collection) {
+ if (collection == null) {
+ return null;
+ }
+ Iterator> iterator = collection.iterator();
+ if (!iterator.hasNext()) {
+ return "";
+ }
+ Object first = iterator.next();
+ if (!iterator.hasNext()) {
+ return first == null ? "" : first.toString();
+ }
+ StringBuilder sb = new StringBuilder();
+ if (first != null) {
+ sb.append(first);
+ }
+ while (iterator.hasNext()) {
+ sb.append(",");
+ Object obj = iterator.next();
+ if (obj != null) {
+ sb.append(obj);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java b/chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java
new file mode 100644
index 0000000..777b899
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/CSVImporter.java
@@ -0,0 +1,109 @@
+package org.xbib.graphics.chart.io;
+
+import org.xbib.graphics.chart.theme.Theme;
+import org.xbib.graphics.chart.xy.XYChart;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+
+/**
+ * This class is used to create a Chart object from a folder containing one or more CSV files. The parent folder's name
+ * becomes the title of the chart. Each CSV file in the folder becomes a series on the chart.
+ * The CSV file's name becomes the series' name.
+ */
+public class CSVImporter {
+
+ public static XYChart getChartFromCSVDir(Path path, DataOrientation dataOrientation, int width, int height)
+ throws IOException {
+ return getChartFromCSVDir(path, dataOrientation, width, height, null);
+ }
+
+ public static XYChart getChartFromCSVDir(Path path, DataOrientation dataOrientation, int width, int height,
+ Theme theme) throws IOException {
+ XYChart chart = new XYChart(width, height, theme);
+ final PathMatcher pathMatcher = path.getFileSystem().getPathMatcher("glob:*.csv");
+ Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (pathMatcher.matches(file.getFileName())) {
+ String[] xAndYData;
+ if (dataOrientation == DataOrientation.Rows) {
+ xAndYData = getSeriesDataFromCSVRows(Files.newInputStream(file));
+ } else {
+ xAndYData = getSeriesDataFromCSVColumns(Files.newInputStream(file));
+ }
+ if (xAndYData[2] == null || xAndYData[2].trim().equalsIgnoreCase("")) {
+ chart.addSeries(file.toString().substring(0, file.toString().indexOf(".csv")),
+ getAxisData(xAndYData[0]), getAxisData(xAndYData[1]));
+ } else {
+ chart.addSeries(file.toString().substring(0, file.toString().indexOf(".csv")),
+ getAxisData(xAndYData[0]), getAxisData(xAndYData[1]), getAxisData(xAndYData[2]));
+ }
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ return chart;
+ }
+
+ private static String[] getSeriesDataFromCSVRows(InputStream inputStream) throws IOException {
+ String[] xAndYData = new String[3];
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,
+ Charset.forName("UTF-8")))) {
+ int counter = 0;
+ String line = null;
+ while ((line = bufferedReader.readLine()) != null) {
+ xAndYData[counter++] = line;
+ }
+ }
+ return xAndYData;
+ }
+
+ private static String[] getSeriesDataFromCSVColumns(InputStream inputStream) throws IOException {
+ String[] xAndYData = new String[3];
+ xAndYData[0] = "";
+ xAndYData[1] = "";
+ xAndYData[2] = "";
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,
+ Charset.forName("UTF-8")))) {
+ String line = null;
+ while ((line = bufferedReader.readLine()) != null) {
+ String[] dataArray = line.split(",");
+ xAndYData[0] += dataArray[0] + ",";
+ xAndYData[1] += dataArray[1] + ",";
+ if (dataArray.length > 2) {
+ xAndYData[2] += dataArray[2] + ",";
+ }
+ }
+ }
+ return xAndYData;
+ }
+
+ private static List getAxisData(String stringData) {
+ List axisData = new ArrayList<>();
+ String[] stringDataArray = stringData.split(",");
+ for (String dataPoint : stringDataArray) {
+ axisData.add(Double.parseDouble(dataPoint));
+ }
+ return axisData;
+ }
+
+ public enum DataOrientation {
+
+ Rows, Columns
+ }
+
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java b/chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java
new file mode 100644
index 0000000..c01323f
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/VectorGraphicsFormat.java
@@ -0,0 +1,5 @@
+package org.xbib.graphics.chart.io;
+
+public enum VectorGraphicsFormat {
+ EPS, PDF, SVG
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java
new file mode 100644
index 0000000..49f183f
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Document.java
@@ -0,0 +1,13 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.intermediate.CommandHandler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public interface Document extends CommandHandler {
+ void write(OutputStream out) throws IOException;
+
+ void close();
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java
new file mode 100644
index 0000000..8c0eb34
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/EPSGraphics2D.java
@@ -0,0 +1,42 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.eps.EPSProcessor;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+
+/**
+ * {@code Graphics2D} implementation that saves all operations to a string
+ * in the Encapsulated PostScript® (EPS) format.
+ */
+public class EPSGraphics2D extends ProcessingPipeline {
+
+ private final Processor processor;
+
+ /**
+ * Initializes a new VectorGraphics2D pipeline for translating Graphics2D
+ * commands to EPS data. The document dimensions must be specified as
+ * parameters.
+ *
+ * @param x Left offset.
+ * @param y Top offset
+ * @param width Width.
+ * @param height Height.
+ */
+ public EPSGraphics2D(double x, double y, double width, double height) {
+ super(x, y, width, height);
+ processor = new EPSProcessor();
+ /*
+ * The following are the default settings for the graphics state in an EPS file.
+ * Although they currently appear in the document output, they do not have to be set explicitly.
+ */
+ // TODO: Default graphics state does not need to be printed in the document
+ setColor(Color.BLACK);
+ setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f));
+ }
+
+ @Override
+ protected Processor getProcessor() {
+ return processor;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java
new file mode 100644
index 0000000..d418b6a
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/GraphicsState.java
@@ -0,0 +1,275 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.util.GraphicsUtils;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.Paint;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+import java.util.Objects;
+
+public class GraphicsState implements Cloneable {
+ /**
+ * Default background color.
+ */
+ public static final Color DEFAULT_BACKGROUND = Color.BLACK;
+ /**
+ * Default color.
+ */
+ public static final Color DEFAULT_COLOR = Color.WHITE;
+ /**
+ * Default clipping shape.
+ */
+ public static final Shape DEFAULT_CLIP = null;
+ /**
+ * Default composite mode.
+ */
+ public static final Composite DEFAULT_COMPOSITE = AlphaComposite.SrcOver;
+ /**
+ * Default font.
+ */
+ public static final Font DEFAULT_FONT = Font.decode(null);
+ /**
+ * Default paint.
+ */
+ public static final Color DEFAULT_PAINT = DEFAULT_COLOR;
+ /**
+ * Default stroke.
+ */
+ public static final Stroke DEFAULT_STROKE = new BasicStroke();
+ /**
+ * Default transformation.
+ */
+ public static final AffineTransform DEFAULT_TRANSFORM =
+ new AffineTransform();
+ /**
+ * Default XOR mode.
+ */
+ public static final Color DEFAULT_XOR_MODE = Color.BLACK;
+
+ /**
+ * Rendering hints.
+ */
+ private RenderingHints hints;
+ /**
+ * Current background color.
+ */
+ private Color background;
+ /**
+ * Current foreground color.
+ */
+ private Color color;
+ /**
+ * Shape used for clipping paint operations.
+ */
+ private Shape clip;
+ /**
+ * Method used for compositing.
+ */
+ private Composite composite;
+ /**
+ * Current font.
+ */
+ private Font font;
+ /**
+ * Paint used to fill shapes.
+ */
+ private Paint paint;
+ /**
+ * Stroke used for drawing shapes.
+ */
+ private Stroke stroke;
+ /**
+ * Current transformation matrix.
+ */
+ private AffineTransform transform;
+ /**
+ * XOR mode used for rendering.
+ */
+ private Color xorMode;
+
+ public GraphicsState() {
+ hints = new RenderingHints(null);
+ background = DEFAULT_BACKGROUND;
+ color = DEFAULT_COLOR;
+ clip = DEFAULT_CLIP;
+ composite = DEFAULT_COMPOSITE;
+ font = DEFAULT_FONT;
+ paint = DEFAULT_PAINT;
+ stroke = DEFAULT_STROKE;
+ transform = new AffineTransform(DEFAULT_TRANSFORM);
+ xorMode = DEFAULT_XOR_MODE;
+ }
+
+ private static Shape transformShape(Shape s, AffineTransform tx) {
+ if (s == null) {
+ return null;
+ }
+ if (tx == null || tx.isIdentity()) {
+ return GraphicsUtils.clone(s);
+ }
+ boolean isRectangle = s instanceof Rectangle2D;
+ int nonRectlinearTxMask = AffineTransform.TYPE_GENERAL_TRANSFORM |
+ AffineTransform.TYPE_GENERAL_ROTATION;
+ boolean isRectlinearTx = (tx.getType() & nonRectlinearTxMask) == 0;
+ if (isRectangle && isRectlinearTx) {
+ Rectangle2D rect = (Rectangle2D) s;
+ double[] corners = new double[]{
+ rect.getMinX(), rect.getMinY(),
+ rect.getMaxX(), rect.getMaxY()
+ };
+ tx.transform(corners, 0, corners, 0, 2);
+ rect = new Rectangle2D.Double();
+ rect.setFrameFromDiagonal(corners[0], corners[1], corners[2],
+ corners[3]);
+ return rect;
+ }
+ return tx.createTransformedShape(s);
+ }
+
+ private static Shape untransformShape(Shape s, AffineTransform tx) {
+ if (s == null) {
+ return null;
+ }
+ try {
+ AffineTransform inverse = tx.createInverse();
+ return transformShape(s, inverse);
+ } catch (NoninvertibleTransformException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ GraphicsState clone = (GraphicsState) super.clone();
+ clone.hints = (RenderingHints) hints.clone();
+ clone.clip = GraphicsUtils.clone(clip);
+ clone.transform = new AffineTransform(transform);
+ return clone;
+ }
+
+ public Shape transformShape(Shape shape) {
+ return transformShape(shape, transform);
+ }
+
+ public Shape untransformShape(Shape shape) {
+ return untransformShape(shape, transform);
+ }
+
+ public RenderingHints getHints() {
+ return hints;
+ }
+
+ public Color getBackground() {
+ return background;
+ }
+
+ public void setBackground(Color background) {
+ this.background = background;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ public Shape getClip() {
+ return untransformShape(clip);
+ }
+
+ public void setClip(Shape clip) {
+ this.clip = transformShape(clip);
+ }
+
+ public Composite getComposite() {
+ return composite;
+ }
+
+ public void setComposite(Composite composite) {
+ this.composite = composite;
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ public Paint getPaint() {
+ return paint;
+ }
+
+ public void setPaint(Paint paint) {
+ this.paint = paint;
+ }
+
+ public Stroke getStroke() {
+ return stroke;
+ }
+
+ public void setStroke(Stroke stroke) {
+ this.stroke = stroke;
+ }
+
+ public AffineTransform getTransform() {
+ return new AffineTransform(transform);
+ }
+
+ public void setTransform(AffineTransform tx) {
+ transform.setTransform(tx);
+ }
+
+ public Color getXorMode() {
+ return xorMode;
+ }
+
+ public void setXorMode(Color xorMode) {
+ this.xorMode = xorMode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof GraphicsState)) {
+ return false;
+ }
+ GraphicsState o = (GraphicsState) obj;
+ return !(!hints.equals(o.hints) || !background.equals(o.background) ||
+ !color.equals(o.color) || !composite.equals(o.composite) ||
+ !font.equals(o.font) || !paint.equals(o.paint) ||
+ !stroke.equals(o.stroke) || !transform.equals(o.transform) ||
+ !xorMode.equals(o.xorMode) ||
+ ((clip == null || o.clip == null) && clip != o.clip) ||
+ (clip != null && !clip.equals(o.clip)));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(hints, background, color, composite, font, paint,
+ stroke, transform, xorMode, clip);
+ }
+
+ public boolean isDefault() {
+ return hints.isEmpty() && background.equals(DEFAULT_BACKGROUND) &&
+ color.equals(DEFAULT_COLOR) && composite.equals(DEFAULT_COMPOSITE) &&
+ font.equals(DEFAULT_FONT) && paint.equals(DEFAULT_PAINT) &&
+ stroke.equals(DEFAULT_STROKE) && transform.equals(DEFAULT_TRANSFORM) &&
+ xorMode.equals(DEFAULT_XOR_MODE) && clip == DEFAULT_CLIP;
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java
new file mode 100644
index 0000000..9a0a8d2
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/PDFGraphics2D.java
@@ -0,0 +1,38 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.pdf.PDFProcessor;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+
+/**
+ * {@code Graphics2D} implementation that saves all operations to a string
+ * in the Portable Document Format (PDF).
+ */
+public class PDFGraphics2D extends ProcessingPipeline {
+ private final Processor processor;
+
+ /**
+ * Initializes a new VectorGraphics2D pipeline for translating Graphics2D
+ * commands to PDF data. The document dimensions must be specified as
+ * parameters.
+ *
+ * @param x Left offset.
+ * @param y Top offset
+ * @param width Width.
+ * @param height Height.
+ */
+ public PDFGraphics2D(double x, double y, double width, double height) {
+ super(x, y, width, height);
+ processor = new PDFProcessor();
+
+ // TODO: Default graphics state does not need to be printed in the document
+ setColor(Color.BLACK);
+ setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f, null, 0f));
+ }
+
+ @Override
+ protected Processor getProcessor() {
+ return processor;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java
new file mode 100644
index 0000000..300b779
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/ProcessingPipeline.java
@@ -0,0 +1,53 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.util.PageSize;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Base class for convenience implementations of {@code VectorGraphics2D}.
+ */
+public abstract class ProcessingPipeline extends VectorGraphics2D {
+ private final PageSize pageSize;
+
+ /**
+ * Initializes a processing pipeline.
+ *
+ * @param x Left offset.
+ * @param y Top offset
+ * @param width Width.
+ * @param height Height.
+ */
+ public ProcessingPipeline(double x, double y, double width, double height) {
+ pageSize = new PageSize(x, y, width, height);
+ }
+
+ public PageSize getPageSize() {
+ return pageSize;
+ }
+
+ protected abstract Processor getProcessor();
+
+ public void writeTo(OutputStream out) throws IOException {
+ Document doc = getProcessor().process(getCommands(), getPageSize());
+ doc.write(out);
+ }
+
+ public byte[] getBytes() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ writeTo(out);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return out.toByteArray();
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java
new file mode 100644
index 0000000..c2e40d9
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/Processor.java
@@ -0,0 +1,9 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.util.PageSize;
+
+public interface Processor {
+ Document process(Iterable> commands, PageSize pageSize);
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java
new file mode 100644
index 0000000..bd2084f
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SVGGraphics2D.java
@@ -0,0 +1,36 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.svg.SVGProcessor;
+
+import java.awt.Color;
+
+/**
+ * {@code Graphics2D} implementation that saves all operations to a string
+ * in the Scaled Vector Graphics (SVG) format.
+ */
+public class SVGGraphics2D extends ProcessingPipeline {
+ private final Processor processor;
+
+ /**
+ * Initializes a new VectorGraphics2D pipeline for translating Graphics2D
+ * commands to SVG data. The document dimensions must be specified as
+ * parameters.
+ *
+ * @param x Left offset.
+ * @param y Top offset
+ * @param width Width.
+ * @param height Height.
+ */
+ public SVGGraphics2D(double x, double y, double width, double height) {
+ super(x, y, width, height);
+ processor = new SVGProcessor();
+
+ // Make graphics state match default state of Graphics2D
+ setColor(Color.BLACK);
+ }
+
+ @Override
+ protected Processor getProcessor() {
+ return processor;
+ }
+}
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java
new file mode 100644
index 0000000..016cea3
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/SizedDocument.java
@@ -0,0 +1,19 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.util.PageSize;
+
+public abstract class SizedDocument implements Document {
+ private final PageSize pageSize;
+
+ public SizedDocument(PageSize pageSize) {
+ this.pageSize = pageSize;
+ }
+
+ public PageSize getPageSize() {
+ return pageSize;
+ }
+
+ public void close() {
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java
new file mode 100644
index 0000000..845bba0
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorGraphics2D.java
@@ -0,0 +1,894 @@
+package org.xbib.graphics.chart.io.vector;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawStringCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.RotateCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.ScaleCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetBackgroundCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetClipCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetCompositeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetFontCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetHintCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetXORModeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.ShearCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.TranslateCommand;
+import org.xbib.graphics.chart.io.vector.util.GraphicsUtils;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.RenderingHints.Key;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ImageObserver;
+import java.awt.image.RenderedImage;
+import java.awt.image.renderable.RenderableImage;
+import java.text.AttributedCharacterIterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Base for classes that want to implement vector export.
+ */
+public class VectorGraphics2D extends Graphics2D implements Cloneable {
+ /**
+ * List of operations that were performed on this graphics object and its
+ * derived objects.
+ */
+ private final List> commands;
+ /**
+ * Device configuration settings.
+ */
+ //private final GraphicsConfiguration deviceConfig;
+ /**
+ * Context settings used to render fonts.
+ */
+ private final FontRenderContext fontRenderContext;
+ /**
+ * Flag that tells whether this graphics object has been disposed.
+ */
+ private boolean disposed;
+
+ private GraphicsState state;
+
+ private Graphics2D _debug_validate_graphics;
+
+ public VectorGraphics2D() {
+ commands = new LinkedList>();
+ emit(new CreateCommand(this));
+ fontRenderContext = new FontRenderContext(null, false, true);
+
+ state = new GraphicsState();
+
+ BufferedImage _debug_validate_bimg = new BufferedImage(200, 250, BufferedImage.TYPE_INT_ARGB);
+ _debug_validate_graphics = (Graphics2D) _debug_validate_bimg.getGraphics();
+ _debug_validate_graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+
+ private static Shape intersectShapes(Shape s1, Shape s2) {
+ if (s1 instanceof Rectangle2D && s2 instanceof Rectangle2D) {
+ Rectangle2D r1 = (Rectangle2D) s1;
+ Rectangle2D r2 = (Rectangle2D) s2;
+ double x1 = Math.max(r1.getMinX(), r2.getMinX());
+ double y1 = Math.max(r1.getMinY(), r2.getMinY());
+ double x2 = Math.min(r1.getMaxX(), r2.getMaxX());
+ double y2 = Math.min(r1.getMaxY(), r2.getMaxY());
+
+ Rectangle2D intersection = new Rectangle2D.Double();
+ if ((x2 < x1) || (y2 < y1)) {
+ intersection.setFrameFromDiagonal(0, 0, 0, 0);
+ } else {
+ intersection.setFrameFromDiagonal(x1, y1, x2, y2);
+ }
+ return intersection;
+ } else {
+ Area intersection = new Area(s1);
+ intersection.intersect(new Area(s2));
+ return intersection;
+ }
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ try {
+ VectorGraphics2D clone = (VectorGraphics2D) super.clone();
+ clone.state = (GraphicsState) state.clone();
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void addRenderingHints(Map, ?> hints) {
+ if (isDisposed()) {
+ return;
+ }
+ for (Entry, ?> entry : hints.entrySet()) {
+ setRenderingHint((Key) entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clip(Shape s) {
+ _debug_validate_graphics.clip(s);
+ Shape clipOld = getClip();
+
+ Shape clip = getClip();
+ if ((clip != null) && (s != null)) {
+ s = intersectShapes(clip, s);
+ }
+ setClip(s);
+
+ Shape clipNew = getClip();
+ if ((clipNew == null || _debug_validate_graphics.getClip() == null) && clipNew != _debug_validate_graphics.getClip()) {
+ System.err.println("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + _debug_validate_graphics.getClip());
+ }
+ if (clipNew != null && !GraphicsUtils.equals(clipNew, _debug_validate_graphics.getClip())) {
+ System.err.println("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + _debug_validate_graphics.getClip());
+ }
+ }
+
+ @Override
+ public void draw(Shape s) {
+ if (isDisposed() || s == null) {
+ return;
+ }
+ emit(new DrawShapeCommand(s));
+
+ _debug_validate_graphics.draw(s);
+ }
+
+ @Override
+ public void drawGlyphVector(GlyphVector g, float x, float y) {
+ Shape s = g.getOutline(x, y);
+ draw(s);
+ }
+
+ @Override
+ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
+ BufferedImage bimg = getTransformedImage(img, xform);
+ return drawImage(bimg, bimg.getMinX(), bimg.getMinY(),
+ bimg.getWidth(), bimg.getHeight(), null, null);
+ }
+
+ /**
+ * Returns a transformed version of an image.
+ *
+ * @param image Image to be transformed
+ * @param xform Affine transform to be applied
+ * @return Image with transformed content
+ */
+ private BufferedImage getTransformedImage(Image image,
+ AffineTransform xform) {
+ Integer interpolationType =
+ (Integer) getRenderingHint(RenderingHints.KEY_INTERPOLATION);
+ if (RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
+ .equals(interpolationType)) {
+ interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
+ } else if (RenderingHints.VALUE_INTERPOLATION_BILINEAR
+ .equals(interpolationType)) {
+ interpolationType = AffineTransformOp.TYPE_BILINEAR;
+ } else {
+ interpolationType = AffineTransformOp.TYPE_BICUBIC;
+ }
+ AffineTransformOp op = new AffineTransformOp(xform, interpolationType);
+ BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image);
+ return op.filter(bufferedImage, null);
+ }
+
+ @Override
+ public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
+ if (op != null) {
+ img = op.filter(img, null);
+ }
+ drawImage(img, x, y, img.getWidth(), img.getHeight(), null, null);
+ }
+
+ @Override
+ public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
+ drawRenderedImage(img.createDefaultRendering(), xform);
+ }
+
+ @Override
+ public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
+ BufferedImage bimg = GraphicsUtils.toBufferedImage(img);
+ drawImage(bimg, xform, null);
+ }
+
+ @Override
+ public void drawString(String str, int x, int y) {
+ drawString(str, (float) x, (float) y);
+ }
+
+ @Override
+ public void drawString(String str, float x, float y) {
+ if (isDisposed() || str == null || str.trim().length() == 0) {
+ return;
+ }
+ boolean isTextAsVectors = false;
+ if (isTextAsVectors) {
+ TextLayout layout = new TextLayout(str, getFont(),
+ getFontRenderContext());
+ Shape s = layout.getOutline(
+ AffineTransform.getTranslateInstance(x, y));
+ fill(s);
+ } else {
+ emit(new DrawStringCommand(str, x, y));
+
+ _debug_validate_graphics.drawString(str, x, y);
+ }
+
+ }
+
+ @Override
+ public void drawString(AttributedCharacterIterator iterator, int x, int y) {
+ drawString(iterator, (float) x, (float) y);
+ }
+
+ @Override
+ public void drawString(AttributedCharacterIterator iterator, float x,
+ float y) {
+ // TODO Draw styled text
+ StringBuilder buf = new StringBuilder();
+ for (char c = iterator.first(); c != AttributedCharacterIterator.DONE;
+ c = iterator.next()) {
+ buf.append(c);
+ }
+ drawString(buf.toString(), x, y);
+ }
+
+ @Override
+ public void fill(Shape s) {
+ if (isDisposed() || s == null) {
+ return;
+ }
+ emit(new FillShapeCommand(s));
+
+ _debug_validate_graphics.fill(s);
+ }
+
+ @Override
+ public Color getBackground() {
+ return state.getBackground();
+ }
+
+ @Override
+ public void setBackground(Color color) {
+ if (isDisposed() || color == null || getColor().equals(color)) {
+ return;
+ }
+ emit(new SetBackgroundCommand(color));
+ state.setBackground(color);
+
+ _debug_validate_graphics.setBackground(color);
+ if (!getBackground().equals(_debug_validate_graphics.getBackground())) {
+ System.err.println("setBackground() validation failed");
+ }
+ }
+
+ @Override
+ public Composite getComposite() {
+ return state.getComposite();
+ }
+
+ @Override
+ public void setComposite(Composite comp) {
+ if (isDisposed()) {
+ return;
+ }
+ if (comp == null) {
+ throw new IllegalArgumentException("Cannot set a null composite.");
+ }
+ emit(new SetCompositeCommand(comp));
+ state.setComposite(comp);
+
+ _debug_validate_graphics.setComposite(comp);
+ if (!getComposite().equals(_debug_validate_graphics.getComposite())) {
+ System.err.println("setComposite() validation failed");
+ }
+ }
+
+ @Override
+ public GraphicsConfiguration getDeviceConfiguration() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FontRenderContext getFontRenderContext() {
+ return fontRenderContext;
+ }
+
+ @Override
+ public Paint getPaint() {
+ return state.getPaint();
+ }
+
+ @Override
+ public void setPaint(Paint paint) {
+ if (isDisposed() || paint == null) {
+ return;
+ }
+ if (paint instanceof Color) {
+ setColor((Color) paint);
+ return;
+ }
+ if (getPaint().equals(paint)) {
+ return;
+ }
+ emit(new SetPaintCommand(paint));
+ state.setPaint(paint);
+
+ _debug_validate_graphics.setPaint(paint);
+ if (!getPaint().equals(_debug_validate_graphics.getPaint())) {
+ System.err.println("setPaint() validation failed");
+ }
+ }
+
+ @Override
+ public Object getRenderingHint(Key hintKey) {
+ if (RenderingHints.KEY_ANTIALIASING.equals(hintKey)) {
+ return RenderingHints.VALUE_ANTIALIAS_OFF;
+ } else if (RenderingHints.KEY_TEXT_ANTIALIASING.equals(hintKey)) {
+ return RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
+ } else if (RenderingHints.KEY_FRACTIONALMETRICS.equals(hintKey)) {
+ return RenderingHints.VALUE_FRACTIONALMETRICS_ON;
+ }
+ return state.getHints().get(hintKey);
+ }
+
+ @Override
+ public RenderingHints getRenderingHints() {
+ return (RenderingHints) state.getHints().clone();
+ }
+
+ @Override
+ public void setRenderingHints(Map, ?> hints) {
+ if (isDisposed()) {
+ return;
+ }
+ state.getHints().clear();
+ for (Entry, ?> hint : hints.entrySet()) {
+ setRenderingHint((Key) hint.getKey(), hint.getValue());
+ }
+ }
+
+ @Override
+ public Stroke getStroke() {
+ return state.getStroke();
+ }
+
+ @Override
+ public void setStroke(Stroke s) {
+ if (isDisposed()) {
+ return;
+ }
+ if (s == null) {
+ throw new IllegalArgumentException("Cannot set a null stroke.");
+ }
+ emit(new SetStrokeCommand(s));
+ state.setStroke(s);
+
+ _debug_validate_graphics.setStroke(s);
+ if (!getStroke().equals(_debug_validate_graphics.getStroke())) {
+ System.err.println("setStroke() validation failed");
+ }
+ }
+
+ @Override
+ public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
+ Shape hitShape = s;
+ if (onStroke) {
+ hitShape = getStroke().createStrokedShape(hitShape);
+ }
+ hitShape = state.transformShape(hitShape);
+ boolean hit = hitShape.intersects(rect);
+
+ boolean _debug_hit = _debug_validate_graphics.hit(rect, s, onStroke);
+ if (hit != _debug_hit) {
+ System.err.println("setClip() validation failed");
+ }
+
+ return hit;
+ }
+
+ @Override
+ public void setRenderingHint(Key hintKey, Object hintValue) {
+ if (isDisposed()) {
+ return;
+ }
+ state.getHints().put(hintKey, hintValue);
+ emit(new SetHintCommand(hintKey, hintValue));
+ }
+
+ @Override
+ public AffineTransform getTransform() {
+ return new AffineTransform(state.getTransform());
+ }
+
+ @Override
+ public void setTransform(AffineTransform tx) {
+ if (isDisposed() || tx == null || state.getTransform().equals(tx)) {
+ return;
+ }
+ emit(new SetTransformCommand(tx));
+ state.setTransform(tx);
+
+ _debug_validate_graphics.setTransform(tx);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("setTransform() validation failed");
+ }
+ }
+
+ @Override
+ public void shear(double shx, double shy) {
+ if (shx == 0.0 && shy == 0.0) {
+ return;
+ }
+ AffineTransform txNew = getTransform();
+ txNew.shear(shx, shy);
+ emit(new ShearCommand(shx, shy));
+ state.setTransform(txNew);
+
+ _debug_validate_graphics.shear(shx, shy);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("shear() validation failed");
+ }
+ }
+
+ @Override
+ public void transform(AffineTransform tx) {
+ if (tx.isIdentity()) {
+ return;
+ }
+ AffineTransform txNew = getTransform();
+ txNew.concatenate(tx);
+ emit(new TransformCommand(tx));
+ state.setTransform(txNew);
+
+ _debug_validate_graphics.transform(tx);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("transform() validation failed");
+ }
+ }
+
+ @Override
+ public void translate(int x, int y) {
+ translate((double) x, (double) y);
+ }
+
+ @Override
+ public void translate(double tx, double ty) {
+ if (tx == 0.0 && ty == 0.0) {
+ return;
+ }
+ AffineTransform txNew = getTransform();
+ txNew.translate(tx, ty);
+ emit(new TranslateCommand(tx, ty));
+ state.setTransform(txNew);
+
+ _debug_validate_graphics.translate(tx, ty);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("translate() validation failed");
+ }
+ }
+
+ @Override
+ public void rotate(double theta) {
+ rotate(theta, 0.0, 0.0);
+ }
+
+ @Override
+ public void rotate(double theta, double x, double y) {
+ if (theta == 0.0) {
+ return;
+ }
+
+ AffineTransform txNew = getTransform();
+ if (x == 0.0 && y == 0.0) {
+ txNew.rotate(theta);
+ } else {
+ txNew.rotate(theta, x, y);
+ }
+
+ emit(new RotateCommand(theta, x, y));
+ state.setTransform(txNew);
+
+ if (x == 0.0 && y == 0.0) {
+ _debug_validate_graphics.rotate(theta);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("rotate(theta) validation failed");
+ }
+ } else {
+ _debug_validate_graphics.rotate(theta, x, y);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("rotate(theta,x,y) validation failed");
+ }
+ }
+ }
+
+ @Override
+ public void scale(double sx, double sy) {
+ if (sx == 1.0 && sy == 1.0) {
+ return;
+ }
+ AffineTransform txNew = getTransform();
+ txNew.scale(sx, sy);
+ emit(new ScaleCommand(sx, sy));
+ state.setTransform(txNew);
+
+ _debug_validate_graphics.scale(sx, sy);
+ if (!getTransform().equals(_debug_validate_graphics.getTransform())) {
+ System.err.println("scale() validation failed");
+ }
+ }
+
+ @Override
+ public void clearRect(int x, int y, int width, int height) {
+ Color colorOld = getColor();
+ setColor(getBackground());
+ fillRect(x, y, width, height);
+ setColor(colorOld);
+ }
+
+ @Override
+ public void clipRect(int x, int y, int width, int height) {
+ clip(new Rectangle(x, y, width, height));
+ }
+
+ @Override
+ public void copyArea(int x, int y, int width, int height, int dx, int dy) {
+ // TODO Implement
+ //throw new UnsupportedOperationException("copyArea() isn't supported by VectorGraphics2D.");
+ }
+
+ @Override
+ public Graphics create() {
+ if (isDisposed()) {
+ return null;
+ }
+ VectorGraphics2D clone = null;
+ try {
+ clone = (VectorGraphics2D) this.clone();
+ emit(new CreateCommand(clone));
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+
+ if (clone != null) {
+ clone._debug_validate_graphics = (Graphics2D) _debug_validate_graphics.create();
+ }
+
+ return clone;
+ }
+
+ @Override
+ public void dispose() {
+ if (isDisposed()) {
+ return;
+ }
+
+ emit(new DisposeCommand(this));
+
+ disposed = true;
+
+ _debug_validate_graphics.dispose();
+ }
+
+ @Override
+ public void drawArc(int x, int y, int width, int height, int startAngle,
+ int arcAngle) {
+ draw(new Arc2D.Double(x, y, width, height,
+ startAngle, arcAngle, Arc2D.OPEN));
+ }
+
+ @Override
+ public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
+ return drawImage(img, x, y, img.getWidth(observer),
+ img.getHeight(observer), null, observer);
+ }
+
+ @Override
+ public boolean drawImage(Image img, int x, int y, Color bgcolor,
+ ImageObserver observer) {
+ return drawImage(img, x, y, img.getWidth(observer),
+ img.getHeight(observer), bgcolor, observer);
+ }
+
+ @Override
+ public boolean drawImage(Image img, int x, int y, int width, int height,
+ ImageObserver observer) {
+ return drawImage(img, x, y, width, height, null, observer);
+ }
+
+ @Override
+ public boolean drawImage(Image img, int x, int y, int width, int height,
+ Color bgcolor, ImageObserver observer) {
+ if (isDisposed() || img == null) {
+ return true;
+ }
+
+ int imageWidth = img.getWidth(observer);
+ int imageHeight = img.getHeight(observer);
+ Rectangle bounds = new Rectangle(x, y, width, height);
+
+ if (bgcolor != null) {
+ // Fill rectangle with bgcolor
+ Color bgcolorOld = getColor();
+ setColor(bgcolor);
+ fill(bounds);
+ setColor(bgcolorOld);
+ }
+
+ emit(new DrawImageCommand(img, imageWidth, imageHeight, x, y, width, height));
+
+ _debug_validate_graphics.drawImage(img, x, y, width, height, bgcolor, observer);
+
+ return true;
+ }
+
+ @Override
+ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
+ return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
+ observer);
+ }
+
+ @Override
+ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
+ int sx1, int sy1, int sx2, int sy2, Color bgcolor,
+ ImageObserver observer) {
+ if (img == null) {
+ return true;
+ }
+
+ int sx = Math.min(sx1, sx2);
+ int sy = Math.min(sy1, sy2);
+ int sw = Math.abs(sx2 - sx1);
+ int sh = Math.abs(sy2 - sy1);
+ int dx = Math.min(dx1, dx2);
+ int dy = Math.min(dy1, dy2);
+ int dw = Math.abs(dx2 - dx1);
+ int dh = Math.abs(dy2 - dy1);
+
+ // Draw image on rectangle
+ BufferedImage bufferedImg = GraphicsUtils.toBufferedImage(img);
+ Image cropped = bufferedImg.getSubimage(sx, sy, sw, sh);
+ return drawImage(cropped, dx, dy, dw, dh, bgcolor, observer);
+ }
+
+ @Override
+ public void drawLine(int x1, int y1, int x2, int y2) {
+ draw(new Line2D.Double(x1, y1, x2, y2));
+ }
+
+ @Override
+ public void drawOval(int x, int y, int width, int height) {
+ draw(new Ellipse2D.Double(x, y, width, height));
+ }
+
+ @Override
+ public void drawPolygon(Polygon p) {
+ draw(p);
+ }
+
+ @Override
+ public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
+ draw(new Polygon(xPoints, yPoints, nPoints));
+ }
+
+ @Override
+ public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
+ Path2D p = new Path2D.Float();
+ for (int i = 0; i < nPoints; i++) {
+ if (i > 0) {
+ p.lineTo(xPoints[i], yPoints[i]);
+ } else {
+ p.moveTo(xPoints[i], yPoints[i]);
+ }
+ }
+ draw(p);
+ }
+
+ @Override
+ public void drawRect(int x, int y, int width, int height) {
+ draw(new Rectangle(x, y, width, height));
+ }
+
+ @Override
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight) {
+ draw(new RoundRectangle2D.Double(x, y, width, height,
+ arcWidth, arcHeight));
+ }
+
+ @Override
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle) {
+ fill(new Arc2D.Double(x, y, width, height,
+ startAngle, arcAngle, Arc2D.PIE));
+ }
+
+ @Override
+ public void fillOval(int x, int y, int width, int height) {
+ fill(new Ellipse2D.Double(x, y, width, height));
+ }
+
+ @Override
+ public void fillPolygon(Polygon p) {
+ fill(p);
+ }
+
+ @Override
+ public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
+ fill(new Polygon(xPoints, yPoints, nPoints));
+ }
+
+ @Override
+ public void fillRect(int x, int y, int width, int height) {
+ fill(new Rectangle(x, y, width, height));
+ }
+
+ @Override
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight) {
+ fill(new RoundRectangle2D.Double(x, y, width, height,
+ arcWidth, arcHeight));
+ }
+
+ @Override
+ public Shape getClip() {
+ return state.getClip();
+ }
+
+ @Override
+ public void setClip(Shape clip) {
+ if (isDisposed()) {
+ return;
+ }
+ emit(new SetClipCommand(clip));
+ state.setClip(clip);
+
+ _debug_validate_graphics.setClip(clip);
+ if (getClip() == null) {
+ if (_debug_validate_graphics.getClip() != null) {
+ System.err.printf("setClip() validation failed: clip=null, validation=%s\n", _debug_validate_graphics.getClip());
+ }
+ } else if (!GraphicsUtils.equals(getClip(), _debug_validate_graphics.getClip())) {
+ System.err.printf("setClip() validation failed: clip=%s, validation=%s\n", getClip(), _debug_validate_graphics.getClip());
+ }
+ }
+
+ @Override
+ public Rectangle getClipBounds() {
+ if (getClip() == null) {
+ return null;
+ }
+ return getClip().getBounds();
+ }
+
+ @Override
+ public Color getColor() {
+ return state.getColor();
+ }
+
+ @Override
+ public void setColor(Color c) {
+ if (isDisposed() || c == null || getColor().equals(c)) {
+ return;
+ }
+ emit(new SetColorCommand(c));
+ state.setColor(c);
+ state.setPaint(c);
+
+ _debug_validate_graphics.setColor(c);
+ if (!getColor().equals(_debug_validate_graphics.getColor())) {
+ System.err.println("setColor() validation failed");
+ }
+ }
+
+ @Override
+ public Font getFont() {
+ return state.getFont();
+ }
+
+ @Override
+ public void setFont(Font font) {
+ if (isDisposed() || (font != null && getFont().equals(font))) {
+ return;
+ }
+ emit(new SetFontCommand(font));
+ state.setFont(font);
+
+ _debug_validate_graphics.setFont(font);
+ if (!getFont().equals(_debug_validate_graphics.getFont())) {
+ System.err.println("setFont() validation failed");
+ }
+ }
+
+ @Override
+ public FontMetrics getFontMetrics(Font f) {
+ BufferedImage bi =
+ new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
+ Graphics g = bi.getGraphics();
+ FontMetrics fontMetrics = g.getFontMetrics(getFont());
+ g.dispose();
+ return fontMetrics;
+ }
+
+ @Override
+ public void setClip(int x, int y, int width, int height) {
+ setClip(new Rectangle(x, y, width, height));
+ }
+
+ @Override
+ public void setPaintMode() {
+ setComposite(AlphaComposite.SrcOver);
+
+ _debug_validate_graphics.setPaintMode();
+ }
+
+ public Color getXORMode() {
+ return state.getXorMode();
+ }
+
+ @Override
+ public void setXORMode(Color c1) {
+ if (isDisposed() || c1 == null) {
+ return;
+ }
+ emit(new SetXORModeCommand(c1));
+ state.setXorMode(c1);
+
+ _debug_validate_graphics.setXORMode(c1);
+ }
+
+ private void emit(Command> command) {
+ commands.add(command);
+ }
+
+ protected Iterable> getCommands() {
+ return commands;
+ }
+
+ protected boolean isDisposed() {
+ return disposed;
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java
new file mode 100644
index 0000000..8e8ae9f
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/VectorHints.java
@@ -0,0 +1,83 @@
+package org.xbib.graphics.chart.io.vector;
+
+import java.awt.RenderingHints;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class VectorHints {
+ public static final Key KEY_EXPORT = new Key(0, "Vector export mode");
+ public static final Object VALUE_EXPORT_READABILITY = new Value(KEY_EXPORT, 0, "Maximize readability for humans");
+ public static final Object VALUE_EXPORT_QUALITY = new Value(KEY_EXPORT, 1, "Maximize render quality");
+ public static final Object VALUE_EXPORT_SIZE = new Value(KEY_EXPORT, 2, "Minimize data size");
+ public static final Key KEY_TEXT = new Key(1, "Text export mode");
+ public static final Object VALUE_TEXT_DEFAULT = new Value(KEY_TEXT, 0, "Keep text");
+ public static final Object VALUE_TEXT_VECTOR = new Value(KEY_TEXT, 1, "Convert text to vector shapes");
+
+ protected VectorHints() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static class Key extends RenderingHints.Key {
+ private final String description;
+
+ public Key(int privateKey, String description) {
+ super(privateKey);
+ this.description = description;
+ }
+
+ public int getIndex() {
+ return intKey();
+ }
+
+ @Override
+ public boolean isCompatibleValue(Object val) {
+ return val instanceof Value && ((Value) val).isCompatibleKey(this);
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+
+ public static class Value {
+ private static final Set values = new HashSet();
+ private final Key key;
+ private final int index;
+ private final String description;
+
+ public Value(Key key, int index, String description) {
+ this.key = key;
+ this.index = index;
+ this.description = description;
+ register(this);
+ }
+
+ private synchronized static void register(Value value) {
+ String id = value.getId();
+ if (values.contains(id)) {
+ throw new ExceptionInInitializerError(
+ "Duplicate index: " + value.getIndex());
+ }
+ values.add(id);
+ }
+
+ public boolean isCompatibleKey(RenderingHints.Key key) {
+ return this.key == key;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public String getId() {
+ return key.getIndex() + ":" + getIndex();
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java
new file mode 100644
index 0000000..1ab1d44
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSDocument.java
@@ -0,0 +1,476 @@
+package org.xbib.graphics.chart.io.vector.eps;
+
+import org.xbib.graphics.chart.io.vector.GraphicsState;
+import org.xbib.graphics.chart.io.vector.SizedDocument;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawShapeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawStringCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.RotateCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.ScaleCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetClipCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetColorCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetCompositeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetFontCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetStrokeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.ShearCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.TranslateCommand;
+import org.xbib.graphics.chart.io.vector.util.ASCII85EncodeStream;
+import org.xbib.graphics.chart.io.vector.util.AlphaToMaskOp;
+import org.xbib.graphics.chart.io.vector.util.DataUtils;
+import org.xbib.graphics.chart.io.vector.util.FlateEncodeStream;
+import org.xbib.graphics.chart.io.vector.util.GraphicsUtils;
+import org.xbib.graphics.chart.io.vector.util.ImageDataStream;
+import org.xbib.graphics.chart.io.vector.util.ImageDataStream.Interleaving;
+import org.xbib.graphics.chart.io.vector.util.LineWrapOutputStream;
+import org.xbib.graphics.chart.io.vector.util.PageSize;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EPSDocument extends SizedDocument {
+ /**
+ * Constant to convert values from millimeters to PostScript® units
+ * (1/72th inch).
+ */
+ private static final double UNITS_PER_MM = 72.0 / 25.4;
+ private static final String CHARSET = "ISO-8859-1";
+ private static final String EOL = "\n";
+ private static final int MAX_LINE_WIDTH = 255;
+ private static final Pattern ELEMENT_SEPARATION_PATTERN = Pattern.compile("(.{1," + MAX_LINE_WIDTH + "})(\\s+|$)");
+
+ /**
+ * Mapping of stroke endcap values from Java to PostScript®.
+ */
+ private static final Map STROKE_ENDCAPS = DataUtils.map(
+ new Integer[]{BasicStroke.CAP_BUTT, BasicStroke.CAP_ROUND, BasicStroke.CAP_SQUARE},
+ new Integer[]{0, 1, 2}
+ );
+
+ /**
+ * Mapping of line join values for path drawing from Java to
+ * PostScript®.
+ */
+ private static final Map STROKE_LINEJOIN = DataUtils.map(
+ new Integer[]{BasicStroke.JOIN_MITER, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL},
+ new Integer[]{0, 1, 2}
+ );
+
+ private static final String FONT_LATIN1_SUFFIX = "Lat";
+
+ private final List elements;
+
+ public EPSDocument(PageSize pageSize) {
+ super(pageSize);
+ elements = new LinkedList();
+ addHeader();
+ }
+
+ private static String getOutput(Color c) {
+ // TODO Handle transparency
+ return String.valueOf(c.getRed() / 255.0) + " " + c.getGreen() / 255.0 + " " + c.getBlue() / 255.0 + " rgb";
+ }
+
+ private static String getOutput(Shape s) {
+ StringBuilder out = new StringBuilder();
+ out.append("newpath ");
+ if (s instanceof Line2D) {
+ Line2D l = (Line2D) s;
+ out.append(l.getX1()).append(" ").append(l.getY1()).append(" M ")
+ .append(l.getX2()).append(" ").append(l.getY2()).append(" L");
+ } else if (s instanceof Rectangle2D) {
+ Rectangle2D r = (Rectangle2D) s;
+ out.append(r.getX()).append(" ").append(r.getY()).append(" ")
+ .append(r.getWidth()).append(" ").append(r.getHeight())
+ .append(" rect Z");
+ } else if (s instanceof Ellipse2D) {
+ Ellipse2D e = (Ellipse2D) s;
+ double x = e.getX() + e.getWidth() / 2.0;
+ double y = e.getY() + e.getHeight() / 2.0;
+ double rx = e.getWidth() / 2.0;
+ double ry = e.getHeight() / 2.0;
+ out.append(x).append(" ").append(y).append(" ")
+ .append(rx).append(" ").append(ry).append(" ")
+ .append(360.0).append(" ").append(0.0)
+ .append(" ellipse Z");
+ } else if (s instanceof Arc2D) {
+ Arc2D e = (Arc2D) s;
+ double x = (e.getX() + e.getWidth() / 2.0);
+ double y = (e.getY() + e.getHeight() / 2.0);
+ double rx = e.getWidth() / 2.0;
+ double ry = e.getHeight() / 2.0;
+ double startAngle = -e.getAngleStart();
+ double endAngle = -(e.getAngleStart() + e.getAngleExtent());
+ out.append(x).append(" ").append(y).append(" ")
+ .append(rx).append(" ").append(ry).append(" ")
+ .append(startAngle).append(" ").append(endAngle)
+ .append(" ellipse");
+ if (e.getArcType() == Arc2D.CHORD) {
+ out.append(" Z");
+ } else if (e.getArcType() == Arc2D.PIE) {
+ out.append(" ").append(x).append(" ").append(y).append(" L Z");
+ }
+ } else {
+ PathIterator segments = s.getPathIterator(null);
+ double[] coordsCur = new double[6];
+ double[] pointPrev = new double[2];
+ for (int i = 0; !segments.isDone(); i++, segments.next()) {
+ if (i > 0) {
+ out.append(" ");
+ }
+ int segmentType = segments.currentSegment(coordsCur);
+ switch (segmentType) {
+ case PathIterator.SEG_MOVETO:
+ out.append(coordsCur[0]).append(" ").append(coordsCur[1])
+ .append(" M");
+ pointPrev[0] = coordsCur[0];
+ pointPrev[1] = coordsCur[1];
+ break;
+ case PathIterator.SEG_LINETO:
+ out.append(coordsCur[0]).append(" ").append(coordsCur[1])
+ .append(" L");
+ pointPrev[0] = coordsCur[0];
+ pointPrev[1] = coordsCur[1];
+ break;
+ case PathIterator.SEG_CUBICTO:
+ out.append(coordsCur[0]).append(" ").append(coordsCur[1])
+ .append(" ").append(coordsCur[2]).append(" ")
+ .append(coordsCur[3]).append(" ").append(coordsCur[4])
+ .append(" ").append(coordsCur[5]).append(" C");
+ pointPrev[0] = coordsCur[4];
+ pointPrev[1] = coordsCur[5];
+ break;
+ case PathIterator.SEG_QUADTO:
+ double x1 = pointPrev[0] + 2.0 / 3.0 * (coordsCur[0] - pointPrev[0]);
+ double y1 = pointPrev[1] + 2.0 / 3.0 * (coordsCur[1] - pointPrev[1]);
+ double x2 = coordsCur[0] + 1.0 / 3.0 * (coordsCur[2] - coordsCur[0]);
+ double y2 = coordsCur[1] + 1.0 / 3.0 * (coordsCur[3] - coordsCur[1]);
+ double x3 = coordsCur[2];
+ double y3 = coordsCur[3];
+ out.append(x1).append(" ").append(y1).append(" ")
+ .append(x2).append(" ").append(y2).append(" ")
+ .append(x3).append(" ").append(y3).append(" C");
+ pointPrev[0] = x3;
+ pointPrev[1] = y3;
+ break;
+ case PathIterator.SEG_CLOSE:
+ out.append("Z");
+ break;
+ default:
+ throw new IllegalStateException("Unknown path operation.");
+ }
+ }
+ }
+ return out.toString();
+ }
+
+ private static String getOutput(Image image, int imageWidth, int imageHeight,
+ double x, double y, double width, double height) {
+ StringBuilder out = new StringBuilder();
+
+ BufferedImage bufferedImage = GraphicsUtils.toBufferedImage(image);
+ int bands = bufferedImage.getSampleModel().getNumBands();
+ int bitsPerSample = DataUtils.max(bufferedImage.getSampleModel().getSampleSize());
+ bitsPerSample = (int) (Math.ceil(bitsPerSample / 8.0) * 8.0);
+ if (bands > 3) {
+ bands = 3;
+ }
+
+ out.append("gsave").append(EOL);
+ if (x != 0.0 || y != 0.0) {
+ out.append(x).append(" ").append(y).append(" translate").append(EOL);
+ }
+ if (width != 1.0 || height != 1.0) {
+ out.append(width).append(" ").append(height).append(" scale").append(EOL);
+ }
+
+ int decodeScale = 1;
+ if (bufferedImage.getColorModel().hasAlpha()) {
+ // TODO Use different InterleaveType (2 or 3) for more efficient compression
+ out.append("<< /ImageType 3 /InterleaveType 1 ")
+ .append("/MaskDict ")
+ .append(imageWidth).append(" ").append(imageHeight).append(" ")
+ .append(1).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ")
+ .append(false).append(" ").append(0).append(" imgdict ")
+ .append("/DataDict ")
+ .append(imageWidth).append(" ").append(imageHeight).append(" ")
+ .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ")
+ .append(true).append(" currentfile /ASCII85Decode filter ")
+ .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ")
+ .append("/FlateDecode filter ")
+ .append("imgdict ")
+ .append(">> image").append(EOL);
+
+ // Convert alpha values to binary mask
+ // FIXME Do alpha conversion in a preprocessing step on commands
+ bufferedImage = new AlphaToMaskOp(true).filter(bufferedImage, null);
+ output(bufferedImage, out);
+ } else {
+ if (bands == 1) {
+ out.append("/DeviceGray setcolorspace").append(EOL);
+ }
+ if (bufferedImage.getType() == BufferedImage.TYPE_BYTE_BINARY) {
+ decodeScale = 255;
+ }
+ out.append(imageWidth).append(" ").append(imageHeight).append(" ")
+ .append(bands).append(" ").append(bitsPerSample).append(" ").append(decodeScale).append(" ")
+ .append(true).append(" currentfile /ASCII85Decode filter ")
+ .append("<< /BitsPerComponent ").append(bitsPerSample).append(" >> ")
+ .append("/FlateDecode filter ")
+ .append("imgdict ")
+ .append("image").append(EOL);
+ output(bufferedImage, out);
+ }
+
+ out.append("grestore");
+ return out.toString();
+ }
+
+ private static void output(BufferedImage image, StringBuilder out) {
+ InputStream imageDataStream =
+ new ImageDataStream(image, Interleaving.SAMPLE);
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+ OutputStream compressionStream = new FlateEncodeStream(
+ new ASCII85EncodeStream(
+ new LineWrapOutputStream(outBytes, 80)));
+ try {
+ DataUtils.transfer(imageDataStream, compressionStream, 1024);
+ compressionStream.close();
+ String compressed = outBytes.toString(CHARSET);
+ out.append(compressed).append(EOL);
+ } catch (IOException e) {
+ // TODO Handle exception
+ e.printStackTrace();
+ }
+ }
+
+ private static String getOutput(String str, double x, double y) {
+
+ return "gsave 1 -1 scale " + x + " " + -y + " M " + getOutput(str) + " show " + "grestore";
+ }
+
+ private static StringBuilder getOutput(String str) {
+ StringBuilder out = new StringBuilder();
+
+ // Escape text
+ str = str.replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll("\t", "\\\\t")
+ .replaceAll("\b", "\\\\b")
+ .replaceAll("\f", "\\\\f")
+ .replaceAll("\\(", "\\\\(")
+ .replaceAll("\\)", "\\\\)")
+ .replaceAll("[\r\n]", "");
+
+ out.append("(").append(str).append(")");
+
+ return out;
+ }
+
+ private static String getOutput(Stroke s) {
+ StringBuilder out = new StringBuilder();
+ if (s instanceof BasicStroke) {
+ BasicStroke bs = (BasicStroke) s;
+ out.append(bs.getLineWidth()).append(" setlinewidth ")
+ .append(STROKE_LINEJOIN.get(bs.getLineJoin())).append(" setlinejoin ")
+ .append(STROKE_ENDCAPS.get(bs.getEndCap())).append(" setlinecap ")
+ .append("[").append(DataUtils.join(" ", bs.getDashArray())).append("] ")
+ .append(bs.getDashPhase()).append(" setdash");
+ } else {
+ out.append("% Custom strokes aren't supported at the moment");
+ }
+ return out.toString();
+ }
+
+ private static String getOutput(Font font) {
+ StringBuilder out = new StringBuilder();
+ font = GraphicsUtils.getPhysicalFont(font);
+ String fontName = font.getPSName();
+
+ // Convert font to ISO-8859-1 encoding
+ String fontNameLatin1 = fontName + FONT_LATIN1_SUFFIX;
+ out.append("/").append(fontNameLatin1).append(" ")
+ .append("/").append(font.getPSName()).append(" latinize ");
+
+ // Use encoded font
+ out.append("/").append(fontNameLatin1).append(" ")
+ .append(font.getSize2D()).append(" selectfont");
+
+ return out.toString();
+ }
+
+ private void addHeader() {
+ double x = getPageSize().x * UNITS_PER_MM,
+ y = getPageSize().y * UNITS_PER_MM,
+ width = getPageSize().width * UNITS_PER_MM,
+ height = getPageSize().height * UNITS_PER_MM;
+ elements.addAll(Arrays.asList(
+ "%!PS-Adobe-3.0 EPSF-3.0",
+ "%%BoundingBox: " + ((int) Math.floor(x)) + " " + ((int) Math.floor(y)) + " " + ((int) Math.ceil(x + width)) + " " + ((int) Math.ceil(y + height)),
+ "%%HiResBoundingBox: " + x + " " + y + " " + (x + width) + " " + (y + height),
+ "%%LanguageLevel: 3",
+ "%%Pages: 1",
+ "%%EndComments",
+ "%%Page: 1 1",
+ "/M /moveto load def",
+ "/L /lineto load def",
+ "/C /curveto load def",
+ "/Z /closepath load def",
+ "/RL /rlineto load def",
+ "/rgb /setrgbcolor load def",
+ "/rect { /height exch def /width exch def /y exch def /x exch def x y M width 0 RL 0 height RL width neg 0 RL } bind def",
+ "/ellipse { /endangle exch def /startangle exch def /ry exch def /rx exch def /y exch def /x exch def /savematrix matrix currentmatrix def x y translate rx ry scale 0 0 1 startangle endangle arcn savematrix setmatrix } bind def",
+ "/imgdict { /datastream exch def /hasdata exch def /decodeScale exch def /bits exch def /bands exch def /imgheight exch def /imgwidth exch def << /ImageType 1 /Width imgwidth /Height imgheight /BitsPerComponent bits /Decode [bands {0 decodeScale} repeat] /ImageMatrix [imgwidth 0 0 imgheight 0 0] hasdata { /DataSource datastream } if >> } bind def",
+ "/latinize { /fontName exch def /fontNameNew exch def fontName findfont 0 dict copy begin /Encoding ISOLatin1Encoding def fontNameNew /FontName def currentdict end dup /FID undef fontNameNew exch definefont pop } bind def",
+ getOutput(GraphicsState.DEFAULT_FONT),
+ "gsave",
+ "clipsave",
+ "/DeviceRGB setcolorspace",
+ "0 " + height + " translate",
+ UNITS_PER_MM + " " + (-UNITS_PER_MM) + " scale",
+ "/basematrix matrix currentmatrix def"
+ ));
+ }
+
+ public void write(OutputStream out) throws IOException {
+ OutputStreamWriter o = new OutputStreamWriter(out, CHARSET);
+ for (String element : elements) {
+ if (element == null) {
+ continue;
+ }
+
+ // Write current element in lines of 255 bytes (excluding line terminators)
+ // Numbers must not be separated by line breaks or errors will occur
+ // TODO: Integrate functionality into LineWrapOutputStream
+ Matcher chunkMatcher = ELEMENT_SEPARATION_PATTERN.matcher(element);
+
+ boolean chunkFound = false;
+ while (chunkMatcher.find()) {
+ chunkFound = true;
+ String chunk = chunkMatcher.group();
+ o.write(chunk, 0, chunk.length());
+ o.append(EOL);
+ }
+ if (!chunkFound) {
+ // TODO: Exception, if no whitespace can be found in the chunk
+ System.err.println("Unable to divide eps element into lines: " + element);
+ }
+ }
+ o.append("%%EOF");
+ o.flush();
+ }
+
+ public void handle(Command> command) {
+ if (command instanceof SetClipCommand) {
+ SetClipCommand c = (SetClipCommand) command;
+ Shape clip = c.getValue();
+ elements.add("cliprestore");
+ if (clip != null) {
+ elements.add(getOutput(clip) + " clip");
+ }
+ } else if (command instanceof SetColorCommand) {
+ SetColorCommand c = (SetColorCommand) command;
+ elements.add(getOutput(c.getValue()));
+ } else if (command instanceof SetCompositeCommand) {
+ SetCompositeCommand c = (SetCompositeCommand) command;
+ // TODO Implement composite rendering for EPS
+ elements.add("% composite not yet implemented: " + c.getValue());
+ } else if (command instanceof SetFontCommand) {
+ SetFontCommand c = (SetFontCommand) command;
+ elements.add(getOutput(c.getValue()));
+ } else if (command instanceof SetPaintCommand) {
+ SetPaintCommand c = (SetPaintCommand) command;
+ // TODO Implement paint rendering for EPS
+ elements.add("% paint not yet implemented: " + c.getValue());
+ } else if (command instanceof SetStrokeCommand) {
+ SetStrokeCommand c = (SetStrokeCommand) command;
+ elements.add(getOutput(c.getValue()));
+ } else if (command instanceof SetTransformCommand) {
+ SetTransformCommand c = (SetTransformCommand) command;
+ StringBuilder e = new StringBuilder();
+ double[] matrix = new double[6];
+ c.getValue().getMatrix(matrix);
+ e.append("basematrix setmatrix [")
+ .append(DataUtils.join(" ", matrix)).append("] concat");
+ elements.add(e.toString());
+ } else if (command instanceof RotateCommand) {
+ RotateCommand c = (RotateCommand) command;
+ StringBuilder e = new StringBuilder();
+ double x = c.getCenterX();
+ double y = c.getCenterY();
+ boolean translated = x != 0.0 || y != 0.0;
+ if (translated) {
+ e.append(x).append(" ").append(y).append(" translate ");
+ }
+ e.append(Math.toDegrees(c.getTheta())).append(" rotate");
+ if (translated) {
+ e.append(" ");
+ e.append(-x).append(" ").append(-y).append(" translate");
+ }
+ elements.add(e.toString());
+ } else if (command instanceof ScaleCommand) {
+ ScaleCommand c = (ScaleCommand) command;
+ elements.add(DataUtils.format(c.getScaleX()) + " " + DataUtils.format(c.getScaleY()) + " scale");
+ } else if (command instanceof ShearCommand) {
+ ShearCommand c = (ShearCommand) command;
+ elements.add("[1 " + DataUtils.format(c.getShearY()) + " " + DataUtils.format(c.getShearX()) + " 1 0 0] concat");
+ } else if (command instanceof TransformCommand) {
+ TransformCommand c = (TransformCommand) command;
+ StringBuilder e = new StringBuilder();
+ double[] matrix = new double[6];
+ c.getValue().getMatrix(matrix);
+ e.append("[").append(DataUtils.join(" ", matrix))
+ .append("] concat");
+ elements.add(e.toString());
+ } else if (command instanceof TranslateCommand) {
+ TranslateCommand c = (TranslateCommand) command;
+ elements.add(String.valueOf(c.getDeltaX()) + " " + c.getDeltaY() + " translate");
+ } else if (command instanceof DrawImageCommand) {
+ DrawImageCommand c = (DrawImageCommand) command;
+ String e = getOutput(c.getValue(),
+ c.getImageWidth(), c.getImageHeight(),
+ c.getX(), c.getY(), c.getWidth(), c.getHeight());
+ elements.add(e);
+ } else if (command instanceof DrawShapeCommand) {
+ DrawShapeCommand c = (DrawShapeCommand) command;
+ elements.add(getOutput(c.getValue()) + " stroke");
+ } else if (command instanceof DrawStringCommand) {
+ DrawStringCommand c = (DrawStringCommand) command;
+ elements.add(getOutput(c.getValue(), c.getX(), c.getY()));
+ } else if (command instanceof FillShapeCommand) {
+ FillShapeCommand c = (FillShapeCommand) command;
+ elements.add(getOutput(c.getValue()) + " fill");
+ } else if (command instanceof CreateCommand) {
+ elements.add("gsave");
+ } else if (command instanceof DisposeCommand) {
+ elements.add("grestore");
+ }
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java
new file mode 100644
index 0000000..65fa2b3
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/eps/EPSProcessor.java
@@ -0,0 +1,23 @@
+package org.xbib.graphics.chart.io.vector.eps;
+
+import org.xbib.graphics.chart.io.vector.Document;
+import org.xbib.graphics.chart.io.vector.Processor;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.filters.FillPaintedShapeAsImageFilter;
+import org.xbib.graphics.chart.io.vector.util.PageSize;
+
+public class EPSProcessor implements Processor {
+ public Document process(Iterable> commands, PageSize pageSize) {
+ // TODO Apply rotate(theta,x,y) => translate-rotate-translate filter
+ // TODO Apply image transparency => image mask filter
+ // TODO Apply optimization filter
+ FillPaintedShapeAsImageFilter paintedShapeAsImageFilter = new FillPaintedShapeAsImageFilter(commands);
+ EPSDocument doc = new EPSDocument(pageSize);
+ for (Command> command : paintedShapeAsImageFilter) {
+ doc.handle(command);
+ }
+ doc.close();
+ return doc;
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java
new file mode 100644
index 0000000..ee0500c
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/CommandHandler.java
@@ -0,0 +1,8 @@
+package org.xbib.graphics.chart.io.vector.intermediate;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+
+public interface CommandHandler {
+ void handle(Command> command);
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java
new file mode 100644
index 0000000..bcfa6c5
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/AffineTransformCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+
+public abstract class AffineTransformCommand extends StateCommand {
+ public AffineTransformCommand(AffineTransform transform) {
+ super(transform);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java
new file mode 100644
index 0000000..e1858fd
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Command.java
@@ -0,0 +1,31 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.util.Locale;
+
+public abstract class Command {
+ private final T value;
+
+ public Command(T value) {
+ this.value = value;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "%s[value=%s]",
+ getClass().getName(), getValue());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !getClass().equals(obj.getClass())) {
+ return false;
+ }
+ Command> o = (Command>) obj;
+ return value == o.value || value.equals(o.value);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java
new file mode 100644
index 0000000..a48e9bb
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/CreateCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import org.xbib.graphics.chart.io.vector.VectorGraphics2D;
+
+public class CreateCommand extends StateCommand {
+ public CreateCommand(VectorGraphics2D graphics) {
+ super(graphics);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java
new file mode 100644
index 0000000..d9c20b9
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DisposeCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import org.xbib.graphics.chart.io.vector.VectorGraphics2D;
+
+public class DisposeCommand extends StateCommand {
+ public DisposeCommand(VectorGraphics2D graphics) {
+ super(graphics);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java
new file mode 100644
index 0000000..60c8ac1
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawImageCommand.java
@@ -0,0 +1,58 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Image;
+import java.util.Locale;
+
+public class DrawImageCommand extends Command {
+ private final int imageWidth;
+ private final int imageHeight;
+ private final double x;
+ private final double y;
+ private final double width;
+ private final double height;
+
+ public DrawImageCommand(Image image, int imageWidth, int imageHeight,
+ double x, double y, double width, double height) {
+ super(image);
+ this.imageWidth = imageWidth;
+ this.imageHeight = imageHeight;
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ public int getImageWidth() {
+ return imageWidth;
+ }
+
+ public int getImageHeight() {
+ return imageHeight;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public double getWidth() {
+ return width;
+ }
+
+ public double getHeight() {
+ return height;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null,
+ "%s[value=%s, imageWidth=%d, imageHeight=%d, x=%f, y=%f, width=%f, height=%f]",
+ getClass().getName(), getValue(),
+ getImageWidth(), getImageHeight(),
+ getX(), getY(), getWidth(), getHeight());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java
new file mode 100644
index 0000000..7757989
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawShapeCommand.java
@@ -0,0 +1,12 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import org.xbib.graphics.chart.io.vector.util.GraphicsUtils;
+
+import java.awt.Shape;
+
+public class DrawShapeCommand extends Command {
+ public DrawShapeCommand(Shape shape) {
+ super(GraphicsUtils.clone(shape));
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java
new file mode 100644
index 0000000..1bb81a0
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/DrawStringCommand.java
@@ -0,0 +1,30 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.util.Locale;
+
+
+public class DrawStringCommand extends Command {
+ private final double x;
+ private final double y;
+
+ public DrawStringCommand(String string, double x, double y) {
+ super(string);
+ this.x = x;
+ this.y = y;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "%s[value=%s, x=%f, y=%f]",
+ getClass().getName(), getValue(), getX(), getY());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java
new file mode 100644
index 0000000..d8abbeb
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/FillShapeCommand.java
@@ -0,0 +1,12 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import org.xbib.graphics.chart.io.vector.util.GraphicsUtils;
+
+import java.awt.Shape;
+
+public class FillShapeCommand extends Command {
+ public FillShapeCommand(Shape shape) {
+ super(GraphicsUtils.clone(shape));
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java
new file mode 100644
index 0000000..0b67f65
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/Group.java
@@ -0,0 +1,16 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Group extends Command>> {
+ public Group() {
+ super(new LinkedList>());
+ }
+
+ public void add(Command> command) {
+ List> group = getValue();
+ group.add(command);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java
new file mode 100644
index 0000000..e05a203
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/RotateCommand.java
@@ -0,0 +1,38 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+import java.util.Locale;
+
+public class RotateCommand extends AffineTransformCommand {
+ private final double theta;
+ private final double centerX;
+ private final double centerY;
+
+ public RotateCommand(double theta, double centerX, double centerY) {
+ super(AffineTransform.getRotateInstance(theta, centerX, centerY));
+ this.theta = theta;
+ this.centerX = centerX;
+ this.centerY = centerY;
+ }
+
+ public double getTheta() {
+ return theta;
+ }
+
+ public double getCenterX() {
+ return centerX;
+ }
+
+ public double getCenterY() {
+ return centerY;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null,
+ "%s[theta=%f, centerX=%f, centerY=%f, value=%s]",
+ getClass().getName(), getTheta(), getCenterX(), getCenterY(),
+ getValue());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java
new file mode 100644
index 0000000..2d61a6b
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ScaleCommand.java
@@ -0,0 +1,31 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+import java.util.Locale;
+
+public class ScaleCommand extends AffineTransformCommand {
+ private final double scaleX;
+ private final double scaleY;
+
+ public ScaleCommand(double scaleX, double scaleY) {
+ super(AffineTransform.getScaleInstance(scaleX, scaleY));
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
+ }
+
+ public double getScaleX() {
+ return scaleX;
+ }
+
+ public double getScaleY() {
+ return scaleY;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null,
+ "%s[scaleX=%f, scaleY=%f, value=%s]", getClass().getName(),
+ getScaleX(), getScaleY(), getValue());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java
new file mode 100644
index 0000000..79baa1f
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetBackgroundCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Color;
+
+public class SetBackgroundCommand extends StateCommand {
+ public SetBackgroundCommand(Color color) {
+ super(color);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java
new file mode 100644
index 0000000..4d865da
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetClipCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Shape;
+
+public class SetClipCommand extends StateCommand {
+ public SetClipCommand(Shape shape) {
+ super(shape);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java
new file mode 100644
index 0000000..d5d3fc1
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetColorCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Color;
+
+public class SetColorCommand extends StateCommand {
+ public SetColorCommand(Color color) {
+ super(color);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java
new file mode 100644
index 0000000..bddd414
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetCompositeCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Composite;
+
+public class SetCompositeCommand extends StateCommand {
+ public SetCompositeCommand(Composite composite) {
+ super(composite);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java
new file mode 100644
index 0000000..4e4d331
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetFontCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Font;
+
+public class SetFontCommand extends StateCommand {
+ public SetFontCommand(Font font) {
+ super(font);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java
new file mode 100644
index 0000000..6a50801
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetHintCommand.java
@@ -0,0 +1,24 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.util.Locale;
+
+public class SetHintCommand extends StateCommand {
+ private final Object key;
+
+ public SetHintCommand(Object hintKey, Object hintValue) {
+ super(hintValue);
+ key = hintKey;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null,
+ "%s[key=%s, value=%s]", getClass().getName(),
+ getKey(), getValue());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java
new file mode 100644
index 0000000..b258680
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetPaintCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Paint;
+
+public class SetPaintCommand extends StateCommand {
+ public SetPaintCommand(Paint paint) {
+ super(paint);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java
new file mode 100644
index 0000000..1489e3f
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetStrokeCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Stroke;
+
+public class SetStrokeCommand extends StateCommand {
+ public SetStrokeCommand(Stroke stroke) {
+ super(stroke);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java
new file mode 100644
index 0000000..6730a8c
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetTransformCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+
+public class SetTransformCommand extends StateCommand {
+ public SetTransformCommand(AffineTransform transform) {
+ super(new AffineTransform(transform));
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java
new file mode 100644
index 0000000..69239b8
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/SetXORModeCommand.java
@@ -0,0 +1,10 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.Color;
+
+public class SetXORModeCommand extends StateCommand {
+ public SetXORModeCommand(Color mode) {
+ super(mode);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java
new file mode 100644
index 0000000..0efd918
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/ShearCommand.java
@@ -0,0 +1,31 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+import java.util.Locale;
+
+public class ShearCommand extends AffineTransformCommand {
+ private final double shearX;
+ private final double shearY;
+
+ public ShearCommand(double shearX, double shearY) {
+ super(AffineTransform.getShearInstance(shearX, shearY));
+ this.shearX = shearX;
+ this.shearY = shearY;
+ }
+
+ public double getShearX() {
+ return shearX;
+ }
+
+ public double getShearY() {
+ return shearY;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null,
+ "%s[shearX=%f, shearY=%f, value=%s]", getClass().getName(),
+ getShearX(), getShearY(), getValue());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java
new file mode 100644
index 0000000..fec7cae
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/StateCommand.java
@@ -0,0 +1,8 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+public abstract class StateCommand extends Command {
+ public StateCommand(T value) {
+ super(value);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java
new file mode 100644
index 0000000..deb6a27
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TransformCommand.java
@@ -0,0 +1,17 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+
+public class TransformCommand extends AffineTransformCommand {
+ private final AffineTransform transform;
+
+ public TransformCommand(AffineTransform transform) {
+ super(transform);
+ this.transform = new AffineTransform(transform);
+ }
+
+ public AffineTransform getTransform() {
+ return transform;
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java
new file mode 100644
index 0000000..a58f092
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/commands/TranslateCommand.java
@@ -0,0 +1,31 @@
+package org.xbib.graphics.chart.io.vector.intermediate.commands;
+
+import java.awt.geom.AffineTransform;
+import java.util.Locale;
+
+public class TranslateCommand extends AffineTransformCommand {
+ private final double deltaX;
+ private final double deltaY;
+
+ public TranslateCommand(double x, double y) {
+ super(AffineTransform.getTranslateInstance(x, y));
+ this.deltaX = x;
+ this.deltaY = y;
+ }
+
+ public double getDeltaX() {
+ return deltaX;
+ }
+
+ public double getDeltaY() {
+ return deltaY;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null,
+ "%s[deltaX=%f, deltaY=%f, value=%s]", getClass().getName(),
+ getDeltaX(), getDeltaY(), getValue());
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java
new file mode 100644
index 0000000..95a2e63
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/AbsoluteToRelativeTransformsFilter.java
@@ -0,0 +1,63 @@
+package org.xbib.graphics.chart.io.vector.intermediate.filters;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.AffineTransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.CreateCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetTransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.TransformCommand;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Stack;
+
+public class AbsoluteToRelativeTransformsFilter extends Filter {
+ private Stack transforms;
+
+ public AbsoluteToRelativeTransformsFilter(Iterable> stream) {
+ super(stream);
+ transforms = new Stack();
+ }
+
+ @Override
+ public Command> next() {
+ Command> nextCommand = super.next();
+ if (nextCommand instanceof AffineTransformCommand) {
+ AffineTransformCommand affineTransformCommand = (AffineTransformCommand) nextCommand;
+ getCurrentTransform().concatenate(affineTransformCommand.getValue());
+ } else if (nextCommand instanceof CreateCommand) {
+ AffineTransform newTransform = transforms.isEmpty() ? new AffineTransform() : new AffineTransform(getCurrentTransform());
+ transforms.push(newTransform);
+ } else if (nextCommand instanceof DisposeCommand) {
+ transforms.pop();
+ }
+
+ return nextCommand;
+ }
+
+ @Override
+ protected List> filter(Command> command) {
+ if (command instanceof SetTransformCommand) {
+ SetTransformCommand setTransformCommand = (SetTransformCommand) command;
+ AffineTransform absoluteTransform = setTransformCommand.getValue();
+ AffineTransform relativeTransform = new AffineTransform();
+ try {
+ AffineTransform invertedOldTransformation = getCurrentTransform().createInverse();
+ relativeTransform.concatenate(invertedOldTransformation);
+ } catch (NoninvertibleTransformException e) {
+ e.printStackTrace();
+ }
+ relativeTransform.concatenate(absoluteTransform);
+ TransformCommand transformCommand = new TransformCommand(relativeTransform);
+ return Arrays.>asList(transformCommand);
+ }
+ return Arrays.>asList(command);
+ }
+
+ private AffineTransform getCurrentTransform() {
+ return transforms.peek();
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java
new file mode 100644
index 0000000..ba43f32
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/FillPaintedShapeAsImageFilter.java
@@ -0,0 +1,70 @@
+package org.xbib.graphics.chart.io.vector.intermediate.filters;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DisposeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.DrawImageCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.FillShapeCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetPaintCommand;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.Arrays;
+import java.util.List;
+
+public class FillPaintedShapeAsImageFilter extends Filter {
+ private SetPaintCommand lastSetPaintCommand;
+
+ public FillPaintedShapeAsImageFilter(Iterable> stream) {
+ super(stream);
+ }
+
+ @Override
+ public Command> next() {
+ Command> nextCommand = super.next();
+
+ if (nextCommand instanceof SetPaintCommand) {
+ lastSetPaintCommand = (SetPaintCommand) nextCommand;
+ } else if (nextCommand instanceof DisposeCommand) {
+ lastSetPaintCommand = null;
+ }
+
+ return nextCommand;
+ }
+
+ private DrawImageCommand getDrawImageCommand(FillShapeCommand shapeCommand, SetPaintCommand paintCommand) {
+ Shape shape = shapeCommand.getValue();
+ Rectangle2D shapeBounds = shape.getBounds2D();
+ double x = shapeBounds.getX();
+ double y = shapeBounds.getY();
+ double width = shapeBounds.getWidth();
+ double height = shapeBounds.getHeight();
+ int imageWidth = (int) Math.round(width);
+ int imageHeight = (int) Math.round(height);
+ BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D imageGraphics = (Graphics2D) image.getGraphics();
+ imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ imageGraphics.scale(imageWidth / width, imageHeight / height);
+ imageGraphics.translate(-shapeBounds.getX(), -shapeBounds.getY());
+ imageGraphics.setPaint(paintCommand.getValue());
+ imageGraphics.fill(shape);
+ imageGraphics.dispose();
+
+ DrawImageCommand drawImageCommand = new DrawImageCommand(image, imageWidth, imageHeight, x, y, width, height);
+ return drawImageCommand;
+ }
+
+ @Override
+ protected List> filter(Command> command) {
+ if (lastSetPaintCommand != null && command instanceof FillShapeCommand) {
+ FillShapeCommand fillShapeCommand = (FillShapeCommand) command;
+ DrawImageCommand drawImageCommand = getDrawImageCommand(fillShapeCommand, lastSetPaintCommand);
+ return Arrays.>asList(drawImageCommand);
+ }
+
+ return Arrays.>asList(command);
+ }
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java
new file mode 100644
index 0000000..c937df7
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/Filter.java
@@ -0,0 +1,48 @@
+package org.xbib.graphics.chart.io.vector.intermediate.filters;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+public abstract class Filter implements Iterable>, Iterator> {
+ private final Queue> buffer;
+ private final Iterator> iterator;
+
+ public Filter(Iterable> stream) {
+ buffer = new LinkedList>();
+ iterator = stream.iterator();
+ }
+
+ public Iterator> iterator() {
+ return this;
+ }
+
+ public boolean hasNext() {
+ findNextCommand();
+ return !buffer.isEmpty();
+ }
+
+ private void findNextCommand() {
+ while (buffer.isEmpty() && iterator.hasNext()) {
+ Command> command = iterator.next();
+ List> commands = filter(command);
+ if (commands != null) {
+ buffer.addAll(commands);
+ }
+ }
+ }
+
+ public Command> next() {
+ findNextCommand();
+ return buffer.poll();
+ }
+
+ public void remove() {
+ }
+
+ protected abstract List> filter(Command> command);
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java
new file mode 100644
index 0000000..a0e60dc
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/GroupingFilter.java
@@ -0,0 +1,47 @@
+package org.xbib.graphics.chart.io.vector.intermediate.filters;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Group;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+public abstract class GroupingFilter extends Filter {
+ private Group group;
+
+ public GroupingFilter(Iterable> stream) {
+ super(stream);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return group != null || super.hasNext();
+ }
+
+ @Override
+ public Command> next() {
+ if (group == null) {
+ return super.next();
+ }
+ Group g = group;
+ group = null;
+ return g;
+ }
+
+ @Override
+ protected List> filter(Command> command) {
+ boolean grouped = isGrouped(command);
+ if (grouped) {
+ if (group == null) {
+ group = new Group();
+ }
+ group.add(command);
+ return null;
+ }
+ return Arrays.>asList(command);
+ }
+
+ protected abstract boolean isGrouped(Command> command);
+}
+
diff --git a/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java
new file mode 100644
index 0000000..82cf1c7
--- /dev/null
+++ b/chart/src/main/java/org/xbib/graphics/chart/io/vector/intermediate/filters/OptimizeFilter.java
@@ -0,0 +1,57 @@
+package org.xbib.graphics.chart.io.vector.intermediate.filters;
+
+import org.xbib.graphics.chart.io.vector.intermediate.commands.AffineTransformCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.Command;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.SetHintCommand;
+import org.xbib.graphics.chart.io.vector.intermediate.commands.StateCommand;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+public class OptimizeFilter extends Filter {
+ private final Queue> buffer;
+
+ public OptimizeFilter(Iterable> stream) {
+ super(stream);
+ buffer = new LinkedList>();
+ }
+
+ private static boolean isStateChange(Command> command) {
+ return (command instanceof StateCommand) &&
+ !(command instanceof AffineTransformCommand) &&
+ !(command instanceof SetHintCommand);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return super.hasNext();
+ }
+
+ @Override
+ public Command> next() {
+ if (buffer.isEmpty()) {
+ return super.next();
+ }
+ return buffer.poll();
+ }
+
+ @Override
+ protected List> filter(Command> command) {
+ if (!isStateChange(command)) {
+ return Arrays.>asList(command);
+ }
+ Iterator