2 * Copyright 2008 ZXing authors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.google.zxing.qrcode.encoder;
19 import com.google.zxing.common.ByteMatrix;
20 import com.google.zxing.WriterException;
21 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
24 * @author satorux@google.com (Satoru Takabayashi) - creator
25 * @author dswitkin@google.com (Daniel Switkin) - ported from C++
27 public final class QRCode {
30 private static final int MIN_VERSION = 1;
31 private static final int MAX_VERSION = 40;
32 // For matrix width, see 7.3.1 of JISX0510:2004 (p.5).
33 private static final int MIN_MATRIX_WIDTH = 21; // Version 1
34 private static final int MAX_MATRIX_WIDTH = 177; // Version 40 (21 + 4 * (40 -1)).
35 public static final int NUM_MASK_PATTERNS = 8;
37 // See table 3 of JISX0510:2004 (p.16)
38 private static final int[][] NUM_BITS_TABLE = {
39 // NUMERIC ALPHANUMERIC 8BIT_BYTE KANJI
40 { 10, 9, 8, 8 }, // Version 1-9
41 { 12, 11, 16, 10 }, // Version 10-26
42 { 14, 13, 16, 12 }, // Version 27-40
46 private ErrorCorrectionLevel ecLevel;
48 private int matrixWidth;
49 private int maskPattern;
50 private int numTotalBytes;
51 private int numDataBytes;
52 private int numECBytes;
53 private int numRSBlocks;
54 private ByteMatrix matrix;
57 // They call encoding "mode". The modes are defined in 8.3 of JISX0510:2004 (p.14). It's unlikely
58 // (probably we will not support complicated modes) but if you add an item to this, please also
59 // add it to modeToString(), getModeCode(), getNumBitsForLength(), Encoder.appendBytes(), and
60 // Encoder.chooseMode().
62 // JAVAPORT: These used to be C++ enums, but the code evaluates them as integers, and requires
63 // negative values. I don't want to take the ParsedResultType approach of a class full of statics
64 // of that class's type. The best compromise here is integer constants.
67 public static final int MODE_UNDEFINED = -1;
68 public static final int MODE_NUMERIC = 0;
69 public static final int MODE_ALPHANUMERIC = 1;
70 public static final int MODE_8BIT_BYTE = 2;
71 public static final int MODE_KANJI = 3; // Shift_JIS
72 // The following modes are unimplemented.
77 public static final int NUM_MODES = 4;
80 mode = MODE_UNDEFINED;
92 // Mode of the QR Code.
93 public int getMode() {
97 // Error correction level of the QR Code.
98 public ErrorCorrectionLevel getECLevel() {
102 // Version of the QR Code. The bigger size, the bigger version.
103 public int getVersion() {
107 // ByteMatrix width of the QR Code.
108 public int getMatrixWidth() {
112 // Mask pattern of the QR Code.
113 public int getMaskPattern() {
117 // Number of total bytes in the QR Code.
118 public int getNumTotalBytes() {
119 return numTotalBytes;
122 // Number of data bytes in the QR Code.
123 public int getNumDataBytes() {
127 // Number of error correction bytes in the QR Code.
128 public int getNumECBytes() {
132 // Number of Reedsolomon blocks in the QR Code.
133 public int getNumRSBlocks() {
137 // ByteMatrix data of the QR Code.
138 public final ByteMatrix getMatrix() {
143 // Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
144 // call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
145 public int at(int x, int y) {
146 // The value must be zero or one.
147 int value = matrix.get(y, x);
148 if (!(value == 0 || value == 1)) {
149 // this is really like an assert... not sure what better exception to use?
150 throw new RuntimeException("Bad value");
155 // Checks all the member variables are set properly. Returns true on success. Otherwise, returns
157 public boolean isValid() {
159 // First check if all version are not uninitialized.
160 mode != MODE_UNDEFINED &&
165 numTotalBytes != -1 &&
166 numDataBytes != -1 &&
169 // Then check them in other ways..
170 isValidVersion(version) &&
172 isValidMatrixWidth(matrixWidth) &&
173 isValidMaskPattern(maskPattern) &&
174 numTotalBytes == numDataBytes + numECBytes &&
177 matrixWidth == matrix.width() &&
178 // See 7.3.1 of JISX0510:2004 (p.5).
179 matrixWidth == MIN_MATRIX_WIDTH + (version - 1) * 4 &&
180 matrix.width() == matrix.height()); // Must be square.
183 // Return debug String.
184 public String toString() {
185 StringBuffer result = new StringBuffer();
186 result.append("<<\n");
187 result.append(" mode: ");
188 result.append(modeToString(mode));
189 result.append("\n ecLevel: ");
190 result.append(ecLevel);
191 result.append("\n version: ");
192 result.append(version);
193 result.append("\n matrixWidth: ");
194 result.append(matrixWidth);
195 result.append("\n maskPattern: ");
196 result.append(maskPattern);
197 result.append("\n numTotalBytes: ");
198 result.append(numTotalBytes);
199 result.append("\n numDataBytes: ");
200 result.append(numDataBytes);
201 result.append("\n numECBytes: ");
202 result.append(numECBytes);
203 result.append("\n numRSBlocks: ");
204 result.append(numRSBlocks);
205 if (matrix == null) {
206 result.append("\n matrix: null\n");
208 result.append("\n matrix:\n");
209 result.append(matrix.toString());
211 result.append(">>\n");
212 return result.toString();
215 public void setMode(int value) {
219 public void setECLevel(ErrorCorrectionLevel value) {
223 public void setVersion(int value) {
227 public void setMatrixWidth(int value) {
231 public void setMaskPattern(int value) {
235 public void setNumTotalBytes(int value) {
236 numTotalBytes = value;
239 public void setNumDataBytes(int value) {
240 numDataBytes = value;
243 public void setNumECBytes(int value) {
247 public void setNumRSBlocks(int value) {
251 // This takes ownership of the 2D array.
252 public void setMatrix(ByteMatrix value) {
256 // Check if "version" is valid.
257 public static boolean isValidVersion(final int version) {
258 return version >= MIN_VERSION && version <= MAX_VERSION;
261 // Check if "mode" is valid.
262 public static boolean isValidMode(final int mode) {
263 return mode >= 0 && mode < NUM_MODES;
266 // Check if "width" is valid.
267 public static boolean isValidMatrixWidth(int width) {
268 return width >= MIN_MATRIX_WIDTH && width <= MAX_MATRIX_WIDTH;
271 // Check if "mask_pattern" is valid.
272 public static boolean isValidMaskPattern(int maskPattern) {
273 return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
276 // Convert "mode" to String for debugging.
277 public static String modeToString(int mode) {
279 case QRCode.MODE_UNDEFINED:
281 case QRCode.MODE_NUMERIC:
283 case QRCode.MODE_ALPHANUMERIC:
284 return "ALPHANUMERIC";
285 case QRCode.MODE_8BIT_BYTE:
287 case QRCode.MODE_KANJI:
295 // Return the code of mode. On error, return -1. The codes of modes are defined in the table 2 of
296 // JISX0510:2004 (p.16).
297 public static int getModeCode(final int mode) throws WriterException {
299 case QRCode.MODE_NUMERIC:
301 case QRCode.MODE_ALPHANUMERIC:
303 case QRCode.MODE_8BIT_BYTE:
305 case QRCode.MODE_KANJI:
308 throw new WriterException("Unknown mode: " + mode);
312 // Return the number of bits needed for representing the length info of QR Code with "version" and
313 // "mode". On error, return -1.
314 static int getNumBitsForLength(int version, int mode) {
315 if (!isValidVersion(version)) {
316 throw new IllegalArgumentException("Invalid version: " + version);
318 if (!isValidMode(mode)) {
319 throw new IllegalArgumentException("Invalid mode: " + mode);
321 if (version >= 1 && version <= 9) {
322 return NUM_BITS_TABLE[0][mode];
323 } else if (version >= 10 && version <= 26) {
324 return NUM_BITS_TABLE[1][mode];
325 } else if (version >= 27 && version <= 40) {
326 return NUM_BITS_TABLE[2][mode];
328 throw new IllegalArgumentException("Bad version: " + version);
331 // Return true if the all values in the matrix are binary numbers.
333 // JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in
334 // production. I'm leaving it because it may be useful for testing. It should be removed entirely
335 // if ByteMatrix is changed never to contain a -1.
337 private static boolean EverythingIsBinary(final ByteMatrix matrix) {
338 for (int y = 0; y < matrix.height(); ++y) {
339 for (int x = 0; x < matrix.width(); ++x) {
340 int value = matrix.get(y, x);
341 if (!(value == 0 || value == 1)) {
342 // Found non zero/one value.