Decoder.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. private $rsDecoder;
  34. public function __construct() {
  35. $this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
  36. }
  37. function decode($variable, $hints=null){
  38. if(is_array($variable)){
  39. return $this->decodeImage($variable,$hints);
  40. }elseif(is_object($variable)&&$variable instanceof BitMatrix){
  41. return $this->decodeBits($variable,$hints);
  42. }elseif(is_object($variable)&&$variable instanceof BitMatrixParser){
  43. return $this->decodeParser($variable,$hints);
  44. }else{
  45. die('decode error Decoder.php');
  46. }
  47. }
  48. /**
  49. * <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
  50. * "true" is taken to mean a black module.</p>
  51. *
  52. * @param image booleans representing white/black QR Code modules
  53. * @param hints decoding hints that should be used to influence decoding
  54. * @return text and bytes encoded within the QR Code
  55. * @throws FormatException if the QR Code cannot be decoded
  56. * @throws ChecksumException if error correction fails
  57. */
  58. public function decodeImage($image, $hints=null)
  59. {
  60. $dimension = count($image);
  61. $bits = new BitMatrix($dimension);
  62. for ($i = 0; $i < $dimension; $i++) {
  63. for ($j = 0; $j < $dimension; $j++) {
  64. if ($image[$i][$j]) {
  65. $bits->set($j, $i);
  66. }
  67. }
  68. }
  69. return $this->decode($bits, $hints);
  70. }
  71. /**
  72. * <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
  73. *
  74. * @param bits booleans representing white/black QR Code modules
  75. * @param hints decoding hints that should be used to influence decoding
  76. * @return text and bytes encoded within the QR Code
  77. * @throws FormatException if the QR Code cannot be decoded
  78. * @throws ChecksumException if error correction fails
  79. */
  80. public function decodeBits($bits, $hints=null)
  81. {
  82. // Construct a parser and read version, error-correction level
  83. $parser = new BitMatrixParser($bits);
  84. $fe = null;
  85. $ce = null;
  86. try {
  87. return $this->decode($parser, $hints);
  88. } catch (FormatException $e) {
  89. $fe = $e;
  90. } catch (ChecksumException $e) {
  91. $ce = $e;
  92. }
  93. try {
  94. // Revert the bit matrix
  95. $parser->remask();
  96. // Will be attempting a mirrored reading of the version and format info.
  97. $parser->setMirror(true);
  98. // Preemptively read the version.
  99. $parser->readVersion();
  100. // Preemptively read the format information.
  101. $parser->readFormatInformation();
  102. /*
  103. * Since we're here, this means we have successfully detected some kind
  104. * of version and format information when mirrored. This is a good sign,
  105. * that the QR code may be mirrored, and we should try once more with a
  106. * mirrored content.
  107. */
  108. // Prepare for a mirrored reading.
  109. $parser->mirror();
  110. $result = $this->decode($parser, $hints);
  111. // Success! Notify the caller that the code was mirrored.
  112. $result->setOther(new QRCodeDecoderMetaData(true));
  113. return $result;
  114. } catch (FormatException $e ) {// catch (FormatException | ChecksumException e) {
  115. // Throw the exception from the original reading
  116. if ($fe != null) {
  117. throw $fe;
  118. }
  119. if ($ce != null) {
  120. throw $ce;
  121. }
  122. throw $e;
  123. }
  124. }
  125. private function decodeParser($parser,$hints=null)
  126. {
  127. $version = $parser->readVersion();
  128. $ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
  129. // Read codewords
  130. $codewords = $parser->readCodewords();
  131. // Separate into data blocks
  132. $dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
  133. // Count total number of data bytes
  134. $totalBytes = 0;
  135. foreach ($dataBlocks as $dataBlock) {
  136. $totalBytes += $dataBlock->getNumDataCodewords();
  137. }
  138. $resultBytes = fill_array(0,$totalBytes,0);
  139. $resultOffset = 0;
  140. // Error-correct and copy data blocks together into a stream of bytes
  141. foreach ($dataBlocks as $dataBlock) {
  142. $codewordBytes = $dataBlock->getCodewords();
  143. $numDataCodewords = $dataBlock->getNumDataCodewords();
  144. $this->correctErrors($codewordBytes, $numDataCodewords);
  145. for ($i = 0; $i < $numDataCodewords; $i++) {
  146. $resultBytes[$resultOffset++] = $codewordBytes[$i];
  147. }
  148. }
  149. // Decode the contents of that stream of bytes
  150. return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
  151. }
  152. /**
  153. * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
  154. * correct the errors in-place using Reed-Solomon error correction.</p>
  155. *
  156. * @param codewordBytes data and error correction codewords
  157. * @param numDataCodewords number of codewords that are data bytes
  158. * @throws ChecksumException if error correction fails
  159. */
  160. private function correctErrors(&$codewordBytes, $numDataCodewords){
  161. $numCodewords = count($codewordBytes);
  162. // First read into an array of ints
  163. $codewordsInts =fill_array(0,$numCodewords,0);
  164. for ($i = 0; $i < $numCodewords; $i++) {
  165. $codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
  166. }
  167. $numECCodewords = count($codewordBytes)- $numDataCodewords;
  168. try {
  169. $this->rsDecoder->decode($codewordsInts, $numECCodewords);
  170. } catch (ReedSolomonException $ignored) {
  171. throw ChecksumException::getChecksumInstance();
  172. }
  173. // Copy back into array of bytes -- only need to worry about the bytes that were data
  174. // We don't care about errors in the error-correction codewords
  175. for ($i = 0; $i < $numDataCodewords; $i++) {
  176. $codewordBytes[$i] = $codewordsInts[$i];
  177. }
  178. }
  179. }