Quellcode durchsuchen

:octocat: +data mode decodeSegment() (WIP)

codemasher vor 4 Jahren
Ursprung
Commit
22f1672fac
6 geänderte Dateien mit 227 neuen und 18 gelöschten Zeilen
  1. 46 1
      src/Data/AlphaNum.php
  2. 21 0
      src/Data/Byte.php
  3. 42 10
      src/Data/ECI.php
  4. 39 6
      src/Data/Kanji.php
  5. 74 1
      src/Data/Number.php
  6. 5 0
      src/Data/QRDataModeInterface.php

+ 46 - 1
src/Data/AlphaNum.php

@@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Data;
 
 use chillerlan\QRCode\Common\{BitBuffer, Mode};
 
-use function ceil, ord, sprintf, str_split;
+use function array_flip, ceil, ord, sprintf, str_split;
 
 /**
  * Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
@@ -98,4 +98,49 @@ final class AlphaNum extends QRDataModeAbstract{
 		return self::CHAR_MAP_ALPHANUM[$chr];
 	}
 
+	/**
+	 * @inheritdoc
+	 *
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
+	 */
+	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+		$length  = $bitBuffer->read(Mode::getLengthBitsForVersion(self::$datamode, $versionNumber));
+		$charmap = array_flip(self::CHAR_MAP_ALPHANUM);
+
+		// @todo
+		$toAlphaNumericChar = function(int $ord) use ($charmap):string{
+
+			if(isset($charmap[$ord])){
+				return $charmap[$ord];
+			}
+
+			throw new QRCodeDataException('invalid character value: '.$ord);
+		};
+
+		$result = '';
+		// Read two characters at a time
+		while($length > 1){
+
+			if($bitBuffer->available() < 11){
+				throw new QRCodeDataException('not enough bits available');
+			}
+
+			$nextTwoCharsBits = $bitBuffer->read(11);
+			$result           .= $toAlphaNumericChar($nextTwoCharsBits / 45);
+			$result           .= $toAlphaNumericChar($nextTwoCharsBits % 45);
+			$length           -= 2;
+		}
+
+		if($length === 1){
+			// special case: one character left
+			if($bitBuffer->available() < 6){
+				throw new QRCodeDataException('not enough bits available');
+			}
+
+			$result .= $toAlphaNumericChar($bitBuffer->read(6));
+		}
+
+		return $result;
+	}
+
 }

+ 21 - 0
src/Data/Byte.php

@@ -60,4 +60,25 @@ final class Byte extends QRDataModeAbstract{
 
 	}
 
+	/**
+	 * @inheritdoc
+	 *
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
+	 */
+	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+		$length = $bitBuffer->read(Mode::getLengthBitsForVersion(self::$datamode, $versionNumber));
+
+		if($bitBuffer->available() < 8 * $length){
+			throw new QRCodeDataException('not enough bits available');
+		}
+
+		$readBytes = '';
+
+		for($i = 0; $i < $length; $i++){
+			$readBytes .= \chr($bitBuffer->read(8));
+		}
+
+		return $readBytes;
+	}
+
 }

+ 42 - 10
src/Data/ECI.php

@@ -43,24 +43,56 @@ final class ECI extends QRDataModeAbstract{
 		return 8;
 	}
 
-	/**
-	 * Unused, but required as per interface
-	 *
+		/**
 	 * @inheritDoc
-	 * @codeCoverageIgnore
+	 */
+	public function write(BitBuffer $bitBuffer, int $versionNumber):void{
+		$bitBuffer
+			->put($this::$datamode, 4)
+			->put($this->encoding, 8)
+		;
+	}
+
+	/**
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
+	 */
+	public static function parseValue(BitBuffer $bitBuffer):int{
+		$firstByte = $bitBuffer->read(8);
+
+		if(($firstByte & 0x80) === 0){
+			// just one byte
+			return $firstByte & 0x7f;
+		}
+
+		if(($firstByte & 0xc0) === 0x80){
+			// two bytes
+			$secondByte = $bitBuffer->read(8);
+
+			return (($firstByte & 0x3f) << 8) | $secondByte;
+		}
+
+		if(($firstByte & 0xe0) === 0xC0){
+			// three bytes
+			$secondThirdBytes = $bitBuffer->read(16);
+
+			return (($firstByte & 0x1f) << 16) | $secondThirdBytes;
+		}
+
+		throw new QRCodeDataException('error decoding ECI value');
+	}
+
+	/**
+	 * @codeCoverageIgnore Unused, but required as per interface
 	 */
 	public static function validateString(string $string):bool{
 		return true;
 	}
 
 	/**
-	 * @inheritDoc
+	 * @codeCoverageIgnore Unused, but required as per interface
 	 */
-	public function write(BitBuffer $bitBuffer, int $versionNumber):void{
-		$bitBuffer
-			->put($this::$datamode, 4)
-			->put($this->encoding, 8)
-		;
+	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+		return '';
 	}
 
 }

+ 39 - 6
src/Data/Kanji.php

@@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Data;
 
 use chillerlan\QRCode\Common\{BitBuffer, Mode};
 
-use function mb_convert_encoding, mb_detect_encoding, mb_strlen, ord, sprintf, strlen;
+use function chr, implode, mb_convert_encoding, mb_detect_encoding, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
 
 /**
  * Kanji mode: double-byte characters from the Shift JIS character set
@@ -57,7 +57,7 @@ final class Kanji extends QRDataModeAbstract{
 		while($i + 1 < $len){
 			$c = ((0xff & ord($string[$i])) << 8) | (0xff & ord($string[$i + 1]));
 
-			if(!($c >= 0x8140 && $c <= 0x9FFC) && !($c >= 0xE040 && $c <= 0xEBBF)){
+			if(!($c >= 0x8140 && $c <= 0x9ffc) && !($c >= 0xe040 && $c <= 0xebbf)){
 				return false;
 			}
 
@@ -84,17 +84,17 @@ final class Kanji extends QRDataModeAbstract{
 		for($i = 0; $i + 1 < $len; $i += 2){
 			$c = ((0xff & ord($this->data[$i])) << 8) | (0xff & ord($this->data[$i + 1]));
 
-			if($c >= 0x8140 && $c <= 0x9FFC){
+			if($c >= 0x8140 && $c <= 0x9ffC){
 				$c -= 0x8140;
 			}
-			elseif($c >= 0xE040 && $c <= 0xEBBF){
-				$c -= 0xC140;
+			elseif($c >= 0xe040 && $c <= 0xebbf){
+				$c -= 0xc140;
 			}
 			else{
 				throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c));
 			}
 
-			$bitBuffer->put(((($c >> 8) & 0xff) * 0xC0) + ($c & 0xff), 13);
+			$bitBuffer->put(((($c >> 8) & 0xff) * 0xc0) + ($c & 0xff), 13);
 		}
 
 		if($i < $len){
@@ -103,4 +103,37 @@ final class Kanji extends QRDataModeAbstract{
 
 	}
 
+	/**
+	 * @inheritdoc
+	 *
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
+	 */
+	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+		$length = $bitBuffer->read(Mode::getLengthBitsForVersion(self::$datamode, $versionNumber));
+
+		if($bitBuffer->available() < $length * 13){
+			throw new QRCodeDataException('not enough bits available');
+		}
+
+		$buffer = [];
+		$offset = 0;
+
+		while($length > 0){
+			// Each 13 bits encodes a 2-byte character
+			$twoBytes          = $bitBuffer->read(13);
+			$assembledTwoBytes = (($twoBytes / 0x0c0) << 8) | ($twoBytes % 0x0c0);
+
+			$assembledTwoBytes += ($assembledTwoBytes < 0x01f00)
+				? 0x08140  // In the 0x8140 to 0x9FFC range
+				: 0x0c140; // In the 0xE040 to 0xEBBF range
+
+			$buffer[$offset]     = chr(0xff & ($assembledTwoBytes >> 8));
+			$buffer[$offset + 1] = chr(0xff & $assembledTwoBytes);
+			$offset              += 2;
+			$length--;
+		}
+
+		return mb_convert_encoding(implode($buffer), mb_internal_encoding(), 'SJIS');
+	}
+
 }

+ 74 - 1
src/Data/Number.php

@@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Data;
 
 use chillerlan\QRCode\Common\{BitBuffer, Mode};
 
-use function ceil, ord, sprintf, str_split, substr;
+use function array_flip, ceil, ord, sprintf, str_split, substr;
 
 /**
  * Numeric mode: decimal digits 0 to 9
@@ -110,4 +110,77 @@ final class Number extends QRDataModeAbstract{
 		return $num;
 	}
 
+	/**
+	 * @inheritdoc
+	 *
+	 * @throws \chillerlan\QRCode\Data\QRCodeDataException
+	 */
+	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
+		$length  = $bitBuffer->read(Mode::getLengthBitsForVersion(self::$datamode, $versionNumber));
+		$charmap = array_flip(self::CHAR_MAP_NUMBER);
+
+		// @todo
+		$toNumericChar = function(int $ord) use ($charmap):string{
+
+			if(isset($charmap[$ord])){
+				return $charmap[$ord];
+			}
+
+			throw new QRCodeDataException('invalid character value: '.$ord);
+		};
+
+		$result = '';
+		// Read three digits at a time
+		while($length >= 3){
+			// Each 10 bits encodes three digits
+			if($bitBuffer->available() < 10){
+				throw new QRCodeDataException('not enough bits available');
+			}
+
+			$threeDigitsBits = $bitBuffer->read(10);
+
+			if($threeDigitsBits >= 1000){
+				throw new QRCodeDataException('error decoding numeric value');
+			}
+
+			$result .= $toNumericChar($threeDigitsBits / 100);
+			$result .= $toNumericChar(($threeDigitsBits / 10) % 10);
+			$result .= $toNumericChar($threeDigitsBits % 10);
+
+			$length -= 3;
+		}
+
+		if($length === 2){
+			// Two digits left over to read, encoded in 7 bits
+			if($bitBuffer->available() < 7){
+				throw new QRCodeDataException('not enough bits available');
+			}
+
+			$twoDigitsBits = $bitBuffer->read(7);
+
+			if($twoDigitsBits >= 100){
+				throw new QRCodeDataException('error decoding numeric value');
+			}
+
+			$result .= $toNumericChar($twoDigitsBits / 10);
+			$result .= $toNumericChar($twoDigitsBits % 10);
+		}
+		elseif($length === 1){
+			// One digit left over to read
+			if($bitBuffer->available() < 4){
+				throw new QRCodeDataException('not enough bits available');
+			}
+
+			$digitBits = $bitBuffer->read(4);
+
+			if($digitBits >= 10){
+				throw new QRCodeDataException('error decoding numeric value');
+			}
+
+			$result .= $toNumericChar($digitBits);
+		}
+
+		return $result;
+	}
+
 }

+ 5 - 0
src/Data/QRDataModeInterface.php

@@ -42,4 +42,9 @@ interface QRDataModeInterface{
 	 */
 	public function write(BitBuffer $bitBuffer, int $versionNumber):void;
 
+	/**
+	 * reads a segment from the BitBuffer and decodes in the current data mode
+	 */
+	public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string;
+
 }