mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-09-15 04:06:24 +00:00
✨ QRInterventionImage (intervention/image)
This commit is contained in:
@@ -52,6 +52,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"chillerlan/php-authenticator": "^5.1",
|
||||
"intervention/image": "^3.5",
|
||||
"phpbench/phpbench": "^1.2.15",
|
||||
"phan/phan": "^5.4",
|
||||
"phpunit/phpunit": "^11.0",
|
||||
@@ -61,6 +62,7 @@
|
||||
},
|
||||
"suggest": {
|
||||
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
|
||||
"intervention/image": "More advanced GD and ImageMagick output.",
|
||||
"setasign/fpdf": "Required to use the QR FPDF output.",
|
||||
"simple-icons/simple-icons": "SVG icons that you can use to embed as logos in the QR Code"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* intervention/image output example
|
||||
*
|
||||
* @created 04.05.2024
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2024 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
use chillerlan\QRCode\Data\QRMatrix;
|
||||
use chillerlan\QRCode\Output\QRInterventionImage;
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
$options = new QROptions;
|
||||
|
||||
$options->version = 7;
|
||||
$options->outputInterface = QRInterventionImage::class;
|
||||
$options->scale = 20;
|
||||
$options->outputBase64 = false;
|
||||
$options->bgColor = '#cccccc';
|
||||
$options->imageTransparent = false;
|
||||
$options->transparencyColor = '#cccccc';
|
||||
$options->drawLightModules = true;
|
||||
$options->drawCircularModules = true;
|
||||
$options->circleRadius = 0.4;
|
||||
$options->keepAsSquare = [
|
||||
QRMatrix::M_FINDER_DARK,
|
||||
QRMatrix::M_FINDER_DOT,
|
||||
QRMatrix::M_ALIGNMENT_DARK,
|
||||
];
|
||||
$options->moduleValues = [
|
||||
// finder
|
||||
QRMatrix::M_FINDER_DARK => '#A71111', // dark (true)
|
||||
QRMatrix::M_FINDER_DOT => '#A71111', // finder dot, dark (true)
|
||||
QRMatrix::M_FINDER => '#FFBFBF', // light (false)
|
||||
// alignment
|
||||
QRMatrix::M_ALIGNMENT_DARK => '#A70364',
|
||||
QRMatrix::M_ALIGNMENT => '#FFC9C9',
|
||||
// timing
|
||||
QRMatrix::M_TIMING_DARK => '#98005D',
|
||||
QRMatrix::M_TIMING => '#FFB8E9',
|
||||
// format
|
||||
QRMatrix::M_FORMAT_DARK => '#003804',
|
||||
QRMatrix::M_FORMAT => '#CCFB12',
|
||||
// version
|
||||
QRMatrix::M_VERSION_DARK => '#650098',
|
||||
QRMatrix::M_VERSION => '#E0B8FF',
|
||||
// data
|
||||
QRMatrix::M_DATA_DARK => '#4A6000',
|
||||
QRMatrix::M_DATA => '#ECF9BE',
|
||||
// darkmodule
|
||||
QRMatrix::M_DARKMODULE => '#080063',
|
||||
// separator
|
||||
QRMatrix::M_SEPARATOR => '#DDDDDD',
|
||||
// quietzone
|
||||
QRMatrix::M_QUIETZONE => '#DDDDDD',
|
||||
];
|
||||
|
||||
$qrcode = new QRCode($options);
|
||||
$qrcode->addByteSegment('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
|
||||
|
||||
$qrOutputInterface = new QRInterventionImage($options, $qrcode->getQRMatrix());
|
||||
// set a different driver
|
||||
$qrOutputInterface->setDriver(new GdDriver);
|
||||
|
||||
$out = $qrOutputInterface->dump();
|
||||
|
||||
header('Content-type: image/png');
|
||||
|
||||
echo $out;
|
||||
|
||||
exit;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* Class QRInterventionImage
|
||||
*
|
||||
* @created 21.01.2024
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2024 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCode\Output;
|
||||
|
||||
use chillerlan\QRCode\Data\QRMatrix;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use chillerlan\Settings\SettingsContainerInterface;
|
||||
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
|
||||
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
|
||||
use Intervention\Image\Geometry\Factories\CircleFactory;
|
||||
use Intervention\Image\Geometry\Factories\RectangleFactory;
|
||||
use Intervention\Image\ImageManager;
|
||||
use Intervention\Image\Interfaces\DriverInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\ImageManagerInterface;
|
||||
use UnhandledMatchError;
|
||||
use function class_exists;
|
||||
use function extension_loaded;
|
||||
use function intdiv;
|
||||
|
||||
/**
|
||||
* intervention/image (GD/ImageMagick) output
|
||||
*
|
||||
* note: this output class works very slow compared to the native GD/Imagick output classes for obvious reasons.
|
||||
* use only if you must.
|
||||
*
|
||||
* @see https://github.com/Intervention/image
|
||||
* @see https://image.intervention.io/
|
||||
*/
|
||||
class QRInterventionImage extends QROutputAbstract{
|
||||
use CssColorModuleValueTrait;
|
||||
|
||||
protected DriverInterface $driver;
|
||||
protected ImageManagerInterface $manager;
|
||||
protected ImageInterface $image;
|
||||
|
||||
/**
|
||||
* QRInterventionImage constructor.
|
||||
*
|
||||
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
||||
*/
|
||||
public function __construct(SettingsContainerInterface|QROptions $options, QRMatrix $matrix){
|
||||
|
||||
if(!class_exists(ImageManager::class)){
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new QRCodeOutputException(
|
||||
'The QRInterventionImage output requires Intervention/image (https://github.com/Intervention/image)'.
|
||||
' as dependency but the class "\\Intervention\\Image\\ImageManager" could not be found.',
|
||||
);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
try{
|
||||
|
||||
$this->driver = match(true){
|
||||
extension_loaded('gd') => new GdDriver,
|
||||
extension_loaded('imagick') => new ImagickDriver,
|
||||
};
|
||||
|
||||
$this->setDriver($this->driver);
|
||||
}
|
||||
catch(UnhandledMatchError){
|
||||
throw new QRCodeOutputException('no image processing extension loaded (gd, imagick)'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
parent::__construct($options, $matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DriverInterface
|
||||
*/
|
||||
public function setDriver(DriverInterface $driver):static{
|
||||
$this->manager = new ImageManager($driver);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function dump(string $file = null):string|ImageInterface{
|
||||
[$width, $height] = $this->getOutputDimensions();
|
||||
|
||||
$this->image = $this->manager->create($width, $height);
|
||||
|
||||
$this->image->fill($this->getDefaultModuleValue(false));
|
||||
|
||||
if($this->options->imageTransparent && $this::moduleValueIsValid($this->options->transparencyColor)){
|
||||
$this->image->setBlendingColor($this->prepareModuleValue($this->options->transparencyColor));
|
||||
}
|
||||
|
||||
if($this::moduleValueIsValid($this->options->bgColor)){
|
||||
$this->image->fill($this->prepareModuleValue($this->options->bgColor));
|
||||
}
|
||||
|
||||
foreach($this->matrix->getMatrix() as $y => $row){
|
||||
foreach($row as $x => $M_TYPE){
|
||||
$this->module($x, $y, $M_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->options->returnResource){
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
$image = $this->image->toPng();
|
||||
$imageData = $image->toString();
|
||||
|
||||
$this->saveToFile($imageData, $file);
|
||||
|
||||
if($this->options->outputBase64){
|
||||
return $image->toDataUri();
|
||||
}
|
||||
|
||||
return $imageData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draws a single pixel at the given position
|
||||
*/
|
||||
protected function module(int $x, int $y, int $M_TYPE):void{
|
||||
|
||||
if(!$this->drawLightModules && !$this->matrix->isDark($M_TYPE)){
|
||||
return;
|
||||
}
|
||||
|
||||
$color = $this->getModuleValue($M_TYPE);
|
||||
|
||||
if($this->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->keepAsSquare)){
|
||||
|
||||
$this->image->drawCircle(
|
||||
(($x * $this->scale) + intdiv($this->scale, 2)),
|
||||
(($y * $this->scale) + intdiv($this->scale, 2)),
|
||||
function(CircleFactory $circle) use ($color):void{
|
||||
$circle->radius((int)($this->circleRadius * $this->scale));
|
||||
$circle->background($color);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->image->drawRectangle(
|
||||
($x * $this->scale),
|
||||
($y * $this->scale),
|
||||
function(RectangleFactory $rectangle) use ($color):void{
|
||||
$rectangle->size($this->scale, $this->scale);
|
||||
$rectangle->background($color);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* Class QRInterventionImageTest
|
||||
*
|
||||
* @created 04.05.2024
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2024 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace chillerlan\QRCodeTest\Output;
|
||||
|
||||
use chillerlan\QRCode\Data\QRMatrix;
|
||||
use chillerlan\QRCode\Output\QRInterventionImage;
|
||||
use chillerlan\QRCode\Output\QROutputInterface;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use chillerlan\Settings\SettingsContainerInterface;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use function extension_loaded;
|
||||
|
||||
/**
|
||||
* Tests the QRInterventionImage output module
|
||||
*/
|
||||
class QRInterventionImageTest extends QROutputTestAbstract{
|
||||
use CssColorModuleValueProviderTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function setUp():void{
|
||||
|
||||
if(!extension_loaded('gd')){
|
||||
$this::markTestSkipped('ext-gd not loaded');
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
protected function getOutputInterface(
|
||||
SettingsContainerInterface|QROptions $options,
|
||||
QRMatrix $matrix
|
||||
):QROutputInterface{
|
||||
return new QRInterventionImage($options, $matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function testSetModuleValues():void{
|
||||
|
||||
$this->options->moduleValues = [
|
||||
// data
|
||||
QRMatrix::M_DATA_DARK => '#4A6000',
|
||||
QRMatrix::M_DATA => '#ECF9BE',
|
||||
];
|
||||
|
||||
$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
|
||||
$this->outputInterface->dump();
|
||||
|
||||
$this::assertTrue(true); // tricking the code coverage
|
||||
}
|
||||
|
||||
public function testOutputGetResource():void{
|
||||
$this->options->returnResource = true;
|
||||
$this->outputInterface = $this->getOutputInterface($this->options, $this->matrix);
|
||||
|
||||
$this::assertInstanceOf(ImageInterface::class, $this->outputInterface->dump());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user