integration of subprojects barcode, chart, io-vector, layout-pdfbox, png
6
.gitignore
vendored
|
@ -1,13 +1,9 @@
|
|||
/data
|
||||
/work
|
||||
/logs
|
||||
/.idea
|
||||
/target
|
||||
.DS_Store
|
||||
/.settings
|
||||
/.classpath
|
||||
/.project
|
||||
/.gradle
|
||||
/build
|
||||
build
|
||||
*~
|
||||
/*.iml
|
|
@ -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
|
@ -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
|
@ -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')}"
|
||||
}
|
6
barcode/src/main/java/module-info.java
Normal 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;
|
||||
}
|
363
barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java
Executable 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}
|
||||
}
|
1989
barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java
Executable file
168
barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java
Executable 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;
|
||||
}
|
||||
}
|
155
barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java
Executable 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java
Executable 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);
|
||||
}
|
||||
}
|
852
barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java
Executable 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
|
||||
}
|
||||
}
|
208
barcode/src/main/java/org/xbib/graphics/barcode/Code11.java
Executable 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);
|
||||
}
|
||||
}
|
843
barcode/src/main/java/org/xbib/graphics/barcode/Code128.java
Executable 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}
|
||||
}
|
769
barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java
Executable 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
|
||||
}
|
||||
|
||||
}
|
493
barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java
Executable 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
|
||||
}
|
||||
}
|
103
barcode/src/main/java/org/xbib/graphics/barcode/Code32.java
Executable 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;
|
||||
}
|
||||
}
|
155
barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java
Executable 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
|
||||
}
|
||||
}
|
79
barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java
Executable 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
|
||||
}
|
||||
}
|
1345
barcode/src/main/java/org/xbib/graphics/barcode/Code49.java
Executable file
192
barcode/src/main/java/org/xbib/graphics/barcode/Code93.java
Executable 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);
|
||||
}
|
||||
}
|
1946
barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java
Executable file
2846
barcode/src/main/java/org/xbib/graphics/barcode/Composite.java
Executable file
698
barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java
Executable 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
|
||||
}
|
||||
}
|
1652
barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java
Executable file
476
barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java
Executable 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;
|
||||
}
|
||||
}
|
1636
barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java
Executable file
366
barcode/src/main/java/org/xbib/graphics/barcode/Ean.java
Executable 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
|
||||
}
|
||||
}
|
2015
barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java
Executable file
22
barcode/src/main/java/org/xbib/graphics/barcode/HumanReadableLocation.java
Executable 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
|
||||
}
|
138
barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java
Executable 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;
|
||||
}
|
||||
}
|
95
barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java
Executable 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;
|
||||
}
|
||||
}
|
61
barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java
Executable 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;
|
||||
}
|
||||
}
|
97
barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java
Executable 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;
|
||||
}
|
||||
}
|
890
barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java
Executable 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;
|
||||
}
|
||||
}
|
1593
barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java
Executable file
188
barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java
Executable 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 & 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
|
||||
}
|
||||
}
|
72
barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java
Executable 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;
|
||||
}
|
||||
}
|
1704
barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java
Executable file
66
barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java
Executable 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;
|
||||
}
|
||||
}
|
106
barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java
Executable 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;
|
||||
}
|
||||
}
|
69
barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java
Executable 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;
|
||||
}
|
||||
}
|
185
barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java
Executable 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
|
||||
}
|
||||
}
|
2008
barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java
Executable file
112
barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java
Executable 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;
|
||||
}
|
||||
}
|
1058
barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java
Executable file
177
barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java
Executable 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
|
||||
}
|
||||
}
|
445
barcode/src/main/java/org/xbib/graphics/barcode/Upc.java
Executable 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
|
||||
}
|
||||
}
|
451
barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java
Executable 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()));
|
||||
}
|
||||
}
|
||||
}
|
114
barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java
Executable 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));
|
||||
}
|
||||
}
|
150
barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java
Executable 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();
|
||||
}
|
||||
}
|
112
barcode/src/main/java/org/xbib/graphics/barcode/util/AddOn.java
Executable 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
barcode/src/main/java/org/xbib/graphics/barcode/util/Hexagon.java
Executable 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);
|
||||
}
|
||||
}
|
||||
}
|
86
barcode/src/main/java/org/xbib/graphics/barcode/util/ReedSolomon.java
Executable 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
barcode/src/main/java/org/xbib/graphics/barcode/util/TextBox.java
Executable 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 + "]";
|
||||
}
|
||||
}
|
19
barcode/src/test/java/org/xbib/graphics/barcode/MaxiCodeTest.java
Executable 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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
608
barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java
Executable 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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
121
barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java
Executable 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java
Executable 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java
Executable 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
barcode/src/test/resources/org/xbib/graphics/barcode/codabar/basic.codewords
Executable file
|
@ -0,0 +1,18 @@
|
|||
12121121
|
||||
11112211
|
||||
11122111
|
||||
11121121
|
||||
21112121
|
||||
22111111
|
||||
21212111
|
||||
11211211
|
||||
11221111
|
||||
21111211
|
||||
21211121
|
||||
12111121
|
||||
11212121
|
||||
12112111
|
||||
12211111
|
||||
21121111
|
||||
11111221
|
||||
11121221
|
After Width: | Height: | Size: 41 KiB |
|
@ -0,0 +1 @@
|
|||
content=B1-2:3.4$5/6+7890C
|
|
@ -0,0 +1,18 @@
|
|||
12121121
|
||||
11112211
|
||||
11122111
|
||||
11121121
|
||||
21112121
|
||||
22111111
|
||||
21212111
|
||||
11211211
|
||||
11221111
|
||||
21111211
|
||||
21211121
|
||||
12111121
|
||||
11212121
|
||||
12112111
|
||||
12211111
|
||||
21121111
|
||||
11111221
|
||||
11121221
|
After Width: | Height: | Size: 46 KiB |
|
@ -0,0 +1,2 @@
|
|||
moduleWidthRatio=2.5
|
||||
content=B1-2:3.4$5/6+7890C
|
|
@ -0,0 +1 @@
|
|||
Invalid characters in input
|
|
@ -0,0 +1 @@
|
|||
content=1234A
|
|
@ -0,0 +1 @@
|
|||
Invalid characters in input
|
|
@ -0,0 +1 @@
|
|||
content=A1234
|
|
@ -0,0 +1,14 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
112211
|
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,2 @@
|
|||
checkDigitCount=1
|
||||
content=01234-56789
|
15
barcode/src/test/resources/org/xbib/graphics/barcode/code11/basic.codewords
Executable 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
|
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,4 @@
|
|||
content=01234-56789
|
||||
|
||||
checkDigitCount=2
|
||||
content=01234-56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,2 @@
|
|||
humanReadableLocation=NONE
|
||||
content=01234-56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,2 @@
|
|||
humanReadableLocation=TOP
|
||||
content=01234-56789
|
|
@ -0,0 +1 @@
|
|||
Invalid characters in input
|
|
@ -0,0 +1 @@
|
|||
content=01234+56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 29 KiB |
|
@ -0,0 +1,2 @@
|
|||
moduleWidthRatio=2.5
|
||||
content=01234-56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,2 @@
|
|||
moduleWidthRatio=3
|
||||
content=01234-56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,2 @@
|
|||
startDelimiter=*
|
||||
content=01234-56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 28 KiB |
|
@ -0,0 +1,3 @@
|
|||
startDelimiter=$
|
||||
stopDelimiter=%
|
||||
content=01234-56789
|
|
@ -0,0 +1,15 @@
|
|||
112211
|
||||
111121
|
||||
211121
|
||||
121121
|
||||
221111
|
||||
112121
|
||||
112111
|
||||
212111
|
||||
122111
|
||||
111221
|
||||
211211
|
||||
211111
|
||||
112121
|
||||
221111
|
||||
112211
|
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,2 @@
|
|||
stopDelimiter=+
|
||||
content=01234-56789
|