Decoder.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /*
  3. * Copyright 2007 ZXing authors
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. namespace Zxing\Qrcode\Decoder;
  18. use Zxing\ChecksumException;
  19. use Zxing\DecodeHintType;
  20. use Zxing\FormatException;
  21. use Zxing\Common\BitMatrix;
  22. use Zxing\Common\DecoderResult;
  23. use Zxing\Common\Reedsolomon\GenericGF;
  24. use Zxing\Common\Reedsolomon\ReedSolomonDecoder;
  25. use Zxing\Common\Reedsolomon\ReedSolomonException;
  26. /**
  27. * <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
  28. * the QR Code from an image.</p>
  29. *
  30. * @author Sean Owen
  31. */
  32. final class Decoder
  33. {
  34. private $rsDecoder;
  35. public function __construct()
  36. {
  37. $this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
  38. }
  39. public function decode($variable, $hints = null)
  40. {
  41. if (is_array($variable)) {
  42. return $this->decodeImage($variable, $hints);
  43. } elseif ($variable instanceof BitMatrix) {
  44. return $this->decodeBits($variable, $hints);
  45. } elseif ($variable instanceof BitMatrixParser) {
  46. return $this->decodeParser($variable, $hints);
  47. }
  48. die('decode error Decoder.php');
  49. }
  50. /**
  51. * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
  52. * "true" is taken to mean a black module.</p>
  53. *
  54. * @param array $image booleans representing white/black QR Code modules
  55. * @param hints decoding hints that should be used to influence decoding
  56. *
  57. * @return text and bytes encoded within the QR Code
  58. * @throws FormatException if the QR Code cannot be decoded
  59. * @throws ChecksumException if error correction fails
  60. */
  61. public function decodeImage($image, $hints = null)
  62. {
  63. $dimension = count($image);
  64. $bits = new BitMatrix($dimension);
  65. for ($i = 0; $i < $dimension; $i++) {
  66. for ($j = 0; $j < $dimension; $j++) {
  67. if ($image[$i][$j]) {
  68. $bits->set($j, $i);
  69. }
  70. }
  71. }
  72. return $this->decode($bits, $hints);
  73. }
  74. /**
  75. * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
  76. *
  77. * @param BitMatrix $bits booleans representing white/black QR Code modules
  78. * @param hints decoding hints that should be used to influence decoding
  79. *
  80. * @return text and bytes encoded within the QR Code
  81. * @throws FormatException if the QR Code cannot be decoded
  82. * @throws ChecksumException if error correction fails
  83. */
  84. public function decodeBits($bits, $hints = null)
  85. {
  86. // Construct a parser and read version, error-correction level
  87. $parser = new BitMatrixParser($bits);
  88. $fe = null;
  89. $ce = null;
  90. try {
  91. return $this->decode($parser, $hints);
  92. } catch (FormatException $e) {
  93. $fe = $e;
  94. } catch (ChecksumException $e) {
  95. $ce = $e;
  96. }
  97. try {
  98. // Revert the bit matrix
  99. $parser->remask();
  100. // Will be attempting a mirrored reading of the version and format info.
  101. $parser->setMirror(true);
  102. // Preemptively read the version.
  103. $parser->readVersion();
  104. // Preemptively read the format information.
  105. $parser->readFormatInformation();
  106. /*
  107. * Since we're here, this means we have successfully detected some kind
  108. * of version and format information when mirrored. This is a good sign,
  109. * that the QR code may be mirrored, and we should try once more with a
  110. * mirrored content.
  111. */
  112. // Prepare for a mirrored reading.
  113. $parser->mirror();
  114. $result = $this->decode($parser, $hints);
  115. // Success! Notify the caller that the code was mirrored.
  116. $result->setOther(new QRCodeDecoderMetaData(true));
  117. return $result;
  118. } catch (FormatException $e) {// catch (FormatException | ChecksumException e) {
  119. // Throw the exception from the original reading
  120. if ($fe != null) {
  121. throw $fe;
  122. }
  123. if ($ce != null) {
  124. throw $ce;
  125. }
  126. throw $e;
  127. }
  128. }
  129. private function decodeParser($parser, $hints = null)
  130. {
  131. $version = $parser->readVersion();
  132. $ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
  133. // Read codewords
  134. $codewords = $parser->readCodewords();
  135. // Separate into data blocks
  136. $dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
  137. // Count total number of data bytes
  138. $totalBytes = 0;
  139. foreach ($dataBlocks as $dataBlock) {
  140. $totalBytes += $dataBlock->getNumDataCodewords();
  141. }
  142. $resultBytes = fill_array(0, $totalBytes, 0);
  143. $resultOffset = 0;
  144. // Error-correct and copy data blocks together into a stream of bytes
  145. foreach ($dataBlocks as $dataBlock) {
  146. $codewordBytes = $dataBlock->getCodewords();
  147. $numDataCodewords = $dataBlock->getNumDataCodewords();
  148. $this->correctErrors($codewordBytes, $numDataCodewords);
  149. for ($i = 0; $i < $numDataCodewords; $i++) {
  150. $resultBytes[$resultOffset++] = $codewordBytes[$i];
  151. }
  152. }
  153. // Decode the contents of that stream of bytes
  154. return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
  155. }
  156. /**
  157. * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
  158. * correct the errors in-place using Reed-Solomon error correction.</p>
  159. *
  160. * @param codewordBytes data and error correction codewords
  161. * @param numDataCodewords number of codewords that are data bytes
  162. *
  163. * @throws ChecksumException if error correction fails
  164. */
  165. private function correctErrors(&$codewordBytes, $numDataCodewords)
  166. {
  167. $numCodewords = count($codewordBytes);
  168. // First read into an array of ints
  169. $codewordsInts = fill_array(0, $numCodewords, 0);
  170. for ($i = 0; $i < $numCodewords; $i++) {
  171. $codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
  172. }
  173. $numECCodewords = count($codewordBytes) - $numDataCodewords;
  174. try {
  175. $this->rsDecoder->decode($codewordsInts, $numECCodewords);
  176. } catch (ReedSolomonException $ignored) {
  177. throw ChecksumException::getChecksumInstance();
  178. }
  179. // Copy back into array of bytes -- only need to worry about the bytes that were data
  180. // We don't care about errors in the error-correction codewords
  181. for ($i = 0; $i < $numDataCodewords; $i++) {
  182. $codewordBytes[$i] = $codewordsInts[$i];
  183. }
  184. }
  185. }