diff --git a/examples/QRImageWithLogo.php b/examples/QRImageWithLogo.php index ac2dacadd..431dddbf6 100644 --- a/examples/QRImageWithLogo.php +++ b/examples/QRImageWithLogo.php @@ -16,9 +16,6 @@ use chillerlan\QRCode\Output\{QRCodeOutputException, QRImage}; use function imagecopyresampled, imagecreatefrompng, imagesx, imagesy, is_file, is_readable; -/** - * @property \chillerlan\QRCodeExamples\LogoOptions $options - */ class QRImageWithLogo extends QRImage{ /** @@ -38,12 +35,6 @@ class QRImageWithLogo extends QRImage{ throw new QRCodeOutputException('invalid logo'); } - $this->matrix->setLogoSpace( - $this->options->logoSpaceWidth, - $this->options->logoSpaceHeight - // not utilizing the position here - ); - // there's no need to save the result of dump() into $this->image here parent::dump($file); diff --git a/examples/imageWithLogo.php b/examples/imageWithLogo.php index 7aabdc021..f61be2c66 100644 --- a/examples/imageWithLogo.php +++ b/examples/imageWithLogo.php @@ -14,27 +14,17 @@ use chillerlan\QRCode\Common\EccLevel; require_once __DIR__.'/../vendor/autoload.php'; $data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s'; -/** - * @property int $logoSpaceWidth - * @property int $logoSpaceHeight - * - * @noinspection PhpIllegalPsrClassPathInspection - */ -class LogoOptions extends QROptions{ - // size in QR modules, multiply with QROptions::$scale for pixel size - protected int $logoSpaceWidth; - protected int $logoSpaceHeight; -} -$options = new LogoOptions; - -$options->version = 7; -$options->eccLevel = EccLevel::H; -$options->imageBase64 = false; -$options->logoSpaceWidth = 13; -$options->logoSpaceWidth = 13; -$options->scale = 5; -$options->imageTransparent = false; +$options = new QROptions([ + 'version' => 7, + 'eccLevel' => EccLevel::H, + 'imageBase64' => false, + 'addLogoSpace' => true, + 'logoSpaceWidth' => 13, + 'logoSpaceHeight' => 13, + 'scale' => 5, + 'imageTransparent' => false, +]); $qrcode = new QRCode($options); $qrcode->addByteSegment($data); diff --git a/src/QRCode.php b/src/QRCode.php index b6d23d661..6dd654c72 100755 --- a/src/QRCode.php +++ b/src/QRCode.php @@ -161,6 +161,16 @@ class QRCode{ $matrix = $dataInterface->writeMatrix($maskPattern); + // add matrix modifications after mask pattern evaluation and before handing over to output + if($this->options->addLogoSpace){ + $matrix->setLogoSpace( + $this->options->logoSpaceWidth, + $this->options->logoSpaceHeight, + $this->options->logoSpaceStartX, + $this->options->logoSpaceStartY + ); + } + if($this->options->addQuietzone){ $matrix->setQuietZone($this->options->quietzoneSize); } diff --git a/src/QROptionsTrait.php b/src/QROptionsTrait.php index 531e2e63b..204a2cec7 100644 --- a/src/QROptionsTrait.php +++ b/src/QROptionsTrait.php @@ -284,6 +284,31 @@ trait QROptionsTrait{ */ protected bool $readerIncreaseContrast = false; + /** + * Toggles logo space creation + */ + protected bool $addLogoSpace = false; + + /** + * width of the logo space + */ + protected int $logoSpaceWidth = 0; + + /** + * height of the logo space + */ + protected int $logoSpaceHeight = 0; + + /** + * optional horizontal start position of the logo space (top left corner) + */ + protected ?int $logoSpaceStartX = null; + + /** + * optional vertical start position of the logo space (top left corner) + */ + protected ?int $logoSpaceStartY = null; + /** * clamp min/max version number */ @@ -412,4 +437,39 @@ trait QROptionsTrait{ : GDLuminanceSource::class; } + /** + * clamp the logo space values between 0 and maximum length (177 modules at version 40) + */ + protected function clampLogoSpaceValue(int $value):int{ + return (int)max(0, min(177, $value)); + } + + /** + * clamp/set logo space width + */ + protected function set_logoSpaceWidth(int $value):void{ + $this->logoSpaceWidth = $this->clampLogoSpaceValue($value); + } + + /** + * clamp/set logo space height + */ + protected function set_logoSpaceHeight(int $value):void{ + $this->logoSpaceHeight = $this->clampLogoSpaceValue($value); + } + + /** + * clamp/set horizontal logo space start + */ + protected function set_logoSpaceStartX(?int $value):void{ + $this->logoSpaceStartX = $value === null ? null : $this->clampLogoSpaceValue($value); + } + + /** + * clamp/set vertical logo space start + */ + protected function set_logoSpaceStartY(?int $value):void{ + $this->logoSpaceStartY = $value === null ? null : $this->clampLogoSpaceValue($value); + } + } diff --git a/tests/QROptionsTest.php b/tests/QROptionsTest.php index dcc19e3e6..d7513aa58 100644 --- a/tests/QROptionsTest.php +++ b/tests/QROptionsTest.php @@ -139,4 +139,51 @@ class QROptionsTest extends TestCase{ $o = new QROptions(['imageTransparencyBG' => ['r', 'g', 'b']]); } + /** + * @return int[][] + */ + public function logoSpaceValueProvider():array{ + return [ + 'negative' => [ -1, 0], + 'zero' => [ 0, 0], + 'normal' => [ 69, 69], + 'max' => [177, 177], + 'exceed' => [178, 177], + ]; + } + + /** + * Tests the clamping (between 0 and 177) of the logo space values + * + * @dataProvider logoSpaceValueProvider + */ + public function testClampLogoSpaceValue(int $value, int $expected):void{ + $o = new QROptions; + + foreach(['logoSpaceWidth', 'logoSpaceHeight', 'logoSpaceStartX', 'logoSpaceStartY'] as $prop){ + $o->{$prop} = $value; + $this::assertSame($expected, $o->{$prop}); + } + + } + + /** + * Tests if the optional logo space start values are nullable + */ + public function testLogoSpaceStartNullable():void{ + $o = new QROptions([ + 'logoSpaceStartX' => 42, + 'logoSpaceStartY' => 69, + ]); + + $this::assertSame(42, $o->logoSpaceStartX); + $this::assertSame(69, $o->logoSpaceStartY); + + $o->logoSpaceStartX = null; + $o->logoSpaceStartY = null; + + $this::assertNull($o->logoSpaceStartX); + $this::assertNull($o->logoSpaceStartY); + } + }