1 /* 2 * Copyright 2008 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.datamatrix; 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 import com.google.zxing.datamatrix.encoder.DefaultPlacement; 24 import com.google.zxing.Dimension; 25 import com.google.zxing.datamatrix.encoder.ErrorCorrection; 26 import com.google.zxing.datamatrix.encoder.HighLevelEncoder; 27 import com.google.zxing.datamatrix.encoder.MinimalEncoder; 28 import com.google.zxing.datamatrix.encoder.SymbolInfo; 29 import com.google.zxing.datamatrix.encoder.SymbolShapeHint; 30 import com.google.zxing.qrcode.encoder.ByteMatrix; 31 32 import java.util.Map; 33 import java.nio.charset.Charset; 34 35 /** 36 * This object renders a Data Matrix code as a BitMatrix 2D array of greyscale values. 37 * 38 * @author dswitkin@google.com (Daniel Switkin) 39 * @author Guillaume Le Biller Added to zxing lib. 40 */ 41 public final class DataMatrixWriter implements Writer { 42 43 @Override encode(String contents, BarcodeFormat format, int width, int height)44 public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) { 45 return encode(contents, format, width, height, null); 46 } 47 48 @Override encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints)49 public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) { 50 51 if (contents.isEmpty()) { 52 throw new IllegalArgumentException("Found empty contents"); 53 } 54 55 if (format != BarcodeFormat.DATA_MATRIX) { 56 throw new IllegalArgumentException("Can only encode DATA_MATRIX, but got " + format); 57 } 58 59 if (width < 0 || height < 0) { 60 throw new IllegalArgumentException("Requested dimensions can't be negative: " + width + 'x' + height); 61 } 62 63 // Try to get force shape & min / max size 64 SymbolShapeHint shape = SymbolShapeHint.FORCE_NONE; 65 Dimension minSize = null; 66 Dimension maxSize = null; 67 if (hints != null) { 68 SymbolShapeHint requestedShape = (SymbolShapeHint) hints.get(EncodeHintType.DATA_MATRIX_SHAPE); 69 if (requestedShape != null) { 70 shape = requestedShape; 71 } 72 @SuppressWarnings("deprecation") 73 Dimension requestedMinSize = (Dimension) hints.get(EncodeHintType.MIN_SIZE); 74 if (requestedMinSize != null) { 75 minSize = requestedMinSize; 76 } 77 @SuppressWarnings("deprecation") 78 Dimension requestedMaxSize = (Dimension) hints.get(EncodeHintType.MAX_SIZE); 79 if (requestedMaxSize != null) { 80 maxSize = requestedMaxSize; 81 } 82 } 83 84 85 //1. step: Data encodation 86 String encoded; 87 88 boolean hasCompactionHint = hints != null && hints.containsKey(EncodeHintType.DATA_MATRIX_COMPACT) && 89 Boolean.parseBoolean(hints.get(EncodeHintType.DATA_MATRIX_COMPACT).toString()); 90 if (hasCompactionHint) { 91 92 boolean hasGS1FormatHint = hints.containsKey(EncodeHintType.GS1_FORMAT) && 93 Boolean.parseBoolean(hints.get(EncodeHintType.GS1_FORMAT).toString()); 94 95 Charset charset = null; 96 boolean hasEncodingHint = hints.containsKey(EncodeHintType.CHARACTER_SET); 97 if (hasEncodingHint) { 98 charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString()); 99 } 100 encoded = MinimalEncoder.encodeHighLevel(contents, charset, hasGS1FormatHint ? 0x1D : -1, shape); 101 } else { 102 boolean hasForceC40Hint = hints != null && hints.containsKey(EncodeHintType.FORCE_C40) && 103 Boolean.parseBoolean(hints.get(EncodeHintType.FORCE_C40).toString()); 104 encoded = HighLevelEncoder.encodeHighLevel(contents, shape, minSize, maxSize, hasForceC40Hint); 105 } 106 107 SymbolInfo symbolInfo = SymbolInfo.lookup(encoded.length(), shape, minSize, maxSize, true); 108 109 //2. step: ECC generation 110 String codewords = ErrorCorrection.encodeECC200(encoded, symbolInfo); 111 112 //3. step: Module placement in Matrix 113 DefaultPlacement placement = 114 new DefaultPlacement(codewords, symbolInfo.getSymbolDataWidth(), symbolInfo.getSymbolDataHeight()); 115 placement.place(); 116 117 //4. step: low-level encoding 118 return encodeLowLevel(placement, symbolInfo, width, height); 119 } 120 121 /** 122 * Encode the given symbol info to a bit matrix. 123 * 124 * @param placement The DataMatrix placement. 125 * @param symbolInfo The symbol info to encode. 126 * @return The bit matrix generated. 127 */ encodeLowLevel(DefaultPlacement placement, SymbolInfo symbolInfo, int width, int height)128 private static BitMatrix encodeLowLevel(DefaultPlacement placement, SymbolInfo symbolInfo, int width, int height) { 129 int symbolWidth = symbolInfo.getSymbolDataWidth(); 130 int symbolHeight = symbolInfo.getSymbolDataHeight(); 131 132 ByteMatrix matrix = new ByteMatrix(symbolInfo.getSymbolWidth(), symbolInfo.getSymbolHeight()); 133 134 int matrixY = 0; 135 136 for (int y = 0; y < symbolHeight; y++) { 137 // Fill the top edge with alternate 0 / 1 138 int matrixX; 139 if ((y % symbolInfo.matrixHeight) == 0) { 140 matrixX = 0; 141 for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) { 142 matrix.set(matrixX, matrixY, (x % 2) == 0); 143 matrixX++; 144 } 145 matrixY++; 146 } 147 matrixX = 0; 148 for (int x = 0; x < symbolWidth; x++) { 149 // Fill the right edge with full 1 150 if ((x % symbolInfo.matrixWidth) == 0) { 151 matrix.set(matrixX, matrixY, true); 152 matrixX++; 153 } 154 matrix.set(matrixX, matrixY, placement.getBit(x, y)); 155 matrixX++; 156 // Fill the right edge with alternate 0 / 1 157 if ((x % symbolInfo.matrixWidth) == symbolInfo.matrixWidth - 1) { 158 matrix.set(matrixX, matrixY, (y % 2) == 0); 159 matrixX++; 160 } 161 } 162 matrixY++; 163 // Fill the bottom edge with full 1 164 if ((y % symbolInfo.matrixHeight) == symbolInfo.matrixHeight - 1) { 165 matrixX = 0; 166 for (int x = 0; x < symbolInfo.getSymbolWidth(); x++) { 167 matrix.set(matrixX, matrixY, true); 168 matrixX++; 169 } 170 matrixY++; 171 } 172 } 173 174 return convertByteMatrixToBitMatrix(matrix, width, height); 175 } 176 177 /** 178 * Convert the ByteMatrix to BitMatrix. 179 * 180 * @param reqHeight The requested height of the image (in pixels) with the Datamatrix code 181 * @param reqWidth The requested width of the image (in pixels) with the Datamatrix code 182 * @param matrix The input matrix. 183 * @return The output matrix. 184 */ convertByteMatrixToBitMatrix(ByteMatrix matrix, int reqWidth, int reqHeight)185 private static BitMatrix convertByteMatrixToBitMatrix(ByteMatrix matrix, int reqWidth, int reqHeight) { 186 int matrixWidth = matrix.getWidth(); 187 int matrixHeight = matrix.getHeight(); 188 int outputWidth = Math.max(reqWidth, matrixWidth); 189 int outputHeight = Math.max(reqHeight, matrixHeight); 190 191 int multiple = Math.min(outputWidth / matrixWidth, outputHeight / matrixHeight); 192 193 int leftPadding = (outputWidth - (matrixWidth * multiple)) / 2 ; 194 int topPadding = (outputHeight - (matrixHeight * multiple)) / 2 ; 195 196 BitMatrix output; 197 198 // remove padding if requested width and height are too small 199 if (reqHeight < matrixHeight || reqWidth < matrixWidth) { 200 leftPadding = 0; 201 topPadding = 0; 202 output = new BitMatrix(matrixWidth, matrixHeight); 203 } else { 204 output = new BitMatrix(reqWidth, reqHeight); 205 } 206 207 output.clear(); 208 for (int inputY = 0, outputY = topPadding; inputY < matrixHeight; inputY++, outputY += multiple) { 209 // Write the contents of this row of the bytematrix 210 for (int inputX = 0, outputX = leftPadding; inputX < matrixWidth; inputX++, outputX += multiple) { 211 if (matrix.get(inputX, inputY) == 1) { 212 output.setRegion(outputX, outputY, multiple, multiple); 213 } 214 } 215 } 216 217 return output; 218 } 219 220 } 221