diff --git a/core/src/com/google/zxing/MultiFormatWriter.java b/core/src/com/google/zxing/MultiFormatWriter.java index 4909a93c9..9b03d7fb2 100644 --- a/core/src/com/google/zxing/MultiFormatWriter.java +++ b/core/src/com/google/zxing/MultiFormatWriter.java @@ -17,6 +17,7 @@ package com.google.zxing; import com.google.zxing.common.BitMatrix; +import com.google.zxing.oned.CodaBarWriter; import com.google.zxing.oned.Code128Writer; import com.google.zxing.oned.Code39Writer; import com.google.zxing.oned.EAN13Writer; @@ -62,6 +63,8 @@ public final class MultiFormatWriter implements Writer { writer = new ITFWriter(); } else if (format == BarcodeFormat.PDF_417) { writer = new PDF417Writer(); + } else if (format == BarcodeFormat.CODABAR) { + writer = new CodaBarWriter(); } else { throw new IllegalArgumentException("No encoder available for format " + format); } diff --git a/core/src/com/google/zxing/oned/CodaBarReader.java b/core/src/com/google/zxing/oned/CodaBarReader.java index 3a311ef0b..eea8692f6 100644 --- a/core/src/com/google/zxing/oned/CodaBarReader.java +++ b/core/src/com/google/zxing/oned/CodaBarReader.java @@ -32,14 +32,14 @@ import com.google.zxing.common.BitArray; public final class CodaBarReader extends OneDReader { private static final String ALPHABET_STRING = "0123456789-$:/.+ABCDTN"; - private static final char[] ALPHABET = ALPHABET_STRING.toCharArray(); + protected static final char[] ALPHABET = ALPHABET_STRING.toCharArray(); /** * These represent the encodings of characters, as patterns of wide and narrow bars. The 7 least-significant bits of * each int correspond to the pattern of wide and narrow, with 1s representing "wide" and 0s representing narrow. NOTE * : c is equal to the * pattern NOTE : d is equal to the e pattern */ - private static final int[] CHARACTER_ENCODINGS = { + protected static final int[] CHARACTER_ENCODINGS = { 0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9 0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD 0x01A, 0x029 //TN @@ -208,7 +208,7 @@ public final class CodaBarReader extends OneDReader { throw NotFoundException.getNotFoundInstance(); } - private static boolean arrayContains(char[] array, char key) { + protected static boolean arrayContains(char[] array, char key) { if (array != null) { for (int i = 0; i < array.length; i++) { if (array[i] == key) { diff --git a/core/src/com/google/zxing/oned/CodaBarWriter.java b/core/src/com/google/zxing/oned/CodaBarWriter.java new file mode 100644 index 000000000..a33267bb6 --- /dev/null +++ b/core/src/com/google/zxing/oned/CodaBarWriter.java @@ -0,0 +1,113 @@ +/* + * Copyright 2011 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.oned; + +import com.google.zxing.common.BitMatrix; + +/** + * This class renders CodaBar as {@link BitMatrix}. + * + * @author dsbnatut@gmail.com (Kazuki Nishiura) + */ +public class CodaBarWriter extends OneDimensionalCodeWriter { + + public CodaBarWriter() { + // Super constructor requires the sum of the left and right margin length. + // CodaBar spec requires a side margin to be more than ten times wider than narrow space. + // In this implementation, narrow space has a unit length, so 20 is required minimum. + super(20); + } + + /* + * @see com.google.zxing.oned.OneDimensionalCodeWriter#encode(java.lang.String) + */ + public byte[] encode(String contents) { + int resultLength; + int position = 0; + + // Verify input and calculate decoded length. + if (!CodaBarReader.arrayContains( + new char[]{'A', 'B', 'C', 'D'}, Character.toUpperCase(contents.charAt(0)))) { + throw new IllegalArgumentException( + "Codabar should start with one of the following: 'A', 'B', 'C' or 'D'"); + } + if (!CodaBarReader.arrayContains(new char[]{'T', 'N', '*', 'E'}, + Character.toUpperCase(contents.charAt(contents.length() - 1)))) { + throw new IllegalArgumentException( + "Codabar should end with one of the following: 'T', 'N', '*' or 'E'"); + } + // The start character and the end character are decoded to 10 length each. + resultLength = 20; + char[] charsWhichAreTenLengthEachAfterDecoded = new char[]{'/', ':', '+', '.'}; + for (int i = 1; i < contents.length()-1; i++) { + if (Character.isDigit(contents.charAt(i)) || contents.charAt(i) == '-' + || contents.charAt(i) == '$') { + resultLength += 9; + } else if(CodaBarReader.arrayContains( + charsWhichAreTenLengthEachAfterDecoded, contents.charAt(i))) { + resultLength += 10; + } else { + throw new IllegalArgumentException("Cannot encode : '" + contents.charAt(i) + "'"); + } + } + // A blank is placed between each character. + resultLength += contents.length() - 1; + + byte[] result = new byte[resultLength]; + for (int index = 0; index < contents.length(); index++) { + char c = Character.toUpperCase(contents.charAt(index)); + int code = 0; + if (index == contents.length() - 1){ + // Neither * nor E are in the CodaBarReader.ALPHABET. + // * is equal to the c pattern, and e is equal to the d pattern + if (c == '*') { + c = 'C'; + } else if (c == 'E') { + c = 'D'; + } + } + for (int i = 0; i < CodaBarReader.ALPHABET.length; i++) { + // Found any, because I checked above. + if (c == CodaBarReader.ALPHABET[i]) { + code = CodaBarReader.CHARACTER_ENCODINGS[i]; + break; + } + } + boolean isBlack = true; + byte color = 1; + int counter = 0; + int bit = 0; + while (bit < 7){ // A character consists of 7 digit. + result[position] = color; + position++; + if (((code >> (6-bit)) & 1) == 0 || counter == 1){ + color ^= 1; // Flip the color. + bit++; + counter = 0; + } else { + counter++; + } + } + if (index < contents.length() - 1) { + result[position] = 0; + position++; + } + } + return result; + } +} + diff --git a/core/src/com/google/zxing/oned/OneDimensionalCodeWriter.java b/core/src/com/google/zxing/oned/OneDimensionalCodeWriter.java new file mode 100644 index 000000000..61d1a0b1b --- /dev/null +++ b/core/src/com/google/zxing/oned/OneDimensionalCodeWriter.java @@ -0,0 +1,118 @@ +/* + * Copyright 2011 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.oned; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.Writer; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; + +import java.util.Hashtable; + +/** + *
Encapsulates functionality and implementation that is common to one-dimensional barcodes.
+ * + * @author dsbnatut@gmail.com (Kazuki Nishiura) + */ +public abstract class OneDimensionalCodeWriter implements Writer { + protected static int sidesMargin; + public OneDimensionalCodeWriter(int sidesMargin) { + this.sidesMargin = sidesMargin; + } + + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) + throws WriterException { + return encode(contents, format, width, height, null); + } + + /** + * Encode the contents following specified format. + * {@code width} and {@code height} are required size. This method may return bigger size + * {@code BitMatrix} when specified size is too small. The user can set both {@code width} and + * {@code height} to zero to get minimum size barcode. If negative value is set to {@code width} + * or {@code height}, {@code IllegalArgumentException} is thrown. + */ + public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, + Hashtable hints) throws WriterException { + if (contents == null || contents.length() == 0) { + throw new IllegalArgumentException("Found empty contents"); + } + + if (width < 0 || height < 0) { + throw new IllegalArgumentException("Negative size is not allowed. Input: " + + width + 'x' + height); + } + + byte[] code = encode(contents); + return renderResult(code, width, height); + } + + /** @return a byte array of horizontal pixels (0 = white, 1 = black) */ + private static BitMatrix renderResult(byte[] code, int width, int height) { + int inputWidth = code.length; + // Add quiet zone on both sides. + int fullWidth = inputWidth + sidesMargin; + int outputWidth = Math.max(width, fullWidth); + int outputHeight = Math.max(1, height); + + int multiple = outputWidth / fullWidth; + int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; + + BitMatrix output = new BitMatrix(outputWidth, outputHeight); + for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) { + if (code[inputX] == 1) { + output.setRegion(outputX, 0, multiple, outputHeight); + } + } + return output; + } + + + /** + * Appends the given pattern to the target array starting at pos. + * + * @param startColor + * starting color - 0 for white, 1 for black + * @return the number of elements added to target. + */ + protected static int appendPattern(byte[] target, int pos, int[] pattern, int startColor) { + if (startColor != 0 && startColor != 1) { + throw new IllegalArgumentException( + "startColor must be either 0 or 1, but got: " + startColor); + } + + byte color = (byte) startColor; + int numAdded = 0; + for (int i = 0; i < pattern.length; i++) { + for (int j = 0; j < pattern[i]; j++) { + target[pos] = color; + pos += 1; + numAdded += 1; + } + color ^= 1; // flip color after each segment + } + return numAdded; + } + + /** + * Encode the contents to byte array expression of one-dimensional barcode. + * Start code and end code should be included in result, and side margins should not be included. + * @return a byte array of horizontal pixels (0 = white, 1 = black) + * */ + public abstract byte[] encode(String contents); +} + diff --git a/core/src/com/google/zxing/oned/UPCEANWriter.java b/core/src/com/google/zxing/oned/UPCEANWriter.java index 68fa4a0cd..145f3f0ca 100644 --- a/core/src/com/google/zxing/oned/UPCEANWriter.java +++ b/core/src/com/google/zxing/oned/UPCEANWriter.java @@ -16,89 +16,17 @@ package com.google.zxing.oned; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.Writer; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; - -import java.util.Hashtable; /** *Encapsulates functionality and implementation that is common to UPC and EAN families * of one-dimensional barcodes.
* * @author aripollak@gmail.com (Ari Pollak) + * @author dsbnatut@gmail.com (Kazuki Nishiura) */ -public abstract class UPCEANWriter implements Writer { - - public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) - throws WriterException { - return encode(contents, format, width, height, null); +public abstract class UPCEANWriter extends OneDimensionalCodeWriter { + public UPCEANWriter() { + super(UPCEANReader.START_END_PATTERN.length << 1); } - - public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, - Hashtable hints) throws WriterException { - if (contents == null || contents.length() == 0) { - throw new IllegalArgumentException("Found empty contents"); - } - - if (width < 0 || height < 0) { - throw new IllegalArgumentException("Requested dimensions are too small: " - + width + 'x' + height); - } - - byte[] code = encode(contents); - return renderResult(code, width, height); - } - - /** @return a byte array of horizontal pixels (0 = white, 1 = black) */ - private static BitMatrix renderResult(byte[] code, int width, int height) { - int inputWidth = code.length; - // Add quiet zone on both sides - int fullWidth = inputWidth + (UPCEANReader.START_END_PATTERN.length << 1); - int outputWidth = Math.max(width, fullWidth); - int outputHeight = Math.max(1, height); - - int multiple = outputWidth / fullWidth; - int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; - - BitMatrix output = new BitMatrix(outputWidth, outputHeight); - for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) { - if (code[inputX] == 1) { - output.setRegion(outputX, 0, multiple, outputHeight); - } - } - return output; - } - - - /** - * Appends the given pattern to the target array starting at pos. - * - * @param startColor - * starting color - 0 for white, 1 for black - * @return the number of elements added to target. - */ - protected static int appendPattern(byte[] target, int pos, int[] pattern, int startColor) { - if (startColor != 0 && startColor != 1) { - throw new IllegalArgumentException( - "startColor must be either 0 or 1, but got: " + startColor); - } - - byte color = (byte) startColor; - int numAdded = 0; - for (int i = 0; i < pattern.length; i++) { - for (int j = 0; j < pattern[i]; j++) { - target[pos] = color; - pos += 1; - numAdded += 1; - } - color ^= 1; // flip color after each segment - } - return numAdded; - } - - /** @return a byte array of horizontal pixels (0 = white, 1 = black) */ - public abstract byte[] encode(String contents); - } + diff --git a/core/test/src/com/google/zxing/oned/CodaBarWriterTestCase.java b/core/test/src/com/google/zxing/oned/CodaBarWriterTestCase.java new file mode 100644 index 000000000..fc9c6b765 --- /dev/null +++ b/core/test/src/com/google/zxing/oned/CodaBarWriterTestCase.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.oned; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import org.junit.Assert; +import org.junit.Test; + +/** + * @author dsbnatut@gmail.com (Kazuki Nishiura) + */ +public final class CodaBarWriterTestCase extends Assert { + + @Test + public void testEncode() throws WriterException { + // 1001001011 0 110101001 0 101011001 0 110101001 0 101001101 0 110010101 0 1101101011 0 + // 1001001011 + String resultStr = "0000000000" + + "1001001011011010100101010110010110101001010100110101100101010110110101101001001011" + + "0000000000"; + BitMatrix result = new CodaBarWriter().encode( +"B515-3/N", BarcodeFormat.CODABAR, resultStr.length(), 0); + for (int i = 0; i < resultStr.length(); i++) { + assertEquals("Element " + i, resultStr.charAt(i) == '1', result.get(i, 0)); + } + } + +}