PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / vendor_prefixed / chillerlan / php-qrcode / src / Data / Hanzi.php

Hanzi.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at vendor_prefixed/chillerlan/php-qrcode/src/Data/Hanzi.php

165 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class Hanzi
5 *
6 * @created 19.11.2020
7 * @author smiley <[email protected]>
8 * @copyright 2020 smiley
9 * @license MIT
10 */
11 namespace WCPOS\Vendor\chillerlan\QRCode\Data;
12
13 use WCPOS\Vendor\chillerlan\QRCode\Common\BitBuffer;
14 use WCPOS\Vendor\chillerlan\QRCode\Common\Mode;
15 use Throwable;
16 use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
17 /**
18 * Hanzi (simplified Chinese) mode, GBT18284-2000: 13-bit double-byte characters from the GB2312/GB18030 character set
19 *
20 * Please note that this is not part of the QR Code specification and may not be supported by all readers (ZXing-based ones do).
21 *
22 * @see https://en.wikipedia.org/wiki/GB_2312
23 * @see http://www.herongyang.com/GB2312/Introduction-of-GB2312.html
24 * @see https://en.wikipedia.org/wiki/GBK_(character_encoding)#Encoding
25 * @see https://gist.github.com/codemasher/91da33c44bfb48a81a6c1426bb8e4338
26 * @see https://github.com/zxing/zxing/blob/dfb06fa33b17a9e68321be151c22846c7b78048f/core/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java#L172-L209
27 * @see https://www.chinesestandard.net/PDF/English.aspx/GBT18284-2000
28 */
29 final class Hanzi extends QRDataModeAbstract
30 {
31 /**
32 * possible values: GB2312, GB18030
33 *
34 * @var string
35 */
36 public const ENCODING = 'GB18030';
37 /**
38 * @todo: other subsets???
39 *
40 * @var int
41 */
42 public const GB2312_SUBSET = 0b1;
43 /**
44 * @inheritDoc
45 */
46 public const DATAMODE = Mode::HANZI;
47 /**
48 * @inheritDoc
49 */
50 protected function getCharCount() : int
51 {
52 return mb_strlen($this->data, self::ENCODING);
53 }
54 /**
55 * @inheritDoc
56 */
57 public function getLengthInBits() : int
58 {
59 return $this->getCharCount() * 13;
60 }
61 /**
62 * @inheritDoc
63 * @throws \chillerlan\QRCode\Data\QRCodeDataException
64 */
65 public static function convertEncoding(string $string) : string
66 {
67 $detected = mb_detect_encoding($string, [mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ'], \true);
68 if ($detected === \false) {
69 throw new QRCodeDataException('mb_detect_encoding error');
70 }
71 if ($detected === self::ENCODING) {
72 return $string;
73 }
74 $string = mb_convert_encoding($string, self::ENCODING, $detected);
75 if (!is_string($string)) {
76 throw new QRCodeDataException('mb_convert_encoding error');
77 }
78 return $string;
79 }
80 /**
81 * checks if a string qualifies as Hanzi/GB2312
82 */
83 public static function validateString(string $string) : bool
84 {
85 try {
86 $string = self::convertEncoding($string);
87 } catch (Throwable $e) {
88 return \false;
89 }
90 $len = strlen($string);
91 if ($len < 2 || $len % 2 !== 0) {
92 return \false;
93 }
94 for ($i = 0; $i < $len; $i += 2) {
95 $byte1 = ord($string[$i]);
96 $byte2 = ord($string[$i + 1]);
97 // byte 1 unused ranges
98 if ($byte1 < 0xa1 || $byte1 > 0xa9 && $byte1 < 0xb0 || $byte1 > 0xf7) {
99 return \false;
100 }
101 // byte 2 unused ranges
102 if ($byte2 < 0xa1 || $byte2 > 0xfe) {
103 return \false;
104 }
105 }
106 return \true;
107 }
108 /**
109 * @inheritDoc
110 *
111 * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
112 */
113 public function write(BitBuffer $bitBuffer, int $versionNumber) : QRDataModeInterface
114 {
115 $bitBuffer->put(self::DATAMODE, 4)->put($this::GB2312_SUBSET, 4)->put($this->getCharCount(), $this::getLengthBits($versionNumber));
116 $len = strlen($this->data);
117 for ($i = 0; $i + 1 < $len; $i += 2) {
118 $c = (0xff & ord($this->data[$i])) << 8 | 0xff & ord($this->data[$i + 1]);
119 if ($c >= 0xa1a1 && $c <= 0xaafe) {
120 $c -= 0xa1a1;
121 } elseif ($c >= 0xb0a1 && $c <= 0xfafe) {
122 $c -= 0xa6a1;
123 } else {
124 throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c));
125 }
126 $bitBuffer->put(($c >> 8 & 0xff) * 0x60 + ($c & 0xff), 13);
127 }
128 if ($i < $len) {
129 throw new QRCodeDataException(sprintf('illegal char at %d', $i + 1));
130 }
131 return $this;
132 }
133 /**
134 * See specification GBT 18284-2000
135 *
136 * @throws \chillerlan\QRCode\Data\QRCodeDataException
137 */
138 public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber) : string
139 {
140 // Hanzi mode contains a subset indicator right after mode indicator
141 if ($bitBuffer->read(4) !== self::GB2312_SUBSET) {
142 throw new QRCodeDataException('ecpected subset indicator for Hanzi mode');
143 }
144 $length = $bitBuffer->read(self::getLengthBits($versionNumber));
145 if ($bitBuffer->available() < $length * 13) {
146 throw new QRCodeDataException('not enough bits available');
147 }
148 // Each character will require 2 bytes. Read the characters as 2-byte pairs and decode as GB2312 afterwards
149 $buffer = [];
150 $offset = 0;
151 while ($length > 0) {
152 // Each 13 bits encodes a 2-byte character
153 $twoBytes = $bitBuffer->read(13);
154 $assembledTwoBytes = intdiv($twoBytes, 0x60) << 8 | $twoBytes % 0x60;
155 $assembledTwoBytes += $assembledTwoBytes < 0xa00 ? 0xa1a1 : 0xa6a1;
156 // In the 0xB0A1 to 0xFAFE range
157 $buffer[$offset] = chr(0xff & $assembledTwoBytes >> 8);
158 $buffer[$offset + 1] = chr(0xff & $assembledTwoBytes);
159 $offset += 2;
160 $length--;
161 }
162 return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
163 }
164 }
165