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.WriterException;
20 import com.google.zxing.common.ByteArray;
21 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
22 import com.google.zxing.qrcode.decoder.Mode;
23 import junit.framework.TestCase;
25 import java.io.UnsupportedEncodingException;
28 * @author satorux@google.com (Satoru Takabayashi) - creator
29 * @author mysen@google.com (Chris Mysen) - ported from C++
31 public final class EncoderTestCase extends TestCase {
33 public void testGetAlphanumericCode() {
34 // The first ten code points are numbers.
35 for (int i = 0; i < 10; ++i) {
36 assertEquals(i, Encoder.getAlphanumericCode('0' + i));
39 // The next 26 code points are capital alphabet letters.
40 for (int i = 10; i < 36; ++i) {
41 assertEquals(i, Encoder.getAlphanumericCode('A' + i - 10));
44 // Others are symbol letters
45 assertEquals(36, Encoder.getAlphanumericCode(' '));
46 assertEquals(37, Encoder.getAlphanumericCode('$'));
47 assertEquals(38, Encoder.getAlphanumericCode('%'));
48 assertEquals(39, Encoder.getAlphanumericCode('*'));
49 assertEquals(40, Encoder.getAlphanumericCode('+'));
50 assertEquals(41, Encoder.getAlphanumericCode('-'));
51 assertEquals(42, Encoder.getAlphanumericCode('.'));
52 assertEquals(43, Encoder.getAlphanumericCode('/'));
53 assertEquals(44, Encoder.getAlphanumericCode(':'));
55 // Should return -1 for other letters;
56 assertEquals(-1, Encoder.getAlphanumericCode('a'));
57 assertEquals(-1, Encoder.getAlphanumericCode('#'));
58 assertEquals(-1, Encoder.getAlphanumericCode('\0'));
61 public void testChooseMode() throws WriterException {
63 assertEquals(Mode.NUMERIC, Encoder.chooseMode("0"));
64 assertEquals(Mode.NUMERIC, Encoder.chooseMode("0123456789"));
66 assertEquals(Mode.ALPHANUMERIC, Encoder.chooseMode("A"));
67 assertEquals(Mode.ALPHANUMERIC,
68 Encoder.chooseMode("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"));
70 assertEquals(Mode.BYTE, Encoder.chooseMode("a"));
71 assertEquals(Mode.BYTE, Encoder.chooseMode("#"));
72 assertEquals(Mode.BYTE, Encoder.chooseMode(""));
73 // Kanji mode. We used to use MODE_KANJI for these, but we stopped
74 // doing that as we cannot distinguish Shift_JIS from other encodings
75 // from data bytes alone. See also comments in qrcode_encoder.h.
77 // AIUE in Hiragana in Shift_JIS
78 assertEquals(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[] {0x8,0xa,0x8,0xa,0x8,0xa,0x8,(byte)0xa6})));
80 // Nihon in Kanji in Shift_JIS.
81 assertEquals(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[] {0x9,0xf,0x9,0x7b})));
83 // Sou-Utsu-Byou in Kanji in Shift_JIS.
84 assertEquals(Mode.BYTE, Encoder.chooseMode(shiftJISString(new byte[] {0xe,0x4,0x9,0x5,0x9,0x61})));
87 public void testEncode() throws WriterException {
88 QRCode qrCode = new QRCode();
89 Encoder.encode("ABCDEF", ErrorCorrectionLevel.H, qrCode);
90 // The following is a valid QR Code that can be read by cell phones.
93 " mode: ALPHANUMERIC\n" +
96 " matrixWidth: 21\n" +
98 " numTotalBytes: 26\n" +
99 " numDataBytes: 9\n" +
100 " numECBytes: 17\n" +
101 " numRSBlocks: 1\n" +
103 " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n" +
104 " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n" +
105 " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n" +
106 " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n" +
107 " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n" +
108 " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n" +
109 " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" +
110 " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n" +
111 " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n" +
112 " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n" +
113 " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n" +
114 " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n" +
115 " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n" +
116 " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n" +
117 " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n" +
118 " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n" +
119 " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n" +
120 " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n" +
121 " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n" +
122 " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n" +
123 " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n" +
125 assertEquals(expected, qrCode.toString());
128 public void testAppendModeInfo() throws WriterException {
129 BitVector bits = new BitVector();
130 Encoder.appendModeInfo(Mode.NUMERIC, bits);
131 assertEquals("0001", bits.toString());
134 public void testAppendLengthInfo() throws WriterException {
136 BitVector bits = new BitVector();
137 Encoder.appendLengthInfo(1, // 1 letter (1/1).
141 assertEquals("0000000001", bits.toString()); // 10 bits.
144 BitVector bits = new BitVector();
145 Encoder.appendLengthInfo(2, // 2 letters (2/1).
149 assertEquals("00000000010", bits.toString()); // 11 bits.
152 BitVector bits = new BitVector();
153 Encoder.appendLengthInfo(255, // 255 letter (255/1).
157 assertEquals("0000000011111111", bits.toString()); // 16 bits.
160 BitVector bits = new BitVector();
161 Encoder.appendLengthInfo(512, // 512 letters (1024/2).
165 assertEquals("001000000000", bits.toString()); // 12 bits.
169 public void testAppendBytes() throws WriterException {
171 // Should use appendNumericBytes.
172 // 1 = 01 = 0001 in 4 bits.
173 BitVector bits = new BitVector();
174 Encoder.appendBytes("1", Mode.NUMERIC, bits);
175 assertEquals("0001" , bits.toString());
178 // Should use appendAlphanumericBytes.
179 // A = 10 = 0xa = 001010 in 6 bits
180 BitVector bits = new BitVector();
181 Encoder.appendBytes("A", Mode.ALPHANUMERIC, bits);
182 assertEquals("001010" , bits.toString());
183 // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC.
185 Encoder.appendBytes("a", Mode.ALPHANUMERIC, bits);
186 } catch (WriterException we) {
191 // Should use append8BitBytes.
193 BitVector bits = new BitVector();
194 Encoder.appendBytes("abc", Mode.BYTE, bits);
195 assertEquals("011000010110001001100011", bits.toString());
196 // Anything can be encoded in QRCode.MODE_8BIT_BYTE.
197 Encoder.appendBytes("\0", Mode.BYTE, bits);
200 // Should use appendKanjiBytes.
202 BitVector bits = new BitVector();
203 Encoder.appendBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), Mode.KANJI, bits);
204 assertEquals("0110110011111", bits.toString());
208 public void testTerminateBits() throws WriterException {
210 BitVector v = new BitVector();
211 Encoder.terminateBits(0, v);
212 assertEquals("", v.toString());
215 BitVector v = new BitVector();
216 Encoder.terminateBits(1, v);
217 assertEquals("00000000", v.toString());
220 BitVector v = new BitVector();
221 v.appendBits(0, 3); // Append 000
222 Encoder.terminateBits(1, v);
223 assertEquals("00000000", v.toString());
226 BitVector v = new BitVector();
227 v.appendBits(0, 5); // Append 00000
228 Encoder.terminateBits(1, v);
229 assertEquals("00000000", v.toString());
232 BitVector v = new BitVector();
233 v.appendBits(0, 8); // Append 00000000
234 Encoder.terminateBits(1, v);
235 assertEquals("00000000", v.toString());
238 BitVector v = new BitVector();
239 Encoder.terminateBits(2, v);
240 assertEquals("0000000011101100", v.toString());
243 BitVector v = new BitVector();
244 v.appendBits(0, 1); // Append 0
245 Encoder.terminateBits(3, v);
246 assertEquals("000000001110110000010001", v.toString());
250 public void testGetNumDataBytesAndNumECBytesForBlockID() throws WriterException {
251 int[] numDataBytes = new int[1];
252 int[] numEcBytes = new int[1];
254 Encoder.getNumDataBytesAndNumECBytesForBlockID(26, 9, 1, 0, numDataBytes, numEcBytes);
255 assertEquals(9, numDataBytes[0]);
256 assertEquals(17, numEcBytes[0]);
258 // Version 3-H. 2 blocks.
259 Encoder.getNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 0, numDataBytes, numEcBytes);
260 assertEquals(13, numDataBytes[0]);
261 assertEquals(22, numEcBytes[0]);
262 Encoder.getNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 1, numDataBytes, numEcBytes);
263 assertEquals(13, numDataBytes[0]);
264 assertEquals(22, numEcBytes[0]);
266 // Version 7-H. (4 + 1) blocks.
267 Encoder.getNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 0, numDataBytes, numEcBytes);
268 assertEquals(13, numDataBytes[0]);
269 assertEquals(26, numEcBytes[0]);
270 Encoder.getNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 4, numDataBytes, numEcBytes);
271 assertEquals(14, numDataBytes[0]);
272 assertEquals(26, numEcBytes[0]);
274 // Version 40-H. (20 + 61) blocks.
275 Encoder.getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 0, numDataBytes, numEcBytes);
276 assertEquals(15, numDataBytes[0]);
277 assertEquals(30, numEcBytes[0]);
278 Encoder.getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 20, numDataBytes, numEcBytes);
279 assertEquals(16, numDataBytes[0]);
280 assertEquals(30, numEcBytes[0]);
281 Encoder.getNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 80, numDataBytes, numEcBytes);
282 assertEquals(16, numDataBytes[0]);
283 assertEquals(30, numEcBytes[0]);
286 public void testInterleaveWithECBytes() throws WriterException {
288 byte[] dataBytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236};
289 BitVector in = new BitVector();
290 for (byte dataByte: dataBytes) {
291 in.appendBits(dataByte, 8);
293 BitVector out = new BitVector();
294 Encoder.interleaveWithECBytes(in, 26, 9, 1, out);
297 32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236,
298 // Error correction bytes.
299 42, (byte)159, 74, (byte)221, (byte)244, (byte)169, (byte)239, (byte)150, (byte)138, 70,
300 (byte)237, 85, (byte)224, 96, 74, (byte)219, 61,
302 assertEquals(expected.length, out.sizeInBytes());
303 byte[] outArray = out.getArray();
304 // Can't use Arrays.equals(), because outArray may be longer than out.sizeInBytes()
305 for (int x = 0; x < expected.length; x++) {
306 assertEquals(expected[x], outArray[x]);
309 // Numbers are from http://www.swetake.com/qr/qr8.html
312 67, 70, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166, (byte)182,
313 (byte)198, (byte)214, (byte)230, (byte)247, 7, 23, 39, 55, 71, 87, 103, 119, (byte)135,
314 (byte)151, (byte)166, 22, 38, 54, 70, 86, 102, 118, (byte)134, (byte)150, (byte)166,
315 (byte)182, (byte)198, (byte)214, (byte)230, (byte)247, 7, 23, 39, 55, 71, 87, 103, 119,
316 (byte)135, (byte)151, (byte)160, (byte)236, 17, (byte)236, 17, (byte)236, 17, (byte)236,
319 BitVector in = new BitVector();
320 for (byte dataByte: dataBytes) {
321 in.appendBits(dataByte, 8);
323 BitVector out = new BitVector();
324 Encoder.interleaveWithECBytes(in, 134, 62, 4, out);
327 67, (byte)230, 54, 55, 70, (byte)247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39,
328 118, 119, 70, 55, (byte)134, (byte)135, 86, 71, (byte)150, (byte)151, 102, 87, (byte)166,
329 (byte)160, 118, 103, (byte)182, (byte)236, (byte)134, 119, (byte)198, 17, (byte)150,
330 (byte)135, (byte)214, (byte)236, (byte)166, (byte)151, (byte)230, 17, (byte)182,
331 (byte)166, (byte)247, (byte)236, (byte)198, 22, 7, 17, (byte)214, 38, 23, (byte)236, 39,
333 // Error correction bytes.
334 (byte)175, (byte)155, (byte)245, (byte)236, 80, (byte)146, 56, 74, (byte)155, (byte)165,
335 (byte)133, (byte)142, 64, (byte)183, (byte)132, 13, (byte)178, 54, (byte)132, 108, 45,
336 113, 53, 50, (byte)214, 98, (byte)193, (byte)152, (byte)233, (byte)147, 50, 71, 65,
337 (byte)190, 82, 51, (byte)209, (byte)199, (byte)171, 54, 12, 112, 57, 113, (byte)155, 117,
338 (byte)211, (byte)164, 117, 30, (byte)158, (byte)225, 31, (byte)190, (byte)242, 38,
339 (byte)140, 61, (byte)179, (byte)154, (byte)214, (byte)138, (byte)147, 87, 27, 96, 77, 47,
340 (byte)187, 49, (byte)156, (byte)214,
342 assertEquals(expected.length, out.sizeInBytes());
343 byte[] outArray = out.getArray();
344 for (int x = 0; x < expected.length; x++) {
345 assertEquals(expected[x], outArray[x]);
350 public void testAppendNumericBytes() {
352 // 1 = 01 = 0001 in 4 bits.
353 BitVector bits = new BitVector();
354 Encoder.appendNumericBytes("1", bits);
355 assertEquals("0001" , bits.toString());
358 // 12 = 0xc = 0001100 in 7 bits.
359 BitVector bits = new BitVector();
360 Encoder.appendNumericBytes("12", bits);
361 assertEquals("0001100" , bits.toString());
364 // 123 = 0x7b = 0001111011 in 10 bits.
365 BitVector bits = new BitVector();
366 Encoder.appendNumericBytes("123", bits);
367 assertEquals("0001111011" , bits.toString());
370 // 1234 = "123" + "4" = 0001111011 + 0100
371 BitVector bits = new BitVector();
372 Encoder.appendNumericBytes("1234", bits);
373 assertEquals("0001111011" + "0100" , bits.toString());
377 BitVector bits = new BitVector();
378 Encoder.appendNumericBytes("", bits);
379 assertEquals("" , bits.toString());
383 public void testAppendAlphanumericBytes() throws WriterException {
385 // A = 10 = 0xa = 001010 in 6 bits
386 BitVector bits = new BitVector();
387 Encoder.appendAlphanumericBytes("A", bits);
388 assertEquals("001010" , bits.toString());
391 // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits
392 BitVector bits = new BitVector();
393 Encoder.appendAlphanumericBytes("AB", bits);
394 assertEquals("00111001101", bits.toString());
397 // ABC = "AB" + "C" = 00111001101 + 001100
398 BitVector bits = new BitVector();
399 Encoder.appendAlphanumericBytes("ABC", bits);
400 assertEquals("00111001101" + "001100" , bits.toString());
404 BitVector bits = new BitVector();
405 Encoder.appendAlphanumericBytes("", bits);
406 assertEquals("" , bits.toString());
410 BitVector bits = new BitVector();
412 Encoder.appendAlphanumericBytes("abc", bits);
413 } catch (WriterException we) {
419 public void testAppend8BitBytes() throws WriterException {
422 BitVector bits = new BitVector();
423 Encoder.append8BitBytes("abc", bits);
424 assertEquals("01100001" + "01100010" + "01100011", bits.toString());
428 BitVector bits = new BitVector();
429 Encoder.append8BitBytes("", bits);
430 assertEquals("", bits.toString());
434 // Numbers are from page 21 of JISX0510:2004
435 public void testAppendKanjiBytes() throws WriterException {
436 BitVector bits = new BitVector();
437 Encoder.appendKanjiBytes(shiftJISString(new byte[] {(byte)0x93,0x5f}), bits);
438 assertEquals("0110110011111", bits.toString());
439 Encoder.appendKanjiBytes(shiftJISString(new byte[] {(byte)0xe4,(byte)0xaa}), bits);
440 assertEquals("0110110011111" + "1101010101010", bits.toString());
443 // Numbers are from http://www.swetake.com/qr/qr3.html and
444 // http://www.swetake.com/qr/qr9.html
445 public void testGenerateECBytes() {
447 final byte[] dataBytes = {32, 65, (byte)205, 69, 41, (byte)220, 46, (byte)128, (byte)236};
448 ByteArray ecBytes = Encoder.generateECBytes(new ByteArray(dataBytes), 17);
449 final int[] expected = {
450 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61
452 assertEquals(expected.length, ecBytes.size());
453 for (int x = 0; x < expected.length; x++) {
454 assertEquals(expected[x], ecBytes.at(x));
458 final byte[] dataBytes = {67, 70, 22, 38, 54, 70, 86, 102, 118,
459 (byte)134, (byte)150, (byte)166, (byte)182, (byte)198, (byte)214};
460 ByteArray ecBytes = Encoder.generateECBytes(new ByteArray(dataBytes), 18);
461 final int[] expected = {
462 175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187
464 assertEquals(expected.length, ecBytes.size());
465 for (int x = 0; x < expected.length; x++) {
466 assertEquals(expected[x], ecBytes.at(x));
470 // High-order zero cofficient case.
471 final byte[] dataBytes = {32, 49, (byte)205, 69, 42, 20, 0, (byte)236, 17};
472 ByteArray ecBytes = Encoder.generateECBytes(new ByteArray(dataBytes), 17);
473 final int[] expected = {
474 0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213
476 assertEquals(expected.length, ecBytes.size());
477 for (int x = 0; x < expected.length; x++) {
478 assertEquals(expected[x], ecBytes.at(x));
483 public void testBugInBitVectorNumBytes() throws WriterException {
484 // There was a bug in BitVector.sizeInBytes() that caused it to return a
485 // smaller-by-one value (ex. 1465 instead of 1466) if the number of bits
486 // in the vector is not 8-bit aligned. In QRCodeEncoder::InitQRCode(),
487 // BitVector::sizeInBytes() is used for finding the smallest QR Code
488 // version that can fit the given data. Hence there were corner cases
489 // where we chose a wrong QR Code version that cannot fit the given
490 // data. Note that the issue did not occur with MODE_8BIT_BYTE, as the
491 // bits in the bit vector are always 8-bit aligned.
493 // Before the bug was fixed, the following test didn't pass, because:
495 // - MODE_NUMERIC is chosen as all bytes in the data are '0'
496 // - The 3518-byte numeric data needs 1466 bytes
497 // - 3518 / 3 * 10 + 7 = 11727 bits = 1465.875 bytes
498 // - 3 numeric bytes are encoded in 10 bits, hence the first
499 // 3516 bytes are encoded in 3516 / 3 * 10 = 11720 bits.
500 // - 2 numeric bytes can be encoded in 7 bits, hence the last
501 // 2 bytes are encoded in 7 bits.
502 // - The version 27 QR Code with the EC level L has 1468 bytes for data.
503 // - 1828 - 360 = 1468
504 // - In InitQRCode(), 3 bytes are reserved for a header. Hence 1465 bytes
505 // (1468 -3) are left for data.
506 // - Because of the bug in BitVector::sizeInBytes(), InitQRCode() determines
507 // the given data can fit in 1465 bytes, despite it needs 1466 bytes.
508 // - Hence QRCodeEncoder.encode() failed and returned false.
509 // - To be precise, it needs 11727 + 4 (getMode info) + 14 (length info) =
510 // 11745 bits = 1468.125 bytes are needed (i.e. cannot fit in 1468
512 StringBuilder builder = new StringBuilder(3518);
513 for (int x = 0; x < 3518; x++) {
516 QRCode qrCode = new QRCode();
517 Encoder.encode(builder.toString(), ErrorCorrectionLevel.L, qrCode);
520 private static String shiftJISString(byte[] bytes) throws WriterException {
522 return new String(bytes, "Shift_JIS");
523 } catch (UnsupportedEncodingException uee) {
524 throw new WriterException(uee.toString());