1 /* 2 * Copyright 2011 ZXing authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.zxing.oned; 18 19 import com.google.zxing.BarcodeFormat; 20 import com.google.zxing.EncodeHintType; 21 import com.google.zxing.Writer; 22 import com.google.zxing.common.BitMatrix; 23 24 import java.util.Collection; 25 import java.util.Map; 26 import java.util.regex.Pattern; 27 28 /** 29 * <p>Encapsulates functionality and implementation that is common to one-dimensional barcodes.</p> 30 * 31 * @author dsbnatut@gmail.com (Kazuki Nishiura) 32 */ 33 public abstract class OneDimensionalCodeWriter implements Writer { 34 private static final Pattern NUMERIC = Pattern.compile("[0-9]+"); 35 36 /** 37 * Encode the contents to boolean array expression of one-dimensional barcode. 38 * Start code and end code should be included in result, and side margins should not be included. 39 * 40 * @param contents barcode contents to encode 41 * @return a {@code boolean[]} of horizontal pixels (false = white, true = black) 42 */ encode(String contents)43 public abstract boolean[] encode(String contents); 44 45 /** 46 * Can be overwritten if the encode requires to read the hints map. Otherwise it defaults to {@code encode}. 47 * @param contents barcode contents to encode 48 * @param hints encoding hints 49 * @return a {@code boolean[]} of horizontal pixels (false = white, true = black) 50 */ encode(String contents, Map<EncodeHintType,?> hints)51 public boolean[] encode(String contents, Map<EncodeHintType,?> hints) { 52 return encode(contents); 53 } 54 55 @Override encode(String contents, BarcodeFormat format, int width, int height)56 public final BitMatrix encode(String contents, BarcodeFormat format, int width, int height) { 57 return encode(contents, format, width, height, null); 58 } 59 60 /** 61 * Encode the contents following specified format. 62 * {@code width} and {@code height} are required size. This method may return bigger size 63 * {@code BitMatrix} when specified size is too small. The user can set both {@code width} and 64 * {@code height} to zero to get minimum size barcode. If negative value is set to {@code width} 65 * or {@code height}, {@code IllegalArgumentException} is thrown. 66 */ 67 @Override encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints)68 public BitMatrix encode(String contents, 69 BarcodeFormat format, 70 int width, 71 int height, 72 Map<EncodeHintType,?> hints) { 73 if (contents.isEmpty()) { 74 throw new IllegalArgumentException("Found empty contents"); 75 } 76 77 if (width < 0 || height < 0) { 78 throw new IllegalArgumentException("Negative size is not allowed. Input: " 79 + width + 'x' + height); 80 } 81 Collection<BarcodeFormat> supportedFormats = getSupportedWriteFormats(); 82 if (supportedFormats != null && !supportedFormats.contains(format)) { 83 throw new IllegalArgumentException("Can only encode " + supportedFormats + 84 ", but got " + format); 85 } 86 87 int sidesMargin = getDefaultMargin(); 88 if (hints != null && hints.containsKey(EncodeHintType.MARGIN)) { 89 sidesMargin = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString()); 90 } 91 92 boolean[] code = encode(contents, hints); 93 return renderResult(code, width, height, sidesMargin); 94 } 95 getSupportedWriteFormats()96 protected Collection<BarcodeFormat> getSupportedWriteFormats() { 97 return null; 98 } 99 100 /** 101 * @return a byte array of horizontal pixels (0 = white, 1 = black) 102 */ renderResult(boolean[] code, int width, int height, int sidesMargin)103 private static BitMatrix renderResult(boolean[] code, int width, int height, int sidesMargin) { 104 int inputWidth = code.length; 105 // Add quiet zone on both sides. 106 int fullWidth = inputWidth + sidesMargin; 107 int outputWidth = Math.max(width, fullWidth); 108 int outputHeight = Math.max(1, height); 109 110 int multiple = outputWidth / fullWidth; 111 int leftPadding = (outputWidth - (inputWidth * multiple)) / 2; 112 113 BitMatrix output = new BitMatrix(outputWidth, outputHeight); 114 for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) { 115 if (code[inputX]) { 116 output.setRegion(outputX, 0, multiple, outputHeight); 117 } 118 } 119 return output; 120 } 121 122 /** 123 * @param contents string to check for numeric characters 124 * @throws IllegalArgumentException if input contains characters other than digits 0-9. 125 */ checkNumeric(String contents)126 protected static void checkNumeric(String contents) { 127 if (!NUMERIC.matcher(contents).matches()) { 128 throw new IllegalArgumentException("Input should only contain digits 0-9"); 129 } 130 } 131 132 /** 133 * @param target encode black/white pattern into this array 134 * @param pos position to start encoding at in {@code target} 135 * @param pattern lengths of black/white runs to encode 136 * @param startColor starting color - false for white, true for black 137 * @return the number of elements added to target. 138 */ appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor)139 protected static int appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor) { 140 boolean color = startColor; 141 int numAdded = 0; 142 for (int len : pattern) { 143 for (int j = 0; j < len; j++) { 144 target[pos++] = color; 145 } 146 numAdded += len; 147 color = !color; // flip color after each segment 148 } 149 return numAdded; 150 } 151 getDefaultMargin()152 public int getDefaultMargin() { 153 // CodaBar spec requires a side margin to be more than ten times wider than narrow space. 154 // This seems like a decent idea for a default for all formats. 155 return 10; 156 } 157 } 158 159