Added basic support for caller-specified character encoding
[zxing.git] / core / src / com / google / zxing / qrcode / encoder / Encoder.java
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.qrcode.encoder;
18
19 import com.google.zxing.WriterException;
20 import com.google.zxing.EncodeHintType;
21 import com.google.zxing.common.ByteArray;
22 import com.google.zxing.common.ByteMatrix;
23 import com.google.zxing.common.reedsolomon.GF256;
24 import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
25 import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
26 import com.google.zxing.qrcode.decoder.Mode;
27 import com.google.zxing.qrcode.decoder.Version;
28
29 import java.util.Vector;
30 import java.util.Hashtable;
31 import java.io.UnsupportedEncodingException;
32
33 /**
34  * @author satorux@google.com (Satoru Takabayashi) - creator
35  * @author dswitkin@google.com (Daniel Switkin) - ported from C++
36  */
37 public final class Encoder {
38
39   // The original table is defined in the table 5 of JISX0510:2004 (p.19).
40   private static final int[] ALPHANUMERIC_TABLE = {
41       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 0x00-0x0f
42       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  // 0x10-0x1f
43       36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,  // 0x20-0x2f
44       0,   1,  2,  3,  4,  5,  6,  7,  8,  9, 44, -1, -1, -1, -1, -1,  // 0x30-0x3f
45       -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,  // 0x40-0x4f
46       25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,  // 0x50-0x5f
47   };
48
49   private Encoder() {
50   }
51
52   // The mask penalty calculation is complicated.  See Table 21 of JISX0510:2004 (p.45) for details.
53   // Basically it applies four rules and summate all penalties.
54   private static int calculateMaskPenalty(ByteMatrix matrix) {
55     int penalty = 0;
56     penalty += MaskUtil.applyMaskPenaltyRule1(matrix);
57     penalty += MaskUtil.applyMaskPenaltyRule2(matrix);
58     penalty += MaskUtil.applyMaskPenaltyRule3(matrix);
59     penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
60     return penalty;
61   }
62
63   private static final class BlockPair {
64
65     private final ByteArray dataBytes;
66     private final ByteArray errorCorrectionBytes;
67
68     BlockPair(ByteArray data, ByteArray errorCorrection) {
69       dataBytes = data;
70       errorCorrectionBytes = errorCorrection;
71     }
72
73     public ByteArray getDataBytes() {
74       return dataBytes;
75     }
76
77     public ByteArray getErrorCorrectionBytes() {
78       return errorCorrectionBytes;
79     }
80
81   }
82
83   /**
84    *  Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
85    * internally by chooseMode(). On success, store the result in "qrCode".
86    *
87    * We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
88    * "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
89    * strong error correction for this purpose.
90    *
91    * Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
92    * with which clients can specify the encoding mode. For now, we don't need the functionality.
93    */
94   public static void encode(String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)
95       throws WriterException {
96     encode(content, ecLevel, null, qrCode);
97   }
98
99   public static void encode(String content, ErrorCorrectionLevel ecLevel, Hashtable hints, QRCode qrCode)
100       throws WriterException {
101
102     String characterEncoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
103     if (characterEncoding == null) {
104       characterEncoding = "ISO-8859-1";
105     }
106
107     // Step 1: Choose the mode (encoding).
108     Mode mode = chooseMode(content);
109
110     // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
111     BitVector dataBits = new BitVector();
112     appendBytes(content, mode, dataBits, characterEncoding);
113     // Step 3: Initialize QR code that can contain "dataBits".
114     int numInputBytes = dataBits.sizeInBytes();
115     initQRCode(numInputBytes, ecLevel, mode, qrCode);
116
117     // Step 4: Build another bit vector that contains header and data.
118     BitVector headerAndDataBits = new BitVector();
119     appendModeInfo(qrCode.getMode(), headerAndDataBits);
120     appendLengthInfo(content.length(), qrCode.getVersion(), qrCode.getMode(), headerAndDataBits);
121     headerAndDataBits.appendBitVector(dataBits);
122
123     // Step 5: Terminate the bits properly.
124     terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);
125
126     // Step 6: Interleave data bits with error correction code.
127     BitVector finalBits = new BitVector();
128     interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
129         qrCode.getNumRSBlocks(), finalBits);
130
131     // Step 7: Choose the mask pattern and set to "qrCode".
132     ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
133     qrCode.setMaskPattern(chooseMaskPattern(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
134         matrix));
135
136     // Step 8.  Build the matrix and set it to "qrCode".
137     MatrixUtil.buildMatrix(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
138         qrCode.getMaskPattern(), matrix);
139     qrCode.setMatrix(matrix);
140     // Step 9.  Make sure we have a valid QR Code.
141     if (!qrCode.isValid()) {
142       throw new WriterException("Invalid QR code: " + qrCode.toString());
143     }
144   }
145
146   /**
147    * @return the code point of the table used in alphanumeric mode or
148    *  -1 if there is no corresponding code in the table.
149    */
150   static int getAlphanumericCode(int code) {
151     if (code < ALPHANUMERIC_TABLE.length) {
152       return ALPHANUMERIC_TABLE[code];
153     }
154     return -1;
155   }
156
157   /**
158    * Choose the best mode by examining the content.
159    *
160    * Note that this function does not return MODE_KANJI, as we cannot distinguish Shift_JIS from
161    * other encodings such as ISO-8859-1, from data bytes alone. For example "\xE0\xE0" can be
162    * interpreted as one character in Shift_JIS, but also two characters in ISO-8859-1.
163    */
164   public static Mode chooseMode(String content) {
165     boolean hasNumeric = false;
166     boolean hasAlphanumeric = false;
167     for (int i = 0; i < content.length(); ++i) {
168       char c = content.charAt(i);
169       if (c >= '0' && c <= '9') {
170         hasNumeric = true;
171       } else if (getAlphanumericCode(c) != -1) {
172         hasAlphanumeric = true;
173       } else {
174         return Mode.BYTE;
175       }
176     }
177     if (hasAlphanumeric) {
178       return Mode.ALPHANUMERIC;
179     } else if (hasNumeric) {
180       return Mode.NUMERIC;
181     }
182     return Mode.BYTE;
183   }
184
185   private static int chooseMaskPattern(BitVector bits, ErrorCorrectionLevel ecLevel, int version,
186       ByteMatrix matrix) throws WriterException {
187
188     int minPenalty = Integer.MAX_VALUE;  // Lower penalty is better.
189     int bestMaskPattern = -1;
190     // We try all mask patterns to choose the best one.
191     for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
192       MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
193       int penalty = calculateMaskPenalty(matrix);
194       if (penalty < minPenalty) {
195         minPenalty = penalty;
196         bestMaskPattern = maskPattern;
197       }
198     }
199     return bestMaskPattern;
200   }
201
202   /**
203    * Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success, modify "qrCode".
204    */
205   private static void initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode, QRCode qrCode)
206       throws WriterException {
207     qrCode.setECLevel(ecLevel);
208     qrCode.setMode(mode);
209
210     // In the following comments, we use numbers of Version 7-H.
211     for (int versionNum = 1; versionNum <= 40; versionNum++) {
212       Version version = Version.getVersionForNumber(versionNum);
213       // numBytes = 196
214       int numBytes = version.getTotalCodewords();
215       // getNumECBytes = 130
216       Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
217       int numEcBytes = ecBlocks.getTotalECCodewords();
218       // getNumRSBlocks = 5
219       int numRSBlocks = ecBlocks.getNumBlocks();
220       // getNumDataBytes = 196 - 130 = 66
221       int numDataBytes = numBytes - numEcBytes;
222       // We want to choose the smallest version which can contain data of "numInputBytes" + some
223       // extra bits for the header (mode info and length info). The header can be three bytes
224       // (precisely 4 + 16 bits) at most. Hence we do +3 here.
225       if (numDataBytes >= numInputBytes + 3) {
226         // Yay, we found the proper rs block info!
227         qrCode.setVersion(versionNum);
228         qrCode.setNumTotalBytes(numBytes);
229         qrCode.setNumDataBytes(numDataBytes);
230         qrCode.setNumRSBlocks(numRSBlocks);
231         // getNumECBytes = 196 - 66 = 130
232         qrCode.setNumECBytes(numEcBytes);
233         // matrix width = 21 + 6 * 4 = 45
234         qrCode.setMatrixWidth(version.getDimensionForVersion());
235         return;
236       }
237     }
238     throw new WriterException("Cannot find proper rs block info (input data too big?)");
239   }
240
241   /**
242    * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
243    */
244   static void terminateBits(int numDataBytes, BitVector bits) throws WriterException {
245     int capacity = numDataBytes << 3;
246     if (bits.size() > capacity) {
247       throw new WriterException("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity);
248     }
249     // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
250     for (int i = 0; i < 4 && bits.size() < capacity; ++i) {
251       bits.appendBit(0);
252     }
253     int numBitsInLastByte = bits.size() % 8;
254     // If the last byte isn't 8-bit aligned, we'll add padding bits.
255     if (numBitsInLastByte > 0) {
256       int numPaddingBits = 8 - numBitsInLastByte;
257       for (int i = 0; i < numPaddingBits; ++i) {
258         bits.appendBit(0);
259       }
260     }
261     // Should be 8-bit aligned here.
262     if (bits.size() % 8 != 0) {
263       throw new WriterException("Number of bits is not a multiple of 8");
264     }
265     // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
266     int numPaddingBytes = numDataBytes - bits.sizeInBytes();
267     for (int i = 0; i < numPaddingBytes; ++i) {
268       if (i % 2 == 0) {
269         bits.appendBits(0xec, 8);
270       } else {
271         bits.appendBits(0x11, 8);
272       }
273     }
274     if (bits.size() != capacity) {
275       throw new WriterException("Bits size does not equal capacity");
276     }
277   }
278
279   /**
280    * Get number of data bytes and number of error correction bytes for block id "blockID". Store
281    * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
282    * JISX0510:2004 (p.30)
283    */
284   static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes,
285       int numRSBlocks, int blockID, int[] numDataBytesInBlock,
286       int[] numECBytesInBlock) throws WriterException {
287     if (blockID >= numRSBlocks) {
288       throw new WriterException("Block ID too large");
289     }
290     // numRsBlocksInGroup2 = 196 % 5 = 1
291     int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
292     // numRsBlocksInGroup1 = 5 - 1 = 4
293     int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
294     // numTotalBytesInGroup1 = 196 / 5 = 39
295     int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
296     // numTotalBytesInGroup2 = 39 + 1 = 40
297     int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
298     // numDataBytesInGroup1 = 66 / 5 = 13
299     int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
300     // numDataBytesInGroup2 = 13 + 1 = 14
301     int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
302     // numEcBytesInGroup1 = 39 - 13 = 26
303     int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
304     // numEcBytesInGroup2 = 40 - 14 = 26
305     int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
306     // Sanity checks.
307     // 26 = 26
308     if (numEcBytesInGroup1 != numEcBytesInGroup2) {
309       throw new WriterException("EC bytes mismatch");
310     }
311     // 5 = 4 + 1.
312     if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
313       throw new WriterException("RS blocks mismatch");
314     }
315     // 196 = (13 + 26) * 4 + (14 + 26) * 1
316     if (numTotalBytes !=
317         ((numDataBytesInGroup1 + numEcBytesInGroup1) *
318             numRsBlocksInGroup1) +
319             ((numDataBytesInGroup2 + numEcBytesInGroup2) *
320                 numRsBlocksInGroup2)) {
321       throw new WriterException("Total bytes mismatch");
322     }
323
324     if (blockID < numRsBlocksInGroup1) {
325       numDataBytesInBlock[0] = numDataBytesInGroup1;
326       numECBytesInBlock[0] = numEcBytesInGroup1;
327     } else {
328       numDataBytesInBlock[0] = numDataBytesInGroup2;
329       numECBytesInBlock[0] = numEcBytesInGroup2;
330     }
331   }
332
333   /**
334    * Interleave "bits" with corresponding error correction bytes. On success, store the result in
335    * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
336    */
337   static void interleaveWithECBytes(BitVector bits, int numTotalBytes,
338       int numDataBytes, int numRSBlocks, BitVector result) throws WriterException {
339
340     // "bits" must have "getNumDataBytes" bytes of data.
341     if (bits.sizeInBytes() != numDataBytes) {
342       throw new WriterException("Number of bits and data bytes does not match");
343     }
344
345     // Step 1.  Divide data bytes into blocks and generate error correction bytes for them. We'll
346     // store the divided data bytes blocks and error correction bytes blocks into "blocks".
347     int dataBytesOffset = 0;
348     int maxNumDataBytes = 0;
349     int maxNumEcBytes = 0;
350
351     // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
352     Vector blocks = new Vector(numRSBlocks);
353
354     for (int i = 0; i < numRSBlocks; ++i) {
355       int[] numDataBytesInBlock = new int[1];
356       int[] numEcBytesInBlock = new int[1];
357       getNumDataBytesAndNumECBytesForBlockID(
358           numTotalBytes, numDataBytes, numRSBlocks, i,
359           numDataBytesInBlock, numEcBytesInBlock);
360
361       ByteArray dataBytes = new ByteArray();
362       dataBytes.set(bits.getArray(), dataBytesOffset, numDataBytesInBlock[0]);
363       ByteArray ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
364       blocks.addElement(new BlockPair(dataBytes, ecBytes));
365
366       maxNumDataBytes = Math.max(maxNumDataBytes, dataBytes.size());
367       maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.size());
368       dataBytesOffset += numDataBytesInBlock[0];
369     }
370     if (numDataBytes != dataBytesOffset) {
371       throw new WriterException("Data bytes does not match offset");
372     }
373
374     // First, place data blocks.
375     for (int i = 0; i < maxNumDataBytes; ++i) {
376       for (int j = 0; j < blocks.size(); ++j) {
377         ByteArray dataBytes = ((BlockPair) blocks.elementAt(j)).getDataBytes();
378         if (i < dataBytes.size()) {
379           result.appendBits(dataBytes.at(i), 8);
380         }
381       }
382     }
383     // Then, place error correction blocks.
384     for (int i = 0; i < maxNumEcBytes; ++i) {
385       for (int j = 0; j < blocks.size(); ++j) {
386         ByteArray ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes();
387         if (i < ecBytes.size()) {
388           result.appendBits(ecBytes.at(i), 8);
389         }
390       }
391     }
392     if (numTotalBytes != result.sizeInBytes()) {  // Should be same.
393       throw new WriterException("Interleaving error: " + numTotalBytes + " and " + result.sizeInBytes() +
394         " differ.");
395     }
396   }
397
398   static ByteArray generateECBytes(ByteArray dataBytes, int numEcBytesInBlock) {
399     int numDataBytes = dataBytes.size();
400     int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
401     for (int i = 0; i < numDataBytes; i++) {
402       toEncode[i] = dataBytes.at(i);
403     }
404     new ReedSolomonEncoder(GF256.QR_CODE_FIELD).encode(toEncode, numEcBytesInBlock);
405
406     ByteArray ecBytes = new ByteArray(numEcBytesInBlock);
407     for (int i = 0; i < numEcBytesInBlock; i++) {
408       ecBytes.set(i, toEncode[numDataBytes + i]);
409     }
410     return ecBytes;
411   }
412
413   /**
414    * Append mode info. On success, store the result in "bits".
415    */
416   static void appendModeInfo(Mode mode, BitVector bits) {
417     bits.appendBits(mode.getBits(), 4);
418   }
419
420
421   /**
422    * Append length info. On success, store the result in "bits".
423    */
424   static void appendLengthInfo(int numLetters, int version, Mode mode, BitVector bits) throws WriterException {
425     int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));
426     if (numLetters > ((1 << numBits) - 1)) {
427       throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
428     }
429     bits.appendBits(numLetters, numBits);
430   }
431
432   /**
433    * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
434    */
435   static void appendBytes(String content, Mode mode, BitVector bits, String encoding) throws WriterException {
436     if (mode.equals(Mode.NUMERIC)) {
437       appendNumericBytes(content, bits);
438     } else if (mode.equals(Mode.ALPHANUMERIC)) {
439       appendAlphanumericBytes(content, bits);
440     } else if (mode.equals(Mode.BYTE)) {
441       append8BitBytes(content, bits, encoding);
442     } else if (mode.equals(Mode.KANJI)) {
443       appendKanjiBytes(content, bits);
444     } else {
445       throw new WriterException("Invalid mode: " + mode);
446     }
447   }
448
449   static void appendNumericBytes(String content, BitVector bits) {
450     int length = content.length();
451     int i = 0;
452     while (i < length) {
453       int num1 = content.charAt(i) - '0';
454       if (i + 2 < length) {
455         // Encode three numeric letters in ten bits.
456         int num2 = content.charAt(i + 1) - '0';
457         int num3 = content.charAt(i + 2) - '0';
458         bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
459         i += 3;
460       } else if (i + 1 < length) {
461         // Encode two numeric letters in seven bits.
462         int num2 = content.charAt(i + 1) - '0';
463         bits.appendBits(num1 * 10 + num2, 7);
464         i += 2;
465       } else {
466         // Encode one numeric letter in four bits.
467         bits.appendBits(num1, 4);
468         i++;
469       }
470     }
471   }
472
473   static void appendAlphanumericBytes(String content, BitVector bits) throws WriterException {
474     int length = content.length();
475     int i = 0;
476     while (i < length) {
477       int code1 = getAlphanumericCode(content.charAt(i));
478       if (code1 == -1) {
479         throw new WriterException();
480       }
481       if (i + 1 < length) {
482         int code2 = getAlphanumericCode(content.charAt(i + 1));
483         if (code2 == -1) {
484           throw new WriterException();
485         }
486         // Encode two alphanumeric letters in 11 bits.
487         bits.appendBits(code1 * 45 + code2, 11);
488         i += 2;
489       } else {
490         // Encode one alphanumeric letter in six bits.
491         bits.appendBits(code1, 6);
492         i++;
493       }
494     }
495   }
496
497   static void append8BitBytes(String content, BitVector bits, String encoding) throws WriterException {
498     byte[] bytes;
499     try {
500       bytes = content.getBytes(encoding);
501     } catch (UnsupportedEncodingException uee) {
502       throw new WriterException(uee.toString());
503     }
504     for (int i = 0; i < bytes.length; ++i) {
505       bits.appendBits(bytes[i], 8);
506     }
507   }
508
509   static void appendKanjiBytes(String content, BitVector bits) throws WriterException {
510     byte[] bytes;
511     try {
512       bytes = content.getBytes("Shift_JIS");
513     } catch (UnsupportedEncodingException uee) {
514       throw new WriterException(uee.toString());
515     }
516     int length = bytes.length;
517     for (int i = 0; i < length; i += 2) {
518       int byte1 = bytes[i] & 0xFF;
519       int byte2 = bytes[i + 1] & 0xFF;
520       int code = (byte1 << 8) | byte2;
521       int subtracted = -1;
522       if (code >= 0x8140 && code <= 0x9ffc) {
523         subtracted = code - 0x8140;
524       } else if (code >= 0xe040 && code <= 0xebbf) {
525         subtracted = code - 0xc140;
526       }
527       if (subtracted == -1) {
528         throw new WriterException("Invalid byte sequence");
529       }
530       int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
531       bits.appendBits(encoded, 13);
532     }
533   }
534
535 }