/*
* 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;
import java.math.BigInteger;
/**
* Implements GS1 DataBar Limited
* According to ISO/IEC 24724:2011
* <p>
* Input data should be a 12 digit Global Trade Identification Number
* without check digit or Application Identifier [01].
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataBarLimited extends Symbol {
private int[] t_even_ltd = {
28, 728, 6454, 203, 2408, 1, 16632
};
private int[] modules_odd_ltd = {
17, 13, 9, 15, 11, 19, 7
};
private int[] modules_even_ltd = {
9, 13, 17, 11, 15, 7, 19
};
private int[] widest_odd_ltd = {
6, 5, 3, 5, 4, 8, 1
};
private int[] widest_even_ltd = {
3, 4, 6, 4, 5, 1, 8
};
private 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 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;
}
protected void unsetLinkageFlag() {
linkageFlag = false;
}
@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];
String bin;
String notbin;
boolean bar_latch;
int writer;
int check_digit = 0;
int count = 0;
String hrt;
int compositeOffset = 0;
if (content.length() > 13) {
error_msg = "Input too long";
return false;
}
if (!(content.matches("[0-9]+?"))) {
error_msg = "Invalid characters in input";
return false;
}
if (content.length() == 13) {
if ((content.charAt(0) != '0') && (content.charAt(0) != '1')) {
error_msg = "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"));
// if (debug) {
// System.out.println("left " + left_reg.toString());
// System.out.println("right " + right_reg.toString());
// }
left_group = 0;
if (left_reg.compareTo(new BigInteger("183063")) == 1) {
left_group = 1;
}
if (left_reg.compareTo(new BigInteger("820063")) == 1) {
left_group = 2;
}
if (left_reg.compareTo(new BigInteger("1000775")) == 1) {
left_group = 3;
}
if (left_reg.compareTo(new BigInteger("1491020")) == 1) {
left_group = 4;
}
if (left_reg.compareTo(new BigInteger("1979844")) == 1) {
left_group = 5;
}
if (left_reg.compareTo(new BigInteger("1996938")) == 1) {
left_group = 6;
}
right_group = 0;
if (right_reg.compareTo(new BigInteger("183063")) == 1) {
right_group = 1;
}
if (right_reg.compareTo(new BigInteger("820063")) == 1) {
right_group = 2;
}
if (right_reg.compareTo(new BigInteger("1000775")) == 1) {
right_group = 3;
}
if (right_reg.compareTo(new BigInteger("1491020")) == 1) {
right_group = 4;
}
if (right_reg.compareTo(new BigInteger("1979844")) == 1) {
right_group = 5;
}
if (right_reg.compareTo(new BigInteger("1996938")) == 1) {
right_group = 6;
}
encodeInfo += "Data Characters: " + Integer.toString(left_group + 1) +
" " + Integer.toString(right_group + 1) + "\n";
// if (debug) {
// System.out.println("left group " + (left_group + 1));
// System.out.println("right group " + (right_group + 1));
// }
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];
// if (debug) {
// System.out.println("left char " + left_character);
// System.out.println("right char " + right_character);
// System.out.println("left even " + left_even);
// System.out.println("right even " + right_even);
// System.out.println("left odd " + left_odd);
// System.out.println("right odd " + right_odd);
// }
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 += "Checksum: " + Integer.toString(checksum) + "\n";
// if (debug) {
// System.out.println("checksum " + checksum);
// }
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 = "";
notbin = "";
writer = 0;
bar_latch = false;
for (i = 0; i < 46; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (bar_latch) {
bin += "1";
notbin += "0";
} else {
bin += "0";
notbin += "1";
}
writer++;
}
if (bar_latch) {
bar_latch = false;
} else {
bar_latch = true;
}
}
if (symbol_width < (writer + 20)) {
symbol_width = writer + 20;
}
// /* add separator pattern if composite symbol */
// if(symbol->symbology == BARCODE_RSS_LTD_CC) {
// for(i = 4; i < 70; i++) {
// if(!(module_is_set(symbol, separator_row + 1, i))) {
// set_module(symbol, separator_row, i);
// }
// }
// }
/* Calculate check digit from Annex A and place human readable text */
readable = "(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 += hrt;
if (linkageFlag) {
compositeOffset = 1;
}
row_count = 1 + compositeOffset;
row_height = new int[1 + compositeOffset];
row_height[0 + compositeOffset] = -1;
pattern = new String[1 + compositeOffset];
pattern[0 + compositeOffset] = "0:" + bin2pat(bin);
if (linkageFlag) {
// Add composite symbol seperator
notbin = notbin.substring(4, 70);
row_height[0] = 1;
pattern[0] = "0:04" + bin2pat(notbin);
}
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;
}
}