/*
* 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 static uk.org.okapibarcode.backend.HumanReadableLocation.NONE;
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP;
import java.awt.geom.Rectangle2D;
/**
* Implements the Code 2 of 5 family of barcode standards.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Code2Of5 extends Symbol {
private enum ToFMode {
MATRIX, INDUSTRIAL, IATA, DATA_LOGIC, INTERLEAVED, ITF14, DPLEIT, DPIDENT
}
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;
/**
* 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;
}
/**
* 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;
}
/**
* 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]+"))) {
error_msg = "Invalid characters in input";
return false;
}
String dest = "311111";
for (int i = 0; i < content.length(); i++) {
dest += C25_MATRIX_TABLE[Character.getNumericValue(content.charAt(i))];
}
dest += "31111";
readable = content;
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
private boolean industrialTof() {
if (!(content.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
String dest = "313111";
readable = content;
for (int i = 0; i < readable.length(); i++) {
dest += C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))];
}
dest += "31113";
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
private boolean iataTof() {
if (!(content.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
String dest = "1111";
readable = content;
for (int i = 0; i < readable.length(); i++) {
dest += C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))];
}
dest += "311";
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
private boolean dataLogic() {
if (!(content.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
String dest = "1111";
readable = content;
for (int i = 0; i < readable.length(); i++) {
dest += C25_MATRIX_TABLE[Character.getNumericValue(readable.charAt(i))];
}
dest += "311";
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
private boolean interleavedTof() {
int i;
String dest;
if ((content.length() & 1) == 0) {
readable = content;
} else {
readable = "0" + content;
}
if (!(readable.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
dest = "1111";
for (i = 0; i < readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
pattern = new String[] { dest };
row_count = 1;
row_height = 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();
String dest;
if (!(content.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
if (input_length > 13) {
error_msg = "Input data too long";
return false;
}
readable = "";
for (i = input_length; i < 13; i++) {
readable += "0";
}
readable += content;
for (i = 12; i >= 0; i--) {
count += readable.charAt(i) - '0';
if ((i & 1) == 0) {
count += 2 * (readable.charAt(i) - '0');
}
}
readable += (char)(((10 - (count % 10)) % 10) + '0');
encodeInfo += "Check Digit: " + (char)(((10 - (count % 10)) % 10) + '0');
encodeInfo += '\n';
dest = "1111";
for (i = 0; i < readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
private boolean deutschePostLeitcode() {
int i, count = 0;
int input_length = content.length();
String dest;
if (!(content.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
if (input_length > 13) {
error_msg = "Input data too long";
return false;
}
readable = "";
for (i = input_length; i < 13; i++) {
readable += "0";
}
readable += content;
for (i = 12; i >= 0; i--) {
count += 4 * (readable.charAt(i) - '0');
if ((i & 1) != 0) {
count += 5 * (readable.charAt(i) - '0');
}
}
readable += (char)(((10 - (count % 10)) % 10) + '0');
encodeInfo += "Check digit: " + (char)(((10 - (count % 10)) % 10) + '0');
encodeInfo += '\n';
dest = "1111";
for (i = 0; i < readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
private boolean deutschePostIdentcode() {
int i, count = 0;
int input_length = content.length();
String dest;
if (!(content.matches("[0-9]+"))) {
error_msg = "Invalid characters in input";
return false;
}
if (input_length > 11) {
error_msg = "Input data too long";
return false;
}
readable = "";
for (i = input_length; i < 11; i++) {
readable += "0";
}
readable += content;
for (i = 10; i >= 0; i--) {
count += 4 * (readable.charAt(i) - '0');
if ((i & 1) != 0) {
count += 5 * (readable.charAt(i) - '0');
}
}
readable += (char)(((10 - (count % 10)) % 10) + '0');
encodeInfo += "Check Digit: " + (char)(((10 - (count % 10)) % 10) + '0');
encodeInfo += '\n';
dest = "1111";
for (i = 0; i < readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
pattern = new String[] { dest };
row_count = 1;
row_height = new int[] { -1 };
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
rectangles.clear();
texts.clear();
int baseY;
if (humanReadableLocation == 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 (row_height[0] == -1) {
h = default_height;
} else {
h = row_height[0];
}
if (w != 0 && h != 0) {
Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h);
rectangles.add(rect);
}
symbol_width = (int) Math.ceil(x + w + (2 * offset));
}
black = !black;
x += w;
}
symbol_height = h;
if (mode == ToFMode.ITF14) {
// Add bounding box
Rectangle2D.Double topBar = new Rectangle2D.Double(0, baseY, symbol_width, 4);
Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, baseY + symbol_height - 4, symbol_width, 4);
Rectangle2D.Double leftBar = new Rectangle2D.Double(0, baseY, 4, symbol_height);
Rectangle2D.Double rightBar = new Rectangle2D.Double(symbol_width - 4, baseY, 4, symbol_height);
rectangles.add(topBar);
rectangles.add(bottomBar);
rectangles.add(leftBar);
rectangles.add(rightBar);
}
if (humanReadableLocation != NONE && !readable.isEmpty()) {
double baseline;
if (humanReadableLocation == TOP) {
baseline = fontSize;
} else {
baseline = getHeight() + fontSize;
}
double centerX = getWidth() / 2.0;
texts.add(new TextBox(centerX, baseline, readable));
}
}
/** {@inheritDoc} */
@Override
protected double getModuleWidth(int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return moduleWidthRatio;
}
}
}