add CodaBarWriter, and make separation from UPCEANWriter to OneDimensionalCodeWriter and UPCEANWriter which inherits OneDimensionalCodeWriter.


git-svn-id: https://zxing.googlecode.com/svn/trunk@1907 59b500cc-1b3d-0410-9834-0bbf25fbcc57
diff --git a/core/src/com/google/zxing/MultiFormatWriter.java b/core/src/com/google/zxing/MultiFormatWriter.java
index 4909a93..9b03d7f 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 @@
       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 3a311ef..eea8692 100644
--- a/core/src/com/google/zxing/oned/CodaBarReader.java
+++ b/core/src/com/google/zxing/oned/CodaBarReader.java
@@ -32,14 +32,14 @@
 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 @@
     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 0000000..a33267b
--- /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 0000000..61d1a0b
--- /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;
+
+/**
+ * <p>Encapsulates functionality and implementation that is common to one-dimensional barcodes.</p>
+ *
+ * @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 68fa4a0..145f3f0 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;
 
 /**
  * <p>Encapsulates functionality and implementation that is common to UPC and EAN families
  * of one-dimensional barcodes.</p>
  *
  * @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 0000000..fc9c6b7
--- /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));
+    }
+  }
+
+}