/* * Copyright 2014 Robin Stuart * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.org.okapibarcode.backend; /** * Implements GS1 DataBar Expanded Omnidirectional and GS1 Expanded Stacked * Omnidirectional * According to ISO/IEC 24724:2011 * <p> * DataBar expanded encodes GS1 data in either a linear or stacked * format. * * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> */ public class DataBarExpanded extends Symbol { private int[] g_sum_exp = { 0, 348, 1388, 2948, 3988 }; private int[] t_even_exp = { 4, 20, 52, 104, 204 }; private int[] modules_odd_exp = { 12, 10, 8, 6, 4 }; private int[] modules_even_exp = { 5, 7, 9, 11, 13 }; private int[] widest_odd_exp = { 7, 5, 4, 3, 1 }; private int[] widest_even_exp = { 2, 4, 5, 6, 8 }; private 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 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 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 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 String binary_string; private String general_field; private encodeMode[] general_field_type; private int[] widths = new int[8]; private boolean linkageFlag; private int preferredNoOfColumns = 0; /** * 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; } private enum dbeMode { UNSTACKED, STACKED }; private dbeMode symbolType; private enum encodeMode { NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO }; public DataBarExpanded() { linkageFlag = false; inputDataType = DataType.GS1; } @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 data_chars; int[] vs = new int[21]; int[] group = new int[21]; int[] v_odd = new int[21]; int[] v_even = new int[21]; int[][] char_widths = new int[21][8]; int checksum; int row; int check_char; int c_group; int c_odd; int c_even; int[] check_widths = new int[8]; int pattern_width; int[] elements = new int[235]; int codeblocks; int stack_rows; int blocksPerRow; int current_block; int current_row; boolean special_case_row; int elements_in_sub; int reader; int writer; int[] sub_elements = new int[235]; int l; int symbol_row; String seperator_binary; String seperator_pattern; boolean black; boolean left_to_right; int compositeOffset; source = content; if (linkageFlag) { binary_string = "1"; compositeOffset = 1; } else { binary_string = "0"; compositeOffset = 0; } if (!calculateBinaryString()) { return false; } data_chars = binary_string.length() / 12; encodeInfo += "Data characters: "; for (i = 0; i < data_chars; i++) { vs[i] = 0; for (j = 0; j < 12; j++) { if (binary_string.charAt((i * 12) + j) == '1') { vs[i] += 2048 >> j; } } // if (debug) { // System.out.println("Data character (vs[" + i + "]) is " + vs[i]); // } encodeInfo += Integer.toString(vs[i]) + " "; } encodeInfo += "\n"; for (i = 0; i < data_chars; 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; } v_odd[i] = (vs[i] - g_sum_exp[group[i] - 1]) / t_even_exp[group[i] - 1]; v_even[i] = (vs[i] - g_sum_exp[group[i] - 1]) % t_even_exp[group[i] - 1]; getWidths(v_odd[i], modules_odd_exp[group[i] - 1], 4, widest_odd_exp[group[i] - 1], 0); char_widths[i][0] = widths[0]; char_widths[i][2] = widths[1]; char_widths[i][4] = widths[2]; char_widths[i][6] = widths[3]; getWidths(v_even[i], modules_even_exp[group[i] - 1], 4, widest_even_exp[group[i] - 1], 1); char_widths[i][1] = widths[0]; char_widths[i][3] = widths[1]; char_widths[i][5] = widths[2]; char_widths[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 < data_chars; i++) { row = weight_rows[(((data_chars - 2) / 2) * 21) + i]; for (j = 0; j < 8; j++) { checksum += (char_widths[i][j] * checksum_weight_exp[(row * 8) + j]); } } check_char = (211 * ((data_chars + 1) - 4)) + (checksum % 211); encodeInfo += "Check Character: " + Integer.toString(check_char) + "\n"; c_group = 1; if ((check_char >= 348) && (check_char <= 1387)) { c_group = 2; } if ((check_char >= 1388) && (check_char <= 2947)) { c_group = 3; } if ((check_char >= 2948) && (check_char <= 3987)) { c_group = 4; } if (check_char >= 3988) { c_group = 5; } c_odd = (check_char - g_sum_exp[c_group - 1]) / t_even_exp[c_group - 1]; c_even = (check_char - g_sum_exp[c_group - 1]) % t_even_exp[c_group - 1]; getWidths(c_odd, modules_odd_exp[c_group - 1], 4, widest_odd_exp[c_group - 1], 0); check_widths[0] = widths[0]; check_widths[2] = widths[1]; check_widths[4] = widths[2]; check_widths[6] = widths[3]; getWidths(c_even, modules_even_exp[c_group - 1], 4, widest_even_exp[c_group - 1], 1); check_widths[1] = widths[0]; check_widths[3] = widths[1]; check_widths[5] = widths[2]; check_widths[7] = widths[3]; /* Initialise element array */ pattern_width = ((((data_chars + 1) / 2) + ((data_chars + 1) & 1)) * 5) + ((data_chars + 1) * 8) + 4; for (i = 0; i < pattern_width; i++) { elements[i] = 0; } elements[0] = 1; elements[1] = 1; elements[pattern_width - 2] = 1; elements[pattern_width - 1] = 1; /* Put finder patterns in element array */ for (i = 0; i < (((data_chars + 1) / 2) + ((data_chars + 1) & 1)); i++) { k = ((((((data_chars + 1) - 2) / 2) + ((data_chars + 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] = check_widths[i]; } /* Put forward reading data characters in element array */ for (i = 1; i < data_chars; i += 2) { for (j = 0; j < 8; j++) { elements[(((i - 1) / 2) * 21) + 23 + j] = char_widths[i][j]; } } /* Put reversed data characters in element array */ for (i = 0; i < data_chars; i += 2) { for (j = 0; j < 8; j++) { elements[((i / 2) * 21) + 15 + j] = char_widths[i][7 - j]; } } if (symbolType == dbeMode.UNSTACKED) { /* Copy elements into symbol */ row_count = 1 + compositeOffset; row_height = new int[1 + compositeOffset]; row_height[0 + compositeOffset] = -1; pattern = new String[1 + compositeOffset]; pattern[0 + compositeOffset] = "0"; writer = 0; black = false; seperator_binary = ""; for (i = 0; i < pattern_width; i++) { pattern[0 + compositeOffset] += (char)(elements[i] + '0'); for (j = 0; j < elements[i]; j++) { if (black) { seperator_binary += "0"; } else { seperator_binary += "1"; } } black = !(black); writer += elements[i]; } seperator_binary = "0000" + seperator_binary.substring(4, writer - 4); for (j = 0; j < (writer / 49); j++) { k = (49 * j) + 18; for (i = 0; i < 15; i++) { if ((seperator_binary.charAt(i + k - 1) == '1') && (seperator_binary.charAt(i + k) == '1')) { seperator_binary = seperator_binary.substring(0, (i + k)) + "0" + seperator_binary.substring(i + k + 1); } } } if (linkageFlag) { // Add composite code seperator pattern[0] = bin2pat(seperator_binary); row_height[0] = 1; } } else { /* RSS Expanded Stacked */ codeblocks = (data_chars + 1) / 2 + ((data_chars + 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; } stack_rows = codeblocks / blocksPerRow; if (codeblocks % blocksPerRow > 0) { stack_rows++; } row_count = (stack_rows * 4) - 3; row_height = new int[row_count + compositeOffset]; pattern = new String[row_count + compositeOffset]; symbol_row = 0; current_block = 0; for (current_row = 1; current_row <= stack_rows; current_row++) { for (i = 0; i < 235; i++) { sub_elements[i] = 0; } special_case_row = false; /* Row Start */ sub_elements[0] = 1; sub_elements[1] = 1; elements_in_sub = 2; /* Row Data */ reader = 0; do { if ((((blocksPerRow & 1) != 0) || ((current_row & 1) != 0)) || ((current_row == stack_rows) && (codeblocks != (current_row * blocksPerRow)) && ((((current_row * blocksPerRow) - codeblocks) & 1)) != 0)) { /* left to right */ left_to_right = true; i = 2 + (current_block * 21); for (j = 0; j < 21; j++) { if ((i + j) < pattern_width) { sub_elements[j + (reader * 21) + 2] = elements[i + j]; elements_in_sub++; } } } else { /* right to left */ left_to_right = false; if ((current_row * blocksPerRow) < codeblocks) { /* a full row */ i = 2 + (((current_row * blocksPerRow) - reader - 1) * 21); for (j = 0; j < 21; j++) { if ((i + j) < pattern_width) { sub_elements[(20 - j) + (reader * 21) + 2] = elements[i + j]; elements_in_sub++; } } } else { /* a partial row */ k = ((current_row * blocksPerRow) - codeblocks); l = (current_row * blocksPerRow) - reader - 1; i = 2 + ((l - k) * 21); for (j = 0; j < 21; j++) { if ((i + j) < pattern_width) { sub_elements[(20 - j) + (reader * 21) + 2] = elements[i + j]; elements_in_sub++; } } } } reader++; current_block++; } while ((reader < blocksPerRow) && (current_block < codeblocks)); /* Row Stop */ sub_elements[elements_in_sub] = 1; sub_elements[elements_in_sub + 1] = 1; elements_in_sub += 2; pattern[symbol_row + compositeOffset] = ""; black = true; row_height[symbol_row + compositeOffset] = -1; if ((current_row & 1) != 0) { pattern[symbol_row + compositeOffset] = "0"; black = false; } else { if ((current_row == stack_rows) && (codeblocks != (current_row * blocksPerRow)) && ((((current_row * blocksPerRow) - codeblocks) & 1) != 0)) { /* Special case bottom row */ special_case_row = true; sub_elements[0] = 2; pattern[symbol_row + compositeOffset] = "0"; black = false; } } writer = 0; seperator_binary = ""; for (i = 0; i < elements_in_sub; i++) { pattern[symbol_row + compositeOffset] += (char)(sub_elements[i] + '0'); for (j = 0; j < sub_elements[i]; j++) { if (black) { seperator_binary += "0"; } else { seperator_binary += "1"; } } black = !(black); writer += sub_elements[i]; } seperator_binary = "0000" + seperator_binary.substring(4, writer - 4); for (j = 0; j < reader; j++) { k = (49 * j) + (special_case_row ? 19 : 18); if (left_to_right) { for (i = 0; i < 15; i++) { if ((seperator_binary.charAt(i + k - 1) == '1') && (seperator_binary.charAt(i + k) == '1')) { seperator_binary = seperator_binary.substring(0, (i + k)) + "0" + seperator_binary.substring(i + k + 1); } } } else { for (i = 14; i >= 0; i--) { if ((seperator_binary.charAt(i + k + 1) == '1') && (seperator_binary.charAt(i + k) == '1')) { seperator_binary = seperator_binary.substring(0, (i + k)) + "0" + seperator_binary.substring(i + k + 1); } } } } seperator_pattern = bin2pat(seperator_binary); if ((current_row == 1) && linkageFlag) { // Add composite code seperator row_height[0] = 1; pattern[0] = seperator_pattern; } if (current_row != 1) { /* middle separator pattern (above current row) */ pattern[symbol_row - 2 + compositeOffset] = "05"; for (j = 5; j < (49 * blocksPerRow); j += 2) { pattern[symbol_row - 2 + compositeOffset] += "11"; } row_height[symbol_row - 2 + compositeOffset] = 1; /* bottom separator pattern (above current row) */ row_height[symbol_row - 1 + compositeOffset] = 1; pattern[symbol_row - 1 + compositeOffset] = seperator_pattern; } if (current_row != stack_rows) { row_height[symbol_row + 1 + compositeOffset] = 1; pattern[symbol_row + 1 + compositeOffset] = seperator_pattern; } symbol_row += 4; } readable = ""; row_count += compositeOffset; } plotSymbol(); return true; } private boolean calculateBinaryString() { /* Handles all data encodation from section 7.2.5 of ISO/IEC 24724 */ encodeMode last_mode = encodeMode.NUMERIC; int encoding_method, i, j, read_posn; boolean latch; int remainder, d1, d2, value; String padstring; double weight; int group_val; int current_length; String patch; read_posn = 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 */ encoding_method = 1; // if (debug) System.out.printf("Choosing Method 1\n"); } else { /* any AIs */ encoding_method = 2; // if (debug) System.out.printf("Choosing Mehod 2\n"); } if (((source.length() >= 20) && (encoding_method == 1)) && ((source.charAt(2) == '9') && (source.charAt(16) == '3'))) { /* Possibly encoding method > 2 */ // if (debug) System.out.printf("Checking for other methods\n"); 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) { encoding_method = 3; } } if (source.length() == 34) { if ((source.charAt(26) == '1') && (source.charAt(27) == '1')) { /* (01), (310x) and (11) - metric weight and production date */ encoding_method = 7; } if ((source.charAt(26) == '1') && (source.charAt(27) == '3')) { /* (01), (310x) and (13) - metric weight and packaging date */ encoding_method = 9; } if ((source.charAt(26) == '1') && (source.charAt(27) == '5')) { /* (01), (310x) and (15) - metric weight and "best before" date */ encoding_method = 11; } if ((source.charAt(26) == '1') && (source.charAt(27) == '7')) { /* (01), (310x) and (17) - metric weight and expiration date */ encoding_method = 13; } } } } // if (debug) System.out.printf("Now using method %d\n", encoding_method); } 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) { encoding_method = 4; } } else { weight /= 100.0; if (weight <= 99.99) { encoding_method = 4; } } } if (source.length() == 34) { if ((source.charAt(26) == '1') && (source.charAt(27) == '1')) { /* (01), (320x) and (11) - English weight and production date */ encoding_method = 8; } if ((source.charAt(26) == '1') && (source.charAt(27) == '3')) { /* (01), (320x) and (13) - English weight and packaging date */ encoding_method = 10; } if ((source.charAt(26) == '1') && (source.charAt(27) == '5')) { /* (01), (320x) and (15) - English weight and "best before" date */ encoding_method = 12; } if ((source.charAt(26) == '1') && (source.charAt(27) == '7')) { /* (01), (320x) and (17) - English weight and expiration date */ encoding_method = 14; } } } } // if (debug) System.out.printf("Now using method %d\n", encoding_method); } 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) */ encoding_method = 5; } if ((source.charAt(18) == '3') && ((source.charAt(19) >= '0') && (source.charAt(19) <= '3'))) { /* (01) and (393x) */ encoding_method = 6; } // if (debug) System.out.printf("Now using method %d\n", encoding_method); } } encodeInfo += "Encoding Method: " + Integer.toString(encoding_method) + "\n"; switch (encoding_method) { /* Encoding method - Table 10 */ case 1: binary_string += "1XX"; read_posn = 16; break; case 2: binary_string += "00XX"; read_posn = 0; break; case 3: binary_string += "0100"; read_posn = source.length(); break; case 4: binary_string += "0101"; read_posn = source.length(); break; case 5: binary_string += "01100XX"; read_posn = 20; break; case 6: binary_string += "01101XX"; read_posn = 23; break; case 7: binary_string += "0111000"; read_posn = source.length(); break; case 8: binary_string += "0111001"; read_posn = source.length(); break; case 9: binary_string += "0111010"; read_posn = source.length(); break; case 10: binary_string += "0111011"; read_posn = source.length(); break; case 11: binary_string += "0111100"; read_posn = source.length(); break; case 12: binary_string += "0111101"; read_posn = source.length(); break; case 13: binary_string += "0111110"; read_posn = source.length(); break; case 14: binary_string += "0111111"; read_posn = source.length(); break; } // if (debug) System.out.printf("Setting binary = %s\n", binary_string); /* 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 < read_posn; i++) { if ((source.charAt(i) < '0') || (source.charAt(i) > '9')) { if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) { /* Something is wrong */ error_msg = "Invalid characters in input data"; return false; } } } /* Now encode the compressed data field */ // if (debug) System.out.printf("Proceeding to encode data\n"); if (encoding_method == 1) { /* Encoding method field "1" - general item identification data */ group_val = source.charAt(2) - '0'; for (j = 0; j < 4; j++) { if ((group_val & (0x08 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } for (i = 1; i < 5; i++) { group_val = 100 * (source.charAt(i * 3) - '0'); group_val += 10 * (source.charAt((i * 3) + 1) - '0'); group_val += source.charAt((i * 3) + 2) - '0'; for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } } if (encoding_method == 3) { /* Encoding method field "0100" - variable weight item (0,001 kilogram icrements) */ for (i = 1; i < 5; i++) { group_val = 100 * (source.charAt(i * 3) - '0'); group_val += 10 * (source.charAt((i * 3) + 1) - '0'); group_val += (source.charAt((i * 3) + 2) - '0'); for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } group_val = 0; for (i = 0; i < 6; i++) { group_val *= 10; group_val += source.charAt(20 + i) - '0'; } for (j = 0; j < 15; j++) { if ((group_val & (0x4000 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } if (encoding_method == 4) { /* Encoding method field "0101" - variable weight item (0,01 or 0,001 pound increment) */ for (i = 1; i < 5; i++) { group_val = 100 * (source.charAt(i * 3) - '0'); group_val += 10 * (source.charAt((i * 3) + 1) - '0'); group_val += (source.charAt((i * 3) + 2) - '0'); for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } group_val = 0; for (i = 0; i < 6; i++) { group_val *= 10; group_val += source.charAt(20 + i) - '0'; } if (source.charAt(19) == '3') { group_val = group_val + 10000; } for (j = 0; j < 15; j++) { if ((group_val & (0x4000 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } if ((encoding_method >= 7) && (encoding_method <= 14)) { /* Encoding method fields "0111000" through "0111111" - variable weight item plus date */ for (i = 1; i < 5; i++) { group_val = 100 * (source.charAt(i * 3) - '0'); group_val += 10 * (source.charAt((i * 3) + 1) - '0'); group_val += (source.charAt((i * 3) + 2) - '0'); for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } group_val = source.charAt(19) - '0'; for (i = 0; i < 5; i++) { group_val *= 10; group_val += source.charAt(21 + i) - '0'; } for (j = 0; j < 20; j++) { if ((group_val & (0x80000 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } if (source.length() == 34) { /* Date information is included */ group_val = ((10 * (source.charAt(28) - '0')) + (source.charAt(29) - '0')) * 384; group_val += (((10 * (source.charAt(30) - '0')) + (source.charAt(31) - '0')) - 1) * 32; group_val += (10 * (source.charAt(32) - '0')) + (source.charAt(33) - '0'); } else { group_val = 38400; } for (j = 0; j < 16; j++) { if ((group_val & (0x8000 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } if (encoding_method == 5) { /* Encoding method field "01100" - variable measure item and price */ for (i = 1; i < 5; i++) { group_val = 100 * (source.charAt(i * 3) - '0'); group_val += 10 * (source.charAt((i * 3) + 1) - '0'); group_val += (source.charAt((i * 3) + 2) - '0'); for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } switch (source.charAt(19)) { case '0': binary_string += "00"; break; case '1': binary_string += "01"; break; case '2': binary_string += "10"; break; case '3': binary_string += "11"; break; } } if (encoding_method == 6) { /* Encoding method "01101" - variable measure item and price with ISO 4217 Currency Code */ for (i = 1; i < 5; i++) { group_val = 100 * (source.charAt(i * 3) - '0'); group_val += 10 * (source.charAt((i * 3) + 1) - '0'); group_val += (source.charAt((i * 3) + 2) - '0'); for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "1"; } } } switch (source.charAt(19)) { case '0': binary_string += "00"; break; case '1': binary_string += "01"; break; case '2': binary_string += "10"; break; case '3': binary_string += "11"; break; } group_val = 0; for (i = 0; i < 3; i++) { group_val *= 10; group_val += source.charAt(20 + i) - '0'; } for (j = 0; j < 10; j++) { if ((group_val & (0x200 >> j)) == 0) { binary_string += "0"; } else { binary_string += "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 */ general_field = source.substring(read_posn); general_field_type = new encodeMode[general_field.length()]; // if (debug) System.out.printf("General field data = %s\n", general_field); if (general_field.length() != 0) { latch = false; for (i = 0; i < general_field.length(); i++) { /* Table 13 - ISO/IEC 646 encodation */ if ((general_field.charAt(i) < ' ') || (general_field.charAt(i) > 'z')) { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } else { general_field_type[i] = encodeMode.ISOIEC; } if (general_field.charAt(i) == '#') { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } if (general_field.charAt(i) == '$') { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } if (general_field.charAt(i) == '@') { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } if (general_field.charAt(i) == 92) { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } if (general_field.charAt(i) == '^') { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } if (general_field.charAt(i) == 96) { general_field_type[i] = encodeMode.INVALID_CHAR; latch = true; } /* Table 12 - Alphanumeric encodation */ if ((general_field.charAt(i) >= 'A') && (general_field.charAt(i) <= 'Z')) { general_field_type[i] = encodeMode.ALPHA_OR_ISO; } if (general_field.charAt(i) == '*') { general_field_type[i] = encodeMode.ALPHA_OR_ISO; } if (general_field.charAt(i) == ',') { general_field_type[i] = encodeMode.ALPHA_OR_ISO; } if (general_field.charAt(i) == '-') { general_field_type[i] = encodeMode.ALPHA_OR_ISO; } if (general_field.charAt(i) == '.') { general_field_type[i] = encodeMode.ALPHA_OR_ISO; } if (general_field.charAt(i) == '/') { general_field_type[i] = encodeMode.ALPHA_OR_ISO; } /* Numeric encodation */ if ((general_field.charAt(i) >= '0') && (general_field.charAt(i) <= '9')) { general_field_type[i] = encodeMode.ANY_ENC; } if (general_field.charAt(i) == '[') { /* FNC1 can be encoded in any system */ general_field_type[i] = encodeMode.ANY_ENC; } } if (latch) { error_msg = "Invalid characters in input data"; return false; } for (i = 0; i < general_field.length() - 1; i++) { if ((general_field_type[i] == encodeMode.ISOIEC) && (general_field.charAt(i + 1) == '[')) { general_field_type[i + 1] = encodeMode.ISOIEC; } } for (i = 0; i < general_field.length() - 1; i++) { if ((general_field_type[i] == encodeMode.ALPHA_OR_ISO) && (general_field.charAt(i + 1) == '[')) { general_field_type[i + 1] = encodeMode.ALPHA_OR_ISO; } } // if (debug) { // System.out.println("General field length = " + general_field.length()); // } latch = applyGeneralFieldRules(); /* Set initial mode if not NUMERIC */ if (general_field_type[0] == encodeMode.ALPHA) { binary_string += "0000"; /* Alphanumeric latch */ last_mode = encodeMode.ALPHA; } if (general_field_type[0] == encodeMode.ISOIEC) { binary_string += "0000"; /* Alphanumeric latch */ binary_string += "00100"; /* ISO/IEC 646 latch */ last_mode = encodeMode.ISOIEC; } i = 0; do { // if (debug) System.out.printf("Processing character %d ", i); switch (general_field_type[i]) { case NUMERIC: // if (debug) System.out.printf("as NUMERIC:"); if (last_mode != encodeMode.NUMERIC) { binary_string += "000"; /* Numeric latch */ // if (debug) System.out.printf("<NUMERIC LATCH>\n"); } // if (debug) System.out.printf(" %c%c > ", general_field.charAt(i), // general_field.charAt(i + 1)); if (general_field.charAt(i) != '[') { d1 = general_field.charAt(i) - '0'; } else { d1 = 10; } if (general_field.charAt(i + 1) != '[') { d2 = general_field.charAt(i + 1) - '0'; } else { d2 = 10; } value = (11 * d1) + d2 + 8; for (j = 0; j < 7; j++) { if ((value & (0x40 >> j)) != 0) { binary_string += "1"; // if (debug) System.out.print("1"); } else { binary_string += "0"; // if (debug) System.out.print("0"); } } i += 2; // if (debug) System.out.printf("\n"); last_mode = encodeMode.NUMERIC; break; case ALPHA: // if (debug) System.out.printf("as ALPHA\n"); if (i != 0) { if (last_mode == encodeMode.NUMERIC) { binary_string += "0000"; /* Alphanumeric latch */ } if (last_mode == encodeMode.ISOIEC) { binary_string += "00100"; /* Alphanumeric latch */ } } if ((general_field.charAt(i) >= '0') && (general_field.charAt(i) <= '9')) { value = general_field.charAt(i) - 43; for (j = 0; j < 5; j++) { if ((value & (0x10 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } if ((general_field.charAt(i) >= 'A') && (general_field.charAt(i) <= 'Z')) { value = general_field.charAt(i) - 33; for (j = 0; j < 6; j++) { if ((value & (0x20 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } last_mode = encodeMode.ALPHA; if (general_field.charAt(i) == '[') { binary_string += "01111"; last_mode = encodeMode.NUMERIC; } /* FNC1/Numeric latch */ if (general_field.charAt(i) == '*') binary_string += "111010"; /* asterisk */ if (general_field.charAt(i) == ',') binary_string += "111011"; /* comma */ if (general_field.charAt(i) == '-') binary_string += "111100"; /* minus or hyphen */ if (general_field.charAt(i) == '.') binary_string += "111101"; /* period or full stop */ if (general_field.charAt(i) == '/') binary_string += "111110"; /* slash or solidus */ i++; break; case ISOIEC: // if (debug) System.out.printf("as ISOIEC\n"); if (i != 0) { if (last_mode == encodeMode.NUMERIC) { binary_string += "0000"; /* Alphanumeric latch */ binary_string += "00100"; /* ISO/IEC 646 latch */ } if (last_mode == encodeMode.ALPHA) { binary_string += "00100"; /* ISO/IEC 646 latch */ } } if ((general_field.charAt(i) >= '0') && (general_field.charAt(i) <= '9')) { value = general_field.charAt(i) - 43; for (j = 0; j < 5; j++) { if ((value & (0x10 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } if ((general_field.charAt(i) >= 'A') && (general_field.charAt(i) <= 'Z')) { value = general_field.charAt(i) - 1; for (j = 0; j < 7; j++) { if ((value & (0x40 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } if ((general_field.charAt(i) >= 'a') && (general_field.charAt(i) <= 'z')) { value = general_field.charAt(i) - 7; for (j = 0; j < 7; j++) { if ((value & (0x40 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } last_mode = encodeMode.ISOIEC; if (general_field.charAt(i) == '[') { binary_string += "01111"; last_mode = encodeMode.NUMERIC; } /* FNC1/Numeric latch */ if (general_field.charAt(i) == '!') binary_string += "11101000"; /* exclamation mark */ if (general_field.charAt(i) == 34) binary_string += "11101001"; /* quotation mark */ if (general_field.charAt(i) == 37) binary_string += "11101010"; /* percent sign */ if (general_field.charAt(i) == '&') binary_string += "11101011"; /* ampersand */ if (general_field.charAt(i) == 39) binary_string += "11101100"; /* apostrophe */ if (general_field.charAt(i) == '(') binary_string += "11101101"; /* left parenthesis */ if (general_field.charAt(i) == ')') binary_string += "11101110"; /* right parenthesis */ if (general_field.charAt(i) == '*') binary_string += "11101111"; /* asterisk */ if (general_field.charAt(i) == '+') binary_string += "11110000"; /* plus sign */ if (general_field.charAt(i) == ',') binary_string += "11110001"; /* comma */ if (general_field.charAt(i) == '-') binary_string += "11110010"; /* minus or hyphen */ if (general_field.charAt(i) == '.') binary_string += "11110011"; /* period or full stop */ if (general_field.charAt(i) == '/') binary_string += "11110100"; /* slash or solidus */ if (general_field.charAt(i) == ':') binary_string += "11110101"; /* colon */ if (general_field.charAt(i) == ';') binary_string += "11110110"; /* semicolon */ if (general_field.charAt(i) == '<') binary_string += "11110111"; /* less-than sign */ if (general_field.charAt(i) == '=') binary_string += "11111000"; /* equals sign */ if (general_field.charAt(i) == '>') binary_string += "11111001"; /* greater-than sign */ if (general_field.charAt(i) == '?') binary_string += "11111010"; /* question mark */ if (general_field.charAt(i) == '_') binary_string += "11111011"; /* underline or low line */ if (general_field.charAt(i) == ' ') binary_string += "11111100"; /* space */ i++; break; } current_length = i; if (latch) { current_length++; } } while (current_length < general_field.length()); // if (debug) System.out.printf("Resultant binary = %s\n", binary_string); // if (debug) System.out.printf("\tLength: %d\n", binary_string.length()); remainder = calculateRemainder (binary_string.length()); if (latch) { /* There is still one more numeric digit to encode */ // if (debug) System.out.printf("Adding extra (odd) numeric digit\n"); if (last_mode == encodeMode.NUMERIC) { if ((remainder >= 4) && (remainder <= 6)) { value = general_field.charAt(i) - '0'; value++; for (j = 0; j < 4; j++) { if ((value & (0x08 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } else { d1 = general_field.charAt(i) - '0'; d2 = 10; value = (11 * d1) + d2 + 8; for (j = 0; j < 7; j++) { if ((value & (0x40 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } } else { value = general_field.charAt(i) - 43; for (j = 0; j < 5; j++) { if ((value & (0x10 >> j)) != 0) { binary_string += "1"; } else { binary_string += "0"; } } } // if (debug) System.out.printf("Resultant binary = %s\n", binary_string); // if (debug) System.out.printf("\tLength: %d\n", binary_string.length()); } } if (binary_string.length() > 252) { error_msg = "Input too long"; return false; } remainder = calculateRemainder (binary_string.length()); /* Now add padding to binary string (7.2.5.5.4) */ i = remainder; if ((general_field.length() != 0) && (last_mode == encodeMode.NUMERIC)) { padstring = "0000"; i -= 4; } else { padstring = ""; } for (; i > 0; i -= 5) { padstring += "00100"; } binary_string += padstring.substring(0, remainder); /* Patch variable length symbol bit field */ patch = ""; if ((((binary_string.length() / 12) + 1) & 1) == 0) { patch += "0"; } else { patch += "1"; } if (binary_string.length() <= 156) { patch += "0"; } else { patch += "1"; } if (encoding_method == 1) { binary_string = binary_string.substring(0, 2) + patch + binary_string.substring(4); } if (encoding_method == 2) { binary_string = binary_string.substring(0, 3) + patch + binary_string.substring(5); } if ((encoding_method == 5) || (encoding_method == 6)) { binary_string = binary_string.substring(0, 6) + patch + binary_string.substring(8); } encodeInfo += "Binary length: " + Integer.toString(binary_string.length()) + "\n"; displayBinaryString(); // if (debug) System.out.printf("Resultant binary = %s\n", binary_string); // if (debug) System.out.printf("\tLength: %d\n", binary_string.length()); return true; } private static int calculateRemainder ( int binaryStringLength ) { int remainder = 12 - (binaryStringLength % 12); if (remainder == 12) { remainder = 0; } if (binaryStringLength < 36) { remainder = 36 - binaryStringLength; } return remainder; } private void displayBinaryString() { int i, nibble; /* Display binary string as hexadecimal */ encodeInfo += "Binary String: "; nibble = 0; for(i = 0; i < binary_string.length(); i++) { switch (i % 4) { case 0: if (binary_string.charAt(i) == '1') { nibble += 8; } break; case 1: if (binary_string.charAt(i) == '1') { nibble += 4; } break; case 2: if (binary_string.charAt(i) == '1') { nibble += 2; } break; case 3: if (binary_string.charAt(i) == '1') { nibble += 1; } encodeInfo += Integer.toHexString(nibble); nibble = 0; break; } } if ((binary_string.length() % 4) != 0) { encodeInfo += Integer.toHexString(nibble); } encodeInfo += "\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 block_count, i, j, k; encodeMode current, next, last; int[] blockLength = new int[200]; encodeMode[] blockType = new encodeMode[200]; block_count = 0; blockLength[block_count] = 1; blockType[block_count] = general_field_type[0]; for (i = 1; i < general_field.length(); i++) { current = general_field_type[i]; last = general_field_type[i - 1]; if (current == last) { blockLength[block_count] = blockLength[block_count] + 1; } else { block_count++; blockLength[block_count] = 1; blockType[block_count] = general_field_type[i]; } } block_count++; for (i = 0; i < block_count; i++) { current = blockType[i]; next = blockType[i + 1]; if ((current == encodeMode.ISOIEC) && (i != (block_count - 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 != (block_count - 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 == block_count - 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 (block_count > 1) { i = 1; while (i < block_count) { if (blockType[i - 1] == blockType[i]) { /* bring together */ blockLength[i - 1] = blockLength[i - 1] + blockLength[i]; j = i + 1; /* decreace the list */ while (j < block_count) { blockLength[j - 1] = blockLength[j]; blockType[j - 1] = blockType[j]; j++; } block_count--; i--; } i++; } } for (i = 0; i < block_count - 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 < block_count; i++) { for (k = 0; k < blockLength[i]; k++) { general_field_type[j] = blockType[i]; j++; } } if ((blockType[block_count - 1] == encodeMode.NUMERIC) && ((blockLength[block_count - 1] & 1) != 0)) { /* If the last block is numeric and an odd size, further processing needs to be done outside this procedure */ return true; } else { return false; } } 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; } }