|
|
<?php |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace Zxing\Qrcode\Decoder; |
|
|
|
|
|
use Zxing\ChecksumException; |
|
|
use Zxing\DecodeHintType; |
|
|
use Zxing\FormatException; |
|
|
use Zxing\Common\BitMatrix; |
|
|
use Zxing\Common\DecoderResult; |
|
|
use Zxing\Common\Reedsolomon\GenericGF; |
|
|
use Zxing\Common\Reedsolomon\ReedSolomonDecoder; |
|
|
use Zxing\Common\Reedsolomon\ReedSolomonException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final class Decoder |
|
|
{ |
|
|
|
|
|
private $rsDecoder; |
|
|
|
|
|
public function __construct() |
|
|
{ |
|
|
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256); |
|
|
} |
|
|
|
|
|
public function decode($variable, $hints = null) |
|
|
{ |
|
|
if (is_array($variable)) { |
|
|
return $this->decodeImage($variable, $hints); |
|
|
} elseif ($variable instanceof BitMatrix) { |
|
|
return $this->decodeBits($variable, $hints); |
|
|
} elseif ($variable instanceof BitMatrixParser) { |
|
|
return $this->decodeParser($variable, $hints); |
|
|
} |
|
|
die('decode error Decoder.php'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function decodeImage($image, $hints = null) |
|
|
{ |
|
|
$dimension = count($image); |
|
|
$bits = new BitMatrix($dimension); |
|
|
for ($i = 0; $i < $dimension; $i++) { |
|
|
for ($j = 0; $j < $dimension; $j++) { |
|
|
if ($image[$i][$j]) { |
|
|
$bits->set($j, $i); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return $this->decode($bits, $hints); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function decodeBits($bits, $hints = null) |
|
|
{ |
|
|
|
|
|
|
|
|
$parser = new BitMatrixParser($bits); |
|
|
$fe = null; |
|
|
$ce = null; |
|
|
try { |
|
|
return $this->decode($parser, $hints); |
|
|
} catch (FormatException $e) { |
|
|
$fe = $e; |
|
|
} catch (ChecksumException $e) { |
|
|
$ce = $e; |
|
|
} |
|
|
|
|
|
try { |
|
|
|
|
|
|
|
|
$parser->remask(); |
|
|
|
|
|
|
|
|
$parser->setMirror(true); |
|
|
|
|
|
|
|
|
$parser->readVersion(); |
|
|
|
|
|
|
|
|
$parser->readFormatInformation(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$parser->mirror(); |
|
|
|
|
|
$result = $this->decode($parser, $hints); |
|
|
|
|
|
|
|
|
$result->setOther(new QRCodeDecoderMetaData(true)); |
|
|
|
|
|
return $result; |
|
|
|
|
|
} catch (FormatException $e) { |
|
|
|
|
|
if ($fe != null) { |
|
|
throw $fe; |
|
|
} |
|
|
if ($ce != null) { |
|
|
throw $ce; |
|
|
} |
|
|
throw $e; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
private function decodeParser($parser, $hints = null) |
|
|
{ |
|
|
$version = $parser->readVersion(); |
|
|
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel(); |
|
|
|
|
|
|
|
|
$codewords = $parser->readCodewords(); |
|
|
|
|
|
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel); |
|
|
|
|
|
|
|
|
$totalBytes = 0; |
|
|
foreach ($dataBlocks as $dataBlock) { |
|
|
$totalBytes += $dataBlock->getNumDataCodewords(); |
|
|
} |
|
|
$resultBytes = fill_array(0, $totalBytes, 0); |
|
|
$resultOffset = 0; |
|
|
|
|
|
|
|
|
foreach ($dataBlocks as $dataBlock) { |
|
|
$codewordBytes = $dataBlock->getCodewords(); |
|
|
$numDataCodewords = $dataBlock->getNumDataCodewords(); |
|
|
$this->correctErrors($codewordBytes, $numDataCodewords); |
|
|
for ($i = 0; $i < $numDataCodewords; $i++) { |
|
|
$resultBytes[$resultOffset++] = $codewordBytes[$i]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function correctErrors(&$codewordBytes, $numDataCodewords) |
|
|
{ |
|
|
$numCodewords = count($codewordBytes); |
|
|
|
|
|
$codewordsInts = fill_array(0, $numCodewords, 0); |
|
|
for ($i = 0; $i < $numCodewords; $i++) { |
|
|
$codewordsInts[$i] = $codewordBytes[$i] & 0xFF; |
|
|
} |
|
|
$numECCodewords = count($codewordBytes) - $numDataCodewords; |
|
|
try { |
|
|
$this->rsDecoder->decode($codewordsInts, $numECCodewords); |
|
|
} catch (ReedSolomonException $ignored) { |
|
|
throw ChecksumException::getChecksumInstance(); |
|
|
} |
|
|
|
|
|
|
|
|
for ($i = 0; $i < $numDataCodewords; $i++) { |
|
|
$codewordBytes[$i] = $codewordsInts[$i]; |
|
|
} |
|
|
} |
|
|
} |
|
|
|