1111namespace chillerlan \QRCode \Data ;
1212
1313use chillerlan \QRCode \Common \{BitBuffer , ECICharset , Mode };
14+ use function mb_convert_encoding , mb_detect_encoding , mb_internal_encoding , sprintf ;
1415
1516/**
1617 * Adds an ECI Designator
1718 *
19+ * ISO/IEC 18004:2000 8.4.1.1
20+ *
1821 * Please note that you have to take care for the correct data encoding when adding with QRCode::add*Segment()
1922 */
2023final class ECI extends QRDataModeAbstract{
@@ -34,48 +37,76 @@ final class ECI extends QRDataModeAbstract{
3437 * @noinspection PhpMissingParentConstructorInspection
3538 */
3639 public function __construct (int $ encoding ){
40+
41+ if ($ encoding < 0 || $ encoding > 999999 ){
42+ throw new QRCodeDataException (sprintf ('invalid encoding id: "%s" ' , $ encoding ));
43+ }
44+
3745 $ this ->encoding = $ encoding ;
3846 }
3947
4048 /**
4149 * @inheritDoc
4250 */
4351 public function getLengthInBits ():int {
44- return 8 ;
52+
53+ if ($ this ->encoding < 128 ){
54+ return 8 ;
55+ }
56+
57+ if ($ this ->encoding < 16384 ){
58+ return 16 ;
59+ }
60+
61+ return 24 ;
4562 }
4663
4764 /**
65+ * Writes an ECI designator to the bitbuffer
66+ *
4867 * @inheritDoc
4968 */
50- public function write (BitBuffer $ bitBuffer , int $ versionNumber ):void {
51- $ bitBuffer
52- ->put ($ this ::$ datamode , 4 )
53- ->put ($ this ->encoding , 8 )
54- ;
69+ public function write (BitBuffer $ bitBuffer , int $ versionNumber ):QRDataModeInterface {
70+ $ bitBuffer ->put ($ this ::$ datamode , 4 );
71+
72+ if ($ this ->encoding < 128 ){
73+ $ bitBuffer ->put ($ this ->encoding , 8 );
74+ }
75+ elseif ($ this ->encoding < 16384 ){
76+ $ bitBuffer ->put (($ this ->encoding | 0x8000 ), 16 );
77+ }
78+ elseif ($ this ->encoding < 1000000 ){
79+ $ bitBuffer ->put (($ this ->encoding | 0xC00000 ), 24 );
80+ }
81+
82+ return $ this ;
5583 }
5684
5785 /**
86+ * Reads and parses the value of an ECI designator
87+ *
5888 * @throws \chillerlan\QRCode\Data\QRCodeDataException
5989 */
6090 public static function parseValue (BitBuffer $ bitBuffer ):ECICharset {
6191 $ firstByte = $ bitBuffer ->read (8 );
6292
63- if (( $ firstByte & 0x80 ) === 0 ){
64- // just one byte
65- return new ECICharset ($ firstByte & 0x7f );
93+ // just one byte
94+ if (( $ firstByte & 0b10000000 ) === 0 ){
95+ $ id = ($ firstByte & 0b01111111 );
6696 }
67-
68- if (($ firstByte & 0xc0 ) === 0x80 ){
69- // two bytes
70- return new ECICharset ((($ firstByte & 0x3f ) << 8 ) | $ bitBuffer ->read (8 ));
97+ // two bytes
98+ elseif (($ firstByte & 0b11000000 ) === 0b10000000 ){
99+ $ id = ((($ firstByte & 0b00111111 ) << 8 ) | $ bitBuffer ->read (8 ));
71100 }
72-
73- if (($ firstByte & 0xe0 ) === 0xC0 ){
74- // three bytes
75- return new ECICharset ((($ firstByte & 0x1f ) << 16 ) | $ bitBuffer ->read (16 ));
101+ // three bytes
102+ elseif (($ firstByte & 0b11100000 ) === 0b11000000 ){
103+ $ id = ((($ firstByte & 0b00011111 ) << 16 ) | $ bitBuffer ->read (16 ));
104+ }
105+ else {
106+ throw new QRCodeDataException (sprintf ('error decoding ECI value first byte: %08b ' , $ firstByte )); // @codeCoverageIgnore
76107 }
77108
78- throw new QRCodeDataException ( ' error decoding ECI value ' );
109+ return new ECICharset ( $ id );
79110 }
80111
81112 /**
@@ -86,10 +117,35 @@ public static function validateString(string $string):bool{
86117 }
87118
88119 /**
89- * @codeCoverageIgnore Unused, but required as per interface
120+ * Reads and decodes the ECI designator including the following byte sequence
121+ *
122+ * @throws \chillerlan\QRCode\Data\QRCodeDataException
90123 */
91124 public static function decodeSegment (BitBuffer $ bitBuffer , int $ versionNumber ):string {
92- return '' ;
125+ $ eciCharset = self ::parseValue ($ bitBuffer );
126+ $ nextMode = $ bitBuffer ->read (4 );
127+
128+ if ($ nextMode !== Mode::BYTE ){
129+ throw new QRCodeDataException (sprintf ('ECI designator followed by invalid mode: "%04b" ' , $ nextMode ));
130+ }
131+
132+ $ data = Byte::decodeSegment ($ bitBuffer , $ versionNumber );
133+ $ encoding = $ eciCharset ->getName ();
134+
135+ if ($ encoding === null ){
136+ // The spec isn't clear on this mode; see
137+ // section 6.4.5: t does not say which encoding to assuming
138+ // upon decoding. I have seen ISO-8859-1 used as well as
139+ // Shift_JIS -- without anything like an ECI designator to
140+ // give a hint.
141+ $ encoding = mb_detect_encoding ($ data , ['ISO-8859-1 ' , 'Windows-1252 ' , 'SJIS ' , 'UTF-8 ' ], true );
142+
143+ if ($ encoding === false ){
144+ throw new QRCodeDataException ('could not determine encoding in ECI mode ' ); // @codeCoverageIgnore
145+ }
146+ }
147+
148+ return mb_convert_encoding ($ data , mb_internal_encoding (), $ encoding );
93149 }
94150
95151}
0 commit comments