Code39 extended mode encoding support (#876)

* asterisk isn't a valid character which can be encoded with Code 39.
the following sample content results in an unreadable barcode: *123456789
because the asterisk is the start-stop character (special meaning, not valid in content)
the really encoded value is **123456789*. The barcode reader stops after reading the second asterisk
and throw the content away.

* remove wrong private modifier for ALPHABET_STRING

* cleanup comments

* add support for Code 39 extended mode encoding

* add support for Code 39 extended mode encoding

* clean ups

* clean ups
This commit is contained in:
Michael Jahn 2017-09-18 18:17:38 +02:00 committed by Sean Owen
parent 76d9dd5cf6
commit 437db89b45
2 changed files with 117 additions and 4 deletions

View file

@ -50,13 +50,23 @@ public final class Code39Writer extends OneDimensionalCodeWriter {
"Requested contents should be less than 80 digits long, but got " + length);
}
for (int i = 0; i < length; i++) {
int indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
if (indexInString < 0) {
contents = tryToConvertToExtendedMode(contents);
length = contents.length();
if (length > 80) {
throw new IllegalArgumentException(
"Requested contents should be less than 80 digits long, but got " + length + " (extended full ASCII mode)");
}
break;
}
}
int[] widths = new int[9];
int codeWidth = 24 + 1 + length;
for (int i = 0; i < length; i++) {
int indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
if (indexInString < 0) {
throw new IllegalArgumentException("Bad contents: " + contents);
}
toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths);
for (int width : widths) {
codeWidth += width;
@ -86,4 +96,60 @@ public final class Code39Writer extends OneDimensionalCodeWriter {
}
}
private static String tryToConvertToExtendedMode(String contents) {
int length = contents.length();
StringBuilder extendedContent = new StringBuilder();
for (int i = 0; i < length; i++) {
char character = contents.charAt(i);
switch (character) {
case '\u0000':
extendedContent.append("%U");
break;
case ' ':
case '-':
case '.':
extendedContent.append(character);
break;
case '@':
extendedContent.append("%V");
break;
case '`':
extendedContent.append("%W");
break;
default:
if (character > 0 && character < 27) {
extendedContent.append('$');
extendedContent.append((char) ('A' + (character - 1)));
} else if (character > 26 && character < ' ') {
extendedContent.append('%');
extendedContent.append((char) ('A' + (character - 27)));
} else if ((character > ' ' && character < '-') || character == '/' || character == ':') {
extendedContent.append('/');
extendedContent.append((char) ('A' + (character - 33)));
} else if (character > '/' && character < ':') {
extendedContent.append((char) ('0' + (character - 48)));
} else if (character > ':' && character < '@') {
extendedContent.append('%');
extendedContent.append((char) ('F' + (character - 59)));
} else if (character > '@' && character < '[') {
extendedContent.append((char) ('A' + (character - 65)));
} else if (character > 'Z' && character < '`') {
extendedContent.append('%');
extendedContent.append((char) ('K' + (character - 91)));
} else if (character > '`' && character < '{') {
extendedContent.append('+');
extendedContent.append((char) ('A' + (character - 97)));
} else if (character > 'z' && character < 128) {
extendedContent.append('%');
extendedContent.append((char) ('P' + (character - 123)));
} else {
throw new IllegalArgumentException("Requested content contains a non-encodable character: '" + contents.charAt(i) + "'");
}
break;
}
}
return extendedContent.toString();
}
}

View file

@ -34,11 +34,58 @@ public final class Code39WriterTestCase extends Assert {
"101101011001010101101100101100101010110100110101011011001101010101001011010110110010" +
"110101010011011010101010011011010110100101011010110010101101101100101010101001101011" +
"01101001101010101100110101010100101101101101001011010101100101101010010110110100000");
// extended mode blocks
doTest("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f",
"000001001011011010101001001001011001010101101001001001010110101001011010010010010101" +
"011010010110100100100101011011010010101001001001010101011001011010010010010101101011" +
"001010100100100101010110110010101001001001010101010011011010010010010101101010011010" +
"100100100101010110100110101001001001010101011001101010010010010101101010100110100100" +
"100101010110101001101001001001010110110101001010010010010101010110100110100100100101" +
"011010110100101001001001010101101101001010010010010101010101100110100100100101011010" +
"101100101001001001010101101011001010010010010101010110110010100100100101011001010101" +
"101001001001010100110101011010010010010101100110101010100100100101010010110101101001" +
"001001010110010110101010010010010101001101101010101001001001011010100101101010010010" +
"010101101001011010100100100101101101001010101001001001010101100101101010010010010110" +
"101100101010010110110100000");
doTest(" !\"#$%&'()*+,-./0123456789:;<=>?",
"000001001011011010100110101101010010010100101101010010110100100101001010110100101101" +
"001001010010110110100101010010010100101010110010110100100101001011010110010101001001" +
"010010101101100101010010010100101010100110110100100101001011010100110101001001010010" +
"101101001101010010010100101010110011010100100101001011010101001101001001010010101101" +
"010011010010101101101100101011010100100101001011010110100101010011011010110100101011" +
"010110010101101101100101010101001101011011010011010101011001101010101001011011011010" +
"010110101011001011010100100101001010011011010101010010010010101101100101010100100100" +
"101010100110110101001001001011010100110101010010010010101101001101010100100100101010" +
"11001101010010110110100000");
doTest("@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_",
"0000010010110110101010010010010100110101011011010100101101011010010110110110100101010" +
"101100101101101011001010101101100101010101001101101101010011010101101001101010101100" +
"110101101010100110101101010011011011010100101010110100110110101101001010110110100101" +
"010101100110110101011001010110101100101010110110010110010101011010011010101101100110" +
"101010100101101011011001011010101001101101010101001001001011010101001101010010010010" +
"101101010011010100100100101101101010010101001001001010101101001101010010010010110101" +
"101001010010110110100000");
doTest("`abcdefghijklmnopqrstuvwxyz{|}~",
"000001001011011010101001001001011001101010101001010010010110101001011010010100100101" +
"011010010110100101001001011011010010101001010010010101011001011010010100100101101011" +
"001010100101001001010110110010101001010010010101010011011010010100100101101010011010" +
"100101001001010110100110101001010010010101011001101010010100100101101010100110100101" +
"001001010110101001101001010010010110110101001010010100100101010110100110100101001001" +
"011010110100101001010010010101101101001010010100100101010101100110100101001001011010" +
"101100101001010010010101101011001010010100100101010110110010100101001001011001010101" +
"101001010010010100110101011010010100100101100110101010100101001001010010110101101001" +
"010010010110010110101010010100100101001101101010101001001001010110110100101010010010" +
"010101010110011010100100100101101010110010101001001001010110101100101010010010010101" +
"011011001010010110110100000");
}
private static void doTest(String input, CharSequence expected) throws WriterException {
BitMatrix result = new Code39Writer().encode(input, BarcodeFormat.CODE_39, 0, 0);
assertEquals(expected, BitMatrixTestCase.matrixToString(result));
assertEquals(input, expected, BitMatrixTestCase.matrixToString(result));
}
}