Skip to content

Commit 6c135ba

Browse files
committed
:octocat: QRGdImage: add BMP and WEBP support, deprecate QROptionsTrait::$jpegQuality and QROptionsTrait::$pngCompression in favor of QROptionsTrait::$quality
1 parent c69531c commit 6c135ba

File tree

5 files changed

+194
-40
lines changed

5 files changed

+194
-40
lines changed

src/Output/QRGdImage.php

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414

1515
use chillerlan\QRCode\Data\QRMatrix;
1616
use chillerlan\Settings\SettingsContainerInterface;
17-
use ErrorException, Throwable;
18-
use function array_values, count, extension_loaded, imagecolorallocate, imagecolortransparent, imagecreatetruecolor,
19-
imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng, imagescale, intdiv, intval,
20-
is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start, restore_error_handler, set_error_handler;
17+
use ErrorException;
18+
use Throwable;
19+
use function array_values, count, extension_loaded, gd_info, imagebmp, imagecolorallocate, imagecolortransparent,
20+
imagecreatetruecolor, imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng,
21+
imagescale, imagewebp, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start,
22+
restore_error_handler, set_error_handler, sprintf;
2123

2224
/**
2325
* Converts the matrix into GD images, raw or base64 output (requires ext-gd)
@@ -55,28 +57,64 @@ class QRGdImage extends QROutputAbstract{
5557
* @noinspection PhpMissingParentConstructorInspection
5658
*/
5759
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
60+
$this->options = $options;
61+
$this->matrix = $matrix;
62+
63+
$this->checkGD();
64+
$this->setMatrixDimensions();
65+
66+
$this->image = $this->createImage();
67+
// set module values after image creation because we need the GdImage instance
68+
$this->setModuleValues();
69+
}
70+
71+
/**
72+
* Checks whether GD is installed and if the given mode is supported
73+
*
74+
* @return void
75+
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
76+
* @codeCoverageIgnore
77+
*/
78+
protected function checkGD():void{
5879

5980
if(!extension_loaded('gd')){
60-
throw new QRCodeOutputException('ext-gd not loaded'); // @codeCoverageIgnore
81+
throw new QRCodeOutputException('ext-gd not loaded');
6182
}
6283

63-
$this->options = $options;
64-
$this->matrix = $matrix;
84+
$info = gd_info();
85+
$mode = [
86+
self::GDIMAGE_BMP => 'BMP Support',
87+
self::GDIMAGE_GIF => 'GIF Create Support',
88+
self::GDIMAGE_JPG => 'JPEG Support',
89+
self::GDIMAGE_PNG => 'PNG Support',
90+
self::GDIMAGE_WEBP => 'WebP Support',
91+
][$this->options->outputType];
92+
93+
if(!isset($info[$mode]) || $info[$mode] !== true){
94+
throw new QRCodeOutputException(sprintf('output mode "%s" not supported', $this->options->outputType));
95+
}
6596

66-
$this->setMatrixDimensions();
97+
}
98+
99+
/**
100+
* Creates a new GdImage resource and scales it if necessary
101+
*
102+
* we're scaling the image up in order to draw crisp round circles, otherwise they appear square-y on small scales
103+
*
104+
* @see https://github.com/chillerlan/php-qrcode/issues/23
105+
*
106+
* @return \GdImage|resource
107+
*/
108+
protected function createImage(){
67109

68-
// we're scaling the image up in order to draw crisp round circles, otherwise they appear square-y on small scales
69-
// @see https://github.com/chillerlan/php-qrcode/issues/23
70110
if($this->options->drawCircularModules && $this->options->scale < 20){
71111
// increase the initial image size by 10
72112
$this->length = (($this->length + 2) * 10);
73113
$this->scale *= 10;
74114
$this->upscaled = true;
75115
}
76116

77-
$this->image = imagecreatetruecolor($this->length, $this->length);
78-
// set module values after image creation because we need the GdImage instance
79-
$this->setModuleValues();
117+
return imagecreatetruecolor($this->length, $this->length);
80118
}
81119

82120
/**
@@ -225,7 +263,7 @@ protected function setTransparencyColor():void{
225263
}
226264

227265
/**
228-
* Creates the QR image
266+
* Draws the QR image
229267
*/
230268
protected function drawImage():void{
231269
foreach($this->matrix->getMatrix() as $y => $row){
@@ -282,16 +320,22 @@ protected function dumpImage():string{
282320
try{
283321

284322
switch($this->options->outputType){
323+
case QROutputInterface::GDIMAGE_BMP:
324+
imagebmp($this->image);
325+
break;
285326
case QROutputInterface::GDIMAGE_GIF:
286327
imagegif($this->image);
287328
break;
288329
case QROutputInterface::GDIMAGE_JPG:
289-
imagejpeg($this->image, null, max(0, min(100, $this->options->jpegQuality)));
330+
imagejpeg($this->image, null, max(-1, min(100, $this->options->quality)));
331+
break;
332+
case QROutputInterface::GDIMAGE_WEBP:
333+
imagewebp($this->image, null, max(-1, min(100, $this->options->quality)));
290334
break;
291335
// silently default to png output
292336
case QROutputInterface::GDIMAGE_PNG:
293337
default:
294-
imagepng($this->image, null, max(-1, min(9, $this->options->pngCompression)));
338+
imagepng($this->image, null, max(-1, min(9, $this->options->quality)));
295339
}
296340

297341
$imageData = ob_get_contents();

src/Output/QROutputInterface.php

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,50 @@
1818
interface QROutputInterface{
1919

2020
/** @var string */
21-
public const MARKUP_HTML = 'html';
21+
public const MARKUP_HTML = 'html';
2222
/** @var string */
23-
public const MARKUP_SVG = 'svg';
23+
public const MARKUP_SVG = 'svg';
2424
/** @var string */
25-
public const GDIMAGE_PNG = 'png';
25+
public const GDIMAGE_BMP = 'bmp';
2626
/** @var string */
27-
public const GDIMAGE_JPG = 'jpg';
27+
public const GDIMAGE_GIF = 'gif';
2828
/** @var string */
29-
public const GDIMAGE_GIF = 'gif';
29+
public const GDIMAGE_JPG = 'jpg';
3030
/** @var string */
31-
public const STRING_JSON = 'json';
31+
public const GDIMAGE_PNG = 'png';
3232
/** @var string */
33-
public const STRING_TEXT = 'text';
33+
public const GDIMAGE_WEBP = 'webp';
3434
/** @var string */
35-
public const IMAGICK = 'imagick';
35+
public const STRING_JSON = 'json';
3636
/** @var string */
37-
public const FPDF = 'fpdf';
37+
public const STRING_TEXT = 'text';
3838
/** @var string */
39-
public const EPS = 'eps';
39+
public const IMAGICK = 'imagick';
4040
/** @var string */
41-
public const CUSTOM = 'custom';
41+
public const FPDF = 'fpdf';
42+
/** @var string */
43+
public const EPS = 'eps';
44+
/** @var string */
45+
public const CUSTOM = 'custom';
4246

4347
/**
4448
* Map of built-in output modes => class FQN
4549
*
4650
* @var string[]
4751
*/
4852
public const MODES = [
49-
self::MARKUP_SVG => QRMarkupSVG::class,
50-
self::MARKUP_HTML => QRMarkupHTML::class,
51-
self::GDIMAGE_PNG => QRGdImage::class,
52-
self::GDIMAGE_GIF => QRGdImage::class,
53-
self::GDIMAGE_JPG => QRGdImage::class,
54-
self::STRING_JSON => QRString::class,
55-
self::STRING_TEXT => QRString::class,
56-
self::IMAGICK => QRImagick::class,
57-
self::FPDF => QRFpdf::class,
58-
self::EPS => QREps::class,
53+
self::MARKUP_SVG => QRMarkupSVG::class,
54+
self::MARKUP_HTML => QRMarkupHTML::class,
55+
self::GDIMAGE_BMP => QRGdImage::class,
56+
self::GDIMAGE_GIF => QRGdImage::class,
57+
self::GDIMAGE_JPG => QRGdImage::class,
58+
self::GDIMAGE_PNG => QRGdImage::class,
59+
self::GDIMAGE_WEBP => QRGdImage::class,
60+
self::STRING_JSON => QRString::class,
61+
self::STRING_TEXT => QRString::class,
62+
self::IMAGICK => QRImagick::class,
63+
self::FPDF => QRFpdf::class,
64+
self::EPS => QREps::class,
5965
];
6066

6167
/**

src/QROptionsTrait.php

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ trait QROptionsTrait{
8989
* The built-in output type
9090
*
9191
* - QROutputInterface::MARKUP_XXXX where XXXX = HTML, SVG
92-
* - QROutputInterface::GDIMAGE_XXX where XXX = PNG, GIF, JPG
92+
* - QROutputInterface::GDIMAGE_XXX where XXX = BMP, GIF, JPG, PNG, WEBP
9393
* - QROutputInterface::STRING_XXXX where XXXX = TEXT, JSON
9494
* - QROutputInterface::IMAGICK
9595
* - QROutputInterface::EPS
@@ -268,18 +268,32 @@ trait QROptionsTrait{
268268
*/
269269
protected $transparencyColor = null;
270270

271+
/**
272+
* Compression quality
273+
*
274+
* The given value depends on the used output type:
275+
*
276+
* @see \imagejpeg()
277+
* @see \imagepng()
278+
* @see \imagewebp()
279+
* @see \Imagick::setImageCompressionQuality()
280+
*/
281+
protected int $quality = -1;
282+
271283

272284
/*
273285
* QRGdImage settings
274286
*/
275287

276288
/**
277-
* @see imagepng()
289+
* @deprecated 5.0.0 use QROptions::$quality instead
290+
* @see \chillerlan\QRCode\QROptions::$quality
278291
*/
279292
protected int $pngCompression = -1;
280293

281294
/**
282-
* @see imagejpeg()
295+
* @deprecated 5.0.0 use QROptions::$quality instead
296+
* @see \chillerlan\QRCode\QROptions::$quality
283297
*/
284298
protected int $jpegQuality = 85;
285299

@@ -541,6 +555,7 @@ protected function set_circleRadius(float $circleRadius):void{
541555
*
542556
* @deprecated 5.0.0 use QROptions::$outputBase64 instead
543557
* @see \chillerlan\QRCode\QROptions::$outputBase64
558+
* @codeCoverageIgnore
544559
*/
545560
protected function set_imageBase64(bool $imageBase64):void{
546561
$this->outputBase64 = $imageBase64;
@@ -551,9 +566,54 @@ protected function set_imageBase64(bool $imageBase64):void{
551566
*
552567
* @deprecated 5.0.0 use QROptions::$outputBase64 instead
553568
* @see \chillerlan\QRCode\QROptions::$outputBase64
569+
* @codeCoverageIgnore
554570
*/
555571
protected function get_imageBase64():bool{
556572
return $this->outputBase64;
557573
}
558574

575+
/**
576+
* redirect call to the new variable
577+
*
578+
* @deprecated 5.0.0 use QROptions::$quality instead
579+
* @see \chillerlan\QRCode\QROptions::$quality
580+
* @codeCoverageIgnore
581+
*/
582+
protected function set_jpegQuality(bool $jpegQuality):void{
583+
$this->quality = $jpegQuality;
584+
}
585+
586+
/**
587+
* redirect call to the new variable
588+
*
589+
* @deprecated 5.0.0 use QROptions::$quality instead
590+
* @see \chillerlan\QRCode\QROptions::$quality
591+
* @codeCoverageIgnore
592+
*/
593+
protected function get_jpegQuality():bool{
594+
return $this->quality;
595+
}
596+
597+
/**
598+
* redirect call to the new variable
599+
*
600+
* @deprecated 5.0.0 use QROptions::$quality instead
601+
* @see \chillerlan\QRCode\QROptions::$quality
602+
* @codeCoverageIgnore
603+
*/
604+
protected function set_pngCompression(bool $pngCompression):void{
605+
$this->quality = $pngCompression;
606+
}
607+
608+
/**
609+
* redirect call to the new variable
610+
*
611+
* @deprecated 5.0.0 use QROptions::$quality instead
612+
* @see \chillerlan\QRCode\QROptions::$quality
613+
* @codeCoverageIgnore
614+
*/
615+
protected function get_pngCompression():bool{
616+
return $this->quality;
617+
}
618+
559619
}

tests/Output/QRGdImageBMPTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Class QRGdImageBMPTest
4+
*
5+
* @created 05.09.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Output;
12+
13+
use chillerlan\QRCode\Output\QROutputInterface;
14+
15+
/**
16+
*
17+
*/
18+
final class QRGdImageBMPTest extends QRGdImageTestAbstract{
19+
20+
protected string $type = QROutputInterface::GDIMAGE_BMP;
21+
22+
}

tests/Output/QRGdImageWEBPTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Class QRGdImageWEBPTest
4+
*
5+
* @created 05.09.2023
6+
* @author smiley <[email protected]>
7+
* @copyright 2023 smiley
8+
* @license MIT
9+
*/
10+
11+
namespace chillerlan\QRCodeTest\Output;
12+
13+
use chillerlan\QRCode\Output\QROutputInterface;
14+
15+
/**
16+
*
17+
*/
18+
final class QRGdImageWEBPTest extends QRGdImageTestAbstract{
19+
20+
protected string $type = QROutputInterface::GDIMAGE_WEBP;
21+
22+
}

0 commit comments

Comments
 (0)