From 437db89b45baabf79292d741506c6e6acf2cf0c9 Mon Sep 17 00:00:00 2001 From: Michael Jahn Date: Mon, 18 Sep 2017 18:17:38 +0200 Subject: [PATCH] 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 --- .../com/google/zxing/oned/Code39Writer.java | 72 ++++++++++++++++++- .../zxing/oned/Code39WriterTestCase.java | 49 ++++++++++++- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/google/zxing/oned/Code39Writer.java b/core/src/main/java/com/google/zxing/oned/Code39Writer.java index d37663246..94a22ba15 100644 --- a/core/src/main/java/com/google/zxing/oned/Code39Writer.java +++ b/core/src/main/java/com/google/zxing/oned/Code39Writer.java @@ -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(); + } + } diff --git a/core/src/test/java/com/google/zxing/oned/Code39WriterTestCase.java b/core/src/test/java/com/google/zxing/oned/Code39WriterTestCase.java index b2511b448..bbf7de8f6 100644 --- a/core/src/test/java/com/google/zxing/oned/Code39WriterTestCase.java +++ b/core/src/test/java/com/google/zxing/oned/Code39WriterTestCase.java @@ -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)); } }