You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Encoder.cs 21 kB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. using System;
  17. using System.Collections.Generic;
  18. using System.Text;
  19. using ZXing.Common;
  20. using ZXing.Common.ReedSolomon;
  21. namespace ZXing.QrCode.Internal
  22. {
  23. /// <summary>
  24. /// </summary>
  25. /// <author>satorux@google.com (Satoru Takabayashi) - creator</author>
  26. /// <author>dswitkin@google.com (Daniel Switkin) - ported from C++</author>
  27. public static class Encoder
  28. {
  29. internal static String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
  30. // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
  31. // Basically it applies four rules and summate all penalties.
  32. private static int calculateMaskPenalty(ByteMatrix matrix)
  33. {
  34. return MaskUtil.applyMaskPenaltyRule1(matrix)
  35. + MaskUtil.applyMaskPenaltyRule2(matrix)
  36. + MaskUtil.applyMaskPenaltyRule3(matrix)
  37. + MaskUtil.applyMaskPenaltyRule4(matrix);
  38. }
  39. /// <summary>
  40. /// Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
  41. /// internally by chooseMode(). On success, store the result in "qrCode".
  42. /// We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
  43. /// "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
  44. /// strong error correction for this purpose.
  45. /// Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
  46. /// with which clients can specify the encoding mode. For now, we don't need the functionality.
  47. /// </summary>
  48. /// <param name="content">text to encode</param>
  49. /// <param name="ecLevel">error correction level to use</param>
  50. /// <returns><see cref="QRCode"/> representing the encoded QR code</returns>
  51. public static QRCode encode(String content, ErrorCorrectionLevel ecLevel)
  52. {
  53. // Determine what character encoding has been specified by the caller, if any
  54. #if !SILVERLIGHT || WINDOWS_PHONE
  55. String encoding = DEFAULT_BYTE_MODE_ENCODING;
  56. //bool generateECI = !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding);
  57. #else
  58. // Silverlight supports only UTF-8 and UTF-16 out-of-the-box
  59. const string encoding = "UTF-8";
  60. // caller of the method can only control if the ECI segment should be written
  61. // character set is fixed to UTF-8; but some scanners doesn't like the ECI segment
  62. bool generateECI = (hints != null && hints.ContainsKey(EncodeHintType.CHARACTER_SET));
  63. #endif
  64. // Pick an encoding mode appropriate for the content. Note that this will not attempt to use
  65. // multiple modes / segments even if that were more efficient. Twould be nice.
  66. Mode mode = Mode.BYTE;
  67. // This will store the header information, like mode and
  68. // length, as well as "header" segments like an ECI segment.
  69. BitArray headerBits = new BitArray();
  70. /*
  71. // Append ECI segment if applicable
  72. if (mode == Mode.BYTE && generateECI)
  73. {
  74. CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
  75. if (eci != null)
  76. {
  77. var eciIsExplicitDisabled = (hints != null && hints.ContainsKey(EncodeHintType.DISABLE_ECI) ? (bool)hints[EncodeHintType.DISABLE_ECI] : false);
  78. if (!eciIsExplicitDisabled)
  79. {
  80. appendECI(eci, headerBits);
  81. }
  82. }
  83. }
  84. * */
  85. // (With ECI in place,) Write the mode marker
  86. appendModeInfo(mode, headerBits);
  87. // Collect data within the main segment, separately, to count its size if needed. Don't add it to
  88. // main payload yet.
  89. BitArray dataBits = new BitArray();
  90. appendBytes(content, mode, dataBits, encoding);
  91. // Hard part: need to know version to know how many bits length takes. But need to know how many
  92. // bits it takes to know version. First we take a guess at version by assuming version will be
  93. // the minimum, 1:
  94. int provisionalBitsNeeded = headerBits.Size
  95. + mode.getCharacterCountBits(Version.getVersionForNumber(1))
  96. + dataBits.Size;
  97. Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
  98. // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
  99. int bitsNeeded = headerBits.Size
  100. + mode.getCharacterCountBits(provisionalVersion)
  101. + dataBits.Size;
  102. Version version = chooseVersion(bitsNeeded, ecLevel);
  103. BitArray headerAndDataBits = new BitArray();
  104. headerAndDataBits.appendBitArray(headerBits);
  105. // Find "length" of main segment and write it
  106. int numLetters = mode == Mode.BYTE ? dataBits.SizeInBytes : content.Length;
  107. appendLengthInfo(numLetters, version, mode, headerAndDataBits);
  108. // Put data together into the overall payload
  109. headerAndDataBits.appendBitArray(dataBits);
  110. Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
  111. int numDataBytes = version.TotalCodewords - ecBlocks.TotalECCodewords;
  112. // Terminate the bits properly.
  113. terminateBits(numDataBytes, headerAndDataBits);
  114. // Interleave data bits with error correction code.
  115. BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
  116. version.TotalCodewords,
  117. numDataBytes,
  118. ecBlocks.NumBlocks);
  119. QRCode qrCode = new QRCode
  120. {
  121. ECLevel = ecLevel,
  122. Mode = mode,
  123. Version = version
  124. };
  125. // Choose the mask pattern and set to "qrCode".
  126. int dimension = version.DimensionForVersion;
  127. ByteMatrix matrix = new ByteMatrix(dimension, dimension);
  128. int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
  129. qrCode.MaskPattern = maskPattern;
  130. // Build the matrix and set it to "qrCode".
  131. MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
  132. qrCode.Matrix = matrix;
  133. return qrCode;
  134. }
  135. private static int chooseMaskPattern(BitArray bits,
  136. ErrorCorrectionLevel ecLevel,
  137. Version version,
  138. ByteMatrix matrix)
  139. {
  140. int minPenalty = Int32.MaxValue; // Lower penalty is better.
  141. int bestMaskPattern = -1;
  142. // We try all mask patterns to choose the best one.
  143. for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++)
  144. {
  145. MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
  146. int penalty = calculateMaskPenalty(matrix);
  147. if (penalty < minPenalty)
  148. {
  149. minPenalty = penalty;
  150. bestMaskPattern = maskPattern;
  151. }
  152. }
  153. return bestMaskPattern;
  154. }
  155. private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel)
  156. {
  157. // In the following comments, we use numbers of Version 7-H.
  158. for (int versionNum = 1; versionNum <= 40; versionNum++)
  159. {
  160. Version version = Version.getVersionForNumber(versionNum);
  161. // numBytes = 196
  162. int numBytes = version.TotalCodewords;
  163. // getNumECBytes = 130
  164. Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
  165. int numEcBytes = ecBlocks.TotalECCodewords;
  166. // getNumDataBytes = 196 - 130 = 66
  167. int numDataBytes = numBytes - numEcBytes;
  168. int totalInputBytes = (numInputBits + 7) / 8;
  169. if (numDataBytes >= totalInputBytes)
  170. {
  171. return version;
  172. }
  173. }
  174. throw new Exception("Data too big");
  175. }
  176. /// <summary>
  177. /// Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
  178. /// </summary>
  179. /// <param name="numDataBytes">The num data bytes.</param>
  180. /// <param name="bits">The bits.</param>
  181. internal static void terminateBits(int numDataBytes, BitArray bits)
  182. {
  183. int capacity = numDataBytes << 3;
  184. if (bits.Size > capacity)
  185. {
  186. throw new Exception("data bits cannot fit in the QR Code" + bits.Size + " > " +
  187. capacity);
  188. }
  189. for (int i = 0; i < 4 && bits.Size < capacity; ++i)
  190. {
  191. bits.appendBit(false);
  192. }
  193. // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
  194. // If the last byte isn't 8-bit aligned, we'll add padding bits.
  195. int numBitsInLastByte = bits.Size & 0x07;
  196. if (numBitsInLastByte > 0)
  197. {
  198. for (int i = numBitsInLastByte; i < 8; i++)
  199. {
  200. bits.appendBit(false);
  201. }
  202. }
  203. // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
  204. int numPaddingBytes = numDataBytes - bits.SizeInBytes;
  205. for (int i = 0; i < numPaddingBytes; ++i)
  206. {
  207. bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
  208. }
  209. if (bits.Size != capacity)
  210. {
  211. throw new Exception("Bits size does not equal capacity");
  212. }
  213. }
  214. /// <summary>
  215. /// Get number of data bytes and number of error correction bytes for block id "blockID". Store
  216. /// the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
  217. /// JISX0510:2004 (p.30)
  218. /// </summary>
  219. /// <param name="numTotalBytes">The num total bytes.</param>
  220. /// <param name="numDataBytes">The num data bytes.</param>
  221. /// <param name="numRSBlocks">The num RS blocks.</param>
  222. /// <param name="blockID">The block ID.</param>
  223. /// <param name="numDataBytesInBlock">The num data bytes in block.</param>
  224. /// <param name="numECBytesInBlock">The num EC bytes in block.</param>
  225. internal static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes,
  226. int numDataBytes,
  227. int numRSBlocks,
  228. int blockID,
  229. int[] numDataBytesInBlock,
  230. int[] numECBytesInBlock)
  231. {
  232. if (blockID >= numRSBlocks)
  233. {
  234. throw new Exception("Block ID too large");
  235. }
  236. // numRsBlocksInGroup2 = 196 % 5 = 1
  237. int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
  238. // numRsBlocksInGroup1 = 5 - 1 = 4
  239. int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
  240. // numTotalBytesInGroup1 = 196 / 5 = 39
  241. int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
  242. // numTotalBytesInGroup2 = 39 + 1 = 40
  243. int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
  244. // numDataBytesInGroup1 = 66 / 5 = 13
  245. int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
  246. // numDataBytesInGroup2 = 13 + 1 = 14
  247. int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
  248. // numEcBytesInGroup1 = 39 - 13 = 26
  249. int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
  250. // numEcBytesInGroup2 = 40 - 14 = 26
  251. int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
  252. // Sanity checks.
  253. // 26 = 26
  254. if (numEcBytesInGroup1 != numEcBytesInGroup2)
  255. {
  256. throw new Exception("EC bytes mismatch");
  257. }
  258. // 5 = 4 + 1.
  259. if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2)
  260. {
  261. throw new Exception("RS blocks mismatch");
  262. }
  263. // 196 = (13 + 26) * 4 + (14 + 26) * 1
  264. if (numTotalBytes !=
  265. ((numDataBytesInGroup1 + numEcBytesInGroup1) *
  266. numRsBlocksInGroup1) +
  267. ((numDataBytesInGroup2 + numEcBytesInGroup2) *
  268. numRsBlocksInGroup2))
  269. {
  270. throw new Exception("Total bytes mismatch");
  271. }
  272. if (blockID < numRsBlocksInGroup1)
  273. {
  274. numDataBytesInBlock[0] = numDataBytesInGroup1;
  275. numECBytesInBlock[0] = numEcBytesInGroup1;
  276. }
  277. else
  278. {
  279. numDataBytesInBlock[0] = numDataBytesInGroup2;
  280. numECBytesInBlock[0] = numEcBytesInGroup2;
  281. }
  282. }
  283. /// <summary>
  284. /// Interleave "bits" with corresponding error correction bytes. On success, store the result in
  285. /// "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
  286. /// </summary>
  287. /// <param name="bits">The bits.</param>
  288. /// <param name="numTotalBytes">The num total bytes.</param>
  289. /// <param name="numDataBytes">The num data bytes.</param>
  290. /// <param name="numRSBlocks">The num RS blocks.</param>
  291. /// <returns></returns>
  292. internal static BitArray interleaveWithECBytes(BitArray bits,
  293. int numTotalBytes,
  294. int numDataBytes,
  295. int numRSBlocks)
  296. {
  297. // "bits" must have "getNumDataBytes" bytes of data.
  298. if (bits.SizeInBytes != numDataBytes)
  299. {
  300. throw new Exception("Number of bits and data bytes does not match");
  301. }
  302. // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
  303. // store the divided data bytes blocks and error correction bytes blocks into "blocks".
  304. int dataBytesOffset = 0;
  305. int maxNumDataBytes = 0;
  306. int maxNumEcBytes = 0;
  307. // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
  308. var blocks = new List<BlockPair>(numRSBlocks);
  309. for (int i = 0; i < numRSBlocks; ++i)
  310. {
  311. int[] numDataBytesInBlock = new int[1];
  312. int[] numEcBytesInBlock = new int[1];
  313. getNumDataBytesAndNumECBytesForBlockID(
  314. numTotalBytes, numDataBytes, numRSBlocks, i,
  315. numDataBytesInBlock, numEcBytesInBlock);
  316. int size = numDataBytesInBlock[0];
  317. byte[] dataBytes = new byte[size];
  318. bits.toBytes(8 * dataBytesOffset, dataBytes, 0, size);
  319. byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
  320. blocks.Add(new BlockPair(dataBytes, ecBytes));
  321. maxNumDataBytes = Math.Max(maxNumDataBytes, size);
  322. maxNumEcBytes = Math.Max(maxNumEcBytes, ecBytes.Length);
  323. dataBytesOffset += numDataBytesInBlock[0];
  324. }
  325. if (numDataBytes != dataBytesOffset)
  326. {
  327. throw new Exception("Data bytes does not match offset");
  328. }
  329. BitArray result = new BitArray();
  330. // First, place data blocks.
  331. for (int i = 0; i < maxNumDataBytes; ++i)
  332. {
  333. foreach (BlockPair block in blocks)
  334. {
  335. byte[] dataBytes = block.DataBytes;
  336. if (i < dataBytes.Length)
  337. {
  338. result.appendBits(dataBytes[i], 8);
  339. }
  340. }
  341. }
  342. // Then, place error correction blocks.
  343. for (int i = 0; i < maxNumEcBytes; ++i)
  344. {
  345. foreach (BlockPair block in blocks)
  346. {
  347. byte[] ecBytes = block.ErrorCorrectionBytes;
  348. if (i < ecBytes.Length)
  349. {
  350. result.appendBits(ecBytes[i], 8);
  351. }
  352. }
  353. }
  354. if (numTotalBytes != result.SizeInBytes)
  355. { // Should be same.
  356. throw new Exception("Interleaving error: " + numTotalBytes + " and " +
  357. result.SizeInBytes + " differ.");
  358. }
  359. return result;
  360. }
  361. internal static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock)
  362. {
  363. int numDataBytes = dataBytes.Length;
  364. int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
  365. for (int i = 0; i < numDataBytes; i++)
  366. {
  367. toEncode[i] = dataBytes[i] & 0xFF;
  368. }
  369. new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock);
  370. byte[] ecBytes = new byte[numEcBytesInBlock];
  371. for (int i = 0; i < numEcBytesInBlock; i++)
  372. {
  373. ecBytes[i] = (byte)toEncode[numDataBytes + i];
  374. }
  375. return ecBytes;
  376. }
  377. /// <summary>
  378. /// Append mode info. On success, store the result in "bits".
  379. /// </summary>
  380. /// <param name="mode">The mode.</param>
  381. /// <param name="bits">The bits.</param>
  382. internal static void appendModeInfo(Mode mode, BitArray bits)
  383. {
  384. bits.appendBits(mode.Bits, 4);
  385. }
  386. /// <summary>
  387. /// Append length info. On success, store the result in "bits".
  388. /// </summary>
  389. /// <param name="numLetters">The num letters.</param>
  390. /// <param name="version">The version.</param>
  391. /// <param name="mode">The mode.</param>
  392. /// <param name="bits">The bits.</param>
  393. internal static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits)
  394. {
  395. int numBits = mode.getCharacterCountBits(version);
  396. if (numLetters >= (1 << numBits))
  397. {
  398. throw new Exception(numLetters + " is bigger than " + ((1 << numBits) - 1));
  399. }
  400. bits.appendBits(numLetters, numBits);
  401. }
  402. /// <summary>
  403. /// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
  404. /// </summary>
  405. /// <param name="content">The content.</param>
  406. /// <param name="mode">The mode.</param>
  407. /// <param name="bits">The bits.</param>
  408. /// <param name="encoding">The encoding.</param>
  409. internal static void appendBytes(String content,
  410. Mode mode,
  411. BitArray bits,
  412. String encoding)
  413. {
  414. if (mode.Equals(Mode.BYTE))
  415. append8BitBytes(content, bits, encoding);
  416. else
  417. throw new Exception("Invalid mode: " + mode);
  418. }
  419. internal static void append8BitBytes(String content, BitArray bits, String encoding)
  420. {
  421. byte[] bytes;
  422. try
  423. {
  424. bytes = Encoding.GetEncoding(encoding).GetBytes(content);
  425. }
  426. #if WindowsCE
  427. catch (PlatformNotSupportedException)
  428. {
  429. try
  430. {
  431. // WindowsCE doesn't support all encodings. But it is device depended.
  432. // So we try here the some different ones
  433. if (encoding == "ISO-8859-1")
  434. {
  435. bytes = Encoding.GetEncoding(1252).GetBytes(content);
  436. }
  437. else
  438. {
  439. bytes = Encoding.GetEncoding("UTF-8").GetBytes(content);
  440. }
  441. }
  442. catch (Exception uee)
  443. {
  444. throw new WriterException(uee.Message, uee);
  445. }
  446. }
  447. #endif
  448. catch (Exception uee)
  449. {
  450. throw new Exception(uee.Message, uee);
  451. }
  452. foreach (byte b in bytes)
  453. {
  454. bits.appendBits(b, 8);
  455. }
  456. }
  457. /*
  458. private static void appendECI(CharacterSetECI eci, BitArray bits)
  459. {
  460. bits.appendBits(Mode.ECI.Bits, 4);
  461. // This is correct for values up to 127, which is all we need now.
  462. bits.appendBits(eci.Value, 8);
  463. }
  464. * */
  465. }
  466. }