/*
* 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 Data Matrix ECC 200 bar code symbology According to ISO/IEC
* 16022:2006
* <p>
* Data Matrix is a 2D matrix symbology capable of encoding characters in the
* ISO/IEC 8859-1 (Latin-1) character set.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataMatrix extends Symbol {
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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
};
static 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 enum dm_mode {
NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256
}
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 <code>false</code> the
* symbol will be the smallest available for the amount of data given. When
* set as <code>true</code> the encoding will not use rectangular symbols.
*
* @param input Forces a square symbol when set to <code>true</code>
*/
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 <code>forceSquare</code> mode has been invoked.
* <table summary="Available Data Matrix symbol sizes">
* <tbody>
* <tr>
* <th><p>
* Input</p></th>
* <th><p>
* Symbol Size</p></th>
* <th><p>
* Input</p></th>
* <th><p>
* Symbol Size</p></th>
* </tr>
* <tr>
* <td><p>
* 1</p></td>
* <td><p>
* 10 x 10</p></td>
* <td><p>
* 16</p></td>
* <td><p>
* 64 x 64</p></td>
* </tr>
* <tr>
* <td><p>
* 2</p></td>
* <td><p>
* 12 x 12</p></td>
* <td><p>
* 17</p></td>
* <td><p>
* 72 x 72</p></td>
* </tr>
* <tr>
* <td><p>
* 3</p></td>
* <td><p>
* 14 x 14</p></td>
* <td><p>
* 18</p></td>
* <td><p>
* 80 x 80</p></td>
* </tr>
* <tr>
* <td><p>
* 4</p></td>
* <td><p>
* 16 x 16</p></td>
* <td><p>
* 19</p></td>
* <td><p>
* 88 x 88</p></td>
* </tr>
* <tr>
* <td><p>
* 5</p></td>
* <td><p>
* 18 x 18</p></td>
* <td><p>
* 20</p></td>
* <td><p>
* 96 x 96</p></td>
* </tr>
* <tr>
* <td><p>
* 6</p></td>
* <td><p>
* 20 x 20</p></td>
* <td><p>
* 21</p></td>
* <td><p>
* 104 x 104</p></td>
* </tr>
* <tr>
* <td><p>
* 7</p></td>
* <td><p>
* 22 x 22</p></td>
* <td><p>
* 22</p></td>
* <td><p>
* 120 x 120</p></td>
* </tr>
* <tr>
* <td><p>
* 8</p></td>
* <td><p>
* 24 x 24</p></td>
* <td><p>
* 23</p></td>
* <td><p>
* 132 x 132</p></td>
* </tr>
* <tr>
* <td><p>
* 9</p></td>
* <td><p>
* 26 x 26</p></td>
* <td><p>
* 24</p></td>
* <td><p>
* 144 x 144</p></td>
* </tr>
* <tr>
* <td><p>
* 10</p></td>
* <td><p>
* 32 x 32</p></td>
* <td><p>
* 25</p></td>
* <td><p>
* 8 x 18</p></td>
* </tr>
* <tr>
* <td><p>
* 11</p></td>
* <td><p>
* 36 x 36</p></td>
* <td><p>
* 26</p></td>
* <td><p>
* 8 x 32</p></td>
* </tr>
* <tr>
* <td><p>
* 12</p></td>
* <td><p>
* 40 x 40</p></td>
* <td><p>
* 27</p></td>
* <td><p>
* 12 x 26</p></td>
* </tr>
* <tr>
* <td><p>
* 13</p></td>
* <td><p>
* 44 x 44</p></td>
* <td><p>
* 28</p></td>
* <td><p>
* 12 x 36</p></td>
* </tr>
* <tr>
* <td><p>
* 14</p></td>
* <td><p>
* 48 x 48</p></td>
* <td><p>
* 29</p></td>
* <td><p>
* 16 x 36</p></td>
* </tr>
* <tr>
* <td><p>
* 15</p></td>
* <td><p>
* 52 x 52</p></td>
* <td><p>
* 30</p></td>
* <td><p>
* 16 x 48</p></td>
* </tr>
* </tbody>
* </table>
*
* @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;
String 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) {
error_msg = "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 */
error_msg = "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 = "";
pattern = new String[H];
row_count = H;
row_height = new int[H];
for (y = H - 1; y >= 0; y--) {
bin = "";
for (x = 0; x < W; x++) {
if (grid[W * y + x] == 1) {
bin += "1";
} else {
bin += "0";
}
}
pattern[(H - y) - 1] = bin2pat(bin);
row_height[(H - y) - 1] = 1;
}
encodeInfo += "Grid Size: " + W + " X " + H + "\n";
encodeInfo += "Data Codewords: " + datablock + "\n";
encodeInfo += "ECC Codewords: " + rsblock + "\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 += "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 += "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 += "ECI ";
}
if (readerInit) {
if (inputDataType == DataType.GS1) {
error_msg = "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 += "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 += "Micro05 ";
} else {
target[tp] = 237;
encodeInfo += "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 += Integer.toString(target[tp] - 130) + " ";
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 += "C40 ";
break;
case DM_TEXT:
target[tp] = 239;
tp++;
binary[binary_length] = ' ';
binary_length++;
encodeInfo += "TEX ";
break;
case DM_X12:
target[tp] = 238;
tp++;
binary[binary_length] = ' ';
binary_length++;
encodeInfo += "X12 ";
break;
case DM_EDIFACT:
target[tp] = 240;
tp++;
binary[binary_length] = ' ';
binary_length++;
encodeInfo += "EDI ";
break;
case DM_BASE256:
target[tp] = 231;
tp++;
binary[binary_length] = ' ';
binary_length++;
encodeInfo += "BAS ";
break;
}
} else {
if (inputData[sp] > 127) {
target[tp] = 235; /* FNC4 */
encodeInfo += "FNC4 ";
tp++;
target[tp] = (inputData[sp] - 128) + 1;
encodeInfo += Integer.toString(target[tp] - 1) + " ";
tp++;
binary[binary_length] = ' ';
binary_length++;
binary[binary_length] = ' ';
binary_length++;
} else {
if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) {
target[tp] = 232; /* FNC1 */
encodeInfo += "FNC1 ";
} else {
target[tp] = inputData[sp] + 1;
encodeInfo += Integer.toString(target[tp] - 1) + " ";
}
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 += "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 += "(" + Integer.toString(process_buffer[0]) +
" " + Integer.toString(process_buffer[1]) + " " +
Integer.toString(process_buffer[2]) + ") ";
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 += "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 += "(" + Integer.toString(process_buffer[0]) +
" " + Integer.toString(process_buffer[1]) + " " +
Integer.toString(process_buffer[2]) + ") ";
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 += "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 += "(" + Integer.toString(process_buffer[0]) +
" " + Integer.toString(process_buffer[1]) + " " +
Integer.toString(process_buffer[2]) + ") ";
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 += "(" + Integer.toString(process_buffer[0]) +
" " + Integer.toString(process_buffer[1]) + " " +
Integer.toString(process_buffer[2]) + ") ";
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 += Integer.toString(target[tp]) + " ";
tp++;
sp++;
binary[binary_length] = 'b';
binary_length++;
} else {
next_mode = dm_mode.DM_ASCII;
encodeInfo += "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 += "\n";
encodeInfo += "Codewords: ";
for (i = 0; i < tp; i++) {
encodeInfo += Integer.toString(target[i]) + " ";
}
encodeInfo += "\n";
// if (debug) {
// System.out.printf("\nHex Data: ");
// for (i = 0; i < tp; i++) {
// System.out.printf("%02X ", target[i]);
// }
// System.out.printf("\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;
}
// if (debug) {
// System.out.printf("+Buffer=: ");
// for (int i = 0; i < target_length; i++) {
// System.out.printf("%02X ", target[i]);
// }
//
// System.out.printf("\n\n");
// }
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 - 0, p, 3);
placeBit(NR, NC, r - 0, c - 2, p, 2);
placeBit(NR, NC, r - 0, c - 1, p, 1);
placeBit(NR, NC, r - 0, c - 0, 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;
}
}