| <?php |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| namespace Zxing\Qrcode\Decoder; |
|
|
| use Zxing\DecodeHintType; |
| use Zxing\FormatException; |
| use Zxing\Common\BitSource; |
| use Zxing\Common\CharacterSetECI; |
| use Zxing\Common\DecoderResult; |
| use Zxing\Common\StringUtils; |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| final class DecodedBitStreamParser |
| { |
|
|
| |
| |
| |
| private static $ALPHANUMERIC_CHARS = [ |
| '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', |
| 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', |
| 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', |
| ' ', '$', '%', '*', '+', '-', '.', '/', ':', |
| ]; |
| private static $GB2312_SUBSET = 1; |
|
|
| public static function decode($bytes, |
| $version, |
| $ecLevel, |
| $hints) |
| { |
| $bits = new BitSource($bytes); |
| $result = ''; |
| $byteSegments = []; |
| $symbolSequence = -1; |
| $parityData = -1; |
|
|
| try { |
| $currentCharacterSetECI = null; |
| $fc1InEffect = false; |
| $mode = ''; |
| do { |
| |
| if ($bits->available() < 4) { |
| |
| $mode = Mode::$TERMINATOR; |
| } else { |
| $mode = Mode::forBits($bits->readBits(4)); |
| } |
| if ($mode != Mode::$TERMINATOR) { |
| if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) { |
| |
| $fc1InEffect = true; |
| } else if ($mode == Mode::$STRUCTURED_APPEND) { |
| if ($bits->available() < 16) { |
| throw FormatException::getFormatInstance(); |
| } |
| |
| |
| $symbolSequence = $bits->readBits(8); |
| $parityData = $bits->readBits(8); |
| } else if ($mode == Mode::$ECI) { |
| |
| $value = self::parseECIValue($bits); |
| $currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value); |
| if ($currentCharacterSetECI == null) { |
| throw FormatException::getFormatInstance(); |
| } |
| } else { |
| |
| if ($mode == Mode::$HANZI) { |
| |
| $subset = $bits->readBits(4); |
| $countHanzi = $bits->readBits($mode->getCharacterCountBits($version)); |
| if ($subset == self::$GB2312_SUBSET) { |
| self::decodeHanziSegment($bits, $result, $countHanzi); |
| } |
| } else { |
| |
| |
| $count = $bits->readBits($mode->getCharacterCountBits($version)); |
| if ($mode == Mode::$NUMERIC) { |
| self::decodeNumericSegment($bits, $result, $count); |
| } else if ($mode == Mode::$ALPHANUMERIC) { |
| self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect); |
| } else if ($mode == Mode::$BYTE) { |
| self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints); |
| } else if ($mode == Mode::$KANJI) { |
| self::decodeKanjiSegment($bits, $result, $count); |
| } else { |
| throw FormatException::getFormatInstance(); |
| } |
| } |
| } |
| } |
| } while ($mode != Mode::$TERMINATOR); |
| } catch (\InvalidArgumentException $iae) { |
| |
| throw FormatException::getFormatInstance(); |
| } |
|
|
| return new DecoderResult($bytes, |
| $result, |
| empty($byteSegments) ? null : $byteSegments, |
| $ecLevel == null ? null : 'L', |
| $symbolSequence, |
| $parityData); |
| } |
|
|
| private static function parseECIValue($bits) |
| { |
| $firstByte = $bits->readBits(8); |
| if (($firstByte & 0x80) == 0) { |
| |
| return $firstByte & 0x7F; |
| } |
| if (($firstByte & 0xC0) == 0x80) { |
| |
| $secondByte = $bits->readBits(8); |
|
|
| return (($firstByte & 0x3F) << 8) | $secondByte; |
| } |
| if (($firstByte & 0xE0) == 0xC0) { |
| |
| $secondThirdBytes = $bits->readBits(16); |
|
|
| return (($firstByte & 0x1F) << 16) | $secondThirdBytes; |
| } |
| throw FormatException::getFormatInstance(); |
| } |
|
|
| |
| |
| |
| private static function decodeHanziSegment($bits, |
| &$result, |
| $count) |
| { |
| |
| if ($count * 13 > $bits->available()) { |
| throw FormatException::getFormatInstance(); |
| } |
|
|
| |
| |
| $buffer = fill_array(0, 2 * $count, 0); |
| $offset = 0; |
| while ($count > 0) { |
| |
| $twoBytes = $bits->readBits(13); |
| $assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060); |
| if ($assembledTwoBytes < 0x003BF) { |
| |
| $assembledTwoBytes += 0x0A1A1; |
| } else { |
| |
| $assembledTwoBytes += 0x0A6A1; |
| } |
| $buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF); |
| $buffer[$offset + 1] = ($assembledTwoBytes & 0xFF); |
| $offset += 2; |
| $count--; |
| } |
| $result .= iconv('GB2312', 'UTF-8', implode($buffer)); |
| } |
|
|
| private static function decodeNumericSegment($bits, |
| &$result, |
| $count) |
| { |
| |
| while ($count >= 3) { |
| |
| if ($bits->available() < 10) { |
| throw FormatException::getFormatInstance(); |
| } |
| $threeDigitsBits = $bits->readBits(10); |
| if ($threeDigitsBits >= 1000) { |
| throw FormatException::getFormatInstance(); |
| } |
| $result .= (self::toAlphaNumericChar($threeDigitsBits / 100)); |
| $result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10)); |
| $result .= (self::toAlphaNumericChar($threeDigitsBits % 10)); |
| $count -= 3; |
| } |
| if ($count == 2) { |
| |
| if ($bits->available() < 7) { |
| throw FormatException::getFormatInstance(); |
| } |
| $twoDigitsBits = $bits->readBits(7); |
| if ($twoDigitsBits >= 100) { |
| throw FormatException::getFormatInstance(); |
| } |
| $result .= (self::toAlphaNumericChar($twoDigitsBits / 10)); |
| $result .= (self::toAlphaNumericChar($twoDigitsBits % 10)); |
| } else if ($count == 1) { |
| |
| if ($bits->available() < 4) { |
| throw FormatException::getFormatInstance(); |
| } |
| $digitBits = $bits->readBits(4); |
| if ($digitBits >= 10) { |
| throw FormatException::getFormatInstance(); |
| } |
| $result .= (self::toAlphaNumericChar($digitBits)); |
| } |
| } |
|
|
| private static function toAlphaNumericChar($value) |
| { |
| if ($value >= count(self::$ALPHANUMERIC_CHARS)) { |
| throw FormatException::getFormatInstance(); |
| } |
|
|
| return self::$ALPHANUMERIC_CHARS[$value]; |
| } |
|
|
| private static function decodeAlphanumericSegment($bits, |
| &$result, |
| $count, |
| $fc1InEffect) |
| { |
| |
| $start = strlen($result); |
| while ($count > 1) { |
| if ($bits->available() < 11) { |
| throw FormatException::getFormatInstance(); |
| } |
| $nextTwoCharsBits = $bits->readBits(11); |
| $result .= (self::toAlphaNumericChar($nextTwoCharsBits / 45)); |
| $result .= (self::toAlphaNumericChar($nextTwoCharsBits % 45)); |
| $count -= 2; |
| } |
| if ($count == 1) { |
| |
| if ($bits->available() < 6) { |
| throw FormatException::getFormatInstance(); |
| } |
| $result .= self::toAlphaNumericChar($bits->readBits(6)); |
| } |
| |
| if ($fc1InEffect) { |
| |
| for ($i = $start; $i < strlen($result); $i++) { |
| if ($result[$i] == '%') { |
| if ($i < strlen($result) - 1 && $result[$i + 1] == '%') { |
| |
| $result = substr_replace($result, '', $i + 1, 1); |
| } else { |
| |
| $result . setCharAt($i, chr(0x1D)); |
| } |
| } |
| } |
| } |
| } |
|
|
| private static function decodeByteSegment($bits, |
| &$result, |
| $count, |
| $currentCharacterSetECI, |
| &$byteSegments, |
| $hints) |
| { |
| |
| if (8 * $count > $bits->available()) { |
| throw FormatException::getFormatInstance(); |
| } |
|
|
| $readBytes = fill_array(0, $count, 0); |
| for ($i = 0; $i < $count; $i++) { |
| $readBytes[$i] = $bits->readBits(8); |
| } |
| $text = implode(array_map('chr', $readBytes)); |
| $encoding = ''; |
| if ($currentCharacterSetECI == null) { |
| |
| |
| |
| |
| |
|
|
| $encoding = mb_detect_encoding($text, $hints); |
| } else { |
| $encoding = $currentCharacterSetECI->name(); |
| } |
| |
| $result .= $text; |
|
|
| $byteSegments = array_merge($byteSegments, $readBytes); |
| } |
|
|
| private static function decodeKanjiSegment($bits, |
| &$result, |
| $count) |
| { |
| |
| if ($count * 13 > $bits->available()) { |
| throw FormatException::getFormatInstance(); |
| } |
|
|
| |
| |
| $buffer = [0, 2 * $count, 0]; |
| $offset = 0; |
| while ($count > 0) { |
| |
| $twoBytes = $bits->readBits(13); |
| $assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0); |
| if ($assembledTwoBytes < 0x01F00) { |
| |
| $assembledTwoBytes += 0x08140; |
| } else { |
| |
| $assembledTwoBytes += 0x0C140; |
| } |
| $buffer[$offset] = ($assembledTwoBytes >> 8); |
| $buffer[$offset + 1] = $assembledTwoBytes; |
| $offset += 2; |
| $count--; |
| } |
| |
|
|
| $result .= iconv('shift-jis', 'utf-8', implode($buffer)); |
| } |
|
|
| private function DecodedBitStreamParser() |
| { |
|
|
| } |
| } |
|
|