PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.9.15
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.9.15
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 1.9.14 All 163 releases
woocommerce-pos / vendor_prefixed / chillerlan / php-qrcode / src / Output / QRGdImage.php

QRGdImage.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.9.15, at vendor_prefixed/chillerlan/php-qrcode/src/Output/QRGdImage.php

313 lines 10.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 QRGdImage
5 *
6 * @created 05.12.2015
7 * @author Smiley <smiley@chillerlan.net>
8 * @copyright 2015 Smiley
9 * @license MIT
10 *
11 * @noinspection PhpComposerExtensionStubsInspection
12 */
13 namespace WCPOS\Vendor\chillerlan\QRCode\Output;
14
15 use WCPOS\Vendor\chillerlan\QRCode\Data\QRMatrix;
16 use WCPOS\Vendor\chillerlan\Settings\SettingsContainerInterface;
17 use ErrorException;
18 use Throwable;
19 use function array_values, count, extension_loaded, imagebmp, imagecolorallocate, imagecolortransparent, imagecreatetruecolor, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng, imagescale, imagetypes, imagewebp, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start, restore_error_handler, set_error_handler, sprintf;
20 use const IMG_BMP, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP;
21 /**
22 * Converts the matrix into GD images, raw or base64 output (requires ext-gd)
23 *
24 * @see https://php.net/manual/book.image.php
25 *
26 * @deprecated 5.0.0 this class will be made abstract in future versions,
27 * calling it directly is deprecated - use one of the child classes instead
28 * @see https://github.com/chillerlan/php-qrcode/issues/223
29 */
30 class QRGdImage extends QROutputAbstract
31 {
32 /**
33 * The GD image resource
34 *
35 * @see imagecreatetruecolor()
36 * @var resource|\GdImage
37 *
38 * @todo: add \GdImage type in v6
39 */
40 protected $image;
41 /**
42 * The allocated background color
43 *
44 * @see \imagecolorallocate()
45 */
46 protected int $background;
47 /**
48 * Whether we're running in upscale mode (scale < 20)
49 *
50 * @see \chillerlan\QRCode\QROptions::$drawCircularModules
51 */
52 protected bool $upscaled = \false;
53 /**
54 * @inheritDoc
55 *
56 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
57 * @noinspection PhpMissingParentConstructorInspection
58 */
59 public function __construct(SettingsContainerInterface $options, QRMatrix $matrix)
60 {
61 $this->options = $options;
62 $this->matrix = $matrix;
63 $this->checkGD();
64 if ($this->options->invertMatrix) {
65 $this->matrix->invert();
66 }
67 $this->copyVars();
68 $this->setMatrixDimensions();
69 }
70 /**
71 * Checks whether GD is installed and if the given mode is supported
72 *
73 * @return void
74 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
75 * @codeCoverageIgnore
76 */
77 protected function checkGD() : void
78 {
79 if (!extension_loaded('gd')) {
80 throw new QRCodeOutputException('ext-gd not loaded');
81 }
82 $modes = [self::GDIMAGE_BMP => IMG_BMP, self::GDIMAGE_GIF => IMG_GIF, self::GDIMAGE_JPG => IMG_JPG, self::GDIMAGE_PNG => IMG_PNG, self::GDIMAGE_WEBP => IMG_WEBP];
83 // likely using default or custom output
84 if (!isset($modes[$this->options->outputType])) {
85 return;
86 }
87 $mode = $modes[$this->options->outputType];
88 if ((imagetypes() & $mode) !== $mode) {
89 throw new QRCodeOutputException(sprintf('output mode "%s" not supported', $this->options->outputType));
90 }
91 }
92 /**
93 * @inheritDoc
94 */
95 public static function moduleValueIsValid($value) : bool
96 {
97 if (!is_array($value) || count($value) < 3) {
98 return \false;
99 }
100 // check the first 3 values of the array
101 foreach (array_values($value) as $i => $val) {
102 if ($i > 2) {
103 break;
104 }
105 if (!is_numeric($val)) {
106 return \false;
107 }
108 }
109 return \true;
110 }
111 /**
112 * @param array $value
113 *
114 * @inheritDoc
115 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
116 */
117 protected function prepareModuleValue($value) : int
118 {
119 $values = [];
120 foreach (array_values($value) as $i => $val) {
121 if ($i > 2) {
122 break;
123 }
124 $values[] = max(0, min(255, intval($val)));
125 }
126 /** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
127 $color = imagecolorallocate($this->image, ...$values);
128 if ($color === \false) {
129 throw new QRCodeOutputException('could not set color: imagecolorallocate() error');
130 }
131 return $color;
132 }
133 /**
134 * @inheritDoc
135 */
136 protected function getDefaultModuleValue(bool $isDark) : int
137 {
138 return $this->prepareModuleValue($isDark ? [0, 0, 0] : [255, 255, 255]);
139 }
140 /**
141 * @inheritDoc
142 *
143 * @return string|resource|\GdImage
144 *
145 * @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
146 * @throws \ErrorException
147 */
148 public function dump(?string $file = null)
149 {
150 set_error_handler(function (int $errno, string $errstr) : bool {
151 throw new ErrorException($errstr, $errno);
152 });
153 $this->image = $this->createImage();
154 // set module values after image creation because we need the GdImage instance
155 $this->setModuleValues();
156 $this->setBgColor();
157 imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $this->background);
158 $this->drawImage();
159 if ($this->upscaled) {
160 // scale down to the expected size
161 $this->image = imagescale($this->image, $this->length / 10, $this->length / 10);
162 $this->upscaled = \false;
163 }
164 // set transparency after scaling, otherwise it would be undone
165 // @see https://www.php.net/manual/en/function.imagecolortransparent.php#77099
166 $this->setTransparencyColor();
167 if ($this->options->returnResource) {
168 restore_error_handler();
169 return $this->image;
170 }
171 $imageData = $this->dumpImage();
172 $this->saveToFile($imageData, $file);
173 if ($this->options->outputBase64) {
174 // @todo: remove mime parameter in v6
175 $imageData = $this->toBase64DataURI($imageData, 'image/' . $this->options->outputType);
176 }
177 restore_error_handler();
178 return $imageData;
179 }
180 /**
181 * Creates a new GdImage resource and scales it if necessary
182 *
183 * we're scaling the image up in order to draw crisp round circles, otherwise they appear square-y on small scales
184 *
185 * @see https://github.com/chillerlan/php-qrcode/issues/23
186 *
187 * @return \GdImage|resource
188 */
189 protected function createImage()
190 {
191 if ($this->drawCircularModules && $this->options->gdImageUseUpscale && $this->options->scale < 20) {
192 // increase the initial image size by 10
193 $this->length *= 10;
194 $this->scale *= 10;
195 $this->upscaled = \true;
196 }
197 return imagecreatetruecolor($this->length, $this->length);
198 }
199 /**
200 * Sets the background color
201 */
202 protected function setBgColor() : void
203 {
204 if (isset($this->background)) {
205 return;
206 }
207 if ($this::moduleValueIsValid($this->options->bgColor)) {
208 $this->background = $this->prepareModuleValue($this->options->bgColor);
209 return;
210 }
211 $this->background = $this->prepareModuleValue([255, 255, 255]);
212 }
213 /**
214 * Sets the transparency color
215 */
216 protected function setTransparencyColor() : void
217 {
218 // @todo: the jpg skip can be removed in v6
219 if ($this->options->outputType === QROutputInterface::GDIMAGE_JPG || !$this->options->imageTransparent) {
220 return;
221 }
222 $transparencyColor = $this->background;
223 if ($this::moduleValueIsValid($this->options->transparencyColor)) {
224 $transparencyColor = $this->prepareModuleValue($this->options->transparencyColor);
225 }
226 imagecolortransparent($this->image, $transparencyColor);
227 }
228 /**
229 * Draws the QR image
230 */
231 protected function drawImage() : void
232 {
233 foreach ($this->matrix->getMatrix() as $y => $row) {
234 foreach ($row as $x => $M_TYPE) {
235 $this->module($x, $y, $M_TYPE);
236 }
237 }
238 }
239 /**
240 * Creates a single QR pixel with the given settings
241 */
242 protected function module(int $x, int $y, int $M_TYPE) : void
243 {
244 if (!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)) {
245 return;
246 }
247 $color = $this->getModuleValue($M_TYPE);
248 if ($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)) {
249 imagefilledellipse($this->image, $x * $this->scale + intdiv($this->scale, 2), $y * $this->scale + intdiv($this->scale, 2), (int) ($this->circleDiameter * $this->scale), (int) ($this->circleDiameter * $this->scale), $color);
250 return;
251 }
252 imagefilledrectangle($this->image, $x * $this->scale, $y * $this->scale, ($x + 1) * $this->scale, ($y + 1) * $this->scale, $color);
253 }
254 /**
255 * Renders the image with the gdimage function for the desired output
256 *
257 * @see \imagebmp()
258 * @see \imagegif()
259 * @see \imagejpeg()
260 * @see \imagepng()
261 * @see \imagewebp()
262 *
263 * @todo: v6.0: make abstract and call from child classes
264 * @see https://github.com/chillerlan/php-qrcode/issues/223
265 * @codeCoverageIgnore
266 */
267 protected function renderImage() : void
268 {
269 switch ($this->options->outputType) {
270 case QROutputInterface::GDIMAGE_BMP:
271 imagebmp($this->image, null, $this->options->quality > 0);
272 break;
273 case QROutputInterface::GDIMAGE_GIF:
274 imagegif($this->image);
275 break;
276 case QROutputInterface::GDIMAGE_JPG:
277 imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
278 break;
279 case QROutputInterface::GDIMAGE_WEBP:
280 imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
281 break;
282 // silently default to png output
283 case QROutputInterface::GDIMAGE_PNG:
284 default:
285 imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
286 }
287 }
288 /**
289 * Creates the final image by calling the desired GD output function
290 *
291 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
292 */
293 protected function dumpImage() : string
294 {
295 $exception = null;
296 $imageData = null;
297 ob_start();
298 try {
299 $this->renderImage();
300 $imageData = ob_get_contents();
301 } catch (Throwable $e) {
302 $exception = $e;
303 }
304 // @codeCoverageIgnoreEnd
305 ob_end_clean();
306 // throw here in case an exception happened within the output buffer
307 if ($exception instanceof Throwable) {
308 throw new QRCodeOutputException($exception->getMessage());
309 }
310 return $imageData;
311 }
312 }
313