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 24 kB

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