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 / QRData.php

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

222 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class QRData
5 *
6 * @created 25.11.2015
7 * @author Smiley <[email protected]>
8 * @copyright 2015 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\EccLevel;
15 use WCPOS\Vendor\chillerlan\QRCode\Common\Mode;
16 use WCPOS\Vendor\chillerlan\QRCode\Common\Version;
17 use WCPOS\Vendor\chillerlan\Settings\SettingsContainerInterface;
18 use function count, sprintf;
19 /**
20 * Processes the binary data and maps it on a QRMatrix which is then being returned
21 */
22 final class QRData
23 {
24 /**
25 * the options instance
26 *
27 * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
28 */
29 private SettingsContainerInterface $options;
30 /**
31 * a BitBuffer instance
32 */
33 private BitBuffer $bitBuffer;
34 /**
35 * an EccLevel instance
36 */
37 private EccLevel $eccLevel;
38 /**
39 * current QR Code version
40 */
41 private Version $version;
42 /**
43 * @var \chillerlan\QRCode\Data\QRDataModeInterface[]
44 */
45 private array $dataSegments = [];
46 /**
47 * Max bits for the current ECC mode
48 *
49 * @var int[]
50 */
51 private array $maxBitsForEcc;
52 /**
53 * QRData constructor.
54 */
55 public function __construct(SettingsContainerInterface $options, array $dataSegments = [])
56 {
57 $this->options = $options;
58 $this->bitBuffer = new BitBuffer();
59 $this->eccLevel = new EccLevel($this->options->eccLevel);
60 $this->maxBitsForEcc = $this->eccLevel->getMaxBits();
61 $this->setData($dataSegments);
62 }
63 /**
64 * Sets the data string (internally called by the constructor)
65 *
66 * Subsequent calls will overwrite the current state - use the QRCode::add*Segement() method instead
67 *
68 * @param \chillerlan\QRCode\Data\QRDataModeInterface[] $dataSegments
69 */
70 public function setData(array $dataSegments) : self
71 {
72 $this->dataSegments = $dataSegments;
73 $this->version = $this->getMinimumVersion();
74 $this->bitBuffer->clear();
75 $this->writeBitBuffer();
76 return $this;
77 }
78 /**
79 * Returns the current BitBuffer instance
80 *
81 * @codeCoverageIgnore
82 */
83 public function getBitBuffer() : BitBuffer
84 {
85 return $this->bitBuffer;
86 }
87 /**
88 * Sets a BitBuffer object
89 *
90 * This can be used instead of setData(), however, the version auto-detection is not available in this case.
91 * The version needs to match the length bits range for the data mode the data has been encoded with,
92 * additionally the bit array needs to contain enough pad bits.
93 *
94 * @throws \chillerlan\QRCode\Data\QRCodeDataException
95 */
96 public function setBitBuffer(BitBuffer $bitBuffer) : self
97 {
98 if ($this->options->version === Version::AUTO) {
99 throw new QRCodeDataException('version auto detection is not available');
100 }
101 if ($bitBuffer->getLength() === 0) {
102 throw new QRCodeDataException('the given BitBuffer is empty');
103 }
104 $this->dataSegments = [];
105 $this->bitBuffer = $bitBuffer;
106 $this->version = new Version($this->options->version);
107 return $this;
108 }
109 /**
110 * returns a fresh matrix object with the data written and masked with the given $maskPattern
111 */
112 public function writeMatrix() : QRMatrix
113 {
114 return (new QRMatrix($this->version, $this->eccLevel))->initFunctionalPatterns()->writeCodewords($this->bitBuffer);
115 }
116 /**
117 * estimates the total length of the several mode segments in order to guess the minimum version
118 *
119 * @throws \chillerlan\QRCode\Data\QRCodeDataException
120 */
121 public function estimateTotalBitLength() : int
122 {
123 $length = 0;
124 foreach ($this->dataSegments as $segment) {
125 // data length of the current segment
126 $length += $segment->getLengthInBits();
127 // +4 bits for the mode descriptor
128 $length += 4;
129 // Hanzi mode sets an additional 4 bit long subset identifier
130 if ($segment instanceof Hanzi) {
131 $length += 4;
132 }
133 }
134 $provisionalVersion = null;
135 foreach ($this->maxBitsForEcc as $version => $maxBits) {
136 if ($length <= $maxBits) {
137 $provisionalVersion = $version;
138 }
139 }
140 if ($provisionalVersion !== null) {
141 // add character count indicator bits for the provisional version
142 foreach ($this->dataSegments as $segment) {
143 $length += Mode::getLengthBitsForVersion($segment::DATAMODE, $provisionalVersion);
144 }
145 // it seems that in some cases the estimated total length is not 100% accurate,
146 // so we substract 4 bits from the total when not in mixed mode
147 if (count($this->dataSegments) <= 1) {
148 $length -= 4;
149 }
150 // we've got a match!
151 // or let's see if there's a higher version number available
152 if ($length <= $this->maxBitsForEcc[$provisionalVersion] || isset($this->maxBitsForEcc[$provisionalVersion + 1])) {
153 return $length;
154 }
155 }
156 throw new QRCodeDataException(sprintf('estimated data exceeds %d bits', $length));
157 }
158 /**
159 * returns the minimum version number for the given string
160 *
161 * @throws \chillerlan\QRCode\Data\QRCodeDataException
162 */
163 public function getMinimumVersion() : Version
164 {
165 if ($this->options->version !== Version::AUTO) {
166 return new Version($this->options->version);
167 }
168 $total = $this->estimateTotalBitLength();
169 // guess the version number within the given range
170 for ($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++) {
171 if ($total <= $this->maxBitsForEcc[$version] - 4) {
172 return new Version($version);
173 }
174 }
175 // it's almost impossible to run into this one as $this::estimateTotalBitLength() would throw first
176 throw new QRCodeDataException('failed to guess minimum version');
177 // @codeCoverageIgnore
178 }
179 /**
180 * creates a BitBuffer and writes the string data to it
181 *
182 * @throws \chillerlan\QRCode\Data\QRCodeDataException on data overflow
183 */
184 private function writeBitBuffer() : void
185 {
186 $MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);
187 foreach ($this->dataSegments as $segment) {
188 $segment->write($this->bitBuffer, $this->version->getVersionNumber());
189 }
190 // overflow, likely caused due to invalid version setting
191 if ($this->bitBuffer->getLength() > $MAX_BITS) {
192 throw new QRCodeDataException(sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS));
193 }
194 // add terminator (ISO/IEC 18004:2000 Table 2)
195 if ($this->bitBuffer->getLength() + 4 <= $MAX_BITS) {
196 $this->bitBuffer->put(Mode::TERMINATOR, 4);
197 }
198 // Padding: ISO/IEC 18004:2000 8.4.9 Bit stream to codeword conversion
199 // if the final codeword is not exactly 8 bits in length, it shall be made 8 bits long
200 // by the addition of padding bits with binary value 0
201 while ($this->bitBuffer->getLength() % 8 !== 0) {
202 if ($this->bitBuffer->getLength() === $MAX_BITS) {
203 break;
204 }
205 $this->bitBuffer->putBit(\false);
206 }
207 // The message bit stream shall then be extended to fill the data capacity of the symbol
208 // corresponding to the Version and Error Correction Level, by the addition of the Pad
209 // Codewords 11101100 and 00010001 alternately.
210 $alternate = \false;
211 while ($this->bitBuffer->getLength() + 8 <= $MAX_BITS) {
212 $this->bitBuffer->put($alternate ? 0b10001 : 0b11101100, 8);
213 $alternate = !$alternate;
214 }
215 // In certain versions of symbol, it may be necessary to add 3, 4 or 7 Remainder Bits (all zeros)
216 // to the end of the message in order exactly to fill the symbol capacity
217 while ($this->bitBuffer->getLength() <= $MAX_BITS) {
218 $this->bitBuffer->putBit(\false);
219 }
220 }
221 }
222