Bumped Barcode Scanner version to 3.5 final.
[zxing.git] / csharp / qrcode / encoder / Encoder.cs
1 /*\r
2 * Copyright 2008 ZXing authors\r
3 *\r
4 * Licensed under the Apache License, Version 2.0 (the "License");\r
5 * you may not use this file except in compliance with the License.\r
6 * You may obtain a copy of the License at\r
7 *\r
8 *      http://www.apache.org/licenses/LICENSE-2.0\r
9 *\r
10 * Unless required by applicable law or agreed to in writing, software\r
11 * distributed under the License is distributed on an "AS IS" BASIS,\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13 * See the License for the specific language governing permissions and\r
14 * limitations under the License.\r
15 */\r
16 using System;\r
17 using WriterException = com.google.zxing.WriterException;\r
18 using EncodeHintType = com.google.zxing.EncodeHintType;\r
19 using ByteArray = com.google.zxing.common.ByteArray;\r
20 using ByteMatrix = com.google.zxing.common.ByteMatrix;\r
21 using CharacterSetECI = com.google.zxing.common.CharacterSetECI;\r
22 using GF256 = com.google.zxing.common.reedsolomon.GF256;\r
23 using ReedSolomonEncoder = com.google.zxing.common.reedsolomon.ReedSolomonEncoder;\r
24 using ErrorCorrectionLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;\r
25 using Mode = com.google.zxing.qrcode.decoder.Mode;\r
26 using Version = com.google.zxing.qrcode.decoder.Version;\r
27 namespace com.google.zxing.qrcode.encoder\r
28 {\r
29         \r
30         /// <author>  satorux@google.com (Satoru Takabayashi) - creator\r
31         /// </author>\r
32         /// <author>  dswitkin@google.com (Daniel Switkin) - ported from C++\r
33         /// </author>\r
34         /// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source \r
35         /// </author>\r
36         public sealed class Encoder\r
37         {\r
38                 \r
39                 // The original table is defined in the table 5 of JISX0510:2004 (p.19).\r
40                 //UPGRADE_NOTE: Final was removed from the declaration of 'ALPHANUMERIC_TABLE'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"\r
41                 private static readonly int[] ALPHANUMERIC_TABLE = new int[]{- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, 36, - 1, - 1, - 1, 37, 38, - 1, - 1, - 1, - 1, 39, 40, - 1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, - 1, - 1, - 1, - 1, - 1, - 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 1, - 1, - 1, - 1, - 1};\r
42                 \r
43                 internal const System.String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";\r
44                 \r
45                 private Encoder()\r
46                 {\r
47                 }\r
48                 \r
49                 // The mask penalty calculation is complicated.  See Table 21 of JISX0510:2004 (p.45) for details.\r
50                 // Basically it applies four rules and summate all penalties.\r
51                 private static int calculateMaskPenalty(ByteMatrix matrix)\r
52                 {\r
53                         int penalty = 0;\r
54                         penalty += MaskUtil.applyMaskPenaltyRule1(matrix);\r
55                         penalty += MaskUtil.applyMaskPenaltyRule2(matrix);\r
56                         penalty += MaskUtil.applyMaskPenaltyRule3(matrix);\r
57                         penalty += MaskUtil.applyMaskPenaltyRule4(matrix);\r
58                         return penalty;\r
59                 }\r
60                 \r
61                 /// <summary>  Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen\r
62                 /// internally by chooseMode(). On success, store the result in "qrCode".\r
63                 /// \r
64                 /// We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for\r
65                 /// "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very\r
66                 /// strong error correction for this purpose.\r
67                 /// \r
68                 /// Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()\r
69                 /// with which clients can specify the encoding mode. For now, we don't need the functionality.\r
70                 /// </summary>\r
71                 public static void  encode(System.String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)\r
72                 {\r
73                         encode(content, ecLevel, null, qrCode);\r
74                 }\r
75                 \r
76                 public static void  encode(System.String content, ErrorCorrectionLevel ecLevel, System.Collections.Hashtable hints, QRCode qrCode)\r
77                 {\r
78                         \r
79                         System.String encoding = hints == null?null:(System.String) hints[EncodeHintType.CHARACTER_SET];\r
80                         if (encoding == null)\r
81                         {\r
82                                 encoding = DEFAULT_BYTE_MODE_ENCODING;\r
83                         }\r
84                         \r
85                         // Step 1: Choose the mode (encoding).\r
86                         Mode mode = chooseMode(content, encoding);\r
87                         \r
88                         // Step 2: Append "bytes" into "dataBits" in appropriate encoding.\r
89                         BitVector dataBits = new BitVector();\r
90                         appendBytes(content, mode, dataBits, encoding);\r
91                         // Step 3: Initialize QR code that can contain "dataBits".\r
92                         int numInputBytes = dataBits.sizeInBytes();\r
93                         initQRCode(numInputBytes, ecLevel, mode, qrCode);\r
94                         \r
95                         // Step 4: Build another bit vector that contains header and data.\r
96                         BitVector headerAndDataBits = new BitVector();\r
97                         \r
98                         // Step 4.5: Append ECI message if applicable\r
99                         if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding))\r
100                         {\r
101                                 CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);\r
102                                 if (eci != null)\r
103                                 {\r
104                                         appendECI(eci, headerAndDataBits);\r
105                                 }\r
106                         }\r
107                         \r
108                         appendModeInfo(mode, headerAndDataBits);\r
109                         \r
110                         int numLetters = mode.Equals(Mode.BYTE)?dataBits.sizeInBytes():content.Length;\r
111                         appendLengthInfo(numLetters, qrCode.Version, mode, headerAndDataBits);\r
112                         headerAndDataBits.appendBitVector(dataBits);\r
113                         \r
114                         // Step 5: Terminate the bits properly.\r
115                         terminateBits(qrCode.NumDataBytes, headerAndDataBits);\r
116                         \r
117                         // Step 6: Interleave data bits with error correction code.\r
118                         BitVector finalBits = new BitVector();\r
119                         interleaveWithECBytes(headerAndDataBits, qrCode.NumTotalBytes, qrCode.NumDataBytes, qrCode.NumRSBlocks, finalBits);\r
120                         \r
121                         // Step 7: Choose the mask pattern and set to "qrCode".\r
122                         ByteMatrix matrix = new ByteMatrix(qrCode.MatrixWidth, qrCode.MatrixWidth);\r
123                         qrCode.MaskPattern = chooseMaskPattern(finalBits, qrCode.ECLevel, qrCode.Version, matrix);\r
124                         \r
125                         // Step 8.  Build the matrix and set it to "qrCode".\r
126                         MatrixUtil.buildMatrix(finalBits, qrCode.ECLevel, qrCode.Version, qrCode.MaskPattern, matrix);\r
127                         qrCode.Matrix = matrix;\r
128                         // Step 9.  Make sure we have a valid QR Code.\r
129                         if (!qrCode.Valid)\r
130                         {\r
131                                 throw new WriterException("Invalid QR code: " + qrCode.ToString());\r
132                         }\r
133                 }\r
134                 \r
135                 /// <returns> the code point of the table used in alphanumeric mode or\r
136                 /// -1 if there is no corresponding code in the table.\r
137                 /// </returns>\r
138                 internal static int getAlphanumericCode(int code)\r
139                 {\r
140                         if (code < ALPHANUMERIC_TABLE.Length)\r
141                         {\r
142                                 return ALPHANUMERIC_TABLE[code];\r
143                         }\r
144                         return - 1;\r
145                 }\r
146                 \r
147                 public static Mode chooseMode(System.String content)\r
148                 {\r
149                         return chooseMode(content, null);\r
150                 }\r
151                 \r
152                 /// <summary> Choose the best mode by examining the content. Note that 'encoding' is used as a hint;\r
153                 /// if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.\r
154                 /// </summary>\r
155                 public static Mode chooseMode(System.String content, System.String encoding)\r
156                 {\r
157                         if ("Shift_JIS".Equals(encoding))\r
158                         {\r
159                                 // Choose Kanji mode if all input are double-byte characters\r
160                                 return isOnlyDoubleByteKanji(content)?Mode.KANJI:Mode.BYTE;\r
161                         }\r
162                         bool hasNumeric = false;\r
163                         bool hasAlphanumeric = false;\r
164                         for (int i = 0; i < content.Length; ++i)\r
165                         {\r
166                                 char c = content[i];\r
167                                 if (c >= '0' && c <= '9')\r
168                                 {\r
169                                         hasNumeric = true;\r
170                                 }\r
171                                 else if (getAlphanumericCode(c) != - 1)\r
172                                 {\r
173                                         hasAlphanumeric = true;\r
174                                 }\r
175                                 else\r
176                                 {\r
177                                         return Mode.BYTE;\r
178                                 }\r
179                         }\r
180                         if (hasAlphanumeric)\r
181                         {\r
182                                 return Mode.ALPHANUMERIC;\r
183                         }\r
184                         else if (hasNumeric)\r
185                         {\r
186                                 return Mode.NUMERIC;\r
187                         }\r
188                         return Mode.BYTE;\r
189                 }\r
190                 \r
191                 private static bool isOnlyDoubleByteKanji(System.String content)\r
192                 {\r
193                         sbyte[] bytes;\r
194                         try\r
195                         {\r
196                                 //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"\r
197                                 bytes = SupportClass.ToSByteArray(System.Text.Encoding.GetEncoding("Shift_JIS").GetBytes(content));\r
198                         }\r
199                         catch (System.IO.IOException uee)\r
200                         {\r
201                                 return false;\r
202                         }\r
203                         int length = bytes.Length;\r
204                         if (length % 2 != 0)\r
205                         {\r
206                                 return false;\r
207                         }\r
208                         for (int i = 0; i < length; i += 2)\r
209                         {\r
210                                 int byte1 = bytes[i] & 0xFF;\r
211                                 if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB))\r
212                                 {\r
213                                         return false;\r
214                                 }\r
215                         }\r
216                         return true;\r
217                 }\r
218                 \r
219                 private static int chooseMaskPattern(BitVector bits, ErrorCorrectionLevel ecLevel, int version, ByteMatrix matrix)\r
220                 {\r
221                         \r
222                         int minPenalty = System.Int32.MaxValue; // Lower penalty is better.\r
223                         int bestMaskPattern = - 1;\r
224                         // We try all mask patterns to choose the best one.\r
225                         for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++)\r
226                         {\r
227                                 MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);\r
228                                 int penalty = calculateMaskPenalty(matrix);\r
229                                 if (penalty < minPenalty)\r
230                                 {\r
231                                         minPenalty = penalty;\r
232                                         bestMaskPattern = maskPattern;\r
233                                 }\r
234                         }\r
235                         return bestMaskPattern;\r
236                 }\r
237                 \r
238                 /// <summary> Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success,\r
239                 /// modify "qrCode".\r
240                 /// </summary>\r
241                 private static void  initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode, QRCode qrCode)\r
242                 {\r
243                         qrCode.ECLevel = ecLevel;\r
244                         qrCode.Mode = mode;\r
245                         \r
246                         // In the following comments, we use numbers of Version 7-H.\r
247                         for (int versionNum = 1; versionNum <= 40; versionNum++)\r
248                         {\r
249                                 Version version = Version.getVersionForNumber(versionNum);\r
250                                 // numBytes = 196\r
251                                 int numBytes = version.TotalCodewords;\r
252                                 // getNumECBytes = 130\r
253                                 Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);\r
254                                 int numEcBytes = ecBlocks.TotalECCodewords;\r
255                                 // getNumRSBlocks = 5\r
256                                 int numRSBlocks = ecBlocks.NumBlocks;\r
257                                 // getNumDataBytes = 196 - 130 = 66\r
258                                 int numDataBytes = numBytes - numEcBytes;\r
259                                 // We want to choose the smallest version which can contain data of "numInputBytes" + some\r
260                                 // extra bits for the header (mode info and length info). The header can be three bytes\r
261                                 // (precisely 4 + 16 bits) at most. Hence we do +3 here.\r
262                                 if (numDataBytes >= numInputBytes + 3)\r
263                                 {\r
264                                         // Yay, we found the proper rs block info!\r
265                                         qrCode.Version = versionNum;\r
266                                         qrCode.NumTotalBytes = numBytes;\r
267                                         qrCode.NumDataBytes = numDataBytes;\r
268                                         qrCode.NumRSBlocks = numRSBlocks;\r
269                                         // getNumECBytes = 196 - 66 = 130\r
270                                         qrCode.NumECBytes = numEcBytes;\r
271                                         // matrix width = 21 + 6 * 4 = 45\r
272                                         qrCode.MatrixWidth = version.DimensionForVersion;\r
273                                         return ;\r
274                                 }\r
275                         }\r
276                         throw new WriterException("Cannot find proper rs block info (input data too big?)");\r
277                 }\r
278                 \r
279                 /// <summary> Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).</summary>\r
280                 internal static void  terminateBits(int numDataBytes, BitVector bits)\r
281                 {\r
282                         int capacity = numDataBytes << 3;\r
283                         if (bits.size() > capacity)\r
284                         {\r
285                                 throw new WriterException("data bits cannot fit in the QR Code" + bits.size() + " > " + capacity);\r
286                         }\r
287                         // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.\r
288                         // TODO: srowen says we can remove this for loop, since the 4 terminator bits are optional if\r
289                         // the last byte has less than 4 bits left. So it amounts to padding the last byte with zeroes\r
290                         // either way.\r
291                         for (int i = 0; i < 4 && bits.size() < capacity; ++i)\r
292                         {\r
293                                 bits.appendBit(0);\r
294                         }\r
295                         int numBitsInLastByte = bits.size() % 8;\r
296                         // If the last byte isn't 8-bit aligned, we'll add padding bits.\r
297                         if (numBitsInLastByte > 0)\r
298                         {\r
299                                 int numPaddingBits = 8 - numBitsInLastByte;\r
300                                 for (int i = 0; i < numPaddingBits; ++i)\r
301                                 {\r
302                                         bits.appendBit(0);\r
303                                 }\r
304                         }\r
305                         // Should be 8-bit aligned here.\r
306                         if (bits.size() % 8 != 0)\r
307                         {\r
308                                 throw new WriterException("Number of bits is not a multiple of 8");\r
309                         }\r
310                         // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).\r
311                         int numPaddingBytes = numDataBytes - bits.sizeInBytes();\r
312                         for (int i = 0; i < numPaddingBytes; ++i)\r
313                         {\r
314                                 if (i % 2 == 0)\r
315                                 {\r
316                                         bits.appendBits(0xec, 8);\r
317                                 }\r
318                                 else\r
319                                 {\r
320                                         bits.appendBits(0x11, 8);\r
321                                 }\r
322                         }\r
323                         if (bits.size() != capacity)\r
324                         {\r
325                                 throw new WriterException("Bits size does not equal capacity");\r
326                         }\r
327                 }\r
328                 \r
329                 /// <summary> Get number of data bytes and number of error correction bytes for block id "blockID". Store\r
330                 /// the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of\r
331                 /// JISX0510:2004 (p.30)\r
332                 /// </summary>\r
333                 internal static void  getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, int numRSBlocks, int blockID, int[] numDataBytesInBlock, int[] numECBytesInBlock)\r
334                 {\r
335                         if (blockID >= numRSBlocks)\r
336                         {\r
337                                 throw new WriterException("Block ID too large");\r
338                         }\r
339                         // numRsBlocksInGroup2 = 196 % 5 = 1\r
340                         int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;\r
341                         // numRsBlocksInGroup1 = 5 - 1 = 4\r
342                         int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;\r
343                         // numTotalBytesInGroup1 = 196 / 5 = 39\r
344                         int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;\r
345                         // numTotalBytesInGroup2 = 39 + 1 = 40\r
346                         int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;\r
347                         // numDataBytesInGroup1 = 66 / 5 = 13\r
348                         int numDataBytesInGroup1 = numDataBytes / numRSBlocks;\r
349                         // numDataBytesInGroup2 = 13 + 1 = 14\r
350                         int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;\r
351                         // numEcBytesInGroup1 = 39 - 13 = 26\r
352                         int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;\r
353                         // numEcBytesInGroup2 = 40 - 14 = 26\r
354                         int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;\r
355                         // Sanity checks.\r
356                         // 26 = 26\r
357                         if (numEcBytesInGroup1 != numEcBytesInGroup2)\r
358                         {\r
359                                 throw new WriterException("EC bytes mismatch");\r
360                         }\r
361                         // 5 = 4 + 1.\r
362                         if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2)\r
363                         {\r
364                                 throw new WriterException("RS blocks mismatch");\r
365                         }\r
366                         // 196 = (13 + 26) * 4 + (14 + 26) * 1\r
367                         if (numTotalBytes != ((numDataBytesInGroup1 + numEcBytesInGroup1) * numRsBlocksInGroup1) + ((numDataBytesInGroup2 + numEcBytesInGroup2) * numRsBlocksInGroup2))\r
368                         {\r
369                                 throw new WriterException("Total bytes mismatch");\r
370                         }\r
371                         \r
372                         if (blockID < numRsBlocksInGroup1)\r
373                         {\r
374                                 numDataBytesInBlock[0] = numDataBytesInGroup1;\r
375                                 numECBytesInBlock[0] = numEcBytesInGroup1;\r
376                         }\r
377                         else\r
378                         {\r
379                                 numDataBytesInBlock[0] = numDataBytesInGroup2;\r
380                                 numECBytesInBlock[0] = numEcBytesInGroup2;\r
381                         }\r
382                 }\r
383                 \r
384                 /// <summary> Interleave "bits" with corresponding error correction bytes. On success, store the result in\r
385                 /// "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.\r
386                 /// </summary>\r
387                 internal static void  interleaveWithECBytes(BitVector bits, int numTotalBytes, int numDataBytes, int numRSBlocks, BitVector result)\r
388                 {\r
389                         \r
390                         // "bits" must have "getNumDataBytes" bytes of data.\r
391                         if (bits.sizeInBytes() != numDataBytes)\r
392                         {\r
393                                 throw new WriterException("Number of bits and data bytes does not match");\r
394                         }\r
395                         \r
396                         // Step 1.  Divide data bytes into blocks and generate error correction bytes for them. We'll\r
397                         // store the divided data bytes blocks and error correction bytes blocks into "blocks".\r
398                         int dataBytesOffset = 0;\r
399                         int maxNumDataBytes = 0;\r
400                         int maxNumEcBytes = 0;\r
401                         \r
402                         // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.\r
403                         System.Collections.ArrayList blocks = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(numRSBlocks));\r
404                         \r
405                         for (int i = 0; i < numRSBlocks; ++i)\r
406                         {\r
407                                 int[] numDataBytesInBlock = new int[1];\r
408                                 int[] numEcBytesInBlock = new int[1];\r
409                                 getNumDataBytesAndNumECBytesForBlockID(numTotalBytes, numDataBytes, numRSBlocks, i, numDataBytesInBlock, numEcBytesInBlock);\r
410                                 \r
411                                 ByteArray dataBytes = new ByteArray();\r
412                                 dataBytes.set_Renamed(bits.Array, dataBytesOffset, numDataBytesInBlock[0]);\r
413                                 ByteArray ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);\r
414                                 blocks.Add(new BlockPair(dataBytes, ecBytes));\r
415                                 \r
416                                 maxNumDataBytes = System.Math.Max(maxNumDataBytes, dataBytes.size());\r
417                                 maxNumEcBytes = System.Math.Max(maxNumEcBytes, ecBytes.size());\r
418                                 dataBytesOffset += numDataBytesInBlock[0];\r
419                         }\r
420                         if (numDataBytes != dataBytesOffset)\r
421                         {\r
422                                 throw new WriterException("Data bytes does not match offset");\r
423                         }\r
424                         \r
425                         // First, place data blocks.\r
426                         for (int i = 0; i < maxNumDataBytes; ++i)\r
427                         {\r
428                                 for (int j = 0; j < blocks.Count; ++j)\r
429                                 {\r
430                                         ByteArray dataBytes = ((BlockPair) blocks[j]).DataBytes;\r
431                                         if (i < dataBytes.size())\r
432                                         {\r
433                                                 result.appendBits(dataBytes.at(i), 8);\r
434                                         }\r
435                                 }\r
436                         }\r
437                         // Then, place error correction blocks.\r
438                         for (int i = 0; i < maxNumEcBytes; ++i)\r
439                         {\r
440                                 for (int j = 0; j < blocks.Count; ++j)\r
441                                 {\r
442                                         ByteArray ecBytes = ((BlockPair) blocks[j]).ErrorCorrectionBytes;\r
443                                         if (i < ecBytes.size())\r
444                                         {\r
445                                                 result.appendBits(ecBytes.at(i), 8);\r
446                                         }\r
447                                 }\r
448                         }\r
449                         if (numTotalBytes != result.sizeInBytes())\r
450                         {\r
451                                 // Should be same.\r
452                                 throw new WriterException("Interleaving error: " + numTotalBytes + " and " + result.sizeInBytes() + " differ.");\r
453                         }\r
454                 }\r
455                 \r
456                 internal static ByteArray generateECBytes(ByteArray dataBytes, int numEcBytesInBlock)\r
457                 {\r
458                         int numDataBytes = dataBytes.size();\r
459                         int[] toEncode = new int[numDataBytes + numEcBytesInBlock];\r
460                         for (int i = 0; i < numDataBytes; i++)\r
461                         {\r
462                                 toEncode[i] = dataBytes.at(i);\r
463                         }\r
464                         new ReedSolomonEncoder(GF256.QR_CODE_FIELD).encode(toEncode, numEcBytesInBlock);\r
465                         \r
466                         ByteArray ecBytes = new ByteArray(numEcBytesInBlock);\r
467                         for (int i = 0; i < numEcBytesInBlock; i++)\r
468                         {\r
469                                 ecBytes.set_Renamed(i, toEncode[numDataBytes + i]);\r
470                         }\r
471                         return ecBytes;\r
472                 }\r
473                 \r
474                 /// <summary> Append mode info. On success, store the result in "bits".</summary>\r
475                 internal static void  appendModeInfo(Mode mode, BitVector bits)\r
476                 {\r
477                         bits.appendBits(mode.Bits, 4);\r
478                 }\r
479                 \r
480                 \r
481                 /// <summary> Append length info. On success, store the result in "bits".</summary>\r
482                 internal static void  appendLengthInfo(int numLetters, int version, Mode mode, BitVector bits)\r
483                 {\r
484                         int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));\r
485                         if (numLetters > ((1 << numBits) - 1))\r
486                         {\r
487                                 throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));\r
488                         }\r
489                         bits.appendBits(numLetters, numBits);\r
490                 }\r
491                 \r
492                 /// <summary> Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".</summary>\r
493                 internal static void  appendBytes(System.String content, Mode mode, BitVector bits, System.String encoding)\r
494                 {\r
495                         if (mode.Equals(Mode.NUMERIC))\r
496                         {\r
497                                 appendNumericBytes(content, bits);\r
498                         }\r
499                         else if (mode.Equals(Mode.ALPHANUMERIC))\r
500                         {\r
501                                 appendAlphanumericBytes(content, bits);\r
502                         }\r
503                         else if (mode.Equals(Mode.BYTE))\r
504                         {\r
505                                 append8BitBytes(content, bits, encoding);\r
506                         }\r
507                         else if (mode.Equals(Mode.KANJI))\r
508                         {\r
509                                 appendKanjiBytes(content, bits);\r
510                         }\r
511                         else\r
512                         {\r
513                                 throw new WriterException("Invalid mode: " + mode);\r
514                         }\r
515                 }\r
516                 \r
517                 internal static void  appendNumericBytes(System.String content, BitVector bits)\r
518                 {\r
519                         int length = content.Length;\r
520                         int i = 0;\r
521                         while (i < length)\r
522                         {\r
523                                 int num1 = content[i] - '0';\r
524                                 if (i + 2 < length)\r
525                                 {\r
526                                         // Encode three numeric letters in ten bits.\r
527                                         int num2 = content[i + 1] - '0';\r
528                                         int num3 = content[i + 2] - '0';\r
529                                         bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);\r
530                                         i += 3;\r
531                                 }\r
532                                 else if (i + 1 < length)\r
533                                 {\r
534                                         // Encode two numeric letters in seven bits.\r
535                                         int num2 = content[i + 1] - '0';\r
536                                         bits.appendBits(num1 * 10 + num2, 7);\r
537                                         i += 2;\r
538                                 }\r
539                                 else\r
540                                 {\r
541                                         // Encode one numeric letter in four bits.\r
542                                         bits.appendBits(num1, 4);\r
543                                         i++;\r
544                                 }\r
545                         }\r
546                 }\r
547                 \r
548                 internal static void  appendAlphanumericBytes(System.String content, BitVector bits)\r
549                 {\r
550                         int length = content.Length;\r
551                         int i = 0;\r
552                         while (i < length)\r
553                         {\r
554                                 int code1 = getAlphanumericCode(content[i]);\r
555                                 if (code1 == - 1)\r
556                                 {\r
557                                         throw new WriterException();\r
558                                 }\r
559                                 if (i + 1 < length)\r
560                                 {\r
561                                         int code2 = getAlphanumericCode(content[i + 1]);\r
562                                         if (code2 == - 1)\r
563                                         {\r
564                                                 throw new WriterException();\r
565                                         }\r
566                                         // Encode two alphanumeric letters in 11 bits.\r
567                                         bits.appendBits(code1 * 45 + code2, 11);\r
568                                         i += 2;\r
569                                 }\r
570                                 else\r
571                                 {\r
572                                         // Encode one alphanumeric letter in six bits.\r
573                                         bits.appendBits(code1, 6);\r
574                                         i++;\r
575                                 }\r
576                         }\r
577                 }\r
578                 \r
579                 internal static void  append8BitBytes(System.String content, BitVector bits, System.String encoding)\r
580                 {\r
581                         sbyte[] bytes;\r
582                         try\r
583                         {\r
584                                 //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"\r
585                                 bytes = SupportClass.ToSByteArray(System.Text.Encoding.GetEncoding(encoding).GetBytes(content));\r
586                         }\r
587                         catch (System.IO.IOException uee)\r
588                         {\r
589                                 //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"\r
590                                 throw new WriterException(uee.ToString());\r
591                         }\r
592                         for (int i = 0; i < bytes.Length; ++i)\r
593                         {\r
594                                 bits.appendBits(bytes[i], 8);\r
595                         }\r
596                 }\r
597                 \r
598                 internal static void  appendKanjiBytes(System.String content, BitVector bits)\r
599                 {\r
600                         sbyte[] bytes;\r
601                         try\r
602                         {\r
603                                 //UPGRADE_TODO: Method 'java.lang.String.getBytes' was converted to 'System.Text.Encoding.GetEncoding(string).GetBytes(string)' which has a different behavior. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1073_javalangStringgetBytes_javalangString'"\r
604                                 bytes = SupportClass.ToSByteArray(System.Text.Encoding.GetEncoding("Shift_JIS").GetBytes(content));\r
605                         }\r
606                         catch (System.IO.IOException uee)\r
607                         {\r
608                                 //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Throwable.toString' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'"\r
609                                 throw new WriterException(uee.ToString());\r
610                         }\r
611                         int length = bytes.Length;\r
612                         for (int i = 0; i < length; i += 2)\r
613                         {\r
614                                 int byte1 = bytes[i] & 0xFF;\r
615                                 int byte2 = bytes[i + 1] & 0xFF;\r
616                                 int code = (byte1 << 8) | byte2;\r
617                                 int subtracted = - 1;\r
618                                 if (code >= 0x8140 && code <= 0x9ffc)\r
619                                 {\r
620                                         subtracted = code - 0x8140;\r
621                                 }\r
622                                 else if (code >= 0xe040 && code <= 0xebbf)\r
623                                 {\r
624                                         subtracted = code - 0xc140;\r
625                                 }\r
626                                 if (subtracted == - 1)\r
627                                 {\r
628                                         throw new WriterException("Invalid byte sequence");\r
629                                 }\r
630                                 int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);\r
631                                 bits.appendBits(encoded, 13);\r
632                         }\r
633                 }\r
634                 \r
635                 private static void  appendECI(CharacterSetECI eci, BitVector bits)\r
636                 {\r
637                         bits.appendBits(Mode.ECI.Bits, 4);\r
638                         // This is correct for values up to 127, which is all we need now.\r
639                         bits.appendBits(eci.Value, 8);\r
640                 }\r
641         }\r
642 }