integration of subprojects barcode, chart, io-vector, layout-pdfbox, png

This commit is contained in:
Jörg Prante 2020-11-03 11:07:42 +01:00
parent 588fd3394b
commit 58a6e85062
1274 changed files with 115564 additions and 1636 deletions

6
.gitignore vendored
View file

@ -1,13 +1,9 @@
/data
/work
/logs
/.idea /.idea
/target
.DS_Store .DS_Store
/.settings /.settings
/.classpath /.classpath
/.project /.project
/.gradle /.gradle
/build build
*~ *~
/*.iml /*.iml

View file

@ -1,5 +0,0 @@
This is a Java 8 version of the project
https://github.com/gredler/jdk9-png-writer-backport
by Daniel Gredler

7
barcode/NOTICE.txt Normal file
View file

@ -0,0 +1,7 @@
This work is based on Okapi Barcode Library
https://github.com/woo-j/OkapiBarcode
(Apache License 2.0)
as of 2016

7
barcode/build.gradle Normal file
View file

@ -0,0 +1,7 @@
dependencies {
testImplementation project(':io-vector')
testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.0"
testImplementation "junit:junit:4.12"
testImplementation "com.google.zxing:javase:${project.property('zxing.version')}"
testImplementation "org.reflections:reflections:${project.property('reflections.version')}"
}

View file

@ -0,0 +1,6 @@
module org.xbib.graphics.barcode {
exports org.xbib.graphics.barcode;
exports org.xbib.graphics.barcode.util;
exports org.xbib.graphics.barcode.render;
requires transitive java.desktop;
}

View file

@ -0,0 +1,363 @@
package org.xbib.graphics.barcode;
import org.xbib.graphics.barcode.util.ReedSolomon;
import java.awt.geom.Rectangle2D;
/**
* Implements the <a href="http://auspost.com.au/media/documents/a-guide-to-printing-the-4state-barcode-v31-mar2012.pdf">Australia Post 4-State barcode</a>.
*/
public class AustraliaPost extends Symbol {
private static final char[] CHARACTER_SET = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '#'
};
private static final String[] N_ENCODING_TABLE = {
"00", "01", "02", "10", "11", "12", "20", "21", "22", "30"
};
private static final String[] C_ENCODING_TABLE = {
"222", "300", "301", "302", "310", "311", "312", "320", "321", "322",
"000", "001", "002", "010", "011", "012", "020", "021", "022", "100", "101", "102", "110",
"111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221",
"023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132", "133", "203",
"213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333",
"003", "013"
};
private static final String[] BAR_VALUE_TABLE = {
"000", "001", "002", "003", "010", "011", "012", "013", "020", "021",
"022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110", "111", "112",
"113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203",
"210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233", "300",
"301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331",
"332", "333"
};
private ausMode mode;
;
public AustraliaPost() {
mode = ausMode.AUSPOST;
}
/**
* Specify encoding of Australia Post Standard Customer Barcode,
* Customer Barcode 2 or Customer Barcode 3 (37-bar, 52-bar and 67-bar
* symbols) depending on input data length. Valid data characters are 0-9,
* A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and
* should not be included in the input data.
* <p>
* Input data should include a 8-digit Deliver Point ID
* (DPID) optionally followed by customer information as shown below.
* <table>
* <caption>Permitted Australia Post input data</caption>
* <tbody>
* <tr>
* <th><p>Input Length</p></th>
* <th><p>Required Input Format</p></th>
* <th><p>Symbol Length</p></th>
* <th><p>FCC</p></th>
* <th><p>Encoding Table</p></th>
* </tr>
* <tr>
* <td><p>8</p></td>
* <td><p>99999999</p></td>
* <td><p>37-bar</p></td>
* <td><p>11</p></td>
* <td><p>None</p></td>
* </tr>
* <tr>
* <td><p>13</p></td>
* <td><p>99999999AAAAA</p></td>
* <td><p>52-bar</p></td>
* <td><p>59</p></td>
* <td><p>C</p></td>
* </tr>
* <tr>
* <td><p>16</p></td>
* <td><p>9999999999999999</p></td>
* <td><p>52-bar</p></td>
* <td><p>59</p></td>
* <td><p>N</p></td>
* </tr>
* <tr>
* <td><p>18</p></td>
* <td><p>99999999AAAAAAAAAA</p></td>
* <td><p>67-bar</p></td>
* <td><p>62</p></td>
* <td><p>C</p></td>
* </tr>
* <tr>
* <td><p>23</p></td>
* <td><p>99999999999999999999999</p></td>
* <td><p>67-bar</p></td>
* <td><p>62</p></td>
* <td><p>N</p></td>
* </tr>
* </tbody>
* </table>
*/
public void setPostMode() {
mode = ausMode.AUSPOST;
}
/**
* Specify encoding of a Reply Paid version of the Australia Post
* 4-State Barcode (FCC 45) which requires an 8-digit DPID input.
*/
public void setReplyMode() {
mode = ausMode.AUSREPLY;
}
/**
* Specify encoding of a Routing version of the Australia Post 4-State
* Barcode (FCC 87) which requires an 8-digit DPID input.
*/
public void setRouteMode() {
mode = ausMode.AUSROUTE;
}
/**
* Specify encoding of a Redirection version of the Australia Post 4-State
* Barcode (FCC 92) which requires an 8-digit DPID input.
*/
public void setRedirectMode() {
mode = ausMode.AUSREDIRECT;
}
@Override
public boolean encode() {
String formatControlCode = "00";
String deliveryPointId;
StringBuilder barStateValues;
StringBuilder zeroPaddedInput = new StringBuilder();
int i;
switch (mode) {
case AUSPOST:
switch (content.length()) {
case 8:
formatControlCode = "11";
break;
case 13:
formatControlCode = "59";
break;
case 16:
formatControlCode = "59";
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
break;
case 18:
formatControlCode = "62";
break;
case 23:
formatControlCode = "62";
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
break;
default:
errorMsg.append("Auspost input is wrong length");
return false;
}
break;
case AUSREPLY:
if (content.length() > 8) {
errorMsg.append("Auspost input is too long");
return false;
} else {
formatControlCode = "45";
}
break;
case AUSROUTE:
if (content.length() > 8) {
errorMsg.append("Auspost input is too long");
return false;
} else {
formatControlCode = "87";
}
break;
case AUSREDIRECT:
if (content.length() > 8) {
errorMsg.append("Auspost input is too long");
return false;
} else {
formatControlCode = "92";
}
break;
}
encodeInfo.append("FCC: ").append(formatControlCode).append('\n');
if (mode != ausMode.AUSPOST) {
for (i = content.length(); i < 8; i++) {
zeroPaddedInput.append("0");
}
}
zeroPaddedInput.append(content);
if (!(content.matches("[0-9A-Za-z #]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
/* Verify that the first 8 characters are numbers */
deliveryPointId = zeroPaddedInput.substring(0, 8);
if (!(deliveryPointId.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in DPID");
return false;
}
encodeInfo.append("DPID: ").append(deliveryPointId).append('\n');
/* Start */
barStateValues = new StringBuilder("13");
/* Encode the FCC */
for (i = 0; i < 2; i++) {
barStateValues.append(N_ENCODING_TABLE[formatControlCode.charAt(i) - '0']);
}
/* Delivery Point Identifier (DPID) */
for (i = 0; i < 8; i++) {
barStateValues.append(N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0']);
}
/* Customer Information */
switch (zeroPaddedInput.length()) {
case 13:
case 18:
for (i = 8; i < zeroPaddedInput.length(); i++) {
barStateValues.append(C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]);
}
break;
case 16:
case 23:
for (i = 8; i < zeroPaddedInput.length(); i++) {
barStateValues.append(N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)]);
}
break;
}
/* Filler bar */
switch (barStateValues.length()) {
case 22:
case 37:
case 52:
barStateValues.append("3");
break;
}
/* Reed Solomon error correction */
barStateValues.append(calcReedSolomon(barStateValues.toString()));
/* Stop character */
barStateValues.append("13");
encodeInfo.append("Total length: ").append(barStateValues.length()).append('\n');
encodeInfo.append("Encoding: ");
for (i = 0; i < barStateValues.length(); i++) {
switch (barStateValues.charAt(i)) {
case '1':
encodeInfo.append("A");
break;
case '2':
encodeInfo.append("D");
break;
case '0':
encodeInfo.append("F");
break;
case '3':
encodeInfo.append("T");
break;
}
}
encodeInfo.append("\n");
readable = new StringBuilder();
pattern = new String[1];
pattern[0] = barStateValues.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
private String calcReedSolomon(String oldBarStateValues) {
ReedSolomon rs = new ReedSolomon();
StringBuilder newBarStateValues = new StringBuilder();
/* Adds Reed-Solomon error correction to auspost */
int barStateCount;
int tripleValueCount = 0;
int[] tripleValue = new int[31];
for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) {
tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4)
+ barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2)
+ barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0);
}
rs.init_gf(0x43);
rs.init_code(4, 1);
rs.encode(tripleValueCount, tripleValue);
for (barStateCount = 4; barStateCount > 0; barStateCount--) {
newBarStateValues.append(BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)]);
}
return newBarStateValues.toString();
}
private int barStateToDecimal(char oldBarStateValues, int shift) {
return (oldBarStateValues - '0') << shift;
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
getRectangles().clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
switch (pattern[0].charAt(xBlock)) {
case '1':
y = 0;
h = 5;
break;
case '2':
y = 3;
h = 5;
break;
case '0':
y = 0;
h = 8;
break;
case '3':
y = 3;
h = 2;
break;
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
x += 2;
}
symbolWidth = pattern[0].length() * 3;
symbolHeight = 8;
}
private enum ausMode {AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,168 @@
package org.xbib.graphics.barcode;
import org.xbib.graphics.barcode.util.ReedSolomon;
/**
* Implements Aztec Runes bar code symbology.
* According to ISO/IEC 24778:2008 Annex A
* Aztec Runes is a fixed-size matrix symbology which can encode whole
* integer values between 0 and 255.
*/
public class AztecRune extends Symbol {
private int[] bitPlacementMap = {
1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9,
28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10,
27, 1, 0, 1, 0, 0, 0, 1, 0, 1, 11,
26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12,
25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13,
24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14,
23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0
};
@Override
public boolean encode() {
int decimalValue = 0;
int i;
int row;
int column;
StringBuilder binaryDataStream;
StringBuilder reversedBinaryDataStream;
int[] dataCodeword = new int[3];
int[] errorCorrectionCodeword = new int[6];
ReedSolomon rs = new ReedSolomon();
StringBuilder rowBinary;
if (content.length() > 3) {
errorMsg.append("Input too large");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid input data");
return false;
}
switch (content.length()) {
case 3:
decimalValue = 100 * (content.charAt(0) - '0');
decimalValue += 10 * (content.charAt(1) - '0');
decimalValue += (content.charAt(2) - '0');
break;
case 2:
decimalValue = 10 * (content.charAt(0) - '0');
decimalValue += (content.charAt(1) - '0');
break;
case 1:
decimalValue = (content.charAt(0) - '0');
break;
}
if (decimalValue > 255) {
errorMsg.append("Input too large");
return false;
}
binaryDataStream = new StringBuilder();
for (i = 0x80; i > 0; i = i >> 1) {
if ((decimalValue & i) != 0) {
binaryDataStream.append("1");
} else {
binaryDataStream.append("0");
}
}
dataCodeword[0] = 0;
dataCodeword[1] = 0;
for (i = 0; i < 2; i++) {
if (binaryDataStream.charAt(i * 4) == '1') {
dataCodeword[i] += 8;
}
if (binaryDataStream.charAt((i * 4) + 1) == '1') {
dataCodeword[i] += 4;
}
if (binaryDataStream.charAt((i * 4) + 2) == '1') {
dataCodeword[i] += 2;
}
if (binaryDataStream.charAt((i * 4) + 3) == '1') {
dataCodeword[i] += 1;
}
}
rs.init_gf(0x13);
rs.init_code(5, 1);
rs.encode(2, dataCodeword);
for (i = 0; i < 5; i++) {
errorCorrectionCodeword[i] = rs.getResult(i);
}
for (i = 0; i < 5; i++) {
if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
}
reversedBinaryDataStream = new StringBuilder();
for (i = 0; i < binaryDataStream.length(); i++) {
if ((i & 1) == 0) {
if (binaryDataStream.charAt(i) == '0') {
reversedBinaryDataStream.append("1");
} else {
reversedBinaryDataStream.append("0");
}
} else {
reversedBinaryDataStream.append(binaryDataStream.charAt(i));
}
}
encodeInfo.append("Binary: ").append(reversedBinaryDataStream).append("\n");
rowBinary = new StringBuilder();
readable = new StringBuilder();
pattern = new String[11];
rowCount = 11;
rowHeight = new int[11];
for (row = 0; row < 11; row++) {
for (column = 0; column < 11; column++) {
if (bitPlacementMap[(row * 11) + column] == 1) {
rowBinary.append("1");
}
if (bitPlacementMap[(row * 11) + column] == 0) {
rowBinary.append("0");
}
if (bitPlacementMap[(row * 11) + column] >= 2) {
rowBinary.append(reversedBinaryDataStream.charAt(bitPlacementMap[(row * 11) + column] - 2));
}
}
pattern[row] = bin2pat(rowBinary.toString());
rowHeight[row] = 1;
rowBinary = new StringBuilder();
}
plotSymbol();
return true;
}
}

View file

@ -0,0 +1,155 @@
package org.xbib.graphics.barcode;
/**
* Implements Channel Code according to ANSI/AIM BC12-1998.
* Channel code encodes whole integer values between 0 and 7,742,862.
*/
public class ChannelCode extends Symbol {
private int[] space = new int[11];
private int[] bar = new int[11];
private double currentValue;
private double targetValue;
private String horizontalSpacing;
private int requestedNumberOfChannels = 0;
/**
* Set the number of channels used to encode data. This setting will be
* ignored if the value to be encoded requires more channels.
*
* @param channels Number of channels in range 3 - 8
*/
public void setNumberOfChannels(int channels) {
if ((channels >= 3) && (channels <= 7)) {
requestedNumberOfChannels = channels;
}
}
@Override
@SuppressWarnings("fallthrough")
public boolean encode() {
int numberOfChannels;
int i;
int leadingZeroCount;
targetValue = 0;
horizontalSpacing = "";
if (content.length() > 7) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if ((requestedNumberOfChannels <= 2) || (requestedNumberOfChannels > 8)) {
numberOfChannels = 3;
} else {
numberOfChannels = requestedNumberOfChannels;
}
for (i = 0; i < content.length(); i++) {
targetValue *= 10;
targetValue += Character.getNumericValue(content.charAt(i));
}
switch (numberOfChannels) {
case 3:
if (targetValue > 26) {
numberOfChannels++;
}
case 4:
if (targetValue > 292) {
numberOfChannels++;
}
case 5:
if (targetValue > 3493) {
numberOfChannels++;
}
case 6:
if (targetValue > 44072) {
numberOfChannels++;
}
case 7:
if (targetValue > 576688) {
numberOfChannels++;
}
case 8:
if (targetValue > 7742862) {
numberOfChannels++;
}
}
if (numberOfChannels == 9) {
errorMsg.append("Value out of range");
return false;
}
encodeInfo.append("Channels Used: ").append(numberOfChannels).append('\n');
for (i = 0; i < 11; i++) {
bar[i] = 0;
space[i] = 0;
}
bar[0] = space[1] = bar[1] = space[2] = bar[2] = 1;
currentValue = 0;
nextSpace(numberOfChannels, 3, numberOfChannels, numberOfChannels);
leadingZeroCount = numberOfChannels - 1 - content.length();
readable = new StringBuilder();
for (i = 0; i < leadingZeroCount; i++) {
readable.append("0");
}
readable.append(content);
pattern = new String[1];
pattern[0] = horizontalSpacing;
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
private void nextSpace(int channels, int i, int maxSpace, int maxBar) {
int s;
for (s = (i < channels + 2) ? 1 : maxSpace; s <= maxSpace; s++) {
space[i] = s;
nextBar(channels, i, maxBar, maxSpace + 1 - s);
}
}
private void nextBar(int channels, int i, int maxBar, int maxSpace) {
int b;
b = (space[i] + bar[i - 1] + space[i - 1] + bar[i - 2] > 4) ? 1 : 2;
if (i < channels + 2) {
for (; b <= maxBar; b++) {
bar[i] = b;
nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b);
}
} else if (b <= maxBar) {
bar[i] = maxBar;
checkIfDone();
currentValue++;
}
}
private void checkIfDone() {
int i;
if (currentValue == targetValue) {
/* Target reached - save the generated pattern */
horizontalSpacing = "11110";
for (i = 0; i < 11; i++) {
horizontalSpacing += (char) (space[i] + '0');
horizontalSpacing += (char) (bar[i] + '0');
}
}
}
}

View file

@ -0,0 +1,86 @@
package org.xbib.graphics.barcode;
/**
* Implements Codabar barcode symbology according to BS EN 798:1996.
* Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27.
* Codabar can encode any length string starting and ending with the letters
* A-D and containing between these letters the numbers 0-9, dash (-), dollar
* ($), colon (:), slash (/), full stop (.) or plus (+). No check digit is
* generated.
*/
public class Codabar extends Symbol {
private static final String[] CODABAR_TABLE = {
"11111221", "11112211", "11121121", "22111111", "11211211",
"21111211", "12111121", "12112111", "12211111", "21121111",
"11122111", "11221111", "21112121", "21211121", "21212111",
"11212121", "11221211", "12121121", "11121221", "11122211"
};
private static final char[] CHARACTER_SET = {
'0', '1', '2', '3', '4',
'5', '6', '7', '8', '9',
'-', '$', ':', '/', '.',
'+', 'A', 'B', 'C', 'D'
};
/**
* Ratio of wide bar width to narrow bar width.
*/
private double moduleWidthRatio = 2;
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return moduleWidthRatio;
}
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually
* between {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
@Override
public boolean encode() {
if (!(content.matches("[A-D]{1}[0-9:/\\$\\.\\+\u002D]+[A-D]{1}"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder horizontalSpacing = new StringBuilder();
int l = content.length();
for (int i = 0; i < l; i++) {
horizontalSpacing.append(CODABAR_TABLE[positionOf(content.charAt(i), CHARACTER_SET)]);
}
readable = new StringBuilder(content);
pattern = new String[]{horizontalSpacing.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
plotSymbol();
return true;
}
@Override
protected double getModuleWidth(int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return moduleWidthRatio;
}
}
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(8);
}
}

View file

@ -0,0 +1,852 @@
package org.xbib.graphics.barcode;
import java.awt.geom.Rectangle2D;
import java.io.UnsupportedEncodingException;
/**
* Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F", 1995.
* Codablock F is a multi-row symbology using Code 128 encoding. It can
* encode any 8-bit ISO 8859-1 (Latin-1) data up to approximately 1000
* alpha-numeric characters or 2000 numeric digits in length.
*/
public class CodablockF extends Symbol {
/* Annex A Table A.1 */
private String[] C128Table = {"212222", "222122", "222221", "121223", "121322", "131222", "122213",
"122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222",
"123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222",
"321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323",
"131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133",
"112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", "213113",
"213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111",
"221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", "112214",
"112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112",
"134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", "421112",
"421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311",
"411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", "211232",
"2331112"};
private final int[][] blockmatrix = new int[44][62];
private int columns_needed;
private int[] source;
private int rows_needed;
private cfMode final_mode;
private final cfMode[] subset_selector = new cfMode[44];
@Override
public boolean encode() {
int input_length, i, j, k;
int min_module_height;
Mode last_mode, this_mode;
double estimate_codelength;
StringBuilder row_pattern;
int[] row_indicator = new int[44];
int[] row_check = new int[44];
int k1_sum, k2_sum;
int k1_check, k2_check;
input_length = content.length();
final_mode = cfMode.MODEA;
if (input_length > 5450) {
errorMsg.append("Input data too long");
return false;
}
if (!content.matches("[\u0000-\u00FF]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
try {
inputBytes = content.getBytes("ISO8859_1");
} catch (UnsupportedEncodingException e) {
errorMsg.append("Character encoding error");
return false;
}
source = new int[input_length + 1];
for (i = 0; i < input_length; i++) {
source[i] = inputBytes[i] & 0xFF;
}
source[input_length] = 0x00;
/* Make a guess at how many characters will be needed to encode the data */
estimate_codelength = 0.0;
last_mode = Mode.AORB; /* Codablock always starts with Code A */
for (i = 0; i < input_length; i++) {
this_mode = findSubset(source[i]);
if (this_mode != last_mode) {
estimate_codelength += 1.0;
}
if (this_mode != Mode.ABORC) {
estimate_codelength += 1.0;
} else {
estimate_codelength += 0.5;
}
if (source[i] > 127) {
estimate_codelength += 1.0;
}
last_mode = this_mode;
}
/* Decide symbol size based on the above guess */
rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45));
if (rows_needed < 2) {
rows_needed = 2;
}
if (rows_needed > 44) {
rows_needed = 44;
}
columns_needed = (int) (estimate_codelength + 2) / rows_needed;
if (columns_needed < 4) {
columns_needed = 4;
}
if (columns_needed > 62) {
errorMsg.append("Input data too long");
return false;
}
/* Encode the data */
if (!(data_encode_blockf())) {
return false;
}
/* Add check digits - Annex F */
k1_sum = 0;
k2_sum = 0;
for (i = 0; i < input_length; i++) {
if ((inputDataType == DataType.GS1) && source[i] == '[') {
k1_sum += (i + 1) * 29; /* GS */
k2_sum += i * 29;
} else {
k1_sum += (i + 1) * source[i];
k2_sum += i * source[i];
}
}
k1_check = k1_sum % 86;
k2_check = k2_sum % 86;
if ((final_mode == cfMode.MODEA) || (final_mode == cfMode.MODEB)) {
k1_check = k1_check + 64;
if (k1_check > 95) {
k1_check -= 96;
}
k2_check = k2_check + 64;
if (k2_check > 95) {
k2_check -= 96;
}
}
blockmatrix[rows_needed - 1][columns_needed - 2] = k1_check;
blockmatrix[rows_needed - 1][columns_needed - 1] = k2_check;
/* Calculate row height (4.6.1.a) */
min_module_height = (int) (0.55 * (columns_needed + 3)) + 3;
if (min_module_height < 8) {
min_module_height = 8;
}
/* Encode the Row Indicator in the First Row of the Symbol - Table D2 */
if (subset_selector[0] == cfMode.MODEC) {
/* Code C */
row_indicator[0] = rows_needed - 2;
} else {
/* Code A or B */
row_indicator[0] = rows_needed + 62;
if (row_indicator[0] > 95) {
row_indicator[0] -= 95;
}
}
/* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */
for (i = 1; i < rows_needed; i++) {
/* Note that the second row is row number 1 because counting starts from 0 */
if (subset_selector[i] == cfMode.MODEC) {
/* Code C */
row_indicator[i] = i + 42;
} else {
/* Code A or B */
if (i < 6)
row_indicator[i] = i + 10;
else
row_indicator[i] = i + 20;
}
}
/* Calculate row check digits - Annex E */
for (i = 0; i < rows_needed; i++) {
k = 103;
switch (subset_selector[i]) {
case MODEA:
k += 98;
break;
case MODEB:
k += 100;
break;
case MODEC:
k += 99;
break;
}
k += 2 * row_indicator[i];
for (j = 0; j < columns_needed; j++) {
k += (j + 3) * blockmatrix[i][j];
}
row_check[i] = k % 103;
}
readable = new StringBuilder();
rowCount = rows_needed;
pattern = new String[rowCount];
rowHeight = new int[rowCount];
encodeInfo.append("Grid size: ").append(columns_needed).append(" X ").append(rows_needed).append('\n');
encodeInfo.append("K1 Check Digit: ").append(k1_check).append("\n");
encodeInfo.append("K2 Check Digit: ").append(k2_check).append("\n");
/* Resolve the data into patterns and place in symbol structure */
encodeInfo.append("Encoding: ");
for (i = 0; i < rows_needed; i++) {
row_pattern = new StringBuilder();
/* Start character */
row_pattern.append(C128Table[103]); /* Always Start A */
switch (subset_selector[i]) {
case MODEA:
row_pattern.append(C128Table[98]);
encodeInfo.append("MODEA ");
break;
case MODEB:
row_pattern.append(C128Table[100]);
encodeInfo.append("MODEB ");
break;
case MODEC:
row_pattern.append(C128Table[99]);
encodeInfo.append("MODEC ");
break;
}
row_pattern.append(C128Table[row_indicator[i]]);
encodeInfo.append(Integer.toString(row_indicator[i])).append(" ");
for (j = 0; j < columns_needed; j++) {
row_pattern.append(C128Table[blockmatrix[i][j]]);
encodeInfo.append(Integer.toString(blockmatrix[i][j])).append(" ");
}
row_pattern.append(C128Table[row_check[i]]);
encodeInfo.append("(").append(Integer.toString(row_check[i])).append(") ");
/* Stop character */
row_pattern.append(C128Table[106]);
/* Write the information into the symbol */
pattern[i] = row_pattern.toString();
rowHeight[i] = 15;
}
encodeInfo.append("\n");
symbolHeight = rows_needed * 15;
plotSymbol();
return true;
}
private Mode findSubset(int letter) {
Mode mode;
if (letter <= 31) {
mode = Mode.SHIFTA;
} else if ((letter >= 48) && (letter <= 57)) {
mode = Mode.ABORC;
} else if (letter <= 95) {
mode = Mode.AORB;
} else if (letter <= 127) {
mode = Mode.SHIFTB;
} else if (letter <= 159) {
mode = Mode.SHIFTA;
} else if (letter <= 223) {
mode = Mode.AORB;
} else {
mode = Mode.SHIFTB;
}
return mode;
}
private boolean data_encode_blockf() {
int i, j, input_position, current_row;
int column_position, c;
cfMode current_mode;
boolean done, exit_status;
exit_status = false;
current_row = 0;
current_mode = cfMode.MODEA;
column_position = 0;
input_position = 0;
c = 0;
do {
done = false;
/* 'done' ensures that the instructions are followed in the correct order for each input character */
if (column_position == 0) {
/* The Beginning of a row */
c = columns_needed;
current_mode = character_subset_select(input_position);
subset_selector[current_row] = current_mode;
if ((current_row == 0) && (inputDataType == DataType.GS1)) {
/* Section 4.4.7.1 */
blockmatrix[current_row][column_position] = 102; /* FNC1 */
column_position++;
c--;
}
}
if ((inputDataType == DataType.GS1) && (source[input_position] == '[')) {
blockmatrix[current_row][column_position] = 102; /* FNC1 */
column_position++;
c--;
input_position++;
done = true;
}
if (!done) {
if (c <= 2) {
/* Annex B section 1 rule 1 */
/* Ensure that there is sufficient encodation capacity to continue (using the rules of Annex B.2). */
switch (current_mode) {
case MODEA: /* Table B1 applies */
if (findSubset(source[input_position]) == Mode.ABORC) {
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
if ((findSubset(source[input_position]) == Mode.SHIFTB) && (c == 1)) {
/* Needs two symbols */
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
done = true;
}
if ((source[input_position] >= 244) && (!done)) {
/* Needs three symbols */
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (c == 1) {
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
}
done = true;
}
if ((source[input_position] >= 128) && (!done) && c == 1) {
/* Needs two symbols */
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
done = true;
}
break;
case MODEB: /* Table B2 applies */
if (findSubset(source[input_position]) == Mode.ABORC) {
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
if ((findSubset(source[input_position]) == Mode.SHIFTA) && (c == 1)) {
/* Needs two symbols */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
if (((source[input_position] >= 128)
&& (source[input_position] <= 159)) && (!done)) {
/* Needs three symbols */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (c == 1) {
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
}
done = true;
}
if ((source[input_position] >= 160) && (!done) && c == 1) {
/* Needs two symbols */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
break;
case MODEC: /* Table B3 applies */
if ((findSubset(source[input_position]) != Mode.ABORC) && (c == 1)) {
/* Needs two symbols */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
if (((findSubset(source[input_position]) == Mode.ABORC)
&& (findSubset(source[input_position + 1]) != Mode.ABORC))
&& (c == 1)) {
/* Needs two symbols */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
if (source[input_position] >= 128) {
/* Needs three symbols */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (c == 1) {
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
}
}
break;
}
}
}
if (!done) {
if (((findSubset(source[input_position]) == Mode.AORB)
|| (findSubset(source[input_position]) == Mode.SHIFTA))
&& (current_mode == cfMode.MODEA)) {
/* Annex B section 1 rule 2 */
/* If in Code Subset A and the next data character can be encoded in Subset A encode the next
character. */
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
if (!done) {
if (((findSubset(source[input_position]) == Mode.AORB)
|| (findSubset(source[input_position]) == Mode.SHIFTB))
&& (current_mode == cfMode.MODEB)) {
/* Annex B section 1 rule 3 */
/* If in Code Subset B and the next data character can be encoded in subset B, encode the next
character. */
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
if (!done) {
if (((findSubset(source[input_position]) == Mode.ABORC)
&& (findSubset(source[input_position + 1]) == Mode.ABORC))
&& (current_mode == cfMode.MODEC)) {
/* Annex B section 1 rule 4 */
/* If in Code Subset C and the next data are 2 digits, encode them. */
blockmatrix[current_row][column_position]
= ((source[input_position] - '0') * 10)
+ (source[input_position + 1] - '0');
column_position++;
c--;
input_position += 2;
done = true;
}
}
if (!done) {
if (((current_mode == cfMode.MODEA) || (current_mode == cfMode.MODEB))
&& ((findSubset(source[input_position]) == Mode.ABORC)
|| ((inputDataType == DataType.GS1) && (source[input_position] == '[')))) {
/* Count the number of numeric digits */
/* If 4 or more numeric data characters occur together when in subsets A or B:
a. If there is an even number of numeric data characters, insert a Code C character before the
first numeric digit to change to subset C.
b. If there is an odd number of numeric data characters, insert a Code Set C character immedi-
ately after the first numeric digit to change to subset C. */
i = 0;
j = 0;
do {
i++;
if ((inputDataType == DataType.GS1) && (source[input_position + j] == '[')) {
i++;
}
j++;
} while ((findSubset(source[input_position + j]) == Mode.ABORC)
|| ((inputDataType == DataType.GS1) && (source[input_position + j] == '[')));
i--;
if (i >= 4) {
/* Annex B section 1 rule 5 */
if ((i % 2) == 1) {
/* Annex B section 1 rule 5a */
blockmatrix[current_row][column_position] = 99; /* Code C */
column_position++;
c--;
blockmatrix[current_row][column_position] = ((source[input_position] - '0') * 10)
+ (source[input_position + 1] - '0');
column_position++;
c--;
input_position += 2;
current_mode = cfMode.MODEC;
} else {
/* Annex B section 1 rule 5b */
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
}
done = true;
} else {
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
}
if (!done) {
if ((current_mode == cfMode.MODEB) && (findSubset(source[input_position]) == Mode.SHIFTA)) {
/* Annex B section 1 rule 6 */
/* When in subset B and an ASCII control character occurs in the data:
a. If there is a lower case character immediately following the control character, insert a Shift
character before the control character.
b. Otherwise, insert a Code A character before the control character to change to subset A. */
if ((source[input_position + 1] >= 96) && (source[input_position + 1] <= 127)) {
/* Annex B section 1 rule 6a */
blockmatrix[current_row][column_position] = 98; /* Shift */
column_position++;
c--;
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
} else {
/* Annex B section 1 rule 6b */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
current_mode = cfMode.MODEA;
}
done = true;
}
}
if (!done) {
if ((current_mode == cfMode.MODEA) && (findSubset(source[input_position]) == Mode.SHIFTB)) {
/* Annex B section 1 rule 7 */
/* When in subset A and a lower case character occurs in the data:
a. If following that character, a control character occurs in the data before the occurrence of
another lower case character, insert a Shift character before the lower case character.
b. Otherwise, insert a Code B character before the lower case character to change to subset B. */
if ((findSubset(source[input_position + 1]) == Mode.SHIFTA)
&& (findSubset(source[input_position + 2]) == Mode.SHIFTB)) {
/* Annex B section 1 rule 7a */
blockmatrix[current_row][column_position] = 98; /* Shift */
column_position++;
c--;
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
} else {
/* Annex B section 1 rule 7b */
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
current_mode = cfMode.MODEB;
}
done = true;
}
}
if (!done) {
if ((current_mode == cfMode.MODEC) && ((findSubset(source[input_position]) != Mode.ABORC)
|| (findSubset(source[input_position + 1]) != Mode.ABORC))) {
/* Annex B section 1 rule 8 */
/* When in subset C and a non-numeric character (or a single digit) occurs in the data, insert a Code
A or Code B character before that character, following rules 8a and 8b to determine between code
subsets A and B.
a. If an ASCII control character (eg NUL) occurs in the data before any lower case character, use
Code A.
b. Otherwise use Code B. */
if (findSubset(source[input_position]) == Mode.SHIFTA) {
/* Annex B section 1 rule 8a */
blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
current_mode = cfMode.MODEA;
} else {
/* Annex B section 1 rule 8b */
blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (source[input_position] >= 128) {
/* Extended ASCII character */
blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
blockmatrix[current_row][column_position] = a3_convert(source[input_position]);
column_position++;
c--;
input_position++;
current_mode = cfMode.MODEB;
}
}
}
if (input_position == content.length()) {
/* End of data - Annex B rule 5a */
if (c == 1) {
if (current_mode == cfMode.MODEA) {
blockmatrix[current_row][column_position] = 100; /* Code B */
current_mode = cfMode.MODEB;
} else {
blockmatrix[current_row][column_position] = 101; /* Code A */
current_mode = cfMode.MODEA;
}
column_position++;
c--;
}
if (c == 0) {
/* Another row is needed */
column_position = 0;
c = columns_needed;
current_row++;
subset_selector[current_row] = cfMode.MODEA;
current_mode = cfMode.MODEA;
}
if (c > 2) {
/* Fill up the last row */
do {
if (current_mode == cfMode.MODEA) {
blockmatrix[current_row][column_position] = 100; /* Code B */
current_mode = cfMode.MODEB;
} else {
blockmatrix[current_row][column_position] = 101; /* Code A */
current_mode = cfMode.MODEA;
}
column_position++;
c--;
} while (c > 2);
}
/* If (c == 2) { do nothing } */
exit_status = true;
final_mode = current_mode;
} else {
if (c <= 0) {
/* Start new row - Annex B rule 5b */
column_position = 0;
current_row++;
if (current_row > 43) {
return false;
}
}
}
} while (!exit_status);
if (current_row == 0) {
/* fill up the first row */
for (c = column_position; c <= columns_needed; c++) {
if (current_mode == cfMode.MODEA) {
blockmatrix[current_row][c] = 100; /* Code B */
current_mode = cfMode.MODEB;
} else {
blockmatrix[current_row][c] = 101; /* Code A */
current_mode = cfMode.MODEA;
}
}
current_row++;
/* add a second row */
subset_selector[current_row] = cfMode.MODEA;
current_mode = cfMode.MODEA;
for (c = 0; c <= columns_needed - 2; c++) {
if (current_mode == cfMode.MODEA) {
blockmatrix[current_row][c] = 100; /* Code B */
current_mode = cfMode.MODEB;
} else {
blockmatrix[current_row][c] = 101; /* Code A */
current_mode = cfMode.MODEA;
}
}
}
rows_needed = current_row + 1;
return true;
}
private cfMode character_subset_select(int input_position) {
/* Section 4.5.2 - Determining the Character Subset Selector in a Row */
if ((source[input_position] >= '0') && (source[input_position + 1] <= '9')) {
/* Rule 1 */
return cfMode.MODEC;
}
if ((source[input_position] >= 128) && (source[input_position] <= 160)) {
/* Rule 2 (i) */
return cfMode.MODEA;
}
if ((source[input_position] >= 0) && (source[input_position] <= 31)) {
/* Rule 3 */
return cfMode.MODEA;
}
/* Rule 4 */
return cfMode.MODEB;
}
private int a3_convert(int source) {
/* Annex A section 3 */
if (source < 32) {
return source + 64;
}
if (source <= 127) {
return source - 32;
}
if (source <= 159) {
return (source - 128) + 64;
}
/* if source >= 160 */
return (source - 128) - 32;
}
@Override
protected void plotSymbol() {
int xBlock, yBlock;
int x, y, w, h;
boolean black;
getRectangles().clear();
y = 1;
h = 1;
for (yBlock = 0; yBlock < rowCount; yBlock++) {
black = true;
x = 0;
for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) {
if (black) {
black = false;
w = pattern[yBlock].charAt(xBlock) - '0';
if (rowHeight[yBlock] == -1) {
h = defaultHeight;
} else {
h = rowHeight[yBlock];
}
if (w != 0 && h != 0) {
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
}
if ((x + w) > symbolWidth) {
symbolWidth = x + w;
}
} else {
black = true;
}
x += (double) (pattern[yBlock].charAt(xBlock) - '0');
}
y += h;
if ((y + h) > symbolHeight) {
symbolHeight = y + h;
}
/* Add bars between rows */
if (yBlock != (rowCount - 1)) {
Rectangle2D.Double rect = new Rectangle2D.Double(11, y - 1, (symbolWidth - 24), 2);
getRectangles().add(rect);
}
}
Rectangle2D.Double top = new Rectangle2D.Double(0, 0, symbolWidth, 2);
getRectangles().add(top);
Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, symbolWidth, 2);
getRectangles().add(bottom);
symbolHeight += 2;
mergeVerticalBlocks();
}
private enum Mode {
SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
}
private enum cfMode {
MODEA, MODEB, MODEC
}
}

View file

@ -0,0 +1,208 @@
package org.xbib.graphics.barcode;
/**
* Implements Code 11 bar code symbology.
* Code 11 can encode any length string consisting of the digits 0-9 and the
* dash character (-). One or two modulo-11 check digits are calculated.
*/
public class Code11 extends Symbol {
private static final String[] CODE_11_TABLE = {
"111121", "211121", "121121", "221111", "112121", "212111",
"122111", "111221", "211211", "211111", "112111"
};
private static final char[] CHARACTER_SET = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'
};
/**
* Ratio of wide bar width to narrow bar width.
*/
private double moduleWidthRatio = 2;
/**
* The number of check digits to calculate ({@code 1} or {@code 2}).
*/
private int checkDigitCount = 2;
/**
* Optional start delimiter to be shown in the human-readable text.
*/
private Character startDelimiter;
/**
* Optional stop delimiter to be shown in the human-readable text.
*/
private Character stopDelimiter;
private static int getCheckDigitC(int[] weight, int length) {
int countC = 0;
int weightC = 1;
for (int i = length - 1; i >= 0; i--) {
countC += (weightC * weight[i]);
weightC++;
if (weightC > 10) {
weightC = 1;
}
}
return countC % 11;
}
private static int getCheckDigitK(int[] weight, int length) {
int countK = 0;
int weightK = 1;
for (int i = length - 1; i >= 0; i--) {
countK += (weightK * weight[i]);
weightK++;
if (weightK > 9) {
weightK = 1;
}
}
return countK % 11;
}
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return moduleWidthRatio;
}
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually
* between {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
/**
* Returns the number of check digits to calculate (1 or 2).
*
* @return the number of check digits to calculate
*/
public int getCheckDigitCount() {
return checkDigitCount;
}
/**
* Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is {@code 2}.
*
* @param checkDigitCount the number of check digits to calculate
*/
public void setCheckDigitCount(int checkDigitCount) {
if (checkDigitCount < 1 || checkDigitCount > 2) {
throw new IllegalArgumentException("Check digit count must be 1 or 2.");
}
this.checkDigitCount = checkDigitCount;
}
/**
* Returns the optional start delimiter to be shown in the human-readable text.
*
* @return the optional start delimiter to be shown in the human-readable text
*/
public Character getStartDelimiter() {
return startDelimiter;
}
/**
* Sets an optional start delimiter to be shown in the human-readable text (defaults to <code>null</code>).
*
* @param startDelimiter an optional start delimiter to be shown in the human-readable text
*/
public void setStartDelimiter(Character startDelimiter) {
this.startDelimiter = startDelimiter;
}
/**
* Returns the optional stop delimiter to be shown in the human-readable text.
*
* @return the optional stop delimiter to be shown in the human-readable text
*/
public Character getStopDelimiter() {
return stopDelimiter;
}
/**
* Sets an optional stop delimiter to be shown in the human-readable text (defaults to <code>null</code>).
*
* @param stopDelimiter an optional stop delimiter to be shown in the human-readable text
*/
public void setStopDelimiter(Character stopDelimiter) {
this.stopDelimiter = stopDelimiter;
}
/**
* {@inheritDoc}
*/
@Override
public boolean encode() {
if (!(content.matches("[0-9-]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder horizontalSpacing = new StringBuilder("112211");
String humanReadable = content;
int length = content.length();
int[] weight = new int[length + 1];
for (int i = 0; i < length; i++) {
char c = content.charAt(i);
weight[i] = positionOf(c, CHARACTER_SET);
horizontalSpacing.append(CODE_11_TABLE[weight[i]]);
}
int checkDigitC = getCheckDigitC(weight, length);
horizontalSpacing.append(CODE_11_TABLE[checkDigitC]);
humanReadable += CHARACTER_SET[checkDigitC];
encodeInfo.append("Check Digit C: ").append(checkDigitC).append("\n");
if (checkDigitCount == 2) {
weight[length] = checkDigitC;
int checkDigitK = getCheckDigitK(weight, length + 1);
horizontalSpacing.append(CODE_11_TABLE[checkDigitK]);
humanReadable += CHARACTER_SET[checkDigitK];
encodeInfo.append("Check Digit K: ").append(checkDigitK).append("\n");
}
horizontalSpacing.append("112211");
readable = new StringBuilder(humanReadable);
if (startDelimiter != null) {
readable = new StringBuilder(startDelimiter).append(readable);
}
if (stopDelimiter != null) {
readable.append(stopDelimiter);
}
pattern = new String[]{horizontalSpacing.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
plotSymbol();
return true;
}
@Override
protected double getModuleWidth(int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return moduleWidthRatio;
}
}
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}

View file

@ -0,0 +1,843 @@
package org.xbib.graphics.barcode;
import java.io.UnsupportedEncodingException;
/**
* Implements Code 128 bar code symbology according to ISO/IEC 15417:2007.
* Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters.
* Setting GS1 mode allows encoding in GS1-128 (also known as UPC/EAN-128).
*/
public class Code128 extends Symbol {
private String[] code128Table = {
"212222", "222122", "222221", "121223", "121322", "131222", "122213",
"122312", "132212", "221213", "221312", "231212", "112232", "122132",
"122231", "113222", "123122", "123221", "223211", "221132", "221231",
"213212", "223112", "312131", "311222", "321122", "321221", "312212",
"322112", "322211", "212123", "212321", "232121", "111323", "131123",
"131321", "112313", "132113", "132311", "211313", "231113", "231311",
"112133", "112331", "132131", "113123", "113321", "133121", "313121",
"211331", "231131", "213113", "213311", "213131", "311123", "311321",
"331121", "312113", "312311", "332111", "314111", "221411", "431111",
"111224", "111422", "121124", "121421", "141122", "141221", "112214",
"112412", "122114", "122411", "142112", "142211", "241211", "221114",
"413111", "241112", "134111", "111242", "121142", "121241", "114212",
"124112", "124211", "411212", "421112", "421211", "212141", "214121",
"412121", "111143", "111341", "131141", "114113", "114311", "411113",
"411311", "113141", "114131", "311141", "411131", "211412", "211214",
"211232", "2331112"
};
private Mode[] mode_type = new Mode[200];
private int[] mode_length = new int[200];
private int index_point = 0, read = 0;
private boolean modeCSupression;
private Composite compositeMode;
public Code128() {
modeCSupression = false;
compositeMode = Composite.OFF;
}
/**
* Allow the use of subset C (numeric compression) in encoding (default).
*/
public void useModeC() {
modeCSupression = false;
}
;
/**
* Disallow the use of subset C (numeric compression) in encoding.
* Numeric values will be encoded using subset B.
*/
public void stopModeC() {
modeCSupression = true;
}
protected void setCca() {
compositeMode = Composite.CCA;
}
protected void setCcb() {
compositeMode = Composite.CCB;
}
protected void setCcc() {
compositeMode = Composite.CCC;
}
public void unsetCc() {
compositeMode = Composite.OFF;
}
@Override
public boolean encode() {
int sourcelen = content.length();
int i, j, k;
int input_point = 0;
Mode mode, last_mode;
Mode last_set, current_set;
double glyph_count;
int bar_characters = 0, total_sum = 0;
FMode f_state = FMode.LATCHN;
int[] values = new int[200];
int c;
StringBuilder dest = new StringBuilder();
int[] inputData;
int c_count;
int linkage_flag = 0;
if (!content.matches("[\u0000-\u00FF]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
try {
inputBytes = content.getBytes("ISO8859_1");
} catch (UnsupportedEncodingException e) {
errorMsg.append("Character encoding error");
return false;
}
inputData = new int[sourcelen];
for (i = 0; i < sourcelen; i++) {
inputData[i] = inputBytes[i] & 0xFF;
}
FMode[] fset = new FMode[200];
Mode[] set = new Mode[200]; /* set[] = Calculated mode for each character */
if (sourcelen > 170) {
errorMsg.append("Input data too long");
return false;
}
/* Detect extended ASCII characters */
for (i = 0; i < sourcelen; i++) {
if (inputData[i] >= 128) {
fset[i] = FMode.SHIFTF;
} else {
fset[i] = FMode.LATCHN;
}
}
/* Decide when to latch to extended mode - Annex E note 3 */
j = 0;
for (i = 0; i < sourcelen; i++) {
if (fset[i] == FMode.SHIFTF) {
j++;
} else {
j = 0;
}
if (j >= 5) {
for (k = i; k > (i - 5); k--) {
fset[k] = FMode.LATCHF;
}
}
if ((j >= 3) && (i == (sourcelen - 1))) {
for (k = i; k > (i - 3); k--) {
fset[k] = FMode.LATCHF;
}
}
}
/* Decide if it is worth reverting to 646 encodation for a few characters as described in 4.3.4.2 (d) */
for (i = 1; i < sourcelen; i++) {
if ((fset[i - 1] == FMode.LATCHF) && (fset[i] == FMode.LATCHN)) {
/* Detected a change from 8859-1 to 646 - count how long for */
for (j = 0; (fset[i + j] == FMode.LATCHN) && ((i + j) < sourcelen); j++) ;
if ((j < 5) || ((j < 3) && ((i + j) == (sourcelen - 1)))) {
/* Uses the same figures recommended by Annex E note 3 */
/* Change to shifting back rather than latching back */
for (k = 0; k < j; k++) {
fset[i + k] = FMode.SHIFTN;
}
}
}
}
/* Decide on mode using same system as PDF417 and rules of ISO 15417 Annex E */
mode = findSubset(inputData[input_point]);
mode_type[0] = mode;
mode_length[0] = 1;
if (inputDataType == DataType.GS1) {
mode = Mode.ABORC;
}
if ((modeCSupression) && (mode == Mode.ABORC)) {
mode = Mode.AORB;
}
for (i = 1; i < sourcelen; i++) {
last_mode = mode;
mode = findSubset(inputData[i]);
if ((inputDataType == DataType.GS1) && inputData[i] == '[') {
mode = Mode.ABORC;
}
if ((modeCSupression) && (mode == Mode.ABORC)) {
mode = Mode.AORB;
}
if (mode == last_mode) {
mode_length[index_point]++;
} else {
index_point++;
mode_type[index_point] = mode;
mode_length[index_point] = 1;
}
}
index_point++;
reduceSubsetChanges();
if (inputDataType == DataType.GS1) {
/* Put set data into set[] */
read = 0;
for (i = 0; i < index_point; i++) {
for (j = 0; j < mode_length[i]; j++) {
set[read] = mode_type[i];
read++;
}
}
/* Resolve odd length LATCHC blocks */
c_count = 0;
for (i = 0; i < read; i++) {
if (set[i] == Mode.LATCHC) {
if (inputData[i] == '[') {
if ((c_count & 1) != 0) {
if ((i - c_count) != 0) {
set[i - c_count] = Mode.LATCHB;
} else {
set[i - 1] = Mode.LATCHB;
}
}
c_count = 0;
} else {
c_count++;
}
} else {
if ((c_count & 1) != 0) {
if ((i - c_count) != 0) {
set[i - c_count] = Mode.LATCHB;
} else {
set[i - 1] = Mode.LATCHB;
}
}
c_count = 0;
}
}
if ((c_count & 1) != 0) {
if ((i - c_count) != 0) {
set[i - c_count] = Mode.LATCHB;
} else {
set[i - 1] = Mode.LATCHB;
}
}
for (i = 1; i < read - 1; i++) {
if ((set[i] == Mode.LATCHC) && ((set[i - 1] == Mode.LATCHB)
&& (set[i + 1] == Mode.LATCHB))) {
set[i] = Mode.LATCHB;
}
}
} else {
/* Resolve odd length LATCHC blocks */
if ((mode_type[0] == Mode.LATCHC) && ((mode_length[0] & 1) != 0)) {
/* Rule 2 */
mode_length[1]++;
mode_length[0]--;
if (index_point == 1) {
mode_length[1] = 1;
mode_type[1] = Mode.LATCHB;
index_point = 2;
}
}
if (index_point > 1) {
for (i = 1; i < index_point; i++) {
if ((mode_type[i] == Mode.LATCHC) && ((mode_length[i] & 1) != 0)) {
/* Rule 3b */
mode_length[i - 1]++;
mode_length[i]--;
}
}
}
/* Put set data into set[] */
for (i = 0; i < index_point; i++) {
for (j = 0; j < mode_length[i]; j++) {
set[read] = mode_type[i];
read++;
}
}
}
/* Adjust for strings which start with shift characters - make them latch instead */
if (set[0] == Mode.SHIFTA) {
i = 0;
do {
set[i] = Mode.LATCHA;
i++;
} while (set[i] == Mode.SHIFTA);
}
if (set[0] == Mode.SHIFTB) {
i = 0;
do {
set[i] = Mode.LATCHB;
i++;
} while (set[i] == Mode.SHIFTB);
}
/* Now we can calculate how long the barcode is going to be - and stop it from
being too long */
last_set = Mode.NULL;
glyph_count = 0.0;
for (i = 0; i < sourcelen; i++) {
if ((set[i] == Mode.SHIFTA) || (set[i] == Mode.SHIFTB)) {
glyph_count += 1.0;
}
if ((fset[i] == FMode.SHIFTF) || (fset[i] == FMode.SHIFTN)) {
glyph_count += 1.0;
}
if (((set[i] == Mode.LATCHA) || (set[i] == Mode.LATCHB)) || (set[i] == Mode.LATCHC)) {
if (set[i] != last_set) {
last_set = set[i];
glyph_count += 1.0;
}
}
if (i == 0) {
if (fset[i] == FMode.LATCHF) {
glyph_count += 2.0;
}
} else {
if ((fset[i] == FMode.LATCHF) && (fset[i - 1] != FMode.LATCHF)) {
glyph_count += 2.0;
}
if ((fset[i] != FMode.LATCHF) && (fset[i - 1] == FMode.LATCHF)) {
glyph_count += 2.0;
}
}
if (set[i] == Mode.LATCHC) {
if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) {
glyph_count += 1.0;
} else {
glyph_count += 0.5;
}
} else {
glyph_count += 1.0;
}
}
if (glyph_count > 80.0) {
errorMsg.append("Input data too long");
return false;
}
encodeInfo.append("Encoding: ");
/* So now we know what start character to use - we can get on with it! */
if (readerInit) {
/* Reader Initialisation mode */
switch (set[0]) {
case LATCHA: /* Start A */
dest.append(code128Table[103]);
values[0] = 103;
current_set = Mode.LATCHA;
dest.append(code128Table[96]); /* FNC3 */
values[1] = 96;
bar_characters++;
encodeInfo.append("STARTA FNC3 ");
break;
case LATCHB: /* Start B */
dest.append(code128Table[104]);
values[0] = 104;
current_set = Mode.LATCHB;
dest.append(code128Table[96]); /* FNC3 */
values[1] = 96;
bar_characters++;
encodeInfo.append("STARTB FNC3 ");
break;
default: /* Start C */
dest.append(code128Table[104]); /* Start B */
values[0] = 105;
dest.append(code128Table[96]); /* FNC3 */
values[1] = 96;
dest.append(code128Table[99]); /* Code C */
values[2] = 99;
bar_characters += 2;
current_set = Mode.LATCHC;
encodeInfo.append("STARTB FNC3 CODEC ");
break;
}
} else {
/* Normal mode */
switch (set[0]) {
case LATCHA:
/* Start A */
dest.append(code128Table[103]);
values[0] = 103;
current_set = Mode.LATCHA;
encodeInfo.append("STARTA ");
break;
case LATCHB:
/* Start B */
dest.append(code128Table[104]);
values[0] = 104;
current_set = Mode.LATCHB;
encodeInfo.append("STARTB ");
break;
default:
/* Start C */
dest.append(code128Table[105]);
values[0] = 105;
current_set = Mode.LATCHC;
encodeInfo.append("STARTC ");
break;
}
}
bar_characters++;
if (inputDataType == DataType.GS1) {
dest.append(code128Table[102]);
values[1] = 102;
bar_characters++;
encodeInfo.append("FNC1 ");
}
if (fset[0] == FMode.LATCHF) {
switch (current_set) {
case LATCHA:
dest.append(code128Table[101]);
dest.append(code128Table[101]);
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
encodeInfo.append("FNC4 FNC4 ");
break;
case LATCHB:
dest.append(code128Table[100]);
dest.append(code128Table[100]);
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
encodeInfo.append("FNC4 FNC4 ");
break;
}
bar_characters += 2;
f_state = FMode.LATCHF;
}
/* Encode the data */
read = 0;
do {
if ((read != 0) && (set[read] != current_set)) { /* Latch different code set */
switch (set[read]) {
case LATCHA:
dest.append(code128Table[101]);
values[bar_characters] = 101;
bar_characters++;
current_set = Mode.LATCHA;
encodeInfo.append("CODEA ");
break;
case LATCHB:
dest.append(code128Table[100]);
values[bar_characters] = 100;
bar_characters++;
current_set = Mode.LATCHB;
encodeInfo.append("CODEB ");
break;
case LATCHC:
dest.append(code128Table[99]);
values[bar_characters] = 99;
bar_characters++;
current_set = Mode.LATCHC;
encodeInfo.append("CODEC ");
break;
}
}
if (read != 0) {
if ((fset[read] == FMode.LATCHF) && (f_state == FMode.LATCHN)) {
/* Latch beginning of extended mode */
switch (current_set) {
case LATCHA:
dest.append(code128Table[101]);
dest.append(code128Table[101]);
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
encodeInfo.append("FNC4 FNC4 ");
break;
case LATCHB:
dest.append(code128Table[100]);
dest.append(code128Table[100]);
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
encodeInfo.append("FNC4 FNC4 ");
break;
}
bar_characters += 2;
f_state = FMode.LATCHN;
}
if ((fset[read] == FMode.LATCHN) && (f_state == FMode.LATCHF)) {
/* Latch end of extended mode */
switch (current_set) {
case LATCHA:
dest.append(code128Table[101]);
dest.append(code128Table[101]);
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
encodeInfo.append("FNC4 FNC4 ");
break;
case LATCHB:
dest.append(code128Table[100]);
dest.append(code128Table[100]);
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
encodeInfo.append("FNC4 FNC4 ");
break;
}
bar_characters += 2;
f_state = FMode.LATCHN;
}
}
if ((fset[read] == FMode.SHIFTF) || (fset[read] == FMode.SHIFTN)) {
/* Shift to or from extended mode */
switch (current_set) {
case LATCHA:
dest.append(code128Table[101]); /* FNC 4 */
values[bar_characters] = 101;
encodeInfo.append("FNC4 ");
break;
case LATCHB:
dest.append(code128Table[100]); /* FNC 4 */
values[bar_characters] = 100;
encodeInfo.append("FNC4 ");
break;
}
bar_characters++;
}
if ((set[read] == Mode.SHIFTA) || (set[read] == Mode.SHIFTB)) {
/* Insert shift character */
dest.append(code128Table[98]);
values[bar_characters] = 98;
encodeInfo.append("SHFT ");
bar_characters++;
}
if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) {
/* Encode data characters */
c = inputData[read];
switch (set[read]) {
case SHIFTA:
case LATCHA:
if (c > 127) {
if (c < 160) {
dest.append(code128Table[(c - 128) + 64]);
values[bar_characters] = (c - 128) + 64;
} else {
dest.append(code128Table[(c - 128) - 32]);
values[bar_characters] = (c - 128) - 32;
}
} else {
if (c < 32) {
dest.append(code128Table[c + 64]);
values[bar_characters] = c + 64;
} else {
dest.append(code128Table[c - 32]);
values[bar_characters] = c - 32;
}
}
encodeInfo.append(Integer.toString(values[bar_characters])).append(" ");
bar_characters++;
read++;
break;
case SHIFTB:
case LATCHB:
if (c > 127) {
dest.append(code128Table[c - 32 - 128]);
values[bar_characters] = c - 32 - 128;
} else {
dest.append(code128Table[c - 32]);
values[bar_characters] = c - 32;
}
encodeInfo.append(Integer.toString(values[bar_characters])).append(" ");
bar_characters++;
read++;
break;
case LATCHC:
int weight;
int d = inputData[read + 1];
weight = (10 * (c - '0')) + (d - '0');
dest.append(code128Table[weight]);
values[bar_characters] = weight;
encodeInfo.append(Integer.toString(values[bar_characters])).append(" ");
bar_characters++;
read += 2;
break;
}
} else {
// FNC1
dest.append(code128Table[102]);
values[bar_characters] = 102;
bar_characters++;
read++;
encodeInfo.append("FNC1 ");
}
} while (read < sourcelen);
encodeInfo.append("\n");
/* "...note that the linkage flag is an extra code set character between
the last data character and the Symbol Check Character" (GS1 Specification) */
/* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */
switch (compositeMode) {
case CCA:
case CCB:
/* CC-A or CC-B 2D component */
switch (set[sourcelen - 1]) {
case LATCHA:
linkage_flag = 100;
break;
case LATCHB:
linkage_flag = 99;
break;
case LATCHC:
linkage_flag = 101;
break;
}
encodeInfo.append("Linkage flag: ").append(linkage_flag).append('\n');
break;
case CCC:
/* CC-C 2D component */
switch (set[sourcelen - 1]) {
case LATCHA:
linkage_flag = 99;
break;
case LATCHB:
linkage_flag = 101;
break;
case LATCHC:
linkage_flag = 100;
break;
}
encodeInfo.append("Linkage flag: ").append(linkage_flag).append('\n');
break;
default:
break;
}
if (linkage_flag != 0) {
dest.append(code128Table[linkage_flag]);
values[bar_characters] = linkage_flag;
bar_characters++;
}
/* check digit calculation */
for (i = 0; i < bar_characters; i++) {
if (i > 0) {
values[i] *= i;
}
total_sum += values[i];
}
dest.append(code128Table[total_sum % 103]);
encodeInfo.append("Data Codewords: ").append(bar_characters).append('\n');
encodeInfo.append("Check Digit: ").append(total_sum % 103).append('\n');
/* Stop character */
dest.append(code128Table[106]);
if (!(inputDataType == DataType.GS1)) {
readable = new StringBuilder(content);
}
if (inputDataType == DataType.HIBC) {
readable.append("*").append(content).append("*");
}
if (compositeMode == Composite.OFF) {
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
} else {
/* Add the separator pattern for composite symbols */
pattern = new String[2];
pattern[0] = "0" + dest;
pattern[1] = dest.toString();
rowCount = 2;
rowHeight = new int[2];
rowHeight[0] = 1;
rowHeight[1] = -1;
}
plotSymbol();
return true;
}
private Mode findSubset(int letter) {
Mode mode;
if (letter <= 31) {
mode = Mode.SHIFTA;
} else if ((letter >= 48) && (letter <= 57)) {
mode = Mode.ABORC;
} else if (letter <= 95) {
mode = Mode.AORB;
} else if (letter <= 127) {
mode = Mode.SHIFTB;
} else if (letter <= 159) {
mode = Mode.SHIFTA;
} else if (letter <= 223) {
mode = Mode.AORB;
} else {
mode = Mode.SHIFTB;
}
return mode;
}
private void reduceSubsetChanges() { /* Implements rules from ISO 15417 Annex E */
int i, length;
Mode current, last, next;
for (i = 0; i < index_point; i++) {
current = mode_type[i];
length = mode_length[i];
if (i != 0) {
last = mode_type[i - 1];
} else {
last = Mode.NULL;
}
if (i != index_point - 1) {
next = mode_type[i + 1];
} else {
next = Mode.NULL;
}
if (i == 0) { /* first block */
if ((index_point == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */
mode_type[i] = Mode.LATCHC;
}
if (current == Mode.ABORC) {
if (length >= 4) { /* Rule 1b */
mode_type[i] = Mode.LATCHC;
} else {
mode_type[i] = Mode.AORB;
current = Mode.AORB;
}
}
if (current == Mode.SHIFTA) { /* Rule 1c */
mode_type[i] = Mode.LATCHA;
}
if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB) { /* Rule 1d */
mode_type[i] = Mode.LATCHB;
}
} else {
if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */
mode_type[i] = Mode.LATCHC;
current = Mode.LATCHC;
}
if (current == Mode.ABORC) {
mode_type[i] = Mode.AORB;
current = Mode.AORB;
}
if ((current == Mode.AORB) && (last == Mode.LATCHA)) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.AORB) && (last == Mode.LATCHB)) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.AORB) && (next == Mode.SHIFTA)) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.AORB) && (next == Mode.SHIFTB)) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.AORB) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
} /* Rule 2 is implemented elsewhere, Rule 6 is implied */
}
combineSubsetBlocks();
}
private void combineSubsetBlocks() {
int i, j;
/* bring together same type blocks */
if (index_point > 1) {
i = 1;
while (i < index_point) {
if (mode_type[i - 1] == mode_type[i]) {
/* bring together */
mode_length[i - 1] = mode_length[i - 1] + mode_length[i];
j = i + 1;
/* decreace the list */
while (j < index_point) {
mode_length[j - 1] = mode_length[j];
mode_type[j - 1] = mode_type[j];
j++;
}
index_point--;
i--;
}
i++;
}
}
}
private enum Mode {
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC
}
private enum FMode {
SHIFTN, LATCHN, SHIFTF, LATCHF
}
private enum Composite {OFF, CCA, CCB, CCC}
}

View file

@ -0,0 +1,769 @@
package org.xbib.graphics.barcode;
import java.awt.geom.Rectangle2D;
import java.io.UnsupportedEncodingException;
/**
* Implements Code 16K symbology
* According to BS EN 12323:2005
* Encodes using a stacked symbology based on Code 128. Supports encoding
* of any 8-bit ISO 8859-1 (Latin-1) data with a maximum data capacity of 77
* alpha-numeric characters or 154 numerical digits.
*/
public class Code16k extends Symbol {
/* EN 12323 Table 1 - "Code 16K" character encodations */
private static final String[] C_16_K_TABLE = {
"212222", "222122", "222221", "121223", "121322", "131222", "122213",
"122312", "132212", "221213", "221312", "231212", "112232", "122132",
"122231", "113222", "123122", "123221", "223211", "221132", "221231",
"213212", "223112", "312131", "311222", "321122", "321221", "312212",
"322112", "322211", "212123", "212321", "232121", "111323", "131123",
"131321", "112313", "132113", "132311", "211313", "231113", "231311",
"112133", "112331", "132131", "113123", "113321", "133121", "313121",
"211331", "231131", "213113", "213311", "213131", "311123", "311321",
"331121", "312113", "312311", "332111", "314111", "221411", "431111",
"111224", "111422", "121124", "121421", "141122", "141221", "112214",
"112412", "122114", "122411", "142112", "142211", "241211", "221114",
"413111", "241112", "134111", "111242", "121142", "121241", "114212",
"124112", "124211", "411212", "421112", "421211", "212141", "214121",
"412121", "111143", "111341", "131141", "114113", "114311", "411113",
"411311", "113141", "114131", "311141", "411131", "211412", "211214",
"211232", "211133"
};
/* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */
private static final String[] C_16_K_START_STOP = {
"3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112"
};
/* EN 12323 Table 5 - Start and stop values defining row numbers */
private static final int[] C_16_K_START_VALUES = {
0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7
};
private static final int[] C_16_K_STOP_VALUES = {
0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3
};
private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */
private final int[] block_length = new int[170]; /* RENAME block_length */
@Override
public boolean encode() {
StringBuilder width_pattern;
int current_row, rows_needed, first_check, second_check;
int indexchaine, pads_needed;
char[] set, fset;
Mode mode;
char last_set, current_set;
int i, j, k, m, read;
int[] values;
int bar_characters;
double glyph_count;
int first_sum, second_sum;
int input_length;
int c_count;
boolean f_state;
int[] inputData;
if (!content.matches("[\u0000-\u00FF]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
try {
inputBytes = content.getBytes("ISO8859_1");
} catch (UnsupportedEncodingException e) {
errorMsg.append("Character encoding error");
return false;
}
input_length = content.length();
inputData = new int[input_length];
for (i = 0; i < input_length; i++) {
inputData[i] = inputBytes[i] & 0xFF;
}
bar_characters = 0;
set = new char[160];
fset = new char[160];
values = new int[160];
if (input_length > 157) {
errorMsg.append("Input too long");
return false;
}
/* Detect extended ASCII characters */
for (i = 0; i < input_length; i++) {
if (inputData[i] >= 128) {
fset[i] = 'f';
} else {
fset[i] = ' ';
}
}
/* Decide when to latch to extended mode */
for (i = 0; i < input_length; i++) {
j = 0;
if (fset[i] == 'f') {
do {
j++;
} while (fset[i + j] == 'f');
if ((j >= 5) || ((j >= 3) && ((i + j) == (input_length - 1)))) {
for (k = 0; k <= j; k++) {
fset[i + k] = 'F';
}
}
}
}
/* Decide if it is worth reverting to 646 encodation for a few characters */
if (input_length > 1) {
for (i = 1; i < input_length; i++) {
if ((fset[i - 1] == 'F') && (fset[i] == ' ')) {
/* Detected a change from 8859-1 to 646 - count how long for */
for (j = 0; (fset[i + j] == ' ') && ((i + j) < input_length); j++)
;
if (j < 5) {
/* Change to shifting back rather than latching back */
for (k = 0; k < j; k++) {
fset[i + k] = 'n';
}
}
}
}
}
/* Detect mode A, B and C characters */
int block_count = 0;
indexchaine = 0;
mode = findSubset(inputData[indexchaine]);
if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) {
mode = Mode.ABORC;
} /* FNC1 */
for (i = 0; i < 160; i++) {
block_length[i] = 0;
}
do {
block_mode[block_count] = mode;
while ((block_mode[block_count] == mode) && (indexchaine < input_length)) {
block_length[block_count]++;
indexchaine++;
if (indexchaine < input_length) {
mode = findSubset(inputData[indexchaine]);
if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) {
mode = Mode.ABORC;
} /* FNC1 */
}
}
block_count++;
} while (indexchaine < input_length);
reduceSubsetChanges(block_count);
/* Put set data into set[] */
read = 0;
for (i = 0; i < block_count; i++) {
for (j = 0; j < block_length[i]; j++) {
switch (block_mode[i]) {
case SHIFTA:
set[read] = 'a';
break;
case LATCHA:
set[read] = 'A';
break;
case SHIFTB:
set[read] = 'b';
break;
case LATCHB:
set[read] = 'B';
break;
case LATCHC:
set[read] = 'C';
break;
}
read++;
}
}
/* Adjust for strings which start with shift characters - make them latch instead */
if (set[0] == 'a') {
i = 0;
do {
set[i] = 'A';
i++;
} while (set[i] == 'a');
}
if (set[0] == 'b') {
i = 0;
do {
set[i] = 'B';
i++;
} while (set[i] == 'b');
}
/* Watch out for odd-length Mode C blocks */
c_count = 0;
for (i = 0; i < read; i++) {
if (set[i] == 'C') {
if (inputData[i] == '[') {
if ((c_count & 1) != 0) {
if ((i - c_count) != 0) {
set[i - c_count] = 'B';
} else {
set[i - 1] = 'B';
}
}
c_count = 0;
} else {
c_count++;
}
} else {
if ((c_count & 1) != 0) {
if ((i - c_count) != 0) {
set[i - c_count] = 'B';
} else {
set[i - 1] = 'B';
}
}
c_count = 0;
}
}
if ((c_count & 1) != 0) {
if ((i - c_count) != 0) {
set[i - c_count] = 'B';
} else {
set[i - 1] = 'B';
}
}
for (i = 1; i < read - 1; i++) {
if ((set[i] == 'C') && ((set[i - 1] == 'B') && (set[i + 1] == 'B'))) {
set[i] = 'B';
}
}
/* Make sure the data will fit in the symbol */
last_set = ' ';
glyph_count = 0.0;
for (i = 0; i < input_length; i++) {
if ((set[i] == 'a') || (set[i] == 'b')) {
glyph_count = glyph_count + 1.0;
}
if ((fset[i] == 'f') || (fset[i] == 'n')) {
glyph_count = glyph_count + 1.0;
}
if (((set[i] == 'A') || (set[i] == 'B')) || (set[i] == 'C')) {
if (set[i] != last_set) {
last_set = set[i];
glyph_count = glyph_count + 1.0;
}
}
if (i == 0) {
if ((set[i] == 'B') && (set[1] == 'C')) {
glyph_count = glyph_count - 1.0;
}
if ((set[i] == 'B') && (set[1] == 'B') && set[2] == 'C') {
glyph_count = glyph_count - 1.0;
}
if (fset[i] == 'F') {
glyph_count = glyph_count + 2.0;
}
} else {
if ((fset[i] == 'F') && (fset[i - 1] != 'F')) {
glyph_count = glyph_count + 2.0;
}
if ((fset[i] != 'F') && (fset[i - 1] == 'F')) {
glyph_count = glyph_count + 2.0;
}
}
if ((set[i] == 'C') && (!((inputDataType == DataType.GS1) && (content.charAt(i) == '[')))) {
glyph_count = glyph_count + 0.5;
} else {
glyph_count = glyph_count + 1.0;
}
}
if ((inputDataType == DataType.GS1) && (set[0] != 'A')) {
/* FNC1 can be integrated with mode character */
glyph_count--;
}
if (glyph_count > 77.0) {
errorMsg.append("Input too long");
return false;
}
/* Calculate how tall the symbol will be */
glyph_count = glyph_count + 2.0;
i = (int) glyph_count;
rows_needed = (i / 5);
if (i % 5 > 0) {
rows_needed++;
}
if (rows_needed == 1) {
rows_needed = 2;
}
/* start with the mode character - Table 2 */
m = 0;
switch (set[0]) {
case 'A':
m = 0;
break;
case 'B':
m = 1;
break;
case 'C':
m = 2;
break;
}
if (readerInit) {
if (m == 2) {
m = 5;
}
if (inputDataType == DataType.GS1) {
errorMsg.append("Cannot use both GS1 mode and Reader Initialisation");
return false;
} else {
if ((set[0] == 'B') && (set[1] == 'C')) {
m = 6;
}
}
values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */
values[bar_characters + 1] = 96; /* FNC3 */
bar_characters += 2;
} else {
if (inputDataType == DataType.GS1) {
/* Integrate FNC1 */
switch (set[0]) {
case 'B':
m = 3;
break;
case 'C':
m = 4;
break;
}
} else {
if ((set[0] == 'B') && (set[1] == 'C')) {
m = 5;
}
if (((set[0] == 'B') && (set[1] == 'B')) && (set[2] == 'C')) {
m = 6;
}
}
}
values[bar_characters] = (7 * (rows_needed - 2)) + m; /* see 4.3.4.2 */
bar_characters++;
//}
current_set = set[0];
f_state = false;
/* f_state remembers if we are in Extended ASCII mode (value 1) or
in ISO/IEC 646 mode (value 0) */
if (fset[0] == 'F') {
switch (current_set) {
case 'A':
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
break;
case 'B':
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
break;
}
bar_characters += 2;
f_state = true;
}
read = 0;
/* Encode the data */
do {
if ((read != 0) && (set[read] != set[read - 1])) { /* Latch different code set */
switch (set[read]) {
case 'A':
values[bar_characters] = 101;
bar_characters++;
current_set = 'A';
break;
case 'B':
values[bar_characters] = 100;
bar_characters++;
current_set = 'B';
break;
case 'C':
if (!((read == 1) && (set[0] == 'B'))) { /* Not Mode C/Shift B */
if (!((read == 2) && ((set[0] == 'B') && (set[1] == 'B')))) {
/* Not Mode C/Double Shift B */
values[bar_characters] = 99;
bar_characters++;
}
}
current_set = 'C';
break;
}
}
if (read != 0) {
if ((fset[read] == 'F') && !f_state) {
/* Latch beginning of extended mode */
switch (current_set) {
case 'A':
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
break;
case 'B':
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
break;
}
bar_characters += 2;
f_state = true;
}
if ((fset[read] == ' ') && f_state) {
/* Latch end of extended mode */
switch (current_set) {
case 'A':
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
break;
case 'B':
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
break;
}
bar_characters += 2;
f_state = false;
}
}
if ((fset[i] == 'f') || (fset[i] == 'n')) {
/* Shift extended mode */
switch (current_set) {
case 'A':
values[bar_characters] = 101; /* FNC 4 */
break;
case 'B':
values[bar_characters] = 100; /* FNC 4 */
break;
}
bar_characters++;
}
if ((set[i] == 'a') || (set[i] == 'b')) {
/* Insert shift character */
values[bar_characters] = 98;
bar_characters++;
}
if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) {
switch (set[read]) { /* Encode data characters */
case 'A':
case 'a':
getValueSubsetA(inputData[read], values, bar_characters);
bar_characters++;
read++;
break;
case 'B':
case 'b':
getValueSubsetB(inputData[read], values, bar_characters);
bar_characters++;
read++;
break;
case 'C':
getValueSubsetC(inputData[read], inputData[read + 1], values, bar_characters);
bar_characters++;
read += 2;
break;
}
} else {
values[bar_characters] = 102;
bar_characters++;
read++;
}
} while (read < input_length);
pads_needed = 5 - ((bar_characters + 2) % 5);
if (pads_needed == 5) {
pads_needed = 0;
}
if ((bar_characters + pads_needed) < 8) {
pads_needed += 8 - (bar_characters + pads_needed);
}
for (i = 0; i < pads_needed; i++) {
values[bar_characters] = 106;
bar_characters++;
}
/* Calculate check digits */
first_sum = 0;
second_sum = 0;
for (i = 0; i < bar_characters; i++) {
first_sum += (i + 2) * values[i];
second_sum += (i + 1) * values[i];
}
first_check = first_sum % 107;
second_sum += first_check * (bar_characters + 1);
second_check = second_sum % 107;
values[bar_characters] = first_check;
values[bar_characters + 1] = second_check;
readable = new StringBuilder();
pattern = new String[rows_needed];
rowCount = rows_needed;
rowHeight = new int[rows_needed];
encodeInfo.append("Symbol Rows: ").append(rows_needed).append("\n");
encodeInfo.append("First Check Digit: ").append(first_check).append("\n");
encodeInfo.append("Second Check Digit: ").append(second_check).append("\n");
encodeInfo.append("Codewords: ");
for (current_row = 0; current_row < rows_needed; current_row++) {
width_pattern = new StringBuilder();
width_pattern.append(C_16_K_START_STOP[C_16_K_START_VALUES[current_row]]);
width_pattern.append("1");
for (i = 0; i < 5; i++) {
width_pattern.append(C_16_K_TABLE[values[(current_row * 5) + i]]);
encodeInfo.append(values[(current_row * 5) + i]).append(" ");
}
width_pattern.append(C_16_K_START_STOP[C_16_K_STOP_VALUES[current_row]]);
pattern[current_row] = width_pattern.toString();
rowHeight[current_row] = 10;
}
encodeInfo.append("\n");
plotSymbol();
return true;
}
private void getValueSubsetA(int source, int[] values, int bar_chars) {
if (source > 127) {
if (source < 160) {
values[bar_chars] = source + 64 - 128;
} else {
values[bar_chars] = source - 32 - 128;
}
} else {
if (source < 32) {
values[bar_chars] = source + 64;
} else {
values[bar_chars] = source - 32;
}
}
}
private void getValueSubsetB(int source, int[] values, int bar_chars) {
if (source > 127) {
values[bar_chars] = source - 32 - 128;
} else {
values[bar_chars] = source - 32;
}
}
private void getValueSubsetC(int source_a, int source_b, int[] values, int bar_chars) {
int weight;
weight = (10 * Character.getNumericValue(source_a)) + Character.getNumericValue(source_b);
values[bar_chars] = weight;
}
private Mode findSubset(int letter) {
Mode mode;
if (letter <= 31) {
mode = Mode.SHIFTA;
} else if ((letter >= 48) && (letter <= 57)) {
mode = Mode.ABORC;
} else if (letter <= 95) {
mode = Mode.AORB;
} else if (letter <= 127) {
mode = Mode.SHIFTB;
} else if (letter <= 159) {
mode = Mode.SHIFTA;
} else if (letter <= 223) {
mode = Mode.AORB;
} else {
mode = Mode.SHIFTB;
}
return mode;
}
private void reduceSubsetChanges(int block_count) { /* Implements rules from ISO 15417 Annex E */
int i, length;
Mode current, last, next;
for (i = 0; i < block_count; i++) {
current = block_mode[i];
length = block_length[i];
if (i != 0) {
last = block_mode[i - 1];
} else {
last = Mode.NULL;
}
if (i != block_count - 1) {
next = block_mode[i + 1];
} else {
next = Mode.NULL;
}
if (i == 0) { /* first block */
if ((block_count == 1) && ((length == 2) && (current == Mode.ABORC))) { /* Rule 1a */
block_mode[i] = Mode.LATCHC;
}
if (current == Mode.ABORC) {
if (length >= 4) { /* Rule 1b */
block_mode[i] = Mode.LATCHC;
} else {
block_mode[i] = Mode.AORB;
current = Mode.AORB;
}
}
if (current == Mode.SHIFTA) { /* Rule 1c */
block_mode[i] = Mode.LATCHA;
}
if ((current == Mode.AORB) && (next == Mode.SHIFTA)) { /* Rule 1c */
block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB) { /* Rule 1d */
block_mode[i] = Mode.LATCHB;
}
} else {
if ((current == Mode.ABORC) && (length >= 4)) { /* Rule 3 */
block_mode[i] = Mode.LATCHC;
current = Mode.LATCHC;
}
if (current == Mode.ABORC) {
block_mode[i] = Mode.AORB;
current = Mode.AORB;
}
if ((current == Mode.AORB) && (last == Mode.LATCHA)) {
block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.AORB) && (last == Mode.LATCHB)) {
block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.AORB) && (next == Mode.SHIFTA)) {
block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.AORB) && (next == Mode.SHIFTB)) {
block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.AORB) {
block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.SHIFTA) && (length > 1)) { /* Rule 4 */
block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.SHIFTB) && (length > 1)) { /* Rule 5 */
block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.SHIFTA) && (last == Mode.LATCHA)) {
block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.SHIFTB) && (last == Mode.LATCHB)) {
block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if ((current == Mode.SHIFTA) && (last == Mode.LATCHC)) {
block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if ((current == Mode.SHIFTB) && (last == Mode.LATCHC)) {
block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
}
}
combineSubsetBlocks(block_count);
}
private void combineSubsetBlocks(int block_count) {
int i, j;
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (block_mode[i - 1] == block_mode[i]) {
block_length[i - 1] = block_length[i - 1] + block_length[i];
j = i + 1;
while (j < block_count) {
block_length[j - 1] = block_length[j];
block_mode[j - 1] = block_mode[j];
j++;
}
block_count = block_count - 1;
i--;
}
i++;
}
}
}
@Override
protected void plotSymbol() {
int xBlock, yBlock;
int x, y, w, h;
boolean black;
getRectangles().clear();
y = 1;
h = 1;
for (yBlock = 0; yBlock < rowCount; yBlock++) {
black = true;
x = 15;
for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) {
if (black) {
black = false;
w = pattern[yBlock].charAt(xBlock) - '0';
if (rowHeight[yBlock] == -1) {
h = defaultHeight;
} else {
h = rowHeight[yBlock];
}
if (w != 0 && h != 0) {
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
}
if ((x + w) > symbolWidth) {
symbolWidth = x + w;
}
} else {
black = true;
}
x += (double) (pattern[yBlock].charAt(xBlock) - '0');
}
y += h;
if ((y + h) > symbolHeight) {
symbolHeight = y + h;
}
/* Add bars between rows */
if (yBlock != (rowCount - 1)) {
Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, (symbolWidth - 15), 2);
getRectangles().add(rect);
}
}
Rectangle2D.Double top = new Rectangle2D.Double(0, 0, (symbolWidth + 15), 2);
getRectangles().add(top);
Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, (symbolWidth + 15), 2);
getRectangles().add(bottom);
symbolWidth += 30;
symbolHeight += 2;
mergeVerticalBlocks();
}
private enum Mode {
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
}
}

View file

@ -0,0 +1,493 @@
package org.xbib.graphics.barcode;
import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.geom.Rectangle2D;
/**
* Implements the Code 2 of 5 family of barcode standards.
*/
public class Code2Of5 extends Symbol {
private static final String[] C25_MATRIX_TABLE = {
"113311", "311131", "131131", "331111", "113131", "313111", "133111", "111331", "311311", "131311"
};
private static final String[] C25_INDUSTRIAL_TABLE = {
"1111313111", "3111111131", "1131111131", "3131111111", "1111311131", "3111311111", "1131311111", "1111113131", "3111113111", "1131113111"
};
private static final String[] C25_INTERLEAVED_TABLE = {
"11331", "31113", "13113", "33111", "11313", "31311", "13311", "11133", "31131", "13131"
};
/**
* The 2-of-5 mode.
*/
private ToFMode mode = ToFMode.MATRIX;
/**
* Ratio of wide bar width to narrow bar width.
*/
private double moduleWidthRatio = 3;
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return moduleWidthRatio;
}
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually
* between {@code 2} and {@code 3}. The default value is {@code 3}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
/**
* Select Standard Code 2 of 5 mode, also known as Code 2 of 5 Matrix. (default)
* Encodes any length numeric input (digits 0-9).
*/
public void setMatrixMode() {
mode = ToFMode.MATRIX;
}
/**
* Select Industrial Code 2 of 5 which can encode any length numeric input
* (digits 0-9) and does not include a check digit.
*/
public void setIndustrialMode() {
mode = ToFMode.INDUSTRIAL;
}
/**
* Select International Air Transport Agency variation of Code 2 of 5.
* Encodes any length numeric input (digits 0-9) and does not include
* a check digit.
*/
public void setIATAMode() {
mode = ToFMode.IATA;
}
/**
* Select Code 2 of 5 Data Logic. Encodes any length numeric input
* (digits 0-9) and does not include a check digit.
*/
public void setDataLogicMode() {
mode = ToFMode.DATA_LOGIC;
}
/**
* Select Interleaved Code 2 of 5. encodes pairs of numbers, and so can
* only encode an even number of digits (0-9). If an odd number of digits
* is entered a leading zero is added. No check digit is calculated.
*/
public void setInterleavedMode() {
mode = ToFMode.INTERLEAVED;
}
/**
* Select ITF-14, also known as UPC Shipping Container Symbol or Case Code.
* Requires a 13 digit numeric input (digits 0-9). One modulo-10 check
* digit is calculated.
*/
public void setITF14Mode() {
mode = ToFMode.ITF14;
}
/**
* Select Deutsche Post Leitcode. Requires a 13-digit numerical input.
* Check digit is calculated.
*/
public void setDPLeitMode() {
mode = ToFMode.DPLEIT;
}
/**
* Select Deutsche Post Identcode. Requires an 11-digit numerical input.
* Check digit is calculated.
*/
public void setDPIdentMode() {
mode = ToFMode.DPIDENT;
}
@Override
public boolean encode() {
boolean retval = false;
switch (mode) {
case MATRIX:
retval = dataMatrixTof();
break;
case INDUSTRIAL:
retval = industrialTof();
break;
case IATA:
retval = iataTof();
break;
case INTERLEAVED:
retval = interleavedTof();
break;
case DATA_LOGIC:
retval = dataLogic();
break;
case ITF14:
retval = itf14();
break;
case DPLEIT:
retval = deutschePostLeitcode();
break;
case DPIDENT:
retval = deutschePostIdentcode();
break;
}
if (retval) {
plotSymbol();
}
return retval;
}
private boolean dataMatrixTof() {
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder dest = new StringBuilder("311111");
for (int i = 0; i < content.length(); i++) {
dest.append(C25_MATRIX_TABLE[Character.getNumericValue(content.charAt(i))]);
}
dest.append("31111");
readable = new StringBuilder(content);
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean industrialTof() {
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder dest = new StringBuilder("313111");
readable = new StringBuilder(content);
for (int i = 0; i < readable.length(); i++) {
dest.append(C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))]);
}
dest.append("31113");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean iataTof() {
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder dest = new StringBuilder("1111");
readable = new StringBuilder(content);
for (int i = 0; i < readable.length(); i++) {
dest.append(C25_INDUSTRIAL_TABLE[Character.getNumericValue(readable.charAt(i))]);
}
dest.append("311");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean dataLogic() {
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder dest = new StringBuilder("1111");
readable = new StringBuilder(content);
for (int i = 0; i < readable.length(); i++) {
dest.append(C25_MATRIX_TABLE[Character.getNumericValue(readable.charAt(i))]);
}
dest.append("311");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean interleavedTof() {
int i;
StringBuilder dest;
if ((content.length() & 1) == 0) {
readable = new StringBuilder(content);
} else {
readable = new StringBuilder("0").append(content);
}
if (!(readable.toString().matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
dest = new StringBuilder("1111");
for (i = 0; i < readable.length(); i += 2) {
dest.append(interlace(i, i + 1));
}
dest.append("311");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private String interlace(int x, int y) {
char a = readable.charAt(x);
char b = readable.charAt(y);
String one = C25_INTERLEAVED_TABLE[Character.getNumericValue(a)];
String two = C25_INTERLEAVED_TABLE[Character.getNumericValue(b)];
String f = "";
for (int i = 0; i < 5; i++) {
f += one.charAt(i);
f += two.charAt(i);
}
return f;
}
private boolean itf14() {
int i, count = 0;
int input_length = content.length();
StringBuilder dest;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (input_length > 13) {
errorMsg.append("Input data too long");
return false;
}
readable = new StringBuilder();
for (i = input_length; i < 13; i++) {
readable.append("0");
}
readable.append(content);
for (i = 12; i >= 0; i--) {
count += readable.charAt(i) - '0';
if ((i & 1) == 0) {
count += 2 * (readable.charAt(i) - '0');
}
}
readable.append((char) (((10 - (count % 10)) % 10) + '0'));
encodeInfo.append("Check Digit: ").append((char) (((10 - (count % 10)) % 10) + '0'));
encodeInfo.append('\n');
dest = new StringBuilder("1111");
for (i = 0; i < readable.length(); i += 2) {
dest.append(interlace(i, i + 1));
}
dest.append("311");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean deutschePostLeitcode() {
int i, count = 0;
int input_length = content.length();
StringBuilder dest;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (input_length > 13) {
errorMsg.append("Input data too long");
return false;
}
readable = new StringBuilder();
for (i = input_length; i < 13; i++) {
readable.append("0");
}
readable.append(content);
for (i = 12; i >= 0; i--) {
count += 4 * (readable.charAt(i) - '0');
if ((i & 1) != 0) {
count += 5 * (readable.charAt(i) - '0');
}
}
readable.append((char) (((10 - (count % 10)) % 10) + '0'));
encodeInfo.append("Check digit: ").append((char) (((10 - (count % 10)) % 10) + '0'));
encodeInfo.append('\n');
dest = new StringBuilder("1111");
for (i = 0; i < readable.length(); i += 2) {
dest.append(interlace(i, i + 1));
}
dest.append("311");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean deutschePostIdentcode() {
int i, count = 0;
int input_length = content.length();
StringBuilder dest;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (input_length > 11) {
errorMsg.append("Input data too long");
return false;
}
readable = new StringBuilder();
for (i = input_length; i < 11; i++) {
readable.append("0");
}
readable.append(content);
for (i = 10; i >= 0; i--) {
count += 4 * (readable.charAt(i) - '0');
if ((i & 1) != 0) {
count += 5 * (readable.charAt(i) - '0');
}
}
readable.append((char) (((10 - (count % 10)) % 10) + '0'));
encodeInfo.append("Check Digit: ").append((char) (((10 - (count % 10)) % 10) + '0'));
encodeInfo.append('\n');
dest = new StringBuilder("1111");
for (i = 0; i < readable.length(); i += 2) {
dest.append(interlace(i, i + 1));
}
dest.append("311");
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
getRectangles().clear();
getTexts().clear();
int baseY;
if (getHumanReadableLocation() == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
double x = 0;
int y = baseY;
int h = 0;
boolean black = true;
int offset = 0;
if (mode == ToFMode.ITF14) {
offset = 20;
}
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
char c = pattern[0].charAt(xBlock);
double w = getModuleWidth(c - '0') * moduleWidth;
if (black) {
if (rowHeight[0] == -1) {
h = defaultHeight;
} else {
h = rowHeight[0];
}
if (w != 0 && h != 0) {
Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h);
getRectangles().add(rect);
}
symbolWidth = (int) Math.ceil(x + w + (2 * offset));
}
black = !black;
x += w;
}
symbolHeight = h;
if (mode == ToFMode.ITF14) {
Rectangle2D.Double topBar = new Rectangle2D.Double(0, baseY, symbolWidth, 4);
Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, baseY + symbolHeight - 4, symbolWidth, 4);
Rectangle2D.Double leftBar = new Rectangle2D.Double(0, baseY, 4, symbolHeight);
Rectangle2D.Double rightBar = new Rectangle2D.Double(symbolWidth - 4, baseY, 4, symbolHeight);
getRectangles().add(topBar);
getRectangles().add(bottomBar);
getRectangles().add(leftBar);
getRectangles().add(rightBar);
}
if (getHumanReadableLocation() != NONE && readable.length() > 0) {
double baseline;
if (getHumanReadableLocation() == TOP) {
baseline = fontSize;
} else {
baseline = getHeight() + fontSize;
}
double centerX = getWidth() / 2.0;
getTexts().add(new TextBox(centerX, baseline, readable.toString()));
}
}
@Override
protected double getModuleWidth(int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return moduleWidthRatio;
}
}
private enum ToFMode {
MATRIX, INDUSTRIAL, IATA, DATA_LOGIC, INTERLEAVED, ITF14, DPLEIT, DPIDENT
}
}

View file

@ -0,0 +1,103 @@
package org.xbib.graphics.barcode;
/**
* Implements Code 32, also known as Italian Pharmacode, A variation of Code
* 39 used by the Italian Ministry of Health ("Ministero della Sanità")
* Requires a numeric input up to 8 digits in length. Check digit is
* calculated.
*/
public class Code32 extends Symbol {
private char[] tabella = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F',
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z'
};
@Override
public boolean encode() {
int i, checksum, checkpart, checkdigit;
int pharmacode, remainder, devisor;
StringBuilder localstr;
StringBuilder risultante;
int[] codeword = new int[6];
Code3Of9 c39 = new Code3Of9();
if (content.length() > 8) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
/* Add leading zeros as required */
localstr = new StringBuilder();
for (i = content.length(); i < 8; i++) {
localstr.append("0");
}
localstr.append(content);
/* Calculate the check digit */
checksum = 0;
checkpart = 0;
for (i = 0; i < 4; i++) {
checkpart = Character.getNumericValue(localstr.charAt(i * 2));
checksum += checkpart;
checkpart = 2 * Character.getNumericValue(localstr.charAt((i * 2) + 1));
if (checkpart >= 10) {
checksum += (checkpart - 10) + 1;
} else {
checksum += checkpart;
}
}
/* Add check digit to data string */
checkdigit = checksum % 10;
localstr.append((char) (checkdigit + '0'));
encodeInfo.append("Check Digit: ").append((char) (checkdigit + '0'));
encodeInfo.append('\n');
/* Convert string into an integer value */
pharmacode = 0;
for (i = 0; i < localstr.length(); i++) {
pharmacode *= 10;
pharmacode += Character.getNumericValue(localstr.charAt(i));
}
/* Convert from decimal to base-32 */
devisor = 33554432;
for (i = 5; i >= 0; i--) {
codeword[i] = pharmacode / devisor;
remainder = pharmacode % devisor;
pharmacode = remainder;
devisor /= 32;
}
/* Look up values in 'Tabella di conversione' */
risultante = new StringBuilder();
for (i = 5; i >= 0; i--) {
risultante.append(tabella[codeword[i]]);
}
/* Plot the barcode using Code 39 */
readable = new StringBuilder("A").append(localstr);
pattern = new String[1];
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
encodeInfo.append("Code 39 Equivalent: ").append(risultante).append('\n');
try {
c39.setContent(risultante.toString());
} catch (Exception e) {
errorMsg.append(e.getMessage());
return false;
}
this.pattern[0] = c39.pattern[0];
this.plotSymbol();
return true;
}
}

View file

@ -0,0 +1,155 @@
package org.xbib.graphics.barcode;
/**
* Implements Code 39 bar code symbology according to ISO/IEC 16388:2007.
* Input data can be of any length and supports the characters 0-9, A-Z, dash
* (-), full stop (.), space, asterisk (*), dollar ($), slash (/), plus (+)
* and percent (%). The standard does not require a check digit but a
* modulo-43 check digit can be added if required.
*/
public class Code3Of9 extends Symbol {
private static final String[] CODE_39 = {
"1112212111", "2112111121", "1122111121", "2122111111", "1112211121",
"2112211111", "1122211111", "1112112121", "2112112111", "1122112111",
"2111121121", "1121121121", "2121121111", "1111221121", "2111221111",
"1121221111", "1111122121", "2111122111", "1121122111", "1111222111",
"2111111221", "1121111221", "2121111211", "1111211221", "2111211211",
"1121211211", "1111112221", "2111112211", "1121112211", "1111212211",
"2211111121", "1221111121", "2221111111", "1211211121", "2211211111",
"1221211111", "1211112121", "2211112111", "1221112111", "1212121111",
"1212111211", "1211121211", "1112121211"
};
private static final char[] LOOKUP = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+',
'%'
};
private CheckDigit checkOption = CheckDigit.NONE;
/**
* Ratio of wide bar width to narrow bar width.
*/
private double moduleWidthRatio = 2;
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return moduleWidthRatio;
}
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually
* between {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
/**
* Select addition of optional Modulo-43 check digit or encoding without
* check digit.
*
* @param checkMode Check digit option.
*/
public void setCheckDigit(CheckDigit checkMode) {
checkOption = checkMode;
}
@Override
public boolean encode() {
if (!(content.matches("[0-9A-Z\\. \\-$/+%]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder p = new StringBuilder();
StringBuilder dest = new StringBuilder();
int l = content.length();
int charval;
char thischar;
int counter = 0;
char check_digit = ' ';
dest.append("1211212111");
for (int i = 0; i < l; i++) {
thischar = content.charAt(i);
charval = positionOf(thischar, LOOKUP);
counter += charval;
p.append(CODE_39[charval]);
}
dest.append(p);
if (checkOption == CheckDigit.MOD43) {
counter = counter % 43;
if (counter < 10) {
check_digit = (char) (counter + '0');
} else {
if (counter < 36) {
check_digit = (char) ((counter - 10) + 'A');
} else {
switch (counter) {
case 36:
check_digit = '-';
break;
case 37:
check_digit = '.';
break;
case 38:
check_digit = ' ';
break;
case 39:
check_digit = '$';
break;
case 40:
check_digit = '/';
break;
case 41:
check_digit = '+';
break;
default:
check_digit = 37;
break;
}
}
}
charval = positionOf(check_digit, LOOKUP);
p.append(CODE_39[charval]);
if (check_digit == ' ') {
check_digit = '_';
}
}
dest.append("121121211");
if (checkOption == CheckDigit.MOD43) {
readable = new StringBuilder("*").append(content).append(check_digit).append("*");
encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
} else {
readable = new StringBuilder("*").append(content).append("*");
}
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
plotSymbol();
return true;
}
@Override
protected double getModuleWidth(int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return moduleWidthRatio;
}
}
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(10);
}
public enum CheckDigit {
NONE, MOD43
}
}

View file

@ -0,0 +1,79 @@
package org.xbib.graphics.barcode;
/**
* Implements Code 3 of 9 Extended, also known as Code 39e and Code39+.
* Supports encoding of all characters in the 7-bit ASCII table. A
* modulo-43 check digit can be added if required.
*/
public class Code3Of9Extended extends Symbol {
private final String[] ECode39 = {
"%U", "$A", "$B", "$C", "$D", "$E", "$F", "$G", "$H", "$I", "$J", "$K",
"$L", "$M", "$N", "$O", "$P", "$Q", "$R", "$S", "$T", "$U", "$V", "$W",
"$X", "$Y", "$Z", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C",
"/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "/Z", "%F", "%G",
"%H", "%I", "%J", "%V", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "%K", "%L", "%M", "%N", "%O", "%W", "+A", "+B", "+C",
"+D", "+E", "+F", "+G", "+H", "+I", "+J", "+K", "+L", "+M", "+N", "+O",
"+P", "+Q", "+R", "+S", "+T", "+U", "+V", "+W", "+X", "+Y", "+Z", "%P",
"%Q", "%R", "%S", "%T"
};
private CheckDigit checkOption;
public Code3Of9Extended() {
checkOption = CheckDigit.NONE;
}
/**
* Select addition of optional Modulo-43 check digit or encoding without
* check digit.
*
* @param checkMode Check digit option
*/
public void setCheckDigit(CheckDigit checkMode) {
checkOption = checkMode;
}
@Override
public boolean encode() {
StringBuilder buffer = new StringBuilder();
int l = content.length();
int asciicode;
Code3Of9 c = new Code3Of9();
if (checkOption == CheckDigit.MOD43) {
c.setCheckDigit(Code3Of9.CheckDigit.MOD43);
}
if (!content.matches("[\u0000-\u007F]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
for (int i = 0; i < l; i++) {
asciicode = content.charAt(i);
buffer.append(ECode39[asciicode]);
}
try {
c.setContent(buffer.toString());
} catch (Exception e) {
errorMsg.append(e.getMessage());
return false;
}
readable = new StringBuilder(content);
pattern = new String[1];
pattern[0] = c.pattern[0];
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
public enum CheckDigit {
NONE, MOD43
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,192 @@
package org.xbib.graphics.barcode;
/**
* Implements <a href="http://en.wikipedia.org/wiki/Code_93">Code 93</a>.
* Supports encoding of 7-bit ASCII text. Two check digits are added.
*/
public class Code93 extends Symbol {
/**
* Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $,
* b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters).
*/
private static final String[] CODE_93_CTRL = {
"bU", "aA", "aB", "aC", "aD", "aE", "aF", "aG", "aH", "aI",
"aJ", "aK", "aL", "aM", "aN", "aO", "aP", "aQ", "aR", "aS",
"aT", "aU", "aV", "aW", "aX", "aY", "aZ", "bA", "bB", "bC",
"bD", "bE", " ", "cA", "cB", "cC", "$", "%", "cF", "cG",
"cH", "cI", "cJ", "+", "cL", "-", ".", "/", "0", "1",
"2", "3", "4", "5", "6", "7", "8", "9", "cZ", "bF",
"bG", "bH", "bI", "bJ", "bV", "A", "B", "C", "D", "E",
"F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y",
"Z", "bK", "bL", "bM", "bN", "bO", "bW", "dA", "dB", "dC",
"dD", "dE", "dF", "dG", "dH", "dI", "dJ", "dK", "dL", "dM",
"dN", "dO", "dP", "dQ", "dR", "dS", "dT", "dU", "dV", "dW",
"dX", "dY", "dZ", "bP", "bQ", "bR", "bS", "bT"};
/**
* Mapping of control characters to pattern table index (NOTE: a = Ctrl $,
* b = Ctrl %, c = Ctrl /, d = Ctrl + for sequences of two characters).
*/
private static final char[] CODE_93_LOOKUP = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$',
'/', '+', '%', 'a', 'b', 'c', 'd'};
/**
* Code 93 pattern table.
*/
private static final String[] CODE_93_TABLE = {
"131112", "111213", "111312", "111411", "121113",
"121212", "121311", "111114", "131211", "141111",
"211113", "211212", "211311", "221112", "221211",
"231111", "112113", "112212", "112311", "122112",
"132111", "111123", "111222", "111321", "121122",
"131121", "212112", "212211", "211122", "211221",
"221121", "222111", "112122", "112221", "122121",
"123111", "121131", "311112", "311211", "321111",
"112131", "113121", "211131", "121221", "312111",
"311121", "122211"};
/**
* Whether or not to show check digits in the human-readable text.
*/
private boolean showCheckDigits = true;
/**
* Optional start/stop delimiter to be shown in the human-readable text.
*/
private Character startStopDelimiter;
private static char[] toControlChars(String s) {
StringBuilder buffer = new StringBuilder();
char[] chars = s.toCharArray();
for (char asciiCode : chars) {
buffer.append(CODE_93_CTRL[asciiCode]);
}
return buffer.toString().toCharArray();
}
private static int calculateCheckDigitC(int[] values, int length) {
int c = 0;
int weight = 1;
for (int i = length - 1; i >= 0; i--) {
c += values[i] * weight;
weight++;
if (weight == 21) {
weight = 1;
}
}
c = c % 47;
return c;
}
private static int calculateCheckDigitK(int[] values, int length) {
int k = 0;
int weight = 1;
for (int i = length - 1; i >= 0; i--) {
k += values[i] * weight;
weight++;
if (weight == 16) {
weight = 1;
}
}
k = k % 47;
return k;
}
private static String toPattern(int[] values) {
StringBuilder buffer = new StringBuilder("111141");
for (int value : values) {
buffer.append(CODE_93_TABLE[value]);
}
buffer.append("1111411");
return buffer.toString();
}
/**
* Returns whether or not this symbol shows check digits in the human-readable text.
*
* @return whether or not this symbol shows check digits in the human-readable text
*/
public boolean getShowCheckDigits() {
return showCheckDigits;
}
/**
* Sets whether or not to show check digits in the human-readable text (defaults to <code>true</code>).
*
* @param showCheckDigits whether or not to show check digits in the human-readable text
*/
public void setShowCheckDigits(boolean showCheckDigits) {
this.showCheckDigits = showCheckDigits;
}
/**
* Returns the optional start/stop delimiter to be shown in the human-readable text.
*
* @return the optional start/stop delimiter to be shown in the human-readable text
*/
public Character getStartStopDelimiter() {
return startStopDelimiter;
}
/**
* Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to <code>null</code>).
*
* @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable text
*/
public void setStartStopDelimiter(Character startStopDelimiter) {
this.startStopDelimiter = startStopDelimiter;
}
@Override
public boolean encode() {
char[] controlChars = toControlChars(content);
int l = controlChars.length;
if (!content.matches("[\u0000-\u007F]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
int[] values = new int[controlChars.length + 2];
for (int i = 0; i < l; i++) {
values[i] = positionOf(controlChars[i], CODE_93_LOOKUP);
}
int c = calculateCheckDigitC(values, l);
values[l] = c;
l++;
int k = calculateCheckDigitK(values, l);
values[l] = k;
readable = new StringBuilder(content);
if (showCheckDigits) {
readable.append(CODE_93_LOOKUP[c]).append(CODE_93_LOOKUP[k]);
}
if (startStopDelimiter != null) {
readable = new StringBuilder(startStopDelimiter).append(readable).append(startStopDelimiter);
}
encodeInfo.append("Check Digit C: ").append(c).append("\n");
encodeInfo.append("Check Digit K: ").append(k).append("\n");
pattern = new String[]{toPattern(values)};
rowCount = 1;
rowHeight = new int[]{-1};
plotSymbol();
return true;
}
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,698 @@
package org.xbib.graphics.barcode;
import java.math.BigInteger;
/**
* Implements GS1 DataBar Omnidirectional and GS1 DataBar Truncated according to ISO/IEC 24724:2011.
* Input data should be a 13 digit Global Trade Identification Number
* without check digit or Application Identifier [01].
*/
public class DataBar14 extends Symbol {
private int[] g_sum_table = {
0, 161, 961, 2015, 2715, 0, 336, 1036, 1516
};
private int[] t_table = {
1, 10, 34, 70, 126, 4, 20, 48, 81
};
private int[] widths = new int[8];
private int[] modules_odd = {
12, 10, 8, 6, 4, 5, 7, 9, 11
};
private int[] modules_even = {
4, 6, 8, 10, 12, 10, 8, 6, 4
};
private int[] widest_odd = {
8, 6, 4, 3, 1, 2, 4, 6, 8
};
private int[] widest_even = {
1, 3, 5, 6, 8, 7, 5, 3, 1
};
private int[] checksum_weight = { /* Table 5 */
1, 3, 9, 27, 2, 6, 18, 54, 4, 12, 36, 29, 8, 24, 72, 58, 16, 48, 65,
37, 32, 17, 51, 74, 64, 34, 23, 69, 49, 68, 46, 59
};
private int[] finder_pattern = {
3, 8, 2, 1, 1, 3, 5, 5, 1, 1, 3, 3, 7, 1, 1, 3, 1, 9, 1, 1, 2, 7, 4,
1, 1, 2, 5, 6, 1, 1, 2, 3, 8, 1, 1, 1, 5, 7, 1, 1, 1, 3, 9, 1, 1
};
private boolean linkageFlag;
private gb14Mode symbolType;
private boolean[][] grid = new boolean[5][100];
private boolean[] seperator = new boolean[100];
public DataBar14() {
linkageFlag = false;
symbolType = gb14Mode.LINEAR;
}
@Override
public void setDataType(DataType dummy) {
// Do nothing!
}
protected void setLinkageFlag() {
linkageFlag = true;
}
protected void unsetLinkageFlag() {
linkageFlag = false;
}
/**
* Set symbol type to DataBar-14
*/
public void setLinearMode() {
symbolType = gb14Mode.LINEAR;
}
/**
* Set symbol type to DataBar-14 Omnidirectional
*/
public void setOmnidirectionalMode() {
symbolType = gb14Mode.OMNI;
}
/**
* Set symbol type to DataBar-14 Omnidirectional Stacked
*/
public void setStackedMode() {
symbolType = gb14Mode.STACKED;
}
@Override
public boolean encode() {
BigInteger accum;
BigInteger left_reg;
BigInteger right_reg;
int[] data_character = new int[4];
int[] data_group = new int[4];
int[] v_odd = new int[4];
int[] v_even = new int[4];
int i;
int[][] data_widths = new int[8][4];
int checksum;
int c_left;
int c_right;
int[] total_widths = new int[46];
int writer;
char latch;
int j;
int count;
int check_digit;
String hrt;
String bin;
int compositeOffset = 0;
if (content.length() > 13) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+?"))) {
errorMsg.append("Invalid characters in input");
return false;
}
accum = new BigInteger(content);
if (linkageFlag) {
accum = accum.add(new BigInteger("10000000000000"));
compositeOffset = 1;
}
/* Calculate left and right pair values */
left_reg = accum.divide(new BigInteger("4537077"));
right_reg = accum.mod(new BigInteger("4537077"));
/* Calculate four data characters */
accum = left_reg.divide(new BigInteger("1597"));
data_character[0] = accum.intValue();
accum = left_reg.mod(new BigInteger("1597"));
data_character[1] = accum.intValue();
accum = right_reg.divide(new BigInteger("1597"));
data_character[2] = accum.intValue();
accum = right_reg.mod(new BigInteger("1597"));
data_character[3] = accum.intValue();
encodeInfo.append("Data characters: ");
for (i = 0; i < 4; i++) {
encodeInfo.append(Integer.toString(data_character[i])).append(" ");
}
encodeInfo.append("\n");
/* Calculate odd and even subset values */
if ((data_character[0] >= 0) && (data_character[0] <= 160)) {
data_group[0] = 0;
}
if ((data_character[0] >= 161) && (data_character[0] <= 960)) {
data_group[0] = 1;
}
if ((data_character[0] >= 961) && (data_character[0] <= 2014)) {
data_group[0] = 2;
}
if ((data_character[0] >= 2015) && (data_character[0] <= 2714)) {
data_group[0] = 3;
}
if ((data_character[0] >= 2715) && (data_character[0] <= 2840)) {
data_group[0] = 4;
}
if ((data_character[1] >= 0) && (data_character[1] <= 335)) {
data_group[1] = 5;
}
if ((data_character[1] >= 336) && (data_character[1] <= 1035)) {
data_group[1] = 6;
}
if ((data_character[1] >= 1036) && (data_character[1] <= 1515)) {
data_group[1] = 7;
}
if ((data_character[1] >= 1516) && (data_character[1] <= 1596)) {
data_group[1] = 8;
}
if ((data_character[3] >= 0) && (data_character[3] <= 335)) {
data_group[3] = 5;
}
if ((data_character[3] >= 336) && (data_character[3] <= 1035)) {
data_group[3] = 6;
}
if ((data_character[3] >= 1036) && (data_character[3] <= 1515)) {
data_group[3] = 7;
}
if ((data_character[3] >= 1516) && (data_character[3] <= 1596)) {
data_group[3] = 8;
}
if ((data_character[2] >= 0) && (data_character[2] <= 160)) {
data_group[2] = 0;
}
if ((data_character[2] >= 161) && (data_character[2] <= 960)) {
data_group[2] = 1;
}
if ((data_character[2] >= 961) && (data_character[2] <= 2014)) {
data_group[2] = 2;
}
if ((data_character[2] >= 2015) && (data_character[2] <= 2714)) {
data_group[2] = 3;
}
if ((data_character[2] >= 2715) && (data_character[2] <= 2840)) {
data_group[2] = 4;
}
v_odd[0] = (data_character[0] - g_sum_table[data_group[0]]) / t_table[data_group[0]];
v_even[0] = (data_character[0] - g_sum_table[data_group[0]]) % t_table[data_group[0]];
v_odd[1] = (data_character[1] - g_sum_table[data_group[1]]) % t_table[data_group[1]];
v_even[1] = (data_character[1] - g_sum_table[data_group[1]]) / t_table[data_group[1]];
v_odd[3] = (data_character[3] - g_sum_table[data_group[3]]) % t_table[data_group[3]];
v_even[3] = (data_character[3] - g_sum_table[data_group[3]]) / t_table[data_group[3]];
v_odd[2] = (data_character[2] - g_sum_table[data_group[2]]) / t_table[data_group[2]];
v_even[2] = (data_character[2] - g_sum_table[data_group[2]]) % t_table[data_group[2]];
/* Use RSS subset width algorithm */
for (i = 0; i < 4; i++) {
if ((i == 0) || (i == 2)) {
getWidths(v_odd[i], modules_odd[data_group[i]], 4, widest_odd[data_group[i]], 1);
data_widths[0][i] = widths[0];
data_widths[2][i] = widths[1];
data_widths[4][i] = widths[2];
data_widths[6][i] = widths[3];
getWidths(v_even[i], modules_even[data_group[i]], 4, widest_even[data_group[i]], 0);
data_widths[1][i] = widths[0];
data_widths[3][i] = widths[1];
data_widths[5][i] = widths[2];
data_widths[7][i] = widths[3];
} else {
getWidths(v_odd[i], modules_odd[data_group[i]], 4, widest_odd[data_group[i]], 0);
data_widths[0][i] = widths[0];
data_widths[2][i] = widths[1];
data_widths[4][i] = widths[2];
data_widths[6][i] = widths[3];
getWidths(v_even[i], modules_even[data_group[i]], 4, widest_even[data_group[i]], 1);
data_widths[1][i] = widths[0];
data_widths[3][i] = widths[1];
data_widths[5][i] = widths[2];
data_widths[7][i] = widths[3];
}
}
checksum = 0;
/* Calculate the checksum */
for (i = 0; i < 8; i++) {
checksum += checksum_weight[i] * data_widths[i][0];
checksum += checksum_weight[i + 8] * data_widths[i][1];
checksum += checksum_weight[i + 16] * data_widths[i][2];
checksum += checksum_weight[i + 24] * data_widths[i][3];
}
checksum %= 79;
/* Calculate the two check characters */
if (checksum >= 8) {
checksum++;
}
if (checksum >= 72) {
checksum++;
}
c_left = checksum / 9;
c_right = checksum % 9;
encodeInfo.append("Checksum: ").append(Integer.toString(checksum)).append("\n");
/* Put element widths together */
total_widths[0] = 1;
total_widths[1] = 1;
total_widths[44] = 1;
total_widths[45] = 1;
for (i = 0; i < 8; i++) {
total_widths[i + 2] = data_widths[i][0];
total_widths[i + 15] = data_widths[7 - i][1];
total_widths[i + 23] = data_widths[i][3];
total_widths[i + 36] = data_widths[7 - i][2];
}
for (i = 0; i < 5; i++) {
total_widths[i + 10] = finder_pattern[i + (5 * c_left)];
total_widths[i + 31] = finder_pattern[(4 - i) + (5 * c_right)];
}
rowCount = 0;
for (i = 0; i < 100; i++) {
seperator[i] = false;
}
/* Put this data into the symbol */
if (symbolType == gb14Mode.LINEAR) {
writer = 0;
latch = '0';
for (i = 0; i < 46; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (latch == '1') {
setGridModule(rowCount, writer);
}
writer++;
}
if (latch == '1') {
latch = '0';
} else {
latch = '1';
}
}
if (symbolWidth < writer) {
symbolWidth = writer;
}
if (linkageFlag) {
/* separator pattern for composite symbol */
for (i = 4; i < 92; i++) {
seperator[i] = (!(grid[0][i]));
}
latch = '1';
for (i = 16; i < 32; i++) {
if (!(grid[0][i])) {
if (latch == '1') {
seperator[i] = true;
latch = '0';
} else {
seperator[i] = false;
latch = '1';
}
} else {
seperator[i] = false;
latch = '1';
}
}
latch = '1';
for (i = 63; i < 78; i++) {
if (!(grid[0][i])) {
if (latch == '1') {
seperator[i] = true;
latch = '0';
} else {
seperator[i] = false;
latch = '1';
}
} else {
seperator[i] = false;
latch = '1';
}
}
}
rowCount = rowCount + 1;
count = 0;
check_digit = 0;
/* Calculate check digit from Annex A and place human readable text */
readable = new StringBuilder("(01)");
hrt = "";
for (i = content.length(); i < 13; i++) {
hrt += "0";
}
hrt += content;
for (i = 0; i < 13; i++) {
count += hrt.charAt(i) - '0';
if ((i & 1) == 0) {
count += 2 * (hrt.charAt(i) - '0');
}
}
check_digit = 10 - (count % 10);
if (check_digit == 10) {
check_digit = 0;
}
hrt += (char) (check_digit + '0');
readable.append(hrt);
}
if (symbolType == gb14Mode.STACKED) {
/* top row */
writer = 0;
latch = '0';
for (i = 0; i < 23; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (latch == '1') {
setGridModule(rowCount, writer);
} else {
unsetGridModule(rowCount, writer);
}
writer++;
}
if (latch == '1') {
latch = '0';
} else {
latch = '1';
}
}
setGridModule(rowCount, writer);
unsetGridModule(rowCount, writer + 1);
/* bottom row */
rowCount = rowCount + 2;
setGridModule(rowCount, 0);
unsetGridModule(rowCount, 1);
writer = 0;
latch = '1';
for (i = 23; i < 46; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (latch == '1') {
setGridModule(rowCount, writer + 2);
} else {
unsetGridModule(rowCount, writer + 2);
}
writer++;
}
if (latch == '1') {
latch = '0';
} else {
latch = '1';
}
}
/* separator pattern */
for (i = 4; i < 46; i++) {
if (gridModuleIsSet(rowCount - 2, i) == gridModuleIsSet(rowCount, i)) {
if (!(gridModuleIsSet(rowCount - 2, i))) {
setGridModule(rowCount - 1, i);
}
} else {
if (!(gridModuleIsSet(rowCount - 1, i - 1))) {
setGridModule(rowCount - 1, i);
}
}
}
if (linkageFlag) {
/* separator pattern for composite symbol */
for (i = 4; i < 46; i++) {
seperator[i] = (!(grid[0][i]));
}
latch = '1';
for (i = 16; i < 32; i++) {
if (!(grid[0][i])) {
if (latch == '1') {
seperator[i] = true;
latch = '0';
} else {
seperator[i] = false;
latch = '1';
}
} else {
seperator[i] = false;
latch = '1';
}
}
}
rowCount = rowCount + 1;
if (symbolWidth < 50) {
symbolWidth = 50;
}
}
if (symbolType == gb14Mode.OMNI) {
/* top row */
writer = 0;
latch = '0';
for (i = 0; i < 23; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (latch == '1') {
setGridModule(rowCount, writer);
} else {
unsetGridModule(rowCount, writer);
}
writer++;
}
latch = (latch == '1' ? '0' : '1');
}
setGridModule(rowCount, writer);
unsetGridModule(rowCount, writer + 1);
/* bottom row */
rowCount = rowCount + 4;
setGridModule(rowCount, 0);
unsetGridModule(rowCount, 1);
writer = 0;
latch = '1';
for (i = 23; i < 46; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (latch == '1') {
setGridModule(rowCount, writer + 2);
} else {
unsetGridModule(rowCount, writer + 2);
}
writer++;
}
if (latch == '1') {
latch = '0';
} else {
latch = '1';
}
}
/* middle separator */
for (i = 5; i < 46; i += 2) {
setGridModule(rowCount - 2, i);
}
/* top separator */
for (i = 4; i < 46; i++) {
if (!(gridModuleIsSet(rowCount - 4, i))) {
setGridModule(rowCount - 3, i);
}
}
latch = '1';
for (i = 17; i < 33; i++) {
if (!(gridModuleIsSet(rowCount - 4, i))) {
if (latch == '1') {
setGridModule(rowCount - 3, i);
latch = '0';
} else {
unsetGridModule(rowCount - 3, i);
latch = '1';
}
} else {
unsetGridModule(rowCount - 3, i);
latch = '1';
}
}
/* bottom separator */
for (i = 4; i < 46; i++) {
if (!(gridModuleIsSet(rowCount, i))) {
setGridModule(rowCount - 1, i);
}
}
latch = '1';
for (i = 16; i < 32; i++) {
if (!(gridModuleIsSet(rowCount, i))) {
if (latch == '1') {
setGridModule(rowCount - 1, i);
latch = '0';
} else {
unsetGridModule(rowCount - 1, i);
latch = '1';
}
} else {
unsetGridModule(rowCount - 1, i);
latch = '1';
}
}
if (symbolWidth < 50) {
symbolWidth = 50;
}
if (linkageFlag) {
/* separator pattern for composite symbol */
for (i = 4; i < 46; i++) {
seperator[i] = (!(grid[0][i]));
}
latch = '1';
for (i = 16; i < 32; i++) {
if (!(grid[0][i])) {
if (latch == '1') {
seperator[i] = true;
latch = '0';
} else {
seperator[i] = false;
latch = '1';
}
} else {
seperator[i] = false;
latch = '1';
}
}
}
rowCount = rowCount + 1;
}
pattern = new String[rowCount + compositeOffset];
rowHeight = new int[rowCount + compositeOffset];
if (linkageFlag) {
bin = "";
for (j = 0; j < symbolWidth; j++) {
if (seperator[j]) {
bin += "1";
} else {
bin += "0";
}
}
pattern[0] = bin2pat(bin);
rowHeight[0] = 1;
}
for (i = 0; i < rowCount; i++) {
bin = "";
for (j = 0; j < symbolWidth; j++) {
if (grid[i][j]) {
bin += "1";
} else {
bin += "0";
}
}
pattern[i + compositeOffset] = bin2pat(bin);
}
if (symbolType == gb14Mode.LINEAR) {
rowHeight[0 + compositeOffset] = -1;
}
if (symbolType == gb14Mode.STACKED) {
rowHeight[0 + compositeOffset] = 5;
rowHeight[1 + compositeOffset] = 1;
rowHeight[2 + compositeOffset] = 7;
}
if (symbolType == gb14Mode.OMNI) {
rowHeight[0 + compositeOffset] = -1;
rowHeight[1 + compositeOffset] = 1;
rowHeight[2 + compositeOffset] = 1;
rowHeight[3 + compositeOffset] = 1;
rowHeight[4 + compositeOffset] = -1;
}
if (linkageFlag) {
rowCount++;
}
plotSymbol();
return true;
}
private int getCombinations(int n, int r) {
int i, j;
int maxDenom, minDenom;
int val;
if (n - r > r) {
minDenom = r;
maxDenom = n - r;
} else {
minDenom = n - r;
maxDenom = r;
}
val = 1;
j = 1;
for (i = n; i > maxDenom; i--) {
val *= i;
if (j <= minDenom) {
val /= j;
j++;
}
}
for (; j <= minDenom; j++) {
val /= j;
}
return (val);
}
private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) {
int bar;
int elmWidth;
int mxwElement;
int subVal, lessVal;
int narrowMask = 0;
for (bar = 0; bar < elements - 1; bar++) {
for (elmWidth = 1, narrowMask |= (1 << bar); ;
elmWidth++, narrowMask &= ~(1 << bar)) {
/* get all combinations */
subVal = getCombinations(n - elmWidth - 1, elements - bar - 2);
/* less combinations with no single-module element */
if ((noNarrow == 0) && (narrowMask == 0)
&& (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2);
}
/* less combinations with elements > maxVal */
if (elements - bar - 1 > 1) {
lessVal = 0;
for (mxwElement = n - elmWidth - (elements - bar - 2);
mxwElement > maxWidth;
mxwElement--) {
lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3);
}
subVal -= lessVal * (elements - 1 - bar);
} else if (n - elmWidth > maxWidth) {
subVal--;
}
val -= subVal;
if (val < 0) break;
}
val += subVal;
n -= elmWidth;
widths[bar] = elmWidth;
}
widths[bar] = n;
}
private void setGridModule(int row, int column) {
grid[row][column] = true;
}
private void unsetGridModule(int row, int column) {
grid[row][column] = false;
}
private boolean gridModuleIsSet(int row, int column) {
return grid[row][column];
}
private enum gb14Mode {
LINEAR, OMNI, STACKED
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,476 @@
package org.xbib.graphics.barcode;
import java.math.BigInteger;
/**
* Implements GS1 DataBar Limited according to ISO/IEC 24724:2011.
* Input data should be a 12 digit Global Trade Identification Number
* without check digit or Application Identifier [01].
*/
public class DataBarLimited extends Symbol {
private static final int[] t_even_ltd = {
28, 728, 6454, 203, 2408, 1, 16632
};
private static final int[] modules_odd_ltd = {
17, 13, 9, 15, 11, 19, 7
};
private static final int[] modules_even_ltd = {
9, 13, 17, 11, 15, 7, 19
};
private static final int[] widest_odd_ltd = {
6, 5, 3, 5, 4, 8, 1
};
private static final int[] widest_even_ltd = {
3, 4, 6, 4, 5, 1, 8
};
private static final int[] checksum_weight_ltd = { /* Table 7 */
1, 3, 9, 27, 81, 65, 17, 51, 64, 14, 42, 37, 22, 66,
20, 60, 2, 6, 18, 54, 73, 41, 34, 13, 39, 28, 84, 74
};
private static final int[] finder_pattern_ltd = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 3, 1, 1, 1,
1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1,
1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1,
1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1,
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1,
1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1,
1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1,
1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1,
1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1,
1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1,
1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1,
1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1,
1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1,
1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1,
1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1,
1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1,
1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1,
1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1,
1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1,
1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1,
1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1,
1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1,
1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1,
1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1,
1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1,
1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1,
1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1,
1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1,
1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1,
1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1,
1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1,
1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1,
1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1,
1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1,
1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1,
2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1,
2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1,
2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1,
2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1 /* ГОСТ ISO/IEC 24724-2011 страница 57 */
};
private boolean linkageFlag;
private int[] widths = new int[8];
public DataBarLimited() {
linkageFlag = false;
}
@Override
public void setDataType(DataType dummy) {
// Do nothing!
}
protected void setLinkageFlag() {
linkageFlag = true;
}
@Override
public boolean encode() {
BigInteger accum;
BigInteger left_reg;
BigInteger right_reg;
int left_group;
int right_group;
int i, j;
int left_character;
int right_character;
int left_odd;
int right_odd;
int left_even;
int right_even;
int[] left_widths = new int[14];
int[] right_widths = new int[14];
int checksum;
int[] check_elements = new int[14];
int[] total_widths = new int[46];
StringBuilder bin;
StringBuilder notbin;
boolean bar_latch;
int writer;
int check_digit;
int count = 0;
StringBuilder hrt;
int compositeOffset = 0;
if (content.length() > 13) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+?"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (content.length() == 13) {
if ((content.charAt(0) != '0') && (content.charAt(0) != '1')) {
errorMsg.append("Input out of range");
return false;
}
}
accum = new BigInteger(content);
if (linkageFlag) {
/* Add symbol linkage flag */
accum = accum.add(new BigInteger("2015133531096"));
}
/* Calculate left and right pair values */
left_reg = accum.divide(new BigInteger("2013571"));
right_reg = accum.mod(new BigInteger("2013571"));
left_group = 0;
if (left_reg.compareTo(new BigInteger("183063")) > 0) {
left_group = 1;
}
if (left_reg.compareTo(new BigInteger("820063")) > 0) {
left_group = 2;
}
if (left_reg.compareTo(new BigInteger("1000775")) > 0) {
left_group = 3;
}
if (left_reg.compareTo(new BigInteger("1491020")) > 0) {
left_group = 4;
}
if (left_reg.compareTo(new BigInteger("1979844")) > 0) {
left_group = 5;
}
if (left_reg.compareTo(new BigInteger("1996938")) > 0) {
left_group = 6;
}
right_group = 0;
if (right_reg.compareTo(new BigInteger("183063")) > 0) {
right_group = 1;
}
if (right_reg.compareTo(new BigInteger("820063")) > 0) {
right_group = 2;
}
if (right_reg.compareTo(new BigInteger("1000775")) > 0) {
right_group = 3;
}
if (right_reg.compareTo(new BigInteger("1491020")) > 0) {
right_group = 4;
}
if (right_reg.compareTo(new BigInteger("1979844")) > 0) {
right_group = 5;
}
if (right_reg.compareTo(new BigInteger("1996938")) > 0) {
right_group = 6;
}
encodeInfo.append("Data Characters: ").append(Integer.toString(left_group + 1)).append(" ").append(Integer.toString(right_group + 1)).append("\n");
switch (left_group) {
case 1:
left_reg = left_reg.subtract(new BigInteger("183064"));
break;
case 2:
left_reg = left_reg.subtract(new BigInteger("820064"));
break;
case 3:
left_reg = left_reg.subtract(new BigInteger("1000776"));
break;
case 4:
left_reg = left_reg.subtract(new BigInteger("1491021"));
break;
case 5:
left_reg = left_reg.subtract(new BigInteger("1979845"));
break;
case 6:
left_reg = left_reg.subtract(new BigInteger("1996939"));
break;
}
switch (right_group) {
case 1:
right_reg = right_reg.subtract(new BigInteger("183064"));
break;
case 2:
right_reg = right_reg.subtract(new BigInteger("820064"));
break;
case 3:
right_reg = right_reg.subtract(new BigInteger("1000776"));
break;
case 4:
right_reg = right_reg.subtract(new BigInteger("1491021"));
break;
case 5:
right_reg = right_reg.subtract(new BigInteger("1979845"));
break;
case 6:
right_reg = right_reg.subtract(new BigInteger("1996939"));
break;
}
left_character = left_reg.intValue();
right_character = right_reg.intValue();
left_odd = left_character / t_even_ltd[left_group];
left_even = left_character % t_even_ltd[left_group];
right_odd = right_character / t_even_ltd[right_group];
right_even = right_character % t_even_ltd[right_group];
getWidths(left_odd, modules_odd_ltd[left_group], 7, widest_odd_ltd[left_group], 1);
left_widths[0] = widths[0];
left_widths[2] = widths[1];
left_widths[4] = widths[2];
left_widths[6] = widths[3];
left_widths[8] = widths[4];
left_widths[10] = widths[5];
left_widths[12] = widths[6];
getWidths(left_even, modules_even_ltd[left_group], 7, widest_even_ltd[left_group], 0);
left_widths[1] = widths[0];
left_widths[3] = widths[1];
left_widths[5] = widths[2];
left_widths[7] = widths[3];
left_widths[9] = widths[4];
left_widths[11] = widths[5];
left_widths[13] = widths[6];
getWidths(right_odd, modules_odd_ltd[right_group], 7, widest_odd_ltd[right_group], 1);
right_widths[0] = widths[0];
right_widths[2] = widths[1];
right_widths[4] = widths[2];
right_widths[6] = widths[3];
right_widths[8] = widths[4];
right_widths[10] = widths[5];
right_widths[12] = widths[6];
getWidths(right_even, modules_even_ltd[right_group], 7, widest_even_ltd[right_group], 0);
right_widths[1] = widths[0];
right_widths[3] = widths[1];
right_widths[5] = widths[2];
right_widths[7] = widths[3];
right_widths[9] = widths[4];
right_widths[11] = widths[5];
right_widths[13] = widths[6];
checksum = 0;
/* Calculate the checksum */
for (i = 0; i < 14; i++) {
checksum += checksum_weight_ltd[i] * left_widths[i];
checksum += checksum_weight_ltd[i + 14] * right_widths[i];
}
checksum %= 89;
encodeInfo.append("Checksum: ").append(Integer.toString(checksum)).append("\n");
for (i = 0; i < 14; i++) {
check_elements[i] = finder_pattern_ltd[i + (checksum * 14)];
}
total_widths[0] = 1;
total_widths[1] = 1;
total_widths[44] = 1;
total_widths[45] = 1;
for (i = 0; i < 14; i++) {
total_widths[i + 2] = left_widths[i];
total_widths[i + 16] = check_elements[i];
total_widths[i + 30] = right_widths[i];
}
bin = new StringBuilder();
notbin = new StringBuilder();
writer = 0;
bar_latch = false;
for (i = 0; i < 46; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (bar_latch) {
bin.append("1");
notbin.append("0");
} else {
bin.append("0");
notbin.append("1");
}
writer++;
}
bar_latch = !bar_latch;
}
if (symbolWidth < (writer + 20)) {
symbolWidth = writer + 20;
}
/* Calculate check digit from Annex A and place human readable text */
readable = new StringBuilder("(01)");
hrt = new StringBuilder();
for (i = content.length(); i < 13; i++) {
hrt.append("0");
}
hrt.append(content);
for (i = 0; i < 13; i++) {
count += (hrt.charAt(i) - '0');
if ((i & 1) == 0) {
count += 2 * (hrt.charAt(i) - '0');
}
}
check_digit = 10 - (count % 10);
if (check_digit == 10) {
check_digit = 0;
}
hrt.append((char) (check_digit + '0'));
readable.append(hrt);
if (linkageFlag) {
compositeOffset = 1;
}
rowCount = 1 + compositeOffset;
rowHeight = new int[1 + compositeOffset];
rowHeight[compositeOffset] = -1;
pattern = new String[1 + compositeOffset];
pattern[compositeOffset] = "0:" + bin2pat(bin.toString());
if (linkageFlag) {
// Add composite symbol seperator
notbin = new StringBuilder(notbin.substring(4, 70));
rowHeight[0] = 1;
pattern[0] = "0:04" + bin2pat(notbin.toString());
}
plotSymbol();
return true;
}
private int getCombinations(int n, int r) {
int i, j;
int maxDenom, minDenom;
int val;
if (n - r > r) {
minDenom = r;
maxDenom = n - r;
} else {
minDenom = n - r;
maxDenom = r;
}
val = 1;
j = 1;
for (i = n; i > maxDenom; i--) {
val *= i;
if (j <= minDenom) {
val /= j;
j++;
}
}
for (; j <= minDenom; j++) {
val /= j;
}
return (val);
}
private void getWidths(int val, int n, int elements, int maxWidth, int noNarrow) {
int bar;
int elmWidth;
int mxwElement;
int subVal, lessVal;
int narrowMask = 0;
for (bar = 0; bar < elements - 1; bar++) {
for (elmWidth = 1, narrowMask |= (1 << bar); ;
elmWidth++, narrowMask &= ~(1 << bar)) {
/* get all combinations */
subVal = getCombinations(n - elmWidth - 1, elements - bar - 2);
/* less combinations with no single-module element */
if ((noNarrow == 0) && (narrowMask == 0)
&& (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) {
subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2);
}
/* less combinations with elements > maxVal */
if (elements - bar - 1 > 1) {
lessVal = 0;
for (mxwElement = n - elmWidth - (elements - bar - 2);
mxwElement > maxWidth;
mxwElement--) {
lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3);
}
subVal -= lessVal * (elements - 1 - bar);
} else if (n - elmWidth > maxWidth) {
subVal--;
}
val -= subVal;
if (val < 0) break;
}
val += subVal;
n -= elmWidth;
widths[bar] = elmWidth;
}
widths[bar] = n;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,366 @@
package org.xbib.graphics.barcode;
import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
import org.xbib.graphics.barcode.util.AddOn;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.geom.Rectangle2D;
/**
* Implements EAN bar code symbology according to BS EN 797:1996.
* European Article Number data can be encoded in EAN-8 or EAN-13 format
* requiring a 7-digit or 12-digit input respectively. EAN-13 numbers map to
* Global Trade Identification Numbers (GTIN) whereas EAN-8 symbols are
* generally for internal use only. Check digit is calculated and should not
* be in input data. Leading zeroes are added as required.
*/
public class Ean extends Symbol {
private boolean useAddOn;
private String addOnContent;
private Mode mode;
private boolean linkageFlag;
private String[] EAN13Parity = {
"AAAAAA", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA",
"ABABAB", "ABABBA", "ABBABA"
};
private String[] EANsetA = {
"3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213",
"3112"
};
private String[] EANsetB = {
"1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121",
"2113"
};
public Ean() {
mode = Mode.EAN13;
useAddOn = false;
addOnContent = "";
linkageFlag = false;
}
public Mode getMode() {
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
protected void setLinkageFlag() {
linkageFlag = true;
}
@Override
public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) {
if (humanReadableLocation == TOP) {
throw new IllegalArgumentException("Cannot display human-readable text above EAN bar codes.");
} else {
super.setHumanReadableLocation(humanReadableLocation);
}
}
@Override
public boolean encode() {
boolean retval = false;
AddOn addOn = new AddOn();
String addOnData;
separateContent();
if (content.length() == 0) {
errorMsg.append("Missing EAN data");
retval = false;
} else {
switch (mode) {
case EAN8:
retval = ean8();
break;
case EAN13:
retval = ean13();
break;
}
}
if ((retval) && (useAddOn)) {
addOnData = addOn.calcAddOn(addOnContent);
if (addOnData.length() == 0) {
errorMsg.append("Invalid Add-On data");
retval = false;
} else {
pattern[0] = pattern[0] + "9" + addOnData;
//add leading zeroes to add-on text
if (addOnContent.length() == 1) {
addOnContent = "0" + addOnContent;
}
if (addOnContent.length() == 3) {
addOnContent = "0" + addOnContent;
}
if (addOnContent.length() == 4) {
addOnContent = "0" + addOnContent;
}
}
}
if (retval) {
plotSymbol();
}
return retval;
}
private void separateContent() {
int splitPoint;
splitPoint = content.indexOf('+');
if (splitPoint != -1) {
// There is a '+' in the input data, use an add-on EAN2 or EAN5
useAddOn = true;
addOnContent = content.substring(splitPoint + 1);
content = content.substring(0, splitPoint);
}
}
private boolean ean13() {
StringBuilder accumulator = new StringBuilder();
StringBuilder dest;
String parity;
int i;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (content.length() > 12) {
errorMsg.append("Input data too long");
return false;
}
for (i = content.length(); i < 12; i++) {
accumulator.append("0");
}
accumulator.append(content);
accumulator.append(calcDigit(accumulator.toString()));
parity = EAN13Parity[accumulator.charAt(0) - '0'];
encodeInfo.append("Parity Digit: ").append(accumulator.charAt(0)).append("\n");
/* Start character */
dest = new StringBuilder("111");
for (i = 1; i < 13; i++) {
if (i == 7) {
dest.append("11111");
}
if ((i >= 1) && (i <= 6)) {
if (parity.charAt(i - 1) == 'B') {
dest.append(EANsetB[accumulator.charAt(i) - '0']);
} else {
dest.append(EANsetA[accumulator.charAt(i) - '0']);
}
} else {
dest.append(EANsetA[accumulator.charAt(i) - '0']);
}
}
dest.append("111");
readable = new StringBuilder(accumulator.toString());
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
return true;
}
private boolean ean8() {
StringBuilder accumulator = new StringBuilder();
int i;
StringBuilder dest;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (content.length() > 7) {
errorMsg.append("Input data too long");
return false;
}
for (i = content.length(); i < 7; i++) {
accumulator.append("0");
}
accumulator.append(content);
accumulator.append(calcDigit(accumulator.toString()));
dest = new StringBuilder("111");
for (i = 0; i < 8; i++) {
if (i == 4) {
dest.append("11111");
}
dest.append(EANsetA[Character.getNumericValue(accumulator.charAt(i))]);
}
dest.append("111");
readable = new StringBuilder(accumulator.toString());
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
return true;
}
private char calcDigit(String x) {
int count = 0;
int c, cdigit;
int p = 0;
for (int i = x.length() - 1; i >= 0; i--) {
c = Character.getNumericValue(x.charAt(i));
if ((p % 2) == 0) {
c = c * 3;
}
count += c;
p++;
}
cdigit = 10 - (count % 10);
if (cdigit == 10) {
cdigit = 0;
}
encodeInfo.append("Check Digit: ").append(cdigit).append("\n");
return (char) (cdigit + '0');
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
boolean black;
int compositeOffset = 0;
int shortLongDiff = 5;
getRectangles().clear();
getTexts().clear();
black = true;
x = 0;
if (linkageFlag) {
compositeOffset = 6;
}
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
if (black) {
y = 0;
black = false;
w = pattern[0].charAt(xBlock) - '0';
h = defaultHeight;
/* Add extension to guide bars */
if (mode == Mode.EAN13) {
if ((x < 3) || (x > 91)) {
h += shortLongDiff;
}
if ((x > 45) && (x < 49)) {
h += shortLongDiff;
}
if (x > 95) {
// Drop add-on
h -= 8;
y = 8;
}
if (linkageFlag) {
if ((x == 0) || (x == 94)) {
h += 2;
y -= 2;
}
}
}
if (mode == Mode.EAN8) {
if ((x < 3) || (x > 62)) {
h += shortLongDiff;
}
if ((x > 30) && (x < 35)) {
h += shortLongDiff;
}
if (x > 66) {
// Drop add-on
h -= 8;
y = 8;
}
if (linkageFlag) {
if ((x == 0) || (x == 66)) {
h += 2;
y -= 2;
}
}
}
Rectangle2D.Double rect = new Rectangle2D.Double(x + 6, y + compositeOffset, w, h);
getRectangles().add(rect);
if ((x + w + 12) > symbolWidth) {
symbolWidth = x + w + 12;
}
} else {
black = true;
}
x += (double) (pattern[0].charAt(xBlock) - '0');
}
if (linkageFlag) {
// Add separator for composite symbology
if (mode == Mode.EAN13) {
getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(94 + 6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
getRectangles().add(new Rectangle2D.Double(95 + 6, 2, 1, 2));
} else {
getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(66 + 6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
getRectangles().add(new Rectangle2D.Double(67 + 6, 2, 1, 2));
}
}
symbolHeight = defaultHeight + 5;
/* Now add the text */
if (getHumanReadableLocation() != NONE) {
double baseline = getHeight() + fontSize - shortLongDiff + compositeOffset;
double addOnBaseline = 6.0 + compositeOffset;
if (mode == Mode.EAN13) {
getTexts().add(new TextBox(3, baseline, readable.substring(0, 1)));
getTexts().add(new TextBox(30, baseline, readable.substring(1, 7)));
getTexts().add(new TextBox(77, baseline, readable.substring(7, 13)));
if (useAddOn) {
if (addOnContent.length() == 2) {
getTexts().add(new TextBox(118, addOnBaseline, addOnContent));
} else {
getTexts().add(new TextBox(133, addOnBaseline, addOnContent));
}
}
} else { // EAN8
getTexts().add(new TextBox(23, baseline, readable.substring(0, 4)));
getTexts().add(new TextBox(55, baseline, readable.substring(4, 8)));
if (useAddOn) {
if (addOnContent.length() == 2) {
getTexts().add(new TextBox(93, addOnBaseline, addOnContent));
} else {
getTexts().add(new TextBox(105, addOnBaseline, addOnContent));
}
}
}
}
}
public enum Mode {
EAN8, EAN13
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
package org.xbib.graphics.barcode;
/**
* The location of a bar code's human-readable text.
*/
public enum HumanReadableLocation {
/**
* Display the human-readable text below the bar code.
*/
BOTTOM,
/**
* Display the human-readable text above the bar code.
*/
TOP,
/**
* Do not display the human-readable text.
*/
NONE
}

View file

@ -0,0 +1,138 @@
package org.xbib.graphics.barcode;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
/**
* Implements the Japanese Postal Code symbology as used to encode address
* data for mail items in Japan. Valid input characters are digits 0-9,
* characters A-Z and the dash (-) character. A modulo-19 check digit is
* added and should not be included in the input data.
*/
public class JapanPost extends Symbol {
private static final String[] JAPAN_TABLE = {
"FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT",
"TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF"
};
private static final char[] KASUT_SET = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c',
'd', 'e', 'f', 'g', 'h'
};
private static final char[] CH_KASUT_SET = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c',
'd', 'e', 'f', 'g', 'h'
};
@Override
public boolean encode() {
StringBuilder dest;
StringBuilder inter;
int i, sum, check;
char c;
content = content.toUpperCase(Locale.ENGLISH);
if (!(content.matches("[0-9A-Z\\-]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
inter = new StringBuilder();
for (i = 0;
(i < content.length()) && (inter.length() < 20); i++) {
c = content.charAt(i);
if ((c >= '0') && (c <= '9')) {
inter.append(c);
}
if (c == '-') {
inter.append(c);
}
if ((c >= 'A') && (c <= 'J')) {
inter.append('a');
inter.append(CH_KASUT_SET[(c - 'A')]);
}
if ((c >= 'K') && (c <= 'O')) {
inter.append('b');
inter.append(CH_KASUT_SET[(c - 'K')]);
}
if ((c >= 'U') && (c <= 'Z')) {
inter.append('c');
inter.append(CH_KASUT_SET[(c - 'U')]);
}
}
for (i = inter.length(); i < 20; i++) {
inter.append("d");
}
dest = new StringBuilder("FD");
sum = 0;
for (i = 0; i < 20; i++) {
dest.append(JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]);
sum += positionOf(inter.charAt(i), CH_KASUT_SET);
}
/* Calculate check digit */
check = 19 - (sum % 19);
if (check == 19) {
check = 0;
}
dest.append(JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]);
dest.append("DF");
encodeInfo.append("Encoding: ").append(dest).append("\n");
encodeInfo.append("Check Digit: ").append(check).append("\n");
readable = new StringBuilder();
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
plotSymbol();
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
getRectangles().clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
switch (pattern[0].charAt(xBlock)) {
case 'A':
y = 0;
h = 5;
break;
case 'D':
y = 3;
h = 5;
break;
case 'F':
y = 0;
h = 8;
break;
case 'T':
y = 3;
h = 2;
break;
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
x += 2;
}
symbolWidth = pattern[0].length() * 3;
symbolHeight = 8;
}
}

View file

@ -0,0 +1,95 @@
package org.xbib.graphics.barcode;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
/**
* Implements Dutch Post KIX Code as used by Royal Dutch TPG Post
* (Netherlands). Input data can consist of digits 0-9 and characters A-Z.
* Input should be 11 characters in length. No check digit is added.
*/
public class KixCode extends Symbol {
/* Handles Dutch Post TNT KIX symbols */
/* The same as RM4SCC but without check digit */
/* Specification at http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx */
private final String[] RoyalTable = {
"TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA",
"DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT",
"FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT"
};
private final char[] krSet = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
@Override
public boolean encode() {
StringBuilder dest;
int i;
content = content.toUpperCase(Locale.ENGLISH);
if (!(content.matches("[0-9A-Z]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
dest = new StringBuilder();
for (i = 0; i < content.length(); i++) {
dest.append(RoyalTable[positionOf(content.charAt(i), krSet)]);
}
encodeInfo.append("Encoding: ").append(dest).append("\n");
readable = new StringBuilder();
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
getRectangles().clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
switch (pattern[0].charAt(xBlock)) {
case 'A':
y = 0;
h = 5;
break;
case 'D':
y = 3;
h = 5;
break;
case 'F':
y = 0;
h = 8;
break;
case 'T':
y = 3;
h = 2;
break;
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
x += 2;
}
symbolWidth = pattern[0].length() * 3;
symbolHeight = 8;
}
}

View file

@ -0,0 +1,61 @@
package org.xbib.graphics.barcode;
/**
* Implements Korea Post Barcode. Input should consist of of a six-digit
* number. A Modulo-10 check digit is calculated and added, and should not form
* part of the input data.
*/
public class KoreaPost extends Symbol {
String[] koreaTable = {
"1313150613", "0713131313", "0417131313", "1506131313", "0413171313",
"17171313", "1315061313", "0413131713", "17131713", "13171713"
};
@Override
public boolean encode() {
StringBuilder accumulator = new StringBuilder();
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (content.length() > 6) {
errorMsg.append("Input data too long");
return false;
}
StringBuilder add_zero = new StringBuilder();
int i, j, total = 0, checkd;
for (i = 0; i < (6 - content.length()); i++) {
add_zero.append("0");
}
add_zero.append(content);
for (i = 0; i < add_zero.length(); i++) {
j = Character.getNumericValue(add_zero.charAt(i));
accumulator.append(koreaTable[j]);
total += j;
}
checkd = 10 - (total % 10);
if (checkd == 10) {
checkd = 0;
}
encodeInfo.append("Check Digit: ").append(checkd).append("\n");
accumulator.append(koreaTable[checkd]);
readable.append(add_zero).append(checkd);
pattern = new String[1];
pattern[0] = accumulator.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
}

View file

@ -0,0 +1,97 @@
package org.xbib.graphics.barcode;
/**
* Implements the LOGMARS (Logistics Applications of Automated Marking
* and Reading Symbols) standard used by the US Department of Defense.
* Input data can be of any length and supports the characters 0-9, A-Z, dash
* (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%).
* A Modulo-43 check digit is calculated and added, and should not form part
* of the input data.
*/
public class Logmars extends Symbol {
private static final String[] CODE39LM = {
"1113313111", "3113111131", "1133111131", "3133111111", "1113311131",
"3113311111", "1133311111", "1113113131", "3113113111", "1133113111",
"3111131131", "1131131131", "3131131111", "1111331131", "3111331111",
"1131331111", "1111133131", "3111133111", "1131133111", "1111333111",
"3111111331", "1131111331", "3131111311", "1111311331", "3111311311",
"1131311311", "1111113331", "3111113311", "1131113311", "1111313311",
"3311111131", "1331111131", "3331111111", "1311311131", "3311311111",
"1331311111", "1311113131", "3311113111", "1331113111", "1313131111",
"1313111311", "1311131311", "1113131311"
};
private static final char[] LOOKUP = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+',
'%'
};
/**
* Ratio of wide bar width to narrow bar width.
*/
private double moduleWidthRatio = 3;
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return moduleWidthRatio;
}
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually
* between {@code 2} and {@code 3}. The default value is {@code 3}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
@Override
protected double getModuleWidth(int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return moduleWidthRatio;
}
}
@Override
public boolean encode() {
if (!(content.matches("[0-9A-Z\\. \\-$/+%]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
StringBuilder p = new StringBuilder();
int l = content.length();
int charval, counter = 0;
char thischar;
char checkDigit;
for (int i = 0; i < l; i++) {
thischar = content.charAt(i);
charval = positionOf(thischar, LOOKUP);
counter += charval;
p.append(CODE39LM[charval]);
}
counter = counter % 43;
checkDigit = LOOKUP[counter];
encodeInfo.append("Check Digit: ").append(checkDigit).append("\n");
p.append(CODE39LM[counter]);
readable = new StringBuilder(content).append(checkDigit);
pattern = new String[]{"1311313111" + p + "131131311"};
rowCount = 1;
rowHeight = new int[]{-1};
plotSymbol();
return true;
}
}

View file

@ -0,0 +1,890 @@
package org.xbib.graphics.barcode;
import org.xbib.graphics.barcode.util.Hexagon;
import org.xbib.graphics.barcode.util.ReedSolomon;
import java.awt.geom.Ellipse2D;
import java.util.Arrays;
/**
* Implements MaxiCode according to ISO 16023:2000.
* MaxiCode employs a pattern of hexagons around a central 'bulls-eye'
* finder pattern. Encoding in several modes is supported, but encoding in
* Mode 2 and 3 require primary messages to be set. Input characters can be
* any from the ISO 8859-1 (Latin-1) character set.
* TODO: Add ECI functionality.
*/
public class MaxiCode extends Symbol {
/**
* MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid).
*/
private static final int[] MAXICODE_GRID = {
122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0,
124, 123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0,
126, 125, 132, 131, 138, 137, 144, 143, 150, 149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818,
284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, 236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0,
286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, 214, 213, 208, 207, 822, 821,
288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0,
290, 289, 296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824,
292, 291, 298, 297, 304, 303, 310, 309, 316, 315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0,
294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, 341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827,
410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, 829, 0,
412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830,
414, 413, 408, 407, 402, 401, 396, 395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0,
416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, 439, 446, 445, 834, 833,
418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0,
420, 419, 426, 425, 432, 431, 108, 107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836,
482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, 457, 452, 451, 838, 0,
484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839,
486, 485, 480, 479, 474, 473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0,
488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, 517, 843, 842,
490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0,
492, 491, 498, 497, 504, 503, 102, 101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845,
560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, 529, 524, 523, 847, 0,
562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848,
564, 563, 558, 557, 552, 551, 546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0,
566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, 613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851,
568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, 640, 639, 646, 645, 853, 0,
570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854,
728, 727, 722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0,
730, 729, 724, 723, 718, 717, 712, 711, 706, 705, 700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857,
732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, 683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0,
734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, 805, 812, 811, 861, 860,
736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0,
738, 737, 744, 743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863
};
/**
* ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A.
* 1 = Set A, 2 = Set B, 3 = Set C, 4 = Set D, 5 = Set E.
* 0 refers to special characters that fit into more than one set (e.g. GS).
*/
private static final int[] MAXICODE_SET = {
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2,
2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4, 3, 3, 3,
4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
};
/**
* ASCII character to symbol value, from ISO/IEC 16023 Appendix A.
*/
private static final int[] MAXICODE_SYMBOL_CHAR = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37,
38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36,
37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41,
42, 42, 47, 43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32,
33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36
};
private int mode;
private int structuredAppendPosition = 1;
private int structuredAppendTotal = 1;
private String primaryData = "";
private int[] codewords;
private int[] source;
private final int[] set = new int[144];
private final int[] character = new int[144];
private final boolean[][] grid = new boolean[33][30];
/**
* Returns the primary message codewords for mode 2.
*
* @param postcode the postal code
* @param country the country code
* @param service the service code
* @return the primary message, as codewords
*/
private static int[] getMode2PrimaryCodewords(String postcode, int country, int service) {
for (int i = 0; i < postcode.length(); i++) {
if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') {
postcode = postcode.substring(0, i);
break;
}
}
int postcodeNum = Integer.parseInt(postcode);
int[] primary = new int[10];
primary[0] = ((postcodeNum & 0x03) << 4) | 2;
primary[1] = ((postcodeNum & 0xfc) >> 2);
primary[2] = ((postcodeNum & 0x3f00) >> 8);
primary[3] = ((postcodeNum & 0xfc000) >> 14);
primary[4] = ((postcodeNum & 0x3f00000) >> 20);
primary[5] = ((postcodeNum & 0x3c000000) >> 26) | ((postcode.length() & 0x3) << 4);
primary[6] = ((postcode.length() & 0x3c) >> 2) | ((country & 0x3) << 4);
primary[7] = (country & 0xfc) >> 2;
primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2);
primary[9] = ((service & 0x3f0) >> 4);
return primary;
}
/**
* Returns the primary message codewords for mode 3.
*
* @param postcode the postal code
* @param country the country code
* @param service the service code
* @return the primary message, as codewords
*/
private static int[] getMode3PrimaryCodewords(String postcode, int country, int service) {
int[] postcodeNums = new int[postcode.length()];
postcode = postcode.toUpperCase();
for (int i = 0; i < postcodeNums.length; i++) {
postcodeNums[i] = postcode.charAt(i);
if (postcode.charAt(i) >= 'A' && postcode.charAt(i) <= 'Z') {
// (Capital) letters shifted to Code Set A values
postcodeNums[i] -= 64;
}
if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) {
// Not a valid postal code character, use space instead
postcodeNums[i] = 32;
}
// Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital
// letters in Code Set A (e.g. LF becomes 'J')
}
int[] primary = new int[10];
primary[0] = ((postcodeNums[5] & 0x03) << 4) | 3;
primary[1] = ((postcodeNums[4] & 0x03) << 4) | ((postcodeNums[5] & 0x3c) >> 2);
primary[2] = ((postcodeNums[3] & 0x03) << 4) | ((postcodeNums[4] & 0x3c) >> 2);
primary[3] = ((postcodeNums[2] & 0x03) << 4) | ((postcodeNums[3] & 0x3c) >> 2);
primary[4] = ((postcodeNums[1] & 0x03) << 4) | ((postcodeNums[2] & 0x3c) >> 2);
primary[5] = ((postcodeNums[0] & 0x03) << 4) | ((postcodeNums[1] & 0x3c) >> 2);
primary[6] = ((postcodeNums[0] & 0x3c) >> 2) | ((country & 0x3) << 4);
primary[7] = (country & 0xfc) >> 2;
primary[8] = ((country & 0x300) >> 8) | ((service & 0xf) << 2);
primary[9] = ((service & 0x3f0) >> 4);
return primary;
}
/**
* Returns the error correction codewords for the specified data codewords.
*
* @param codewords the codewords that we need error correction codewords for
* @param ecclen the number of error correction codewords needed
* @return the error correction codewords for the specified data codewords
*/
private static int[] getErrorCorrection(int[] codewords, int ecclen) {
ReedSolomon rs = new ReedSolomon();
rs.init_gf(0x43);
rs.init_code(ecclen, 1);
rs.encode(codewords.length, codewords);
int[] results = new int[ecclen];
for (int i = 0; i < ecclen; i++) {
results[i] = rs.getResult(results.length - 1 - i);
}
return results;
}
/**
* Returns the MaxiCode mode being used. Only modes 2 to 6 are supported.
*
* @return the MaxiCode mode being used
*/
public int getMode() {
return mode;
}
/**
* Sets the MaxiCode mode to use. Only modes 2 to 6 are supported.
*
* @param mode the MaxiCode mode to use
*/
public void setMode(int mode) {
if (mode < 2 || mode > 6) {
throw new IllegalArgumentException("Invalid MaxiCode mode: " + mode);
}
this.mode = mode;
}
/**
* Returns the position of this MaxiCode symbol in a series of symbols using structured append. If this symbol is not part of
* such a series, this method will return <code>1</code>.
*
* @return the position of this MaxiCode symbol in a series of symbols using structured append
*/
public int getStructuredAppendPosition() {
return structuredAppendPosition;
}
/**
* If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the
* position of this symbol in the series. Valid values are 1 through 8 inclusive.
*
* @param position the position of this MaxiCode symbol in the structured append series
*/
public void setStructuredAppendPosition(int position) {
if (position < 1 || position > 8) {
throw new IllegalArgumentException("Invalid MaxiCode structured append position: " + position);
}
this.structuredAppendPosition = position;
}
/**
* Returns the size of the series of MaxiCode symbols using structured append that this symbol is part of. If this symbol is
* not part of a structured append series, this method will return <code>1</code>.
*
* @return size of the series that this symbol is part of
*/
public int getStructuredAppendTotal() {
return structuredAppendTotal;
}
/**
* If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured format, this method sets the total
* number of symbols in the series. Valid values are 1 through 8 inclusive. A value of 1 indicates that this symbol is not
* part of a structured append series.
*
* @param total the total number of MaxiCode symbols in the structured append series
*/
public void setStructuredAppendTotal(int total) {
if (total < 1 || total > 8) {
throw new IllegalArgumentException("Invalid MaxiCode structured append total: " + total);
}
this.structuredAppendTotal = total;
}
/**
* Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3.
*
* @return the primary data for this MaxiCode symbol
*/
public String getPrimary() {
return primaryData;
}
/**
* Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following structure:
* <table><caption>Expected primary data structure</caption>
* <tr><th>Characters</th><th>Meaning</th></tr>
* <tr><td>1-9</td><td>Postal code data which can consist of up to 9 digits (for mode 2) or up to 6
* alphanumeric characters (for mode 3). Remaining unused characters should be
* filled with the SPACE character (ASCII 32).</td></tr>
* <tr><td>10-12</td><td>Three-digit country code according to ISO-3166.</td></tr>
* <tr><td>13-15</td><td>Three digit service code. This depends on your parcel courier.</td></tr>
* </table>
*
* @param primary the primary data
*/
public void setPrimary(String primary) {
primaryData = primary;
}
@Override
public boolean encode() {
// copy input data over into source
int sourcelen = content.length();
source = new int[sourcelen];
eciProcess();
for (int i = 0; i < sourcelen; i++) {
source[i] = inputBytes[i] & 0xFF;
}
// mode 2 -> mode 3 if postal code isn't strictly numeric
if (mode == 2) {
for (int i = 0; i < 10 && i < primaryData.length(); i++) {
if ((primaryData.charAt(i) < '0') || (primaryData.charAt(i) > '9')) {
mode = 3;
break;
}
}
}
// initialize the set and character arrays
if (!processText()) {
errorMsg.append("Input data too long");
return false;
}
// start building the codeword array, starting with a copy of the character data
// insert primary message if this is a structured carrier message; insert mode otherwise
codewords = Arrays.copyOf(character, character.length);
if (mode == 2 || mode == 3) {
int[] primary = getPrimaryCodewords();
if (primary == null) {
return false;
}
codewords = insert(codewords, 0, primary);
} else {
codewords = insert(codewords, 0, new int[]{mode});
}
// insert structured append flag if necessary
if (structuredAppendTotal > 1) {
int[] flag = new int[2];
flag[0] = 33; // padding
flag[1] = ((structuredAppendPosition - 1) << 3) | (structuredAppendTotal - 1); // position + total
int index;
if (mode == 2 || mode == 3) {
index = 10; // first two data symbols in the secondary message
} else {
index = 1; // first two data symbols in the primary message (first symbol at index 0 isn't a data symbol)
}
codewords = insert(codewords, index, flag);
}
int secondaryMax, secondaryECMax;
if (mode == 5) {
// 68 data codewords, 56 error corrections in secondary message
secondaryMax = 68;
secondaryECMax = 56;
} else {
// 84 data codewords, 40 error corrections in secondary message
secondaryMax = 84;
secondaryECMax = 40;
}
// truncate data codewords to maximum data space available
int totalMax = secondaryMax + 10;
if (codewords.length > totalMax) {
codewords = Arrays.copyOfRange(codewords, 0, totalMax);
}
// insert primary error correction between primary message and secondary message (always EEC)
int[] primary = Arrays.copyOfRange(codewords, 0, 10);
int[] primaryCheck = getErrorCorrection(primary, 10);
codewords = insert(codewords, 10, primaryCheck);
// calculate secondary error correction
int[] secondary = Arrays.copyOfRange(codewords, 20, codewords.length);
int[] secondaryOdd = new int[secondary.length / 2];
int[] secondaryEven = new int[secondary.length / 2];
for (int i = 0; i < secondary.length; i++) {
if ((i & 1) != 0) { // odd
secondaryOdd[(i - 1) / 2] = secondary[i];
} else { // even
secondaryEven[i / 2] = secondary[i];
}
}
int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2);
int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2);
// add secondary error correction after secondary message
codewords = Arrays.copyOf(codewords, codewords.length + secondaryECOdd.length + secondaryECEven.length);
for (int i = 0; i < secondaryECOdd.length; i++) {
codewords[20 + secondaryMax + (2 * i) + 1] = secondaryECOdd[i];
}
for (int i = 0; i < secondaryECEven.length; i++) {
codewords[20 + secondaryMax + (2 * i)] = secondaryECEven[i];
}
encodeInfo.append("Mode: ").append(mode).append("\n");
encodeInfo.append("ECC Codewords: ").append(secondaryECMax).append("\n");
encodeInfo.append("Codewords: ");
for (int codeword : codewords) {
encodeInfo.append(Integer.toString(codeword)).append(" ");
}
encodeInfo.append("\n");
// copy data into symbol grid
int[] bit_pattern = new int[7];
for (int i = 0; i < 33; i++) {
for (int j = 0; j < 30; j++) {
int block = (MAXICODE_GRID[(i * 30) + j] + 5) / 6;
int bit = (MAXICODE_GRID[(i * 30) + j] + 5) % 6;
if (block != 0) {
bit_pattern[0] = (codewords[block - 1] & 0x20) >> 5;
bit_pattern[1] = (codewords[block - 1] & 0x10) >> 4;
bit_pattern[2] = (codewords[block - 1] & 0x8) >> 3;
bit_pattern[3] = (codewords[block - 1] & 0x4) >> 2;
bit_pattern[4] = (codewords[block - 1] & 0x2) >> 1;
bit_pattern[5] = (codewords[block - 1] & 0x1);
grid[i][j] = bit_pattern[bit] != 0;
}
}
}
// add orientation markings
grid[0][28] = true; // top right filler
grid[0][29] = true;
grid[9][10] = true; // top left marker
grid[9][11] = true;
grid[10][11] = true;
grid[15][7] = true; // left hand marker
grid[16][8] = true;
grid[16][20] = true; // right hand marker
grid[17][20] = true;
grid[22][10] = true; // bottom left marker
grid[23][10] = true;
grid[22][17] = true; // bottom right marker
grid[23][17] = true;
// the following is provided for compatibility, but the results are not useful
rowCount = 33;
readable = new StringBuilder();
pattern = new String[33];
rowHeight = new int[33];
for (int i = 0; i < 33; i++) {
StringBuilder bin = new StringBuilder(30);
for (int j = 0; j < 30; j++) {
if (grid[i][j]) {
bin.append("1");
} else {
bin.append("0");
}
}
pattern[i] = bin2pat(bin.toString());
rowHeight[i] = 1;
}
symbolHeight = 72;
symbolWidth = 74;
plotSymbol();
return true;
}
/**
* Extracts the postal code, country code and service code from the primary data and returns the corresponding primary message
* codewords.
*
* @return the primary message codewords
*/
private int[] getPrimaryCodewords() {
assert mode == 2 || mode == 3;
if (primaryData.length() != 15) {
errorMsg.append("Invalid Primary String");
return null;
}
for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */
if ((primaryData.charAt(i) < '0') || (primaryData.charAt(i) > '9')) {
errorMsg.append("Invalid Primary String");
return null;
}
}
String postcode;
if (mode == 2) {
postcode = primaryData.substring(0, 9);
int index = postcode.indexOf(' ');
if (index != -1) {
postcode = postcode.substring(0, index);
}
} else {
// if (mode == 3)
postcode = primaryData.substring(0, 6);
}
int country = Integer.parseInt(primaryData.substring(9, 12));
int service = Integer.parseInt(primaryData.substring(12, 15));
if (mode == 2) {
return getMode2PrimaryCodewords(postcode, country, service);
} else { // mode == 3
return getMode3PrimaryCodewords(postcode, country, service);
}
}
/**
* Formats text according to Appendix A, populating the {@link #set} and {@link #character} arrays.
*
* @return true if the content fits in this symbol and was formatted; false otherwise
*/
private boolean processText() {
int length = content.length();
int i, j, count, current_set;
if (length > 138) {
return false;
}
for (i = 0; i < 144; i++) {
set[i] = -1;
character[i] = 0;
}
for (i = 0; i < length; i++) {
/* Look up characters in table from Appendix A - this gives
value and code set for most characters */
set[i] = MAXICODE_SET[source[i]];
character[i] = MAXICODE_SYMBOL_CHAR[source[i]];
}
// If a character can be represented in more than one code set, pick which version to use.
if (set[0] == 0) {
if (character[0] == 13) {
character[0] = 0;
}
set[0] = 1;
}
for (i = 1; i < length; i++) {
if (set[i] == 0) {
/* Special character that can be represented in more than one code set. */
if (character[i] == 13) {
/* Carriage Return */
set[i] = bestSurroundingSet(i, length, 1, 5);
if (set[i] == 5) {
character[i] = 13;
} else {
character[i] = 0;
}
} else if (character[i] == 28) {
/* FS */
set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (set[i] == 5) {
character[i] = 32;
}
} else if (character[i] == 29) {
/* GS */
set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (set[i] == 5) {
character[i] = 33;
}
} else if (character[i] == 30) {
/* RS */
set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (set[i] == 5) {
character[i] = 34;
}
} else if (character[i] == 32) {
/* Space */
set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (set[i] == 1) {
character[i] = 32;
} else if (set[i] == 2) {
character[i] = 47;
} else {
character[i] = 59;
}
} else if (character[i] == 44) {
/* Comma */
set[i] = bestSurroundingSet(i, length, 1, 2);
if (set[i] == 2) {
character[i] = 48;
}
} else if (character[i] == 46) {
/* Full Stop */
set[i] = bestSurroundingSet(i, length, 1, 2);
if (set[i] == 2) {
character[i] = 49;
}
} else if (character[i] == 47) {
/* Slash */
set[i] = bestSurroundingSet(i, length, 1, 2);
if (set[i] == 2) {
character[i] = 50;
}
} else if (character[i] == 58) {
/* Colon */
set[i] = bestSurroundingSet(i, length, 1, 2);
if (set[i] == 2) {
character[i] = 51;
}
}
}
}
for (i = length; i < set.length; i++) {
/* Add the padding */
if (set[length - 1] == 2) {
set[i] = 2;
} else {
set[i] = 1;
}
character[i] = 33;
}
/* Find candidates for number compression (not allowed in primary message in modes 2 and 3). */
if (mode == 2 || mode == 3) {
j = 9;
} else {
j = 0;
}
count = 0;
for (i = j; i < 143; i++) {
if (set[i] == 1 && character[i] >= 48 && character[i] <= 57) {
/* Character is a number */
count++;
} else {
count = 0;
}
if (count == 9) {
/* Nine digits in a row can be compressed */
set[i] = 6;
set[i - 1] = 6;
set[i - 2] = 6;
set[i - 3] = 6;
set[i - 4] = 6;
set[i - 5] = 6;
set[i - 6] = 6;
set[i - 7] = 6;
set[i - 8] = 6;
count = 0;
}
}
/* Add shift and latch characters */
current_set = 1;
i = 0;
do {
if ((set[i] != current_set) && (set[i] != 6)) {
switch (set[i]) {
case 1:
if (i + 1 < set.length && set[i + 1] == 1) {
if (i + 2 < set.length && set[i + 2] == 1) {
if (i + 3 < set.length && set[i + 3] == 1) {
/* Latch A */
insert(i, 63);
current_set = 1;
length++;
i += 3;
} else {
/* 3 Shift A */
insert(i, 57);
length++;
i += 2;
}
} else {
/* 2 Shift A */
insert(i, 56);
length++;
i++;
}
} else {
/* Shift A */
insert(i, 59);
length++;
}
break;
case 2:
if (i + 1 < set.length && set[i + 1] == 2) {
/* Latch B */
insert(i, 63);
current_set = 2;
length++;
i++;
} else {
/* Shift B */
insert(i, 59);
length++;
}
break;
case 3:
if (i + 3 < set.length && set[i + 1] == 3 && set[i + 2] == 3 && set[i + 3] == 3) {
/* Lock In C */
insert(i, 60);
insert(i, 60);
current_set = 3;
length++;
i += 3;
} else {
/* Shift C */
insert(i, 60);
length++;
}
break;
case 4:
if (i + 3 < set.length && set[i + 1] == 4 && set[i + 2] == 4 && set[i + 3] == 4) {
/* Lock In D */
insert(i, 61);
insert(i, 61);
current_set = 4;
length++;
i += 3;
} else {
/* Shift D */
insert(i, 61);
length++;
}
break;
case 5:
if (i + 3 < set.length && set[i + 1] == 5 && set[i + 2] == 5 && set[i + 3] == 5) {
/* Lock In E */
insert(i, 62);
insert(i, 62);
current_set = 5;
length++;
i += 3;
} else {
/* Shift E */
insert(i, 62);
length++;
}
break;
default:
throw new IllegalStateException("Unexpected set " + set[i] + " at index " + i + ".");
}
i++;
}
i++;
} while (i < set.length);
/* Number compression has not been forgotten! It's handled below. */
i = 0;
do {
if (set[i] == 6) {
/* Number compression */
int value = 0;
for (j = 0; j < 9; j++) {
value *= 10;
value += (character[i + j] - '0');
}
character[i] = 31; /* NS */
character[i + 1] = (value & 0x3f000000) >> 24;
character[i + 2] = (value & 0xfc0000) >> 18;
character[i + 3] = (value & 0x3f000) >> 12;
character[i + 4] = (value & 0xfc0) >> 6;
character[i + 5] = (value & 0x3f);
i += 6;
for (j = i; j < 140; j++) {
set[j] = set[j + 3];
character[j] = character[j + 3];
}
length -= 3;
} else {
i++;
}
} while (i < set.length);
/* Inject ECI codes to beginning of data, according to Table 3 */
if (eciMode != 3) {
insert(0, 27); // ECI
if ((eciMode >= 0) && (eciMode <= 31)) {
insert(1, eciMode & 0x1F);
length += 2;
}
if ((eciMode >= 32) && (eciMode <= 1023)) {
insert(1, 0x20 + (eciMode >> 6));
insert(2, eciMode & 0x3F);
length += 3;
}
if ((eciMode >= 1024) && (eciMode <= 32767)) {
insert(1, 0x30 + (eciMode >> 12));
insert(2, (eciMode >> 6) & 0x3F);
insert(3, eciMode & 0x3F);
length += 4;
}
if ((eciMode >= 32768) && (eciMode <= 999999)) {
insert(1, 0x38 + (eciMode >> 18));
insert(2, (eciMode >> 12) & 0x3F);
insert(3, (eciMode >> 6) & 0x3F);
insert(4, eciMode & 0x3F);
length += 5;
}
}
/* Make sure we haven't exceeded the maximum data length. */
return ((mode != 2 && mode != 3) || length <= 84) && ((mode != 4 && mode != 6) || length <= 93) && (mode != 5 || length <= 77);
}
/**
* Guesses the best set to use at the specified index by looking at the surrounding sets. In general, characters in
* lower-numbered sets are more common, so we choose them if we can. If no good surrounding sets can be found, the default
* value returned is the first value from the valid set.
*
* @param index the current index
* @param length the maximum length to look at
* @param valid the valid sets for this index
* @return the best set to use at the specified index
*/
private int bestSurroundingSet(int index, int length, int... valid) {
int option1 = set[index - 1];
if (index + 1 < length) {
// we have two options to check
int option2 = set[index + 1];
if (contains(valid, option1) && contains(valid, option2)) {
return Math.min(option1, option2);
} else if (contains(valid, option1)) {
return option1;
} else if (contains(valid, option2)) {
return option2;
} else {
return valid[0];
}
} else {
// we only have one option to check
if (contains(valid, option1)) {
return option1;
} else {
return valid[0];
}
}
}
/**
* Moves everything up so that the specified shift or latch character can be inserted.
*
* @param position the position beyond which everything needs to be shifted
* @param c the latch or shift character to insert at the specified position, after everything has been shifted
*/
private void insert(int position, int c) {
for (int i = 143; i > position; i--) {
set[i] = set[i - 1];
character[i] = character[i - 1];
}
character[position] = c;
}
@Override
protected void plotSymbol() {
// hexagons
for (int row = 0; row < 33; row++) {
for (int col = 0; col < 30; col++) {
if (grid[row][col]) {
double x = (2.46 * col) + 1.23;
if ((row & 1) != 0) {
x += 1.23;
}
double y = (2.135 * row) + 1.43;
getHexagons().add(new Hexagon(x, y));
}
}
}
// circles
double[] radii = {10.85, 8.97, 7.10, 5.22, 3.31, 1.43};
for (double aRadii : radii) {
Ellipse2D.Double circle = new Ellipse2D.Double();
circle.setFrameFromCenter(35.76, 35.60, 35.76 + aRadii, 35.60 + aRadii);
getTarget().add(circle);
}
}
@Override
protected int[] getCodewords() {
return codewords;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,188 @@
package org.xbib.graphics.barcode;
/**
* Implements the MSI (Modified Plessey) bar code symbology.
* MSI Plessey can encode a string of numeric digits and has a range
* of check digit options.
*/
public class MsiPlessey extends Symbol {
private final String[] MSI_PlessTable = {
"12121212", "12121221", "12122112", "12122121", "12211212", "12211221",
"12212112", "12212121", "21121212", "21121221"
};
private CheckDigit checkOption;
public MsiPlessey() {
checkOption = CheckDigit.NONE;
}
/**
* Set the check digit scheme to use. Options are: None, Modulo-10,
* 2 x Modulo-10, Modulo-11 and Modulo-11 &amp; 10.
*
* @param checkMode Type of check digit to add to symbol
*/
public void setCheckDigit(CheckDigit checkMode) {
checkOption = checkMode;
}
@Override
public boolean encode() {
StringBuilder intermediate;
int length = content.length();
int i;
StringBuilder evenString;
StringBuilder oddString;
String addupString;
int spacer;
int addup;
int weight;
int checkDigit1;
int checkDigit2;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
intermediate = new StringBuilder("21"); // Start
for (i = 0; i < length; i++) {
intermediate.append(MSI_PlessTable[Character.getNumericValue(content.charAt(i))]);
}
readable = new StringBuilder(content);
if ((checkOption == CheckDigit.MOD10) || (checkOption == CheckDigit.MOD10_MOD10)) {
/* Add Modulo-10 check digit */
evenString = new StringBuilder();
oddString = new StringBuilder();
spacer = content.length() & 1;
for (i = content.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString.insert(0, content.charAt(i));
} else {
oddString.insert(0, content.charAt(i));
}
} else {
if ((i & 1) != 0) {
oddString.insert(0, content.charAt(i));
} else {
evenString.insert(0, content.charAt(i));
}
}
}
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString.toString()) * 2);
}
addupString += evenString;
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
checkDigit1 = 10 - (addup % 10);
if (checkDigit1 == 10) {
checkDigit1 = 0;
}
intermediate.append(MSI_PlessTable[checkDigit1]);
readable.append(checkDigit1);
}
if ((checkOption == CheckDigit.MOD11) || (checkOption == CheckDigit.MOD11_MOD10)) {
/* Add a Modulo-11 check digit */
weight = 2;
addup = 0;
for (i = content.length() - 1; i >= 0; i--) {
addup += (content.charAt(i) - '0') * weight;
weight++;
if (weight == 8) {
weight = 2;
}
}
checkDigit1 = 11 - (addup % 11);
if (checkDigit1 == 11) {
checkDigit1 = 0;
}
readable.append(checkDigit1);
if (checkDigit1 == 10) {
intermediate.append(MSI_PlessTable[1]);
intermediate.append(MSI_PlessTable[0]);
} else {
intermediate.append(MSI_PlessTable[checkDigit1]);
}
}
if ((checkOption == CheckDigit.MOD10_MOD10) || (checkOption == CheckDigit.MOD11_MOD10)) {
/* Add a second Modulo-10 check digit */
evenString = new StringBuilder();
oddString = new StringBuilder();
spacer = readable.length() & 1;
for (i = readable.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString.insert(0, readable.charAt(i));
} else {
oddString.insert(0, readable.charAt(i));
}
} else {
if ((i & 1) != 0) {
oddString.insert(0, readable.charAt(i));
} else {
evenString.insert(0, readable.charAt(i));
}
}
}
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString.toString()) * 2);
}
addupString += evenString;
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
checkDigit2 = 10 - (addup % 10);
if (checkDigit2 == 10) {
checkDigit2 = 0;
}
intermediate.append(MSI_PlessTable[checkDigit2]);
readable.append(checkDigit2);
}
intermediate.append("121"); // Stop
pattern = new String[1];
pattern[0] = intermediate.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
public enum CheckDigit {
NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10
}
}

View file

@ -0,0 +1,72 @@
package org.xbib.graphics.barcode;
import java.util.stream.IntStream;
/**
* Calculate NVE-18 (Nummer der Versandeinheit)
* Also SSCC-18 (Serial Shipping Container Code)
* Encodes a 17 digit number, adding a Modulo-10 check digit.
*/
public class Nve18 extends Symbol {
@Override
public boolean encode() {
StringBuilder gs1Equivalent = new StringBuilder();
int zeroes;
int count = 0;
int c, cdigit;
int p = 0;
Code128 code128 = new Code128();
if (content.length() > 17) {
errorMsg.append("Input data too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
// Add leading zeroes
zeroes = 17 - content.length();
IntStream.range(0, zeroes).mapToObj(i -> "0").forEach(gs1Equivalent::append);
gs1Equivalent.append(content);
// Add Modulus-10 check digit
for (int i = gs1Equivalent.length() - 1; i >= 0; i--) {
c = Character.getNumericValue(gs1Equivalent.charAt(i));
if ((p % 2) == 0) {
c = c * 3;
}
count += c;
p++;
}
cdigit = 10 - (count % 10);
if (cdigit == 10) {
cdigit = 0;
}
encodeInfo.append("NVE Check Digit: ").append(cdigit).append("\n");
content = "[00]" + gs1Equivalent + cdigit;
// Defer to Code 128
code128.setDataType(DataType.GS1);
code128.setHumanReadableLocation(getHumanReadableLocation());
try {
code128.setContent(content);
} catch (Exception e) {
errorMsg.append(e.getMessage());
return false;
}
getRectangles().clear();
getRectangles().addAll(code128.getRectangles());
getTexts().clear();
getTexts().addAll(code128.getTexts());
symbolHeight = code128.symbolHeight;
symbolWidth = code128.symbolWidth;
encodeInfo.append(code128.encodeInfo);
return true;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
package org.xbib.graphics.barcode;
/**
* Implements the <a href="http://en.wikipedia.org/wiki/Pharmacode">Pharmacode</a>
* bar code symbology.
* Pharmacode is used for the identification of pharmaceuticals. The symbology
* is able to encode whole numbers between 3 and 131070.
*/
public class Pharmacode extends Symbol {
@Override
public boolean encode() {
int tester = 0;
int i;
StringBuilder inter = new StringBuilder();
StringBuilder dest = new StringBuilder();
if (content.length() > 6) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
for (i = 0; i < content.length(); i++) {
tester *= 10;
tester += Character.getNumericValue(content.charAt(i));
}
if ((tester < 3) || (tester > 131070)) {
errorMsg.append("Data out of range");
return false;
}
do {
if ((tester & 1) == 0) {
inter.append("W");
tester = (tester - 2) / 2;
} else {
inter.append("N");
tester = (tester - 1) / 2;
}
} while (tester != 0);
for (i = inter.length() - 1; i >= 0; i--) {
if (inter.charAt(i) == 'W') {
dest.append("32");
} else {
dest.append("12");
}
}
readable = new StringBuilder();
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
}

View file

@ -0,0 +1,106 @@
package org.xbib.graphics.barcode;
import java.awt.geom.Rectangle2D;
/**
* Implements the Two-Track Pharmacode bar code symbology.
* Pharmacode Two-Track is an alternative system to Pharmacode One-Track used
* for the identification of pharmaceuticals. The symbology is able to encode
* whole numbers between 4 and 64570080.
*/
public class Pharmacode2Track extends Symbol {
@Override
public boolean encode() {
int i, tester = 0;
StringBuilder inter;
StringBuilder dest;
if (content.length() > 8) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
for (i = 0; i < content.length(); i++) {
tester *= 10;
tester += Character.getNumericValue(content.charAt(i));
}
if ((tester < 4) || (tester > 64570080)) {
errorMsg.append("Data out of range");
return false;
}
inter = new StringBuilder();
do {
switch (tester % 3) {
case 0:
inter.append("F");
tester = (tester - 3) / 3;
break;
case 1:
inter.append("D");
tester = (tester - 1) / 3;
break;
case 2:
inter.append("A");
tester = (tester - 2) / 3;
break;
}
}
while (tester != 0);
dest = new StringBuilder();
for (i = (inter.length() - 1); i >= 0; i--) {
dest.append(inter.charAt(i));
}
encodeInfo.append("Encoding: ").append(dest).append("\n");
readable = new StringBuilder();
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
getRectangles().clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
switch (pattern[0].charAt(xBlock)) {
case 'A':
y = 0;
h = defaultHeight / 2;
break;
case 'D':
y = defaultHeight / 2;
h = defaultHeight / 2;
break;
case 'F':
y = 0;
h = defaultHeight;
break;
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
x += 2;
}
symbolWidth = pattern[0].length() * 2;
symbolHeight = defaultHeight;
}
}

View file

@ -0,0 +1,69 @@
package org.xbib.graphics.barcode;
/**
* PZN8 is a Code 39 based symbology used by the pharmaceutical industry in
* Germany. PZN8 encodes a 7 digit number and includes a modulo-10 check digit.
*/
public class Pharmazentralnummer extends Symbol {
/* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra
* check digit. Now generates PZN-8.
*/
@Override
public boolean encode() {
int l = content.length();
StringBuilder localstr;
int zeroes, count = 0, check_digit;
Code3Of9 c = new Code3Of9();
if (l > 7) {
errorMsg.append("Input data too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
localstr = new StringBuilder("-");
zeroes = 7 - l + 1;
for (int i = 1; i < zeroes; i++)
localstr.append('0');
localstr.append(content);
for (int i = 1; i < 8; i++) {
count += i * Character.getNumericValue(localstr.charAt(i));
}
check_digit = count % 11;
if (check_digit == 11) {
check_digit = 0;
}
if (check_digit == 10) {
errorMsg.append("Not a valid PZN identifier");
return false;
}
encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
localstr.append((char) (check_digit + '0'));
try {
c.setContent(localstr.toString());
} catch (Exception e) {
errorMsg.append(e.getMessage());
return false;
}
readable = new StringBuilder("PZN").append(localstr);
pattern = new String[1];
pattern[0] = c.pattern[0];
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
}

View file

@ -0,0 +1,185 @@
package org.xbib.graphics.barcode;
import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.geom.Rectangle2D;
/**
* Implements <a href="http://en.wikipedia.org/wiki/POSTNET">POSTNET</a> and
* <a href="http://en.wikipedia.org/wiki/Postal_Alpha_Numeric_Encoding_Technique">PLANET</a>
* bar code symbologies.
* POSTNET and PLANET both use numerical input data and include a modulo-10
* check digit.
*/
public class Postnet extends Symbol {
private static final String[] PN_TABLE = {
"LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS"
};
;
private static final String[] PL_TABLE = {
"SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL"
};
private Mode mode;
public Postnet() {
this.mode = Mode.POSTNET;
this.defaultHeight = 12;
setHumanReadableLocation(HumanReadableLocation.NONE);
}
/**
* Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET.
*
* @return the barcode mode (PLANET or POSTNET)
*/
public Mode getMode() {
return mode;
}
/**
* Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET.
*
* @param mode the barcode mode (PLANET or POSTNET)
*/
public void setMode(Mode mode) {
this.mode = mode;
}
@Override
public boolean encode() {
boolean retval;
if (mode == Mode.POSTNET) {
retval = makePostnet();
} else {
retval = makePlanet();
}
if (retval) {
plotSymbol();
}
return retval;
}
private boolean makePostnet() {
int i, sum, check_digit;
StringBuilder dest;
if (content.length() > 38) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
sum = 0;
dest = new StringBuilder("L");
for (i = 0; i < content.length(); i++) {
dest.append(PN_TABLE[content.charAt(i) - '0']);
sum += content.charAt(i) - '0';
}
check_digit = (10 - (sum % 10)) % 10;
encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
dest.append(PN_TABLE[check_digit]);
dest.append("L");
encodeInfo.append("Encoding: ").append(dest).append("\n");
readable = new StringBuilder(content);
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
private boolean makePlanet() {
int i, sum, check_digit;
StringBuilder dest;
if (content.length() > 38) {
errorMsg.append("Input too long");
return false;
}
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
sum = 0;
dest = new StringBuilder("L");
for (i = 0; i < content.length(); i++) {
dest.append(PL_TABLE[content.charAt(i) - '0']);
sum += content.charAt(i) - '0';
}
check_digit = (10 - (sum % 10)) % 10;
encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
dest.append(PL_TABLE[check_digit]);
dest.append("L");
encodeInfo.append("Encoding: ").append(dest).append("\n");
readable = new StringBuilder(content);
pattern = new String[]{dest.toString()};
rowCount = 1;
rowHeight = new int[]{-1};
return true;
}
@Override
protected void plotSymbol() {
int xBlock, shortHeight;
double x, y, w, h;
getRectangles().clear();
getTexts().clear();
int baseY;
if (getHumanReadableLocation() == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
x = 0;
w = moduleWidth;
shortHeight = (int) (0.4 * defaultHeight);
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
if (pattern[0].charAt(xBlock) == 'L') {
y = baseY;
h = defaultHeight;
} else {
y = baseY + defaultHeight - shortHeight;
h = shortHeight;
}
getRectangles().add(new Rectangle2D.Double(x, y, w, h));
x += (2.5 * w);
}
symbolWidth = (int) Math.ceil(((pattern[0].length() - 1) * 2.5 * w) + w); // final bar doesn't need extra whitespace
symbolHeight = defaultHeight;
if (getHumanReadableLocation() != NONE && readable.length() > 0) {
double baseline;
if (getHumanReadableLocation() == TOP) {
baseline = fontSize;
} else {
baseline = getHeight() + fontSize;
}
double centerX = getWidth() / 2.0;
getTexts().add(new TextBox(centerX, baseline, readable.toString()));
}
}
public enum Mode {
PLANET, POSTNET
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
package org.xbib.graphics.barcode;
import java.awt.geom.Rectangle2D;
import java.util.Locale;
/**
* Encodes data according to the Royal Mail 4-State Country Code
* Data input can consist of numbers 0-9 and letters A-Z and usually includes
* delivery postcode followed by house number. A check digit is calculated
* and added.
*/
public class RoyalMail4State extends Symbol {
/* Handles the 4 State barcodes used in the UK by Royal Mail */
private String[] RoyalTable = {
"TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA",
"DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT",
"FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT"
};
private char[] krSet = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
@Override
public boolean encode() {
StringBuilder dest;
int i, top = 0, bottom = 0;
int row, column;
int index;
content = content.toUpperCase(Locale.ENGLISH);
if (!(content.matches("[0-9A-Z]+"))) {
errorMsg.append("Invalid characters in data");
return false;
}
dest = new StringBuilder("A");
for (i = 0; i < content.length(); i++) {
index = positionOf(content.charAt(i), krSet);
dest.append(RoyalTable[index]);
top += (index + 1) % 6;
bottom += ((index / 6) + 1) % 6;
}
/* calculate check digit */
row = (top % 6) - 1;
column = (bottom % 6) - 1;
if (row == -1) {
row = 5;
}
if (column == -1) {
column = 5;
}
dest.append(RoyalTable[(6 * row) + column]);
encodeInfo.append("Check Digit: ").append((6 * row) + column).append("\n");
/* Stop character */
dest.append("F");
encodeInfo.append("Encoding: ").append(dest).append("\n");
readable = new StringBuilder();
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
getRectangles().clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
switch (pattern[0].charAt(xBlock)) {
case 'A':
y = 0;
h = 5;
break;
case 'D':
y = 3;
h = 5;
break;
case 'F':
y = 0;
h = 8;
break;
case 'T':
y = 3;
h = 2;
break;
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
x += 2;
}
symbolWidth = pattern[0].length() * 3;
symbolHeight = 8;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,177 @@
package org.xbib.graphics.barcode;
/**
* Telepen (also known as Telepen Alpha) can encode ASCII text input and
* includes a modulo-127 check digit. Telepen Numeric allows compression of
* numeric data into a Telepen symbol. Data can consist of pairs of numbers
* or pairs consisting of a numerical digit followed an X character.
* Telepen Numeric also includes a modulo-127 check digit.
*/
public class Telepen extends Symbol {
public tp_mode mode;
private String[] TeleTable = {
"1111111111111111", "1131313111", "33313111", "1111313131",
"3111313111", "11333131", "13133131", "111111313111", "31333111",
"1131113131", "33113131", "1111333111", "3111113131", "1113133111",
"1311133111", "111111113131", "3131113111", "11313331", "333331",
"111131113111", "31113331", "1133113111", "1313113111", "1111113331",
"31131331", "113111113111", "3311113111", "1111131331", "311111113111",
"1113111331", "1311111331", "11111111113111", "31313311", "1131311131",
"33311131", "1111313311", "3111311131", "11333311", "13133311",
"111111311131", "31331131", "1131113311", "33113311", "1111331131",
"3111113311", "1113131131", "1311131131", "111111113311", "3131111131",
"1131131311", "33131311", "111131111131", "3111131311", "1133111131",
"1313111131", "111111131311", "3113111311", "113111111131",
"3311111131", "111113111311", "311111111131", "111311111311",
"131111111311", "11111111111131", "3131311111", "11313133", "333133",
"111131311111", "31113133", "1133311111", "1313311111", "1111113133",
"313333", "113111311111", "3311311111", "11113333", "311111311111",
"11131333", "13111333", "11111111311111", "31311133", "1131331111",
"33331111", "1111311133", "3111331111", "11331133", "13131133",
"111111331111", "3113131111", "1131111133", "33111133", "111113131111",
"3111111133", "111311131111", "131111131111", "111111111133",
"31311313", "113131111111", "3331111111", "1111311313", "311131111111",
"11331313", "13131313", "11111131111111", "3133111111", "1131111313",
"33111313", "111133111111", "3111111313", "111313111111",
"131113111111", "111111111313", "313111111111", "1131131113",
"33131113", "11113111111111", "3111131113", "113311111111",
"131311111111", "111111131113", "3113111113", "11311111111111",
"331111111111", "111113111113", "31111111111111", "111311111113",
"131111111113"
};
public Telepen() {
mode = tp_mode.NORMAL;
}
public void setNormalMode() {
mode = tp_mode.NORMAL;
}
public void setNumericMode() {
mode = tp_mode.NUMERIC;
}
@Override
public boolean encode() {
if (mode == tp_mode.NORMAL) {
return normal_mode();
} else {
return numeric_mode();
}
}
private boolean normal_mode() {
int count = 0, asciicode, check_digit;
StringBuilder p = new StringBuilder();
String dest;
int l = content.length();
if (!content.matches("[\u0000-\u007F]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
dest = TeleTable['_']; // Start
for (int i = 0; i < l; i++) {
asciicode = content.charAt(i);
p.append(TeleTable[asciicode]);
count += asciicode;
}
check_digit = 127 - (count % 127);
if (check_digit == 127) {
check_digit = 0;
}
p.append(TeleTable[check_digit]);
encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
dest += p;
dest += TeleTable['z']; // Stop
readable = new StringBuilder(content);
pattern = new String[1];
pattern[0] = dest;
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
public boolean numeric_mode() {
int count = 0, check_digit;
StringBuilder p = new StringBuilder();
String t;
String dest;
int l = content.length();
int tl, glyph;
char c1, c2;
//FIXME: Ensure no extended ASCII or Unicode charcters are entered
if (!(content.matches("[0-9X]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
/* If input is an odd length, add a leading zero */
if ((l & 1) == 1) {
t = "0" + content;
tl = l + 1;
} else {
t = content;
tl = l;
}
dest = TeleTable['_']; // Start
for (int i = 0; i < tl; i += 2) {
c1 = t.charAt(i);
c2 = t.charAt(i + 1);
/* Input nX is allowed, but Xn is not */
if (c1 == 'X') {
errorMsg.append("Invalid position of X in data");
return false;
}
if (c2 == 'X') {
glyph = (c1 - '0') + 17;
count += glyph;
} else {
glyph = ((10 * (c1 - '0')) + (c2 - '0')) + 27;
count += glyph;
}
p.append(TeleTable[glyph]);
}
check_digit = 127 - (count % 127);
if (check_digit == 127) {
check_digit = 0;
}
p.append(TeleTable[check_digit]);
encodeInfo.append("Check Digit: ").append(check_digit).append("\n");
dest += p;
dest += TeleTable['z']; // Stop
readable = new StringBuilder(content);
pattern = new String[1];
pattern[0] = dest;
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
public enum tp_mode {
NORMAL, NUMERIC
}
}

View file

@ -0,0 +1,445 @@
package org.xbib.graphics.barcode;
import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
import org.xbib.graphics.barcode.util.AddOn;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.geom.Rectangle2D;
/**
* Implements UPC bar code symbology.
* According to BS EN 797:1996
* UPC-A requires an 11 digit article number. The check digit is calculated.
* UPC-E is a zero-compressed version of UPC-A developed for smaller packages.
* The code requires a 6 digit article number (digits 0-9). The check digit
* is calculated. Also supports Number System 1 encoding by entering a 7-digit
* article number stating with the digit 1. In addition EAN-2 and EAN-5 add-on
* symbols can be added using the + character followed by the add-on data.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Upc extends Symbol {
private boolean useAddOn;
;
private String addOnContent;
private Mode mode;
private boolean linkageFlag;
private String[] setAC = {
"3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312",
"1213", "3112"
};
private String[] setB = {
"1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131",
"3121", "2113"
};
private String[] UPCParity0 = {
"BBBAAA", "BBABAA", "BBAABA", "BBAAAB", "BABBAA", "BAABBA", "BAAABB",
"BABABA", "BABAAB", "BAABAB"
}; /* Number set for UPC-E symbol (EN Table 4) */
private String[] UPCParity1 = {
"AAABBB", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA",
"ABABAB", "ABABBA", "ABBABA"
}; /* Not covered by BS EN 797 */
public Upc() {
mode = Mode.UPCA;
linkageFlag = false;
}
public Mode getMode() {
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
public void setLinkageFlag() {
linkageFlag = true;
}
public void unsetLinkageFlag() {
linkageFlag = false;
}
@Override
public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) {
if (humanReadableLocation == TOP) {
throw new IllegalArgumentException("Cannot display human-readable text above UPC bar codes.");
} else {
super.setHumanReadableLocation(humanReadableLocation);
}
}
@Override
public boolean encode() {
boolean retval;
AddOn addOn = new AddOn();
String addOnData = "";
separateContent();
if (content.length() == 0) {
errorMsg.append("Missing UPC data");
retval = false;
} else {
if (mode == Mode.UPCA) {
retval = upca();
} else {
retval = upce();
}
}
if (useAddOn) {
addOnData = addOn.calcAddOn(addOnContent);
if (addOnData.length() == 0) {
errorMsg.append("Invalid Add-On data");
retval = false;
} else {
pattern[0] = pattern[0] + "9" + addOnData;
//add leading zeroes to add-on text
if (addOnContent.length() == 1) {
addOnContent = "0" + addOnContent;
}
if (addOnContent.length() == 3) {
addOnContent = "0" + addOnContent;
}
if (addOnContent.length() == 4) {
addOnContent = "0" + addOnContent;
}
}
}
if (retval) {
plotSymbol();
}
return retval;
}
private void separateContent() {
int splitPoint;
splitPoint = content.indexOf('+');
if (splitPoint != -1) {
// There is a '+' in the input data, use an add-on EAN2 or EAN5
useAddOn = true;
addOnContent = content.substring(splitPoint + 1);
content = content.substring(0, splitPoint);
}
}
private boolean upca() {
StringBuilder accumulator;
StringBuilder dest;
int i;
char check;
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (content.length() > 11) {
errorMsg.append("Input data too long");
return false;
}
accumulator = new StringBuilder();
for (i = content.length(); i < 11; i++) {
accumulator.append("0");
}
accumulator.append(content);
check = calcDigit(accumulator.toString());
accumulator.append(check);
dest = new StringBuilder("111");
for (i = 0; i < 12; i++) {
if (i == 6) {
dest.append("11111");
}
dest.append(setAC[Character.getNumericValue(accumulator.charAt(i))]);
}
dest.append("111");
encodeInfo.append("Check Digit: ").append(check).append("\n");
readable = accumulator;
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
return true;
}
private boolean upce() {
int i, num_system;
char emode, check;
StringBuilder source;
String parity;
StringBuilder dest;
char[] equivalent = new char[12];
StringBuilder equiv = new StringBuilder();
if (!(content.matches("[0-9]+"))) {
errorMsg.append("Invalid characters in input");
return false;
}
if (content.length() > 7) {
errorMsg.append("Input data too long");
return false;
}
source = new StringBuilder();
for (i = content.length(); i < 7; i++) {
source.append("0");
}
source.append(content);
/* Two number systems can be used - system 0 and system 1 */
switch (source.charAt(0)) {
case '0':
num_system = 0;
break;
case '1':
num_system = 1;
break;
default:
errorMsg.append("Invalid input data");
return false;
}
/* Expand the zero-compressed UPCE code to make a UPCA equivalent (EN Table 5) */
emode = source.charAt(6);
for (i = 0; i < 11; i++) {
equivalent[i] = '0';
}
equivalent[0] = source.charAt(0);
equivalent[1] = source.charAt(1);
equivalent[2] = source.charAt(2);
switch (emode) {
case '0':
case '1':
case '2':
equivalent[3] = emode;
equivalent[8] = source.charAt(3);
equivalent[9] = source.charAt(4);
equivalent[10] = source.charAt(5);
break;
case '3':
equivalent[3] = source.charAt(3);
equivalent[9] = source.charAt(4);
equivalent[10] = source.charAt(5);
if (((source.charAt(3) == '0') || (source.charAt(3) == '1'))
|| (source.charAt(3) == '2')) {
/* Note 1 - "X3 shall not be equal to 0, 1 or 2" */
errorMsg.append("Invalid UPC-E data");
return false;
}
break;
case '4':
equivalent[3] = source.charAt(3);
equivalent[4] = source.charAt(4);
equivalent[10] = source.charAt(5);
if (source.charAt(4) == '0') {
/* Note 2 - "X4 shall not be equal to 0" */
errorMsg.append("Invalid UPC-E data");
return false;
}
break;
case '5':
case '6':
case '7':
case '8':
case '9':
equivalent[3] = source.charAt(3);
equivalent[4] = source.charAt(4);
equivalent[5] = source.charAt(5);
equivalent[10] = emode;
if (source.charAt(5) == '0') {
/* Note 3 - "X5 shall not be equal to 0" */
errorMsg.append("Invalid UPC-E data");
return false;
}
break;
}
for (i = 0; i < 11; i++) {
equiv.append(equivalent[i]);
}
/* Get the check digit from the expanded UPCA code */
check = calcDigit(equiv.toString());
encodeInfo.append("Check Digit: ").append(check).append("\n");
/* Use the number system and check digit information to choose a parity scheme */
if (num_system == 1) {
parity = UPCParity1[check - '0'];
} else {
parity = UPCParity0[check - '0'];
}
/* Take all this information and make the barcode pattern */
/* start character */
dest = new StringBuilder("111");
for (i = 0; i <= 5; i++) {
switch (parity.charAt(i)) {
case 'A':
dest.append(setAC[source.charAt(i + 1) - '0']);
break;
case 'B':
dest.append(setB[source.charAt(i + 1) - '0']);
break;
}
}
/* stop character */
dest.append("111111");
readable = new StringBuilder(source.toString()).append(check);
pattern = new String[1];
pattern[0] = dest.toString();
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
return true;
}
private char calcDigit(String x) {
int count = 0;
int c, cdigit;
for (int i = 0; i < 11; i++) {
c = Character.getNumericValue(x.charAt(i));
if ((i % 2) == 0) {
c = c * 3;
}
count = count + c;
}
cdigit = 10 - (count % 10);
if (cdigit == 10) {
cdigit = 0;
}
return (char) (cdigit + '0');
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
boolean black;
int compositeOffset = 0;
int shortLongDiff = 5;
getRectangles().clear();
getTexts().clear();
black = true;
x = 0;
if (linkageFlag) {
compositeOffset = 6;
}
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
if (black) {
y = 0;
black = false;
w = pattern[0].charAt(xBlock) - '0';
h = defaultHeight;
/* Add extension to guide bars */
if (mode == Mode.UPCA) {
if ((x < 10) || (x > 84)) {
h += shortLongDiff;
}
if ((x > 45) && (x < 49)) {
h += shortLongDiff;
}
if (x > 95) {
// Drop add-on
h -= 8;
y = 8;
}
if (linkageFlag && (x == 0) || (x == 94)) {
h += 2;
y -= 2;
}
} else {
if ((x < 4) || (x > 45)) {
h += shortLongDiff;
}
if (x > 52) {
// Drop add-on
h -= 8;
y = 8;
}
if (linkageFlag && (x == 0) || (x == 50)) {
h += 2;
y -= 2;
}
}
Rectangle2D.Double rect = new Rectangle2D.Double(x + 6, y + compositeOffset, w, h);
getRectangles().add(rect);
if ((x + w + 12) > symbolWidth) {
symbolWidth = x + w + 12;
}
} else {
black = true;
}
x += pattern[0].charAt(xBlock) - '0';
}
if (linkageFlag) {
// Add separator for composite symbology
if (mode == Mode.UPCA) {
getRectangles().add(new Rectangle2D.Double(6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(94 + 6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
getRectangles().add(new Rectangle2D.Double(95 + 6, 2, 1, 2));
} else { // UPCE
getRectangles().add(new Rectangle2D.Double(0 + 6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(50 + 6, 0, 1, 2));
getRectangles().add(new Rectangle2D.Double(-1 + 6, 2, 1, 2));
getRectangles().add(new Rectangle2D.Double(51 + 6, 2, 1, 2));
}
}
symbolHeight = defaultHeight + 5;
/* Now add the text */
if (getHumanReadableLocation() != NONE) {
double baseline = getHeight() + fontSize - shortLongDiff + compositeOffset;
double addOnBaseline = 6.0 + compositeOffset;
if (mode == Mode.UPCA) {
getTexts().add(new TextBox(3, baseline, readable.substring(0, 1)));
getTexts().add(new TextBox(34, baseline, readable.substring(1, 6)));
getTexts().add(new TextBox(73, baseline, readable.substring(6, 11)));
getTexts().add(new TextBox(104, baseline, readable.substring(11, 12)));
if (useAddOn) {
if (addOnContent.length() == 2) {
getTexts().add(new TextBox(118, addOnBaseline, addOnContent));
} else {
getTexts().add(new TextBox(133, addOnBaseline, addOnContent));
}
}
} else { // UPCE
getTexts().add(new TextBox(3, baseline, readable.substring(0, 1)));
getTexts().add(new TextBox(30, baseline, readable.substring(1, 7)));
getTexts().add(new TextBox(61, baseline, readable.substring(7, 8)));
if (useAddOn) {
if (addOnContent.length() == 2) {
getTexts().add(new TextBox(75, addOnBaseline, addOnContent));
} else {
getTexts().add(new TextBox(90, addOnBaseline, addOnContent));
}
}
}
}
}
public enum Mode {
UPCA, UPCE
}
}

View file

@ -0,0 +1,451 @@
package org.xbib.graphics.barcode;
import static org.xbib.graphics.barcode.HumanReadableLocation.NONE;
import static org.xbib.graphics.barcode.HumanReadableLocation.TOP;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.geom.Rectangle2D;
import java.math.BigInteger;
/**
* USPS OneCode (also known as Intelligent Mail Barcode)
* According to USPS-B-3200F
* Specification at https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf
* OneCode is a fixed length (65-bar) symbol which combines routing and
* customer information in a single symbol. Input data consists of a 20 digit
* tracking code, followed by a dash (-), followed by a delivery point
* zip-code which can be 0, 5, 9 or 11 digits in length.
*/
public class UspsOneCode extends Symbol {
/* The following lookup tables were generated using the code in Appendix C */
private int[] byte_array = new int[13];
private int[] AppxD_I = { /* Appendix D Table 1 - 5 of 13 characters */
0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780,
0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, 0x1740,
0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0,
0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0,
0x007C, 0x07C0, 0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720,
0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0,
0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6, 0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0,
0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60,
0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360, 0x00DA, 0x0B60,
0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0,
0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0,
0x00F8, 0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710,
0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90,
0x0133, 0x1990, 0x0135, 0x1590, 0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90,
0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50,
0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159, 0x1350, 0x015A, 0x0B50,
0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0,
0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0,
0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30,
0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30,
0x019C, 0x0730, 0x01A3, 0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0,
0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0,
0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470, 0x01C6, 0x0C70, 0x01C9, 0x1270,
0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570,
0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8, 0x02F0,
0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08,
0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988,
0x0235, 0x1588, 0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788,
0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948,
0x0255, 0x1548, 0x0256, 0x0D48, 0x0259, 0x1348, 0x025A, 0x0B48, 0x025C, 0x0748,
0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8,
0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8, 0x0278, 0x03C8,
0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928,
0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728,
0x02A3, 0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8,
0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8,
0x02C3, 0x1868, 0x02C5, 0x1468, 0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68,
0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368,
0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307, 0x1C18, 0x030B, 0x1A18,
0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18,
0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498,
0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198,
0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458,
0x0346, 0x0C58, 0x0349, 0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158,
0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8,
0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38, 0x0389, 0x1238, 0x038A, 0x0A38,
0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8,
0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4, 0x0478,
0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04,
0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984,
0x0435, 0x1584, 0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784,
0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944,
0x0455, 0x1544, 0x0456, 0x0D44, 0x0459, 0x1344, 0x045A, 0x0B44, 0x045C, 0x0744,
0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4,
0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4, 0x0487, 0x1C24,
0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524,
0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4,
0x04A5, 0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4,
0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464,
0x04C6, 0x0C64, 0x04C9, 0x1264, 0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164,
0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14,
0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513, 0x1914, 0x0515, 0x1514,
0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894,
0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694,
0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454,
0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154,
0x0552, 0x0954, 0x0561, 0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434,
0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134,
0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4, 0x05C1, 0x1074, 0x05C2, 0x0874,
0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C,
0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C, 0x070C,
0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C,
0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C,
0x0646, 0x0C4C, 0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C,
0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C,
0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691, 0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC,
0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C,
0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C, 0x0712, 0x091C,
0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C,
0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702,
0x081E, 0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82,
0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82,
0x0847, 0x1C42, 0x084B, 0x1A42, 0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942,
0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2,
0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A, 0x0AC2, 0x0871, 0x11C2,
0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22,
0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22,
0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2,
0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62,
0x08C9, 0x1262, 0x08CA, 0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2,
0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912,
0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312, 0x091A, 0x0B12, 0x0923, 0x1892,
0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192,
0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949, 0x1252,
0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432,
0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2,
0x09C1, 0x1072, 0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A,
0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A,
0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26, 0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A,
0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A,
0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A, 0x0A86, 0x0C2A,
0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A,
0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A,
0x0B41, 0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606,
0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306,
0x0C23, 0x1886, 0x0C25, 0x1486, 0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186,
0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6,
0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91, 0x1126, 0x0CA1, 0x10A6,
0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116,
0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E,
0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E,
0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701,
0x1027, 0x1C81, 0x102B, 0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581,
0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941,
0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1, 0x1065, 0x14C1, 0x1069, 0x12C1,
0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921,
0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9, 0x12A1,
0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161,
0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511,
0x1119, 0x1311, 0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191,
0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431,
0x1189, 0x1231, 0x1207, 0x1C09, 0x120B, 0x1A09, 0x120D, 0x1609, 0x1213, 0x1909,
0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289,
0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429, 0x1303, 0x1819,
0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905,
0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825,
0x1503, 0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903,
0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952,
0x08E2, 0x064C, 0x0554, 0x04E4, 0x0358, 0x02E8, 0x01F0
};
private int[] AppxD_II = { /* Appendix D Table II - 2 of 13 characters */
0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00,
0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018, 0x0300,
0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180,
0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140,
0x0060, 0x00C0, 0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220,
0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210,
0x0201, 0x1008, 0x0202, 0x0808, 0x0204, 0x0408, 0x0401, 0x1004, 0x0402, 0x0804,
0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0
};
private int[] AppxD_IV = { /* Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */
67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97,
2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94, 44, 77, 112, 70, 100, 39, 30, 107,
15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84,
129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9, 17, 49, 124, 79, 64, 91, 42, 69, 53,
60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55,
5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83, 13, 73, 3
};
public UspsOneCode() {
this.defaultHeight = 8;
setHumanReadableLocation(HumanReadableLocation.NONE);
}
@Override
public boolean encode() {
StringBuilder zip = new StringBuilder();
String zip_adder;
StringBuilder tracker = new StringBuilder();
int i, j;
int length = content.length();
BigInteger accum;
BigInteger x_reg;
BigInteger mask;
int usps_crc;
int[] codeword = new int[10];
int[] characters = new int[10];
boolean[] bar_map = new boolean[130];
char c;
if (!content.matches("[0-9\u002D]+")) {
errorMsg.append("Invalid characters in input data");
return false;
}
if (length > 32) {
errorMsg.append("Input too long");
return false;
}
/* separate the tracking code from the routing code */
j = 0;
for (i = 0; i < length; i++) {
if (content.charAt(i) == '-') {
j = 1;
} else {
if (j == 0) {
/* reading tracker */
tracker.append(content.charAt(i));
} else {
/* reading zip code */
zip.append(content.charAt(i));
}
}
}
if (tracker.length() != 20) {
errorMsg.append("Invalid length tracking code");
return false;
}
if (zip.length() > 11) {
errorMsg.append("Invalid ZIP code");
return false;
}
/* *** Step 1 - Conversion of Data Fields into Binary Data *** */
/* Routing code first */
if (zip.length() > 0) {
x_reg = new BigInteger(zip.toString());
} else {
x_reg = new BigInteger("0");
}
/* add weight to routing code */
if (zip.length() > 9) {
zip_adder = "1000100001";
} else {
if (zip.length() > 5) {
zip_adder = "100001";
} else {
if (zip.length() > 0) {
zip_adder = "1";
} else {
zip_adder = "0";
}
}
}
accum = new BigInteger(zip_adder);
accum = accum.add(x_reg);
accum = accum.multiply(BigInteger.valueOf(10));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0))));
accum = accum.multiply(BigInteger.valueOf(5));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1))));
for (i = 2; i < tracker.length(); i++) {
accum = accum.multiply(BigInteger.valueOf(10));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i))));
}
/* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */
for (i = 0; i < 13; i++) {
mask = accum.shiftRight(96 - (8 * i));
mask = mask.and(new BigInteger("255"));
byte_array[i] = mask.intValue();
}
usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence();
/* *** Step 3 - Conversion from Binary Data to Codewords *** */
/* start with codeword J which is base 636 */
x_reg = accum.mod(BigInteger.valueOf(636));
codeword[9] = x_reg.intValue();
accum = accum.subtract(x_reg);
accum = accum.divide(BigInteger.valueOf(636));
for (i = 8; i >= 0; i--) {
x_reg = accum.mod(BigInteger.valueOf(1365));
codeword[i] = x_reg.intValue();
accum = accum.subtract(x_reg);
accum = accum.divide(BigInteger.valueOf(1365));
}
for (i = 0; i < 9; i++) {
if (codeword[i] == 1365) {
codeword[i] = 0;
}
}
/* *** Step 4 - Inserting Additional Information into Codewords *** */
codeword[9] = codeword[9] * 2;
if (usps_crc >= 1024) {
codeword[0] += 659;
}
encodeInfo.append("Codewords: ");
for (i = 0; i < 10; i++) {
encodeInfo.append(Integer.toString(codeword[i])).append(" ");
}
encodeInfo.append("\n");
/* *** Step 5 - Conversion from Codewords to Characters *** */
for (i = 0; i < 10; i++) {
if (codeword[i] < 1287) {
characters[i] = AppxD_I[codeword[i]];
} else {
characters[i] = AppxD_II[codeword[i] - 1287];
}
}
for (i = 0; i < 10; i++) {
if ((usps_crc & (1 << i)) != 0) {
characters[i] = 0x1FFF - characters[i];
}
}
/* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */
for (i = 0; i < 10; i++) {
for (j = 0; j < 13; j++) {
bar_map[AppxD_IV[(13 * i) + j] - 1] = (characters[i] & (1 << j)) != 0;
}
}
readable = new StringBuilder(content);
pattern = new String[1];
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
pattern[0] = "";
for (i = 0; i < 65; i++) {
c = 'T';
if (bar_map[i]) {
c = 'D';
}
if (bar_map[i + 65]) {
c = 'A';
}
if (bar_map[i] && bar_map[i + 65]) {
c = 'F';
}
pattern[0] += c;
}
encodeInfo.append("Encoding: ").append(pattern[0]).append("\n");
plotSymbol();
return true;
}
private int USPS_MSB_Math_CRC11GenerateFrameCheckSequence() {
int GeneratorPolynomial = 0x0F35;
int FrameCheckSequence = 0x07FF;
int Data;
int ByteIndex, Bit;
int ByteArrayPtr = 0;
/* Do most significant byte skipping the 2 most significant bits */
Data = byte_array[ByteArrayPtr] << 5;
ByteArrayPtr++;
for (Bit = 2; Bit < 8; Bit++) {
if (((FrameCheckSequence ^ Data) & 0x400) != 0)
FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial;
else
FrameCheckSequence = (FrameCheckSequence << 1);
FrameCheckSequence &= 0x7FF;
Data <<= 1;
}
/* Do rest of the bytes */
for (ByteIndex = 1; ByteIndex < 13; ByteIndex++) {
Data = byte_array[ByteArrayPtr] << 3;
ByteArrayPtr++;
for (Bit = 0; Bit < 8; Bit++) {
if (((FrameCheckSequence ^ Data) & 0x0400) != 0) {
FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial;
} else {
FrameCheckSequence = (FrameCheckSequence << 1);
}
FrameCheckSequence &= 0x7FF;
Data <<= 1;
}
}
return FrameCheckSequence;
}
@Override
protected void plotSymbol() {
int xBlock, shortHeight, longHeight;
double x, y, w, h;
getRectangles().clear();
getTexts().clear();
int baseY;
if (getHumanReadableLocation() == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
x = 0;
w = moduleWidth;
y = 0;
h = 0;
shortHeight = (int) (0.25 * defaultHeight);
longHeight = (int) (0.625 * defaultHeight);
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
switch (pattern[0].charAt(xBlock)) {
case 'A':
y = baseY;
h = longHeight;
break;
case 'D':
y = baseY + defaultHeight - longHeight;
h = longHeight;
break;
case 'F':
y = baseY;
h = defaultHeight;
break;
case 'T':
y = baseY + defaultHeight - longHeight;
h = shortHeight;
break;
}
Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
getRectangles().add(rect);
x += (2.43 * w);
}
symbolWidth = (int) Math.ceil(((pattern[0].length() - 1) * 2.43 * w) + w); // final bar doesn't need extra whitespace
symbolHeight = defaultHeight;
if (getHumanReadableLocation() != NONE && readable.length() > 0) {
double baseline;
if (getHumanReadableLocation() == TOP) {
baseline = fontSize;
} else {
baseline = getHeight() + fontSize;
}
double centerX = getWidth() / 2.0;
getTexts().add(new TextBox(centerX, baseline, readable.toString()));
}
}
}

View file

@ -0,0 +1,114 @@
package org.xbib.graphics.barcode;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.geom.Rectangle2D;
/**
* USPS Intelligent Mail Package Barcode (IMpb)<br>
* A linear barcode based on GS1-128. Includes additional data checks.
* Specification at https://ribbs.usps.gov/intelligentmail_package/documents/tech_guides/BarcodePackageIMSpec.pdf
*/
public class UspsPackage extends Symbol {
@Override
public boolean encode() {
StringBuilder hrt;
StringBuilder spacedHrt;
boolean fourTwenty = false;
int bracketCount = 0;
if (!(content.matches("[0-9\\[\\]]+"))) {
/* Input must be numeric only */
errorMsg.append("Invalid IMpd data");
return false;
}
if ((content.length() % 2) != 0) {
/* Input must be even length */
errorMsg.append("Invalid IMpd data");
return false;
}
Code128 code128 = new Code128();
code128.unsetCc();
code128.setDataType(DataType.GS1);
code128.setContent(content);
if (content.length() > 4) {
fourTwenty = ((content.charAt(1) == '4') && (content.charAt(2) == '2') &&
(content.charAt(3) == '0'));
}
hrt = new StringBuilder();
for (int i = 0; i < content.length(); i++) {
if (content.charAt(i) == '[') {
bracketCount++;
}
if (!(fourTwenty && bracketCount < 2)) {
if ((content.charAt(i) >= '0') && (content.charAt(i) <= '9')) {
hrt.append(content.charAt(i));
}
}
}
spacedHrt = new StringBuilder();
for (int i = 0; i < hrt.length(); i++) {
spacedHrt.append(hrt.charAt(i));
if (i % 4 == 3) {
spacedHrt.append(" ");
}
}
readable = new StringBuilder(spacedHrt.toString());
pattern = new String[1];
pattern[0] = code128.pattern[0];
rowCount = 1;
rowHeight = new int[1];
rowHeight[0] = -1;
plotSymbol();
return true;
}
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
boolean black;
int offset = 20;
int yoffset = 15;
String banner = "USPS TRACKING #";
getRectangles().clear();
getTexts().clear();
y = yoffset;
h = 0;
black = true;
x = 0;
for (xBlock = 0; xBlock < pattern[0].length(); xBlock++) {
w = pattern[0].charAt(xBlock) - '0';
if (black) {
if (rowHeight[0] == -1) {
h = defaultHeight;
} else {
h = rowHeight[0];
}
if (w != 0 && h != 0) {
Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h);
getRectangles().add(rect);
}
symbolWidth = x + w + (2 * offset);
}
black = !black;
x += w;
}
symbolHeight = h + (2 * yoffset);
// Add boundary bars
Rectangle2D.Double topBar = new Rectangle2D.Double(0, 0, symbolWidth, 2);
Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, symbolHeight - 2, symbolWidth, 2);
getRectangles().add(topBar);
getRectangles().add(bottomBar);
double centerX = getWidth() / 2.0;
getTexts().add(new TextBox(centerX, getHeight() - 6.0, readable.toString()));
getTexts().add(new TextBox(centerX, 12.0, banner));
}
}

View file

@ -0,0 +1,150 @@
package org.xbib.graphics.barcode.render;
import org.xbib.graphics.barcode.HumanReadableLocation;
import org.xbib.graphics.barcode.Symbol;
import org.xbib.graphics.barcode.util.Hexagon;
import org.xbib.graphics.barcode.util.TextBox;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Renders symbologies using the Java Graphics API.
*/
public class GraphicsRenderer {
/**
* The graphics to render to.
*/
private final Graphics2D g2d;
/**
* The scaling factor.
*/
private final double scalingFactor;
/**
* The paper (background) color.
*/
private final Color background;
/**
* The ink (foreground) color.
*/
private final Color foreground;
private final boolean antialias;
private final boolean transparentBackground;
/**
* Creates a new Java 2D renderer.
*
* @param g2d the graphics to render to
* @param scalingFactor the scaling factor to apply
* @param background the paper (background) color
* @param foreground the ink (foreground) color
* @param antialias if true give anti alias hint
*/
public GraphicsRenderer(Graphics2D g2d,
double scalingFactor,
Color background,
Color foreground,
boolean antialias,
boolean transparentBackground) {
this.g2d = g2d;
this.scalingFactor = scalingFactor;
this.background = background;
this.foreground = foreground;
this.antialias = antialias;
this.transparentBackground = transparentBackground;
}
public void render(Symbol symbol) {
RenderingHints oldRenderingHints = g2d.getRenderingHints();
Color oldColor = g2d.getColor();
Color oldBackground = g2d.getBackground();
if (antialias) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
double marginX = symbol.getQuietZoneHorizontal() * scalingFactor;
double marginY = symbol.getQuietZoneVertical() * scalingFactor;
g2d.setBackground(background);
if (!transparentBackground) {
g2d.setColor(background);
g2d.fill(g2d.getDeviceConfiguration().getBounds());
g2d.setColor(foreground);
}
for (Rectangle2D.Double rect : symbol.getRectangles()) {
double x = rect.x * scalingFactor + marginX;
double y = rect.y * scalingFactor + marginY;
double w = rect.width * scalingFactor;
double h = rect.height * scalingFactor;
Path2D path = new Path2D.Double();
path.moveTo(x, y);
path.lineTo(x + w, y);
path.lineTo(x + w, y + h);
path.lineTo(x, y + h);
path.closePath();
g2d.fill(path);
}
if (symbol.getHumanReadableLocation() != HumanReadableLocation.NONE) {
Map<TextAttribute, Object> attributes = new HashMap<>();
attributes.put(TextAttribute.TRACKING, 0);
Font f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * scalingFactor)).deriveFont(attributes);
Font oldFont = g2d.getFont();
g2d.setFont(f);
FontMetrics fm = g2d.getFontMetrics();
for (TextBox text : symbol.getTexts()) {
Rectangle2D bounds = fm.getStringBounds(text.text, g2d);
double x = (text.x * scalingFactor) - (bounds.getWidth() / 2) + marginX;
double y = (text.y * scalingFactor) + marginY;
g2d.drawString(text.text, (float) x, (float) y);
}
g2d.setFont(oldFont);
}
for (Hexagon hexagon : symbol.getHexagons()) {
Path2D path = new Path2D.Double();
path.moveTo(hexagon.pointX[0] * scalingFactor + marginX, hexagon.pointY[0] * scalingFactor + marginY);
for(int i = 1; i < 6; ++i) {
double x = hexagon.pointX[i] * scalingFactor + marginX;
double y = hexagon.pointY[i] * scalingFactor + marginY;
path.lineTo(x, y);
}
path.closePath();
g2d.fill(path);
}
List<Ellipse2D.Double> targets = symbol.getTarget();
for (int i = 0; i < targets.size(); i++) {
Ellipse2D.Double ellipse = targets.get(i);
double x = ellipse.x * scalingFactor + marginX;
double y = ellipse.y * scalingFactor + marginY;
double w = ellipse.width * scalingFactor;
double h = ellipse.height * scalingFactor;
if ((i & 1) == 0) {
g2d.setColor(foreground);
} else {
g2d.setColor(background);
}
g2d.fill(new Ellipse2D.Double(x, y, w, h));
}
g2d.setColor(oldColor);
g2d.setBackground(oldBackground);
g2d.setRenderingHints(oldRenderingHints);
}
public void close() {
g2d.dispose();
}
}

View file

@ -0,0 +1,112 @@
package org.xbib.graphics.barcode.util;
/**
* Encode Add-On barcodes from UPC/EAN.
*/
public class AddOn {
private String content;
private String dest;
private String[] EANsetA = {
"3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213",
"3112"
};
private String[] EANsetB = {
"1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121",
"2113"
};
private String[] EAN2Parity = {
"AA", "AB", "BA", "BB"
};
private String[] EAN5Parity = {
"BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA",
"ABAAB", "AABAB"
};
public String calcAddOn(String input) {
dest = "";
content = input;
if (!(content.matches("[0-9]{1,5}"))) {
return "";
}
if (content.length() > 2) {
ean5();
} else {
ean2();
}
return dest;
}
private void ean2() {
String parity;
String accumulator = "";
int i, code_value;
if (!(content.matches("[0-9]+?"))) {
return;
}
for (i = content.length(); i < 2; i++) {
accumulator += "0";
}
accumulator += content;
code_value = ((accumulator.charAt(0) - '0') * 10)
+ (accumulator.charAt(1) - '0');
parity = EAN2Parity[code_value % 4];
dest = "112"; /* Start */
for (i = 0; i < 2; i++) {
if ((parity.charAt(i) == 'B')) {
dest += EANsetB[Character.getNumericValue(accumulator.charAt(i))];
} else {
dest += EANsetA[Character.getNumericValue(accumulator.charAt(i))];
}
if (i != 1) { /* Glyph separator */
dest += "11";
}
}
}
private void ean5() {
String parity;
String accumulator = "";
int i, parity_sum;
if (!(content.matches("[0-9]+?"))) {
return;
}
for (i = content.length(); i < 5; i++) {
accumulator += "0";
}
accumulator += content;
parity_sum = 0;
for (i = 0; i < 5; i++) {
if ((i % 2) == 0) {
parity_sum += 3 * (accumulator.charAt(i) - '0');
} else {
parity_sum += 9 * (accumulator.charAt(i) - '0');
}
}
parity = EAN5Parity[parity_sum % 10];
dest = "112"; /* Start */
for (i = 0; i < 5; i++) {
if ((parity.charAt(i) == 'B')) {
dest += EANsetB[Character.getNumericValue(accumulator.charAt(i))];
} else {
dest += EANsetA[Character.getNumericValue(accumulator.charAt(i))];
}
if (i != 4) { /* Glyph separator */
dest += "11";
}
}
}
}

View file

@ -0,0 +1,23 @@
package org.xbib.graphics.barcode.util;
/**
* Calculate a set of points to make a hexagon.
*/
public class Hexagon {
private static final double INK_SPREAD = 1.25;
private static final double[] OFFSET_X = {0.0, 0.86, 0.86, 0.0, -0.86, -0.86};
private static final double[] OFFSET_Y = {1.0, 0.5, -0.5, -1.0, -0.5, 0.5};
public final double[] pointX = new double[6];
public final double[] pointY = new double[6];
public Hexagon(double centreX, double centreY) {
for (int i = 0; i < 6; i++) {
pointX[i] = centreX + (OFFSET_X[i] * INK_SPREAD);
pointY[i] = centreY + (OFFSET_Y[i] * INK_SPREAD);
}
}
}

View file

@ -0,0 +1,86 @@
package org.xbib.graphics.barcode.util;
/**
*/
public class ReedSolomon {
private int[] res;
private int logmod;
private int rlen;
private int[] logt;
private int[] alog;
private int[] rspoly;
public int getResult(int count) {
return res[count];
}
public void init_gf(int poly) {
int m, b, p, v;
// Find the top bit, and hence the symbol size
for (b = 1, m = 0; b <= poly; b <<= 1) {
m++;
}
b >>= 1;
m--;
// Calculate the log/alog tables
logmod = (1 << m) - 1;
logt = new int[logmod + 1];
alog = new int[logmod];
for (p = 1, v = 0; v < logmod; v++) {
alog[v] = p;
logt[p] = v;
p <<= 1;
if ((p & b) != 0) {
p ^= poly;
}
}
}
public void init_code(int nsym, int index) {
int i, k;
rspoly = new int[nsym + 1];
rlen = nsym;
rspoly[0] = 1;
for (i = 1; i <= nsym; i++) {
rspoly[i] = 1;
for (k = i - 1; k > 0; k--) {
if (rspoly[k] != 0) {
rspoly[k] = alog[(logt[rspoly[k]] + index) % logmod];
}
rspoly[k] ^= rspoly[k - 1];
}
rspoly[0] = alog[(logt[rspoly[0]] + index) % logmod];
index++;
}
}
public void encode(int len, int[] data) {
int i, k, m;
res = new int[rlen];
for (i = 0; i < rlen; i++) {
res[i] = 0;
}
for (i = 0; i < len; i++) {
m = res[rlen - 1] ^ data[i];
for (k = rlen - 1; k > 0; k--) {
if ((m != 0) && (rspoly[k] != 0)) {
res[k] = res[k - 1] ^ alog[(logt[m] + logt[rspoly[k]]) % logmod];
} else {
res[k] = res[k - 1];
}
}
if ((m != 0) && (rspoly[0] != 0)) {
res[0] = alog[(logt[m] + logt[rspoly[0]]) % logmod];
} else {
res[0] = 0;
}
}
}
}

View file

@ -0,0 +1,40 @@
package org.xbib.graphics.barcode.util;
/**
* A simple text item class.
*/
public class TextBox {
/**
* X position that the text should be centered on horizontally.
*/
public final double x;
/**
* Y position of the text baseline.
*/
public final double y;
/**
* Text value.
*/
public final String text;
/**
* Creates a new instance.
*
* @param x the X position that the text should be centered on horizontally
* @param y the Y position of the text baseline
* @param text the text value
*/
public TextBox(double x, double y, String text) {
this.x = x;
this.y = y;
this.text = text;
}
@Override
public String toString() {
return "TextBox[x=" + x + ", y=" + y + ", text=" + text + "]";
}
}

View file

@ -0,0 +1,19 @@
package org.xbib.graphics.barcode;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* {@link MaxiCode} tests that can't be run via the {@link SymbolTest}.
*/
public class MaxiCodeTest {
@Test
public void testHumanReadableHeight() {
MaxiCode maxicode = new MaxiCode();
maxicode.setMode(4);
maxicode.setContent("ABC");
assertEquals(0, maxicode.getHumanReadableHeight());
}
}

View file

@ -0,0 +1,253 @@
package org.xbib.graphics.barcode;
import static java.util.Collections.singletonList;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
import org.junit.platform.commons.util.CollectionUtils;
import org.junit.platform.commons.util.ReflectionUtils;
import org.junit.runners.Parameterized;
public class ParameterizedExtension implements TestTemplateInvocationContextProvider {
private final static ExtensionContext.Namespace PARAMETERS = ExtensionContext.Namespace.create(
ParameterizedExtension.class);
/**
* Indicate whether we can provide parameterized support.
* This requires the testClass to either have a static {@code @Parameters} method
* and correct {@code @Parameter} and their corresponding values
* or to have a constructor that could be injected.
*/
public boolean supportsTestTemplate(ExtensionContext context) {
return hasParametersMethod(context) && validInjectionMix(context);
}
private static boolean validInjectionMix(ExtensionContext context) {
List<Field> fields = parametersFields(context);
boolean hasParameterFields = !fields.isEmpty();
boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields);
boolean hasArgsConstructor = hasArgsConstructor(context);
if (hasArgsConstructor) {
return !hasParameterFields;
}
else {
return !hasParameterFields || hasCorrectParameterFields;
}
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
// grabbing the parent ensures the PARAMETERS are stored in the same store across multiple TestTemplates.
return context.getParent().flatMap(ParameterizedExtension::parameters).map(
o -> testTemplateContextsFromParameters(o, context)).orElse(Stream.empty());
}
private static boolean areParametersFormedCorrectly(List<Field> fields) {
List<Integer> parameterValues = parameterIndexes(fields);
List<Integer> duplicateIndexes = duplicatedIndexes(parameterValues);
boolean hasAllIndexes = indexRangeComplete(parameterValues);
return hasAllIndexes && duplicateIndexes.isEmpty();
}
private static List<Integer> parameterIndexes(List<Field> fields) {
// @formatter:off
return fields.stream()
.map(f -> f.getAnnotation(Parameterized.Parameter.class))
.map(Parameterized.Parameter::value)
.collect(toList());
// @formatter:on
}
private static List<Integer> duplicatedIndexes(List<Integer> parameterValues) {
// @formatter:off
return parameterValues.stream().collect(groupingBy(identity())).entrySet().stream()
.filter(e -> e.getValue().size() > 1)
.map(Map.Entry::getKey)
.collect(toList());
// @formatter:on
}
private static Boolean indexRangeComplete(List<Integer> parameterValues) {
// @formatter:off
return parameterValues.stream()
.max(Integer::compareTo)
.map(i -> parameterValues.containsAll(IntStream.range(0, i).boxed().collect(toList())))
.orElse(false);
// @formatter:on
}
private static Optional<Collection<Object[]>> parameters(ExtensionContext context) {
return context.getStore(PARAMETERS).getOrComputeIfAbsent("parameterMethod",
k -> new ParameterWrapper(callParameters(context)), ParameterWrapper.class).getValue();
}
private static Optional<Collection<Object[]>> callParameters(ExtensionContext context) {
// @formatter:off
return findParametersMethod(context)
.map(m -> ReflectionUtils.invokeMethod(m, null))
.map(ParameterizedExtension::convertParametersMethodReturnType);
// @formatter:on
}
private static boolean hasParametersMethod(ExtensionContext context) {
return findParametersMethod(context).isPresent();
}
private static Optional<Method> findParametersMethod(ExtensionContext extensionContext) {
// @formatter:off
return extensionContext.getTestClass()
.flatMap(ParameterizedExtension::ensureSingleParametersMethod)
.filter(ReflectionUtils::isPublic);
// @formatter:on
}
private static Optional<Method> ensureSingleParametersMethod(Class<?> testClass) {
return ReflectionUtils.findMethods(testClass,
m -> m.isAnnotationPresent(Parameterized.Parameters.class)).stream().findFirst();
}
private static Stream<TestTemplateInvocationContext> testTemplateContextsFromParameters(Collection<Object[]> o,
ExtensionContext context) {
List<Field> fields = parametersFields(context);
boolean hasParameterFields = !fields.isEmpty();
boolean hasCorrectParameterFields = areParametersFormedCorrectly(fields);
if (!hasParameterFields) {
return o.stream().map(ParameterizedExtension::parameterResolver);
}
else if (hasCorrectParameterFields) {
return o.stream().map(ParameterizedExtension::contextFactory);
}
return Stream.empty();
}
private static TestTemplateInvocationContext parameterResolver(Object[] objects) {
List<Extension> parameterResolvers = singletonList(new ParameterResolver() {
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
final Executable declaringExecutable = parameterContext.getDeclaringExecutable();
return declaringExecutable instanceof Constructor
&& declaringExecutable.getParameterCount() == objects.length;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return objects[parameterContext.getIndex()];
}
});
return templateWithExtensions(parameterResolvers);
}
private static TestTemplateInvocationContext contextFactory(Object[] parameters) {
return templateWithExtensions(singletonList(new InjectionExtension(parameters)));
}
private static class InjectionExtension implements TestInstancePostProcessor {
private final Object[] parameters;
public InjectionExtension(Object[] parameters) {
this.parameters = parameters;
}
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
List<Field> parameters = parametersFields(context);
if (!parameters.isEmpty() && parameters.size() != this.parameters.length) {
throw unMatchedAmountOfParametersException();
}
for (Field param : parameters) {
Parameterized.Parameter annotation = param.getAnnotation(Parameterized.Parameter.class);
int paramIndex = annotation.value();
param.set(testInstance, this.parameters[paramIndex]);
}
}
}
private static TestTemplateInvocationContext templateWithExtensions(List<Extension> extensions) {
return new TestTemplateInvocationContext() {
@Override
public List<Extension> getAdditionalExtensions() {
return extensions;
}
};
}
private static boolean hasArgsConstructor(ExtensionContext context) {
// @formatter:off
return context.getTestClass()
.map(ReflectionUtils::getDeclaredConstructor)
.filter(c -> c.getParameterCount() > 0)
.isPresent();
// @formatter:on
}
private static List<Field> parametersFields(ExtensionContext context) {
// @formatter:off
Stream<Field> fieldStream = context.getTestClass()
.map(Class::getDeclaredFields)
.map(Stream::of)
.orElse(Stream.empty());
// @formatter:on
return fieldStream.filter(f -> f.isAnnotationPresent(Parameterized.Parameter.class)).filter(
ReflectionUtils::isPublic).collect(toList());
}
private static ParameterResolutionException unMatchedAmountOfParametersException() {
return new ParameterResolutionException(
"The amount of parametersFields in the constructor doesn't match those in the provided parametersFields");
}
private static Collection<Object[]> convertParametersMethodReturnType(Object obj) {
return CollectionUtils.toStream(obj).map(o -> {
if (o instanceof Object[]) {
return (Object[]) o;
}
return new Object[] { o };
}).collect(toList());
}
private static class ParameterWrapper {
private final Optional<Collection<Object[]>> value;
public ParameterWrapper(Optional<Collection<Object[]>> value) {
this.value = value;
}
public Optional<Collection<Object[]>> getValue() {
return value;
}
}
}

View file

@ -0,0 +1,608 @@
package org.xbib.graphics.barcode;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.oned.CodaBarReader;
import com.google.zxing.oned.Code39Reader;
import com.google.zxing.oned.Code93Reader;
import com.google.zxing.oned.EAN13Reader;
import com.google.zxing.oned.EAN8Reader;
import com.google.zxing.oned.UPCAReader;
import com.google.zxing.oned.UPCEReader;
import com.google.zxing.pdf417.PDF417Reader;
import com.google.zxing.qrcode.QRCodeReader;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runners.Parameterized;
import org.reflections.Reflections;
import org.xbib.graphics.barcode.render.GraphicsRenderer;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
/**
* Scans the test resources for file-based bar code tests.
*
* Tests that verify successful behavior will contain the following sets of files:
*
* <pre>
* /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].properties (bar code initialization attributes)
* /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].codewords (expected intermediate coding of the bar code)
* /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].png (expected final rendering of the bar code)
* </pre>
*
* <p>
* Tests that verify error conditions will contain the following sets of files:
*
* <pre>
* /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].properties (bar code initialization attributes)
* /src/test/resources/uk/org/okapibarcode/backend/[symbol-name]/[test-name].error (expected error message)
* </pre>
*
* If a properties file is found with no matching expectation files, we assume that it was recently added to the test suite and
* that we need to generate suitable expectation files for it.
*
* A single properties file can contain multiple test configurations (separated by an empty line), as long as the expected output
* is the same for all of those tests.
*/
@ExtendWith(ParameterizedExtension.class)
public class SymbolTest {
/** The font used to render human-readable text when drawing the symbologies; allows for consistent results across operating systems. */
private static Font DEJA_VU_SANS;
/** The type of symbology being tested. */
private final Class< ? extends Symbol> symbolType;
/** The test configuration properties. */
private final Map< String, String > properties;
/** The file containing the expected intermediate coding of the bar code, if this test verifies successful behavior. */
private final File codewordsFile;
/** The file containing the expected final rendering of the bar code, if this test verifies successful behavior. */
private final File pngFile;
/** The file containing the expected error message, if this test verifies a failure. */
private final File errorFile;
/**
* Finds all test resources and returns the information that JUnit needs to dynamically create the corresponding test cases.
*
* @return the test data needed to dynamically create the test cases
* @throws IOException if there is an error reading a file
*/
@Parameterized.Parameters
public static List< Object[] > data() throws IOException {
String path = "/org/xbib/graphics/barcode/fonts/OkapiDejaVuSans.ttf";
try {
InputStream is = SymbolTest.class.getResourceAsStream(path);
DEJA_VU_SANS = Font.createFont(Font.TRUETYPE_FONT, is);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
boolean registered = ge.registerFont(DEJA_VU_SANS);
assertTrue(registered);
} catch (IOException | FontFormatException e) {
throw new IOException(e.getMessage(), e);
}
String backend = "org.xbib.graphics.barcode";
Reflections reflections = new Reflections(backend);
Set< Class< ? extends Symbol >> symbols = reflections.getSubTypesOf(Symbol.class);
List< Object[] > data = new ArrayList<>();
for (Class< ? extends Symbol > symbol : symbols) {
String symbolName = symbol.getSimpleName().toLowerCase();
String dir = "src/test/resources/" + backend.replace('.', '/') + "/" + symbolName;
for (File file : getPropertiesFiles(dir)) {
String fileBaseName = file.getName().replaceAll(".properties", "");
File codewordsFile = new File(file.getParentFile(), fileBaseName + ".codewords");
File pngFile = new File(file.getParentFile(), fileBaseName + ".png");
File errorFile = new File(file.getParentFile(), fileBaseName + ".error");
for (Map< String, String > properties : readProperties(file)) {
data.add(new Object[] { symbol, properties, codewordsFile, pngFile, errorFile });
}
}
}
return data;
}
/**
* Creates a new test.
*
* @param symbolType the type of symbol being tested
* @param properties the test configuration properties
* @param codewordsFile the file containing the expected intermediate coding of the bar code, if this test verifies successful behavior
* @param pngFile the file containing the expected final rendering of the bar code, if this test verifies successful behavior
* @param errorFile the file containing the expected error message, if this test verifies a failure
*/
public SymbolTest(Class< ? extends Symbol > symbolType,
Map< String, String > properties,
File codewordsFile,
File pngFile,
File errorFile) {
this.symbolType = symbolType;
this.properties = properties;
this.codewordsFile = codewordsFile;
this.pngFile = pngFile;
this.errorFile = errorFile;
}
/**
* Runs the test. If there are no expectation files yet, we generate them instead of checking against them.
*
* @throws Exception if any error occurs during the test
*/
@TestTemplate
public void test() throws Exception {
Symbol symbol = symbolType.getDeclaredConstructor().newInstance();
symbol.setFontName(DEJA_VU_SANS.getFontName());
try {
setProperties(symbol, properties);
} catch (InvocationTargetException e) {
symbol.errorMsg.append(e.getCause().getMessage());
}
if (codewordsFile.exists()) {
verifySuccess(symbol);
} else if (errorFile.exists()) {
verifyError(symbol);
}
if (!pngFile.exists()) {
generateExpectationFiles(symbol);
}
}
/**
* Verifies that the specified symbol was encoded and rendered in a way that matches expectations.
*
* @param symbol the symbol to check
* @throws IOException if there is any I/O error
* @throws ReaderException if ZXing has an issue decoding the barcode image
*/
private void verifySuccess(Symbol symbol) throws IOException, ReaderException {
if (symbol.errorMsg.length() > 0) {
fail("got error message: " + symbol.errorMsg);
}
List< String > expectedList = Files.readAllLines(codewordsFile.toPath(), StandardCharsets.UTF_8);
try {
// try to verify codewords
int[] actualCodewords = symbol.getCodewords();
assertEquals(expectedList.size(), actualCodewords.length);
for (int i = 0; i < actualCodewords.length; i++) {
int expected = getInt(expectedList.get(i));
int actual = actualCodewords[i];
assertEquals(expected, actual, "at codeword index " + i);
}
} catch (UnsupportedOperationException e) {
// codewords aren't supported, try to verify patterns
String[] actualPatterns = symbol.pattern;
if (actualPatterns != null) {
assertEquals(expectedList.size(), actualPatterns.length);
for (int i = 0; i < actualPatterns.length; i++) {
String expected = expectedList.get(i);
String actual = actualPatterns[i];
assertEquals(expected, actual, "at pattern index " + i);
}
}
}
// make sure the barcode images match
if (pngFile.exists()) {
BufferedImage expected = ImageIO.read(pngFile);
BufferedImage actual = draw(symbol, 10.0d);
if (expected != null && actual != null) {
assertEqualImage(pngFile.getName(), expected, actual);
}
// if possible, ensure an independent third party (ZXing) can read the generated barcode and agrees on what it represents
Reader zxingReader = findReader(symbol);
if (zxingReader != null && expected != null) {
LuminanceSource source = new BufferedImageLuminanceSource(expected);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Map<DecodeHintType, Boolean> hints = Collections.singletonMap(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
Result result = zxingReader.decode(bitmap, hints);
String zxingData = removeChecksum(result.getText(), symbol);
String ourData = removeStartStopChars(symbol.getContent(), symbol);
assertEquals(ourData, zxingData, "checking against ZXing results");
}
}
}
/**
* Returns a ZXing reader that can read the specified symbol.
*
* @param symbol the symbol to be read
* @return a ZXing reader that can read the specified symbol
*/
private static Reader findReader(Symbol symbol) {
if (symbol instanceof Code93) {
return new Code93Reader();
} else if (symbol instanceof Code3Of9) {
return new Code39Reader();
} else if (symbol instanceof Codabar) {
return new CodaBarReader();
} else if (symbol instanceof QrCode) {
return new QRCodeReader();
} else if (symbol instanceof Ean) {
Ean ean = (Ean) symbol;
if (ean.getMode() == Ean.Mode.EAN8) {
return new EAN8Reader();
} else {
return new EAN13Reader();
}
} else if (symbol instanceof Pdf417) {
Pdf417 pdf417 = (Pdf417) symbol;
if (pdf417.getMode() != Pdf417.Mode.MICRO) {
return new PDF417Reader();
}
} else if (symbol instanceof Upc) {
Upc upc = (Upc) symbol;
if (upc.getMode() == Upc.Mode.UPCA) {
return new UPCAReader();
} else {
return new UPCEReader();
}
}
// no corresponding ZXing reader exists, or it behaves badly so we don't use it for testing
return null;
}
/**
* Removes the checksum from the specified barcode content, according to the type of symbol that encoded the content.
*
* @param s the barcode content
* @param symbol the symbol which encoded the content
* @return the barcode content, without the checksum
*/
private static String removeChecksum(String s, Symbol symbol) {
if (symbol instanceof Ean || symbol instanceof Upc) {
return s.substring(0, s.length() - 1);
} else {
return s;
}
}
/**
* Removes the start/stop characters from the specified barcode content, according to the type of symbol that encoded the
* content.
*
* @param s the barcode content
* @param symbol the symbol which encoded the content
* @return the barcode content, without the start/stop characters
*/
private static String removeStartStopChars(String s, Symbol symbol) {
if (symbol instanceof Codabar) {
return s.substring(1, s.length() - 1);
} else {
return s;
}
}
/**
* Verifies that the specified symbol encountered the expected error during encoding.
*
* @param symbol the symbol to check
* @throws IOException if there is any I/O error
*/
private void verifyError(Symbol symbol) throws IOException {
String expectedError = Files.readAllLines(errorFile.toPath(), StandardCharsets.UTF_8).get(0);
assertTrue(symbol.errorMsg.toString().startsWith(expectedError));
}
/**
* Generates the expectation files for the specified symbol.
*
* @param symbol the symbol to generate expectation files for
* @throws IOException if there is any I/O error
*/
private void generateExpectationFiles(Symbol symbol) throws IOException {
if (symbol.errorMsg != null && symbol.errorMsg.length() > 0) {
generateErrorExpectationFile(symbol);
} else {
generateCodewordsExpectationFile(symbol);
generatePngExpectationFile(symbol);
}
}
/**
* Generates the error expectation file for the specified symbol.
*
* @param symbol the symbol to generate the error expectation file for
* @throws IOException if there is any I/O error
*/
private void generateErrorExpectationFile(Symbol symbol) throws IOException {
if (!errorFile.exists()) {
PrintWriter writer = new PrintWriter(errorFile);
writer.println(symbol.errorMsg);
writer.close();
}
}
/**
* Generates the codewords expectation file for the specified symbol.
*
* @param symbol the symbol to generate codewords for
* @throws IOException if there is any I/O error
*/
private void generateCodewordsExpectationFile(Symbol symbol) throws IOException {
if (!codewordsFile.exists()) {
PrintWriter writer = new PrintWriter(codewordsFile);
try {
int[] codewords = symbol.getCodewords();
for (int codeword : codewords) {
writer.println(codeword);
}
} catch (UnsupportedOperationException e) {
for (String pattern : symbol.pattern) {
writer.println(pattern);
}
}
writer.close();
}
}
/**
* Generates the image expectation file for the specified symbol.
*
* @param symbol the symbol to draw
* @throws IOException if there is any I/O error
*/
private void generatePngExpectationFile(Symbol symbol) throws IOException {
BufferedImage img = draw(symbol, 10.0d);
if (img != null) {
ImageIO.write(img, "png", pngFile);
}
}
/**
* Returns the integer contained in the specified string. If the string contains a tab character, it and everything after it
* is ignored.
*
* @param s the string to extract the integer from
* @return the integer contained in the specified string
*/
private static int getInt(String s) {
int i = s.indexOf('\t');
if (i != -1) {
s = s.substring(0, i);
}
return Integer.parseInt(s);
}
/**
* Draws the specified symbol and returns the resultant image.
*
* @param symbol the symbol to draw
* @return the resultant image
*/
private static BufferedImage draw(Symbol symbol, double scalingFactor) {
int width = (int) (symbol.getWidth() * scalingFactor);
int height = (int) (symbol.getHeight() * scalingFactor);
if (width > 0 && height > 0) {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2d = img.createGraphics();
g2d.setPaint(Color.WHITE);
g2d.fillRect(0, 0, width, height);
GraphicsRenderer renderer = new GraphicsRenderer(g2d, scalingFactor, Color.WHITE, Color.BLACK, true, true);
renderer.render(symbol);
g2d.dispose();
return img;
}
return null;
}
/**
* Initializes the specified symbol using the specified properties, where keys are attribute names and values are attribute
* values.
*
* @param symbol the symbol to initialize
* @param properties the attribute names and values to set
* @throws ReflectiveOperationException if there is any reflection error
*/
private static void setProperties(Symbol symbol, Map< String, String > properties) throws ReflectiveOperationException {
for (Map.Entry< String, String > entry : properties.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method setter = getMethod(symbol.getClass(), setterName);
invoke(symbol, setter, value);
}
}
/**
* Returns the method with the specified name in the specified class, or throws an exception if the specified method cannot be
* found.
*
* @param clazz the class to search in
* @param name the name of the method to search for
* @return the method with the specified name in the specified class
*/
private static Method getMethod(Class< ? > clazz, String name) {
for (Method method : clazz.getMethods()) {
if (method.getName().equals(name)) {
return method;
}
}
throw new IllegalArgumentException("Unable to find method: " + name);
}
/**
* Invokes the specified method on the specified object with the specified parameter.
*
* @param object the object to invoke the method on
* @param setter the method to invoke
* @param parameter the parameter to pass to the method
* @throws ReflectiveOperationException if there is any reflection error
* @throws IllegalArgumentException if the specified parameter is not valid
*/
@SuppressWarnings("unchecked")
private static < E extends Enum< E >> void invoke(Object object, Method setter, Object parameter)
throws ReflectiveOperationException, IllegalArgumentException {
Class< ? > paramType = setter.getParameterTypes()[0];
if (String.class.equals(paramType)) {
setter.invoke(object, parameter.toString());
} else if (boolean.class.equals(paramType)) {
setter.invoke(object, Boolean.valueOf(parameter.toString()));
} else if (int.class.equals(paramType)) {
setter.invoke(object, Integer.parseInt(parameter.toString()));
} else if (double.class.equals(paramType)) {
setter.invoke(object, Double.parseDouble(parameter.toString()));
} else if (Character.class.equals(paramType)) {
setter.invoke(object, parameter.toString().charAt(0));
} else if (paramType.isEnum()) {
Class< E > e = (Class< E >) paramType;
setter.invoke(object, Enum.valueOf(e, parameter.toString()));
} else {
throw new IllegalArgumentException("Unknown setter type: " + paramType);
}
}
/**
* Returns all .properties files in the specified directory, or an empty array if none are found.
*
* @param dir the directory to search in
* @return all .properties files in the specified directory, or an empty array if none are found
*/
private static File[] getPropertiesFiles(String dir) {
File[] files = new File(dir).listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".properties");
}
});
return Objects.requireNonNullElseGet(files, () -> new File[0]);
}
/**
* Verifies that the specified images match.
*
* @param expected the expected image to check against
* @param actual the actual image
*/
private static void assertEqualImage(String name, BufferedImage expected, BufferedImage actual) {
int w = expected.getWidth();
int h = expected.getHeight();
assertEquals(w, actual.getWidth(), "width");
assertEquals(h, actual.getHeight(), "height");
int[] expectedPixels = new int[w * h];
expected.getRGB(0, 0, w, h, expectedPixels, 0, w);
int[] actualPixels = new int[w * h];
actual.getRGB(0, 0, w, h, actualPixels, 0, w);
for (int i = 0; i < expectedPixels.length; i++) {
int expectedPixel = expectedPixels[i];
int actualPixel = actualPixels[i];
if (expectedPixel != actualPixel) {
int x = i % w;
int y = i / w;
fail(name + ": pixel mismatch at " + x + ", " + y + " " +
Integer.toHexString(expectedPixel) + " " + Integer.toHexString(actualPixel));
}
}
}
/**
* Extracts test configuration properties from the specified properties file. A single properties file can contain
* configuration properties for multiple tests.
*
* @param propertiesFile the properties file to read
* @return the test configuration properties in the specified file
* @throws IOException if there is an error reading the properties file
*/
private static List<Map< String, String>> readProperties(File propertiesFile) throws IOException {
String content;
try {
byte[] bytes = Files.readAllBytes(propertiesFile.toPath());
content = replacePlaceholders(decode(bytes, StandardCharsets.UTF_8));
} catch (CharacterCodingException e) {
throw new IOException("Invalid UTF-8 content in file " + propertiesFile.getAbsolutePath(), e);
}
String eol = System.lineSeparator();
String[] lines = content.split(eol);
List< Map< String, String > > allProperties = new ArrayList<>();
Map< String, String > properties = new LinkedHashMap<>();
for (String line : lines) {
if (line.isEmpty()) {
// an empty line signals the start of a new test configuration within this single file
if (!properties.isEmpty()) {
allProperties.add(properties);
properties = new LinkedHashMap<>();
}
} else if (!line.startsWith("#")) {
int index = line.indexOf('=');
if (index != -1) {
String name = line.substring(0, index);
String value = line.substring(index + 1);
properties.put(name, value);
} else {
throw new IOException(propertiesFile.getAbsolutePath() + ": found line without '=' character; unintentional newline?");
}
}
}
if (!properties.isEmpty()) {
allProperties.add(properties);
}
return allProperties;
}
/**
* Equivalent to {@link String#String(byte[], Charset)},
* except that encoding errors result in exceptions instead of
* silent character replacement.
*
* @param bytes the bytes to decode
* @param charset the character set use to decode the bytes
* @return the specified bytes, as a string
* @throws CharacterCodingException if there is an error decoding the specified bytes
*/
private static String decode(byte[] bytes, Charset charset) throws CharacterCodingException {
CharsetDecoder decoder = charset.newDecoder();
decoder.onMalformedInput(CodingErrorAction.REPORT);
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
CharBuffer chars = decoder.decode(ByteBuffer.wrap(bytes));
return chars.toString();
}
/**
* Replaces any special placeholders supported in test properties files with their raw values.
*
* @param s the string to check for placeholders
* @return the specified string, with placeholders replaced
*/
private static String replacePlaceholders(String s) {
return s.replaceAll("\\\\r", "\r") // "\r" -> CR
.replaceAll("\\\\n", "\n"); // "\n" -> LF
}
}

View file

@ -0,0 +1,59 @@
package org.xbib.graphics.barcode.output;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.barcode.Code3Of9;
import org.xbib.graphics.barcode.HumanReadableLocation;
import org.xbib.graphics.barcode.render.GraphicsRenderer;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.imageio.ImageIO;
public class Code39Test {
@Test
public void createBarcode1() throws IOException {
Code3Of9 code3Of9 = new Code3Of9();
code3Of9.setContent("20180123456");
code3Of9.setHumanReadableLocation(HumanReadableLocation.BOTTOM);
// pixels = (mm * dpi) / 25.4
double scalingFactor = (1.0d * 72.0d) / 25.4;
int width = (int) (code3Of9.getWidth() * scalingFactor);
int height = (int) (code3Of9.getHeight() * scalingFactor);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
GraphicsRenderer renderer = createRenderer(bufferedImage, scalingFactor);
renderer.render(code3Of9);
renderer.close();
OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode1.png"));
ImageIO.write(bufferedImage, "png", outputStream);
outputStream.close();
}
@Test
public void createBarcode2() throws IOException {
int width = 512;
int height = 150;
Code3Of9 code3Of9 = new Code3Of9();
//code3Of9.setContent("20180123456");
code3Of9.setContent("11111111111");
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
GraphicsRenderer renderer = createRenderer(bufferedImage, 3.0d);
renderer.render(code3Of9);
renderer.close();
OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode2.png"));
ImageIO.write(bufferedImage, "png", outputStream);
outputStream.close();
}
private GraphicsRenderer createRenderer(BufferedImage bufferedImage, double scalingFactor) {
Graphics2D g2d = bufferedImage.createGraphics();
g2d.setPaint(Color.WHITE);
g2d.setBackground(Color.BLACK);
g2d.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
return new GraphicsRenderer(g2d, scalingFactor, Color.WHITE, Color.BLACK, false, false);
}
}

View file

@ -0,0 +1,121 @@
package org.xbib.graphics.barcode.output;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.barcode.Code93;
import org.xbib.graphics.barcode.render.GraphicsRenderer;
import org.xbib.graphics.barcode.MaxiCode;
import org.xbib.graphics.barcode.Symbol;
import org.xbib.graphics.io.vector.eps.EPSGraphics2D;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Locale;
public class EPSRendererTest {
private Locale originalDefaultLocale;
@BeforeEach
public void before() {
// ensure use of correct decimal separator (period), regardless of default locale
originalDefaultLocale = Locale.getDefault();
Locale.setDefault(Locale.GERMANY);
}
@AfterEach
public void after() {
Locale.setDefault(originalDefaultLocale);
}
@Test
public void testCode93Basic() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.eps");
}
@Test
public void testCode93Margin() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.eps");
}
@Test
public void testCode93Magnification() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.eps");
}
@Test
public void testCode93Colors() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.eps");
}
@Test
public void testCode93CustomFont() throws IOException {
Code93 code93 = new Code93();
code93.setFontName("Arial");
code93.setFontSize(26);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.eps");
}
@Test
public void testMaxiCodeBasic() throws IOException {
MaxiCode maxicode = new MaxiCode();
maxicode.setMode(4);
maxicode.setContent("123456789");
test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.eps");
}
private void test(Symbol symbol,
double magnification,
Color paper,
Color ink,
int margin,
String expectationFile) throws IOException {
symbol.setQuietZoneHorizontal(margin);
symbol.setQuietZoneVertical(margin);
int width = (int) (symbol.getWidth() * magnification);
int height = (int) (symbol.getHeight() * magnification);
EPSGraphics2D epsGraphics2D = new EPSGraphics2D(0, 0, width, height);
GraphicsRenderer graphicsRenderer = new GraphicsRenderer(epsGraphics2D, magnification, paper, ink, false, false);
graphicsRenderer.render(symbol);
graphicsRenderer.close();
byte[] actualBytes = epsGraphics2D.getBytes();
String actual = new String(actualBytes, StandardCharsets.UTF_8);
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) {
bufferedWriter.write(actual);
}
BufferedReader actualReader = new BufferedReader(new StringReader(actual));
InputStream is = getClass().getResourceAsStream(expectationFile);
if (is != null) {
byte[] expectedBytes = new byte[is.available()];
is.read(expectedBytes);
String expected = new String(expectedBytes, StandardCharsets.UTF_8);
BufferedReader expectedReader = new BufferedReader(new StringReader(expected));
int line = 1;
String actualLine = actualReader.readLine();
String expectedLine = expectedReader.readLine();
while (actualLine != null && expectedLine != null) {
assertEquals(expectedLine, actualLine, "Line " + line);
actualLine = actualReader.readLine();
expectedLine = expectedReader.readLine();
line++;
}
}
}
}

View file

@ -0,0 +1,121 @@
package org.xbib.graphics.barcode.output;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.barcode.Code93;
import org.xbib.graphics.barcode.MaxiCode;
import org.xbib.graphics.barcode.Symbol;
import org.xbib.graphics.barcode.render.GraphicsRenderer;
import org.xbib.graphics.io.vector.pdf.PDFGraphics2D;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Locale;
public class PDFRendererTest {
private Locale originalDefaultLocale;
@BeforeEach
public void before() {
// ensure use of correct decimal separator (period), regardless of default locale
originalDefaultLocale = Locale.getDefault();
Locale.setDefault(Locale.GERMANY);
}
@AfterEach
public void after() {
Locale.setDefault(originalDefaultLocale);
}
@Test
public void testCode93Basic() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.pdf");
}
@Test
public void testCode93Margin() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.pdf");
}
@Test
public void testCode93Magnification() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.pdf");
}
@Test
public void testCode93Colors() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.pdf");
}
@Test
public void testCode93CustomFont() throws IOException {
Code93 code93 = new Code93();
code93.setFontName("Arial");
code93.setFontSize(26);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.pdf");
}
@Test
public void testMaxiCodeBasic() throws IOException {
MaxiCode maxicode = new MaxiCode();
maxicode.setMode(4);
maxicode.setContent("123456789");
test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.pdf");
}
private void test(Symbol symbol,
double magnification,
Color paper,
Color ink,
int margin,
String expectationFile) throws IOException {
symbol.setQuietZoneHorizontal(margin);
symbol.setQuietZoneVertical(margin);
int width = (int) (symbol.getWidth() * magnification);
int height = (int) (symbol.getHeight() * magnification);
PDFGraphics2D pdfGraphics2D = new PDFGraphics2D(0, 0, width, height);
GraphicsRenderer graphicsRenderer = new GraphicsRenderer(pdfGraphics2D, magnification, paper, ink, false, false);
graphicsRenderer.render(symbol);
graphicsRenderer.close();
byte[] actualBytes = pdfGraphics2D.getBytes();
String actual = new String(actualBytes, StandardCharsets.UTF_8);
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) {
bufferedWriter.write(actual);
}
BufferedReader actualReader = new BufferedReader(new StringReader(actual));
InputStream is = getClass().getResourceAsStream(expectationFile);
if (is != null) {
byte[] expectedBytes = new byte[is.available()];
is.read(expectedBytes);
String expected = new String(expectedBytes, StandardCharsets.UTF_8);
BufferedReader expectedReader = new BufferedReader(new StringReader(expected));
int line = 1;
String actualLine = actualReader.readLine();
String expectedLine = expectedReader.readLine();
while (actualLine != null && expectedLine != null) {
assertEquals(expectedLine, actualLine, "Line " + line);
actualLine = actualReader.readLine();
expectedLine = expectedReader.readLine();
line++;
}
}
}
}

View file

@ -0,0 +1,121 @@
package org.xbib.graphics.barcode.output;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.barcode.Code93;
import org.xbib.graphics.barcode.MaxiCode;
import org.xbib.graphics.barcode.Symbol;
import org.xbib.graphics.barcode.render.GraphicsRenderer;
import org.xbib.graphics.io.vector.svg.SVGGraphics2D;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Locale;
public class SvgRendererTest {
private Locale originalDefaultLocale;
@BeforeEach
public void before() {
// ensure use of correct decimal separator (period), regardless of default locale
originalDefaultLocale = Locale.getDefault();
Locale.setDefault(Locale.GERMANY);
}
@AfterEach
public void after() {
Locale.setDefault(originalDefaultLocale);
}
@Test
public void testCode93Basic() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-basic.svg");
}
@Test
public void testCode93Margin() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 20, "code93-margin-size-20.svg");
}
@Test
public void testCode93Magnification() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 2, Color.WHITE, Color.BLACK, 5, "code93-magnification-2.svg");
}
@Test
public void testCode93Colors() throws IOException {
Code93 code93 = new Code93();
code93.setContent("123456789");
test(code93, 1, Color.GREEN, Color.RED, 5, "code93-colors.svg");
}
@Test
public void testCode93CustomFont() throws IOException {
Code93 code93 = new Code93();
code93.setFontName("Arial");
code93.setFontSize(26);
code93.setContent("123456789");
test(code93, 1, Color.WHITE, Color.BLACK, 5, "code93-custom-font.svg");
}
@Test
public void testMaxiCodeBasic() throws IOException {
MaxiCode maxicode = new MaxiCode();
maxicode.setMode(4);
maxicode.setContent("123456789");
test(maxicode, 1.0, Color.WHITE, Color.BLACK, 5, "maxicode-basic.svg");
}
private void test(Symbol symbol,
double magnification,
Color paper,
Color ink,
int margin,
String expectationFile) throws IOException {
symbol.setQuietZoneHorizontal(margin);
symbol.setQuietZoneVertical(margin);
int width = (int) (symbol.getWidth() * magnification);
int height = (int) (symbol.getHeight() * magnification);
SVGGraphics2D svgGraphics2D = new SVGGraphics2D(0, 0, width, height);
GraphicsRenderer graphicsRenderer = new GraphicsRenderer(svgGraphics2D, magnification, paper, ink, false, false);
graphicsRenderer.render(symbol);
graphicsRenderer.close();
byte[] actualBytes = svgGraphics2D.getBytes();
String actual = new String(actualBytes, StandardCharsets.UTF_8);
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) {
bufferedWriter.write(actual);
}
BufferedReader actualReader = new BufferedReader(new StringReader(actual));
InputStream is = getClass().getResourceAsStream(expectationFile);
if (is != null) {
byte[] expectedBytes = new byte[is.available()];
is.read(expectedBytes);
String expected = new String(expectedBytes, StandardCharsets.UTF_8);
BufferedReader expectedReader = new BufferedReader(new StringReader(expected));
int line = 1;
String actualLine = actualReader.readLine();
String expectedLine = expectedReader.readLine();
while (actualLine != null && expectedLine != null) {
assertEquals(expectedLine, actualLine, "Line " + line);
actualLine = actualReader.readLine();
expectedLine = expectedReader.readLine();
line++;
}
}
}
}

View file

@ -0,0 +1,18 @@
12121121
11112211
11122111
11121121
21112121
22111111
21212111
11211211
11221111
21111211
21211121
12111121
11212121
12112111
12211111
21121111
11111221
11121221

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -0,0 +1 @@
content=B1-2:3.4$5/6+7890C

View file

@ -0,0 +1,18 @@
12121121
11112211
11122111
11121121
21112121
22111111
21212111
11211211
11221111
21111211
21211121
12111121
11212121
12112111
12211111
21121111
11111221
11121221

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -0,0 +1,2 @@
moduleWidthRatio=2.5
content=B1-2:3.4$5/6+7890C

View file

@ -0,0 +1 @@
Invalid characters in input

View file

@ -0,0 +1 @@
content=1234A

View file

@ -0,0 +1 @@
Invalid characters in input

View file

@ -0,0 +1 @@
content=A1234

View file

@ -0,0 +1,14 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,2 @@
checkDigitCount=1
content=01234-56789

View file

@ -0,0 +1,15 @@
112211 start
111121 0
211121 1
121121 2
221111 3
112121 4
112111 -
212111 5
122111 6
111221 7
211211 8
211111 9
112121 4 (check digit C)
221111 3 (check digit K)
112211 stop

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,4 @@
content=01234-56789
checkDigitCount=2
content=01234-56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,2 @@
humanReadableLocation=NONE
content=01234-56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -0,0 +1,2 @@
humanReadableLocation=TOP
content=01234-56789

View file

@ -0,0 +1 @@
Invalid characters in input

View file

@ -0,0 +1 @@
content=01234+56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

@ -0,0 +1,2 @@
moduleWidthRatio=2.5
content=01234-56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,2 @@
moduleWidthRatio=3
content=01234-56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,2 @@
startDelimiter=*
content=01234-56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,3 @@
startDelimiter=$
stopDelimiter=%
content=01234-56789

View file

@ -0,0 +1,15 @@
112211
111121
211121
121121
221111
112121
112111
212111
122111
111221
211211
211111
112121
221111
112211

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,2 @@
stopDelimiter=+
content=01234-56789

Some files were not shown because too many files have changed in this diff Show more