v1.0 Initial commit of project

This commit is contained in:
2026-01-01 10:54:18 +01:00
commit 768cf78b57
990 changed files with 241213 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode;
use BaconQrCode\Common\ErrorCorrectionLevel as BaconErrorCorrectionLevel;
use MyCLabs\Enum\Enum;
/**
* @method static ErrorCorrectionLevel LOW()
* @method static ErrorCorrectionLevel MEDIUM()
* @method static ErrorCorrectionLevel QUARTILE()
* @method static ErrorCorrectionLevel HIGH()
*
* @extends Enum<string>
* @psalm-immutable
*/
class ErrorCorrectionLevel extends Enum
{
const LOW = 'low';
const MEDIUM = 'medium';
const QUARTILE = 'quartile';
const HIGH = 'high';
/**
* @psalm-suppress ImpureMethodCall
*/
public function toBaconErrorCorrectionLevel(): BaconErrorCorrectionLevel
{
$name = strtoupper(substr($this->getValue(), 0, 1));
return BaconErrorCorrectionLevel::valueOf($name);
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class GenerateImageException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class InvalidFontException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class InvalidLogoException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class InvalidWriterException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class MissingExtensionException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class MissingFunctionException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class MissingLogoHeightException extends QrCodeException
{
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
use Exception;
abstract class QrCodeException extends Exception
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class UnsupportedExtensionException extends QrCodeException
{
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Exception;
class ValidationException extends QrCodeException
{
}

View File

@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Factory;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\Exception\ValidationException;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\QrCodeInterface;
use Endroid\QrCode\WriterRegistryInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccess;
class QrCodeFactory implements QrCodeFactoryInterface
{
private $writerRegistry;
/** @var OptionsResolver */
private $optionsResolver;
/** @var array<string, mixed> */
private $defaultOptions;
/** @var array<int, string> */
private $definedOptions = [
'writer',
'writer_options',
'size',
'margin',
'foreground_color',
'background_color',
'encoding',
'round_block_size',
'round_block_size_mode',
'error_correction_level',
'logo_path',
'logo_width',
'logo_height',
'label',
'label_font_size',
'label_font_path',
'label_alignment',
'label_margin',
'validate_result',
];
/** @param array<string, mixed> $defaultOptions */
public function __construct(array $defaultOptions = [], WriterRegistryInterface $writerRegistry = null)
{
$this->defaultOptions = $defaultOptions;
$this->writerRegistry = $writerRegistry;
}
public function create(string $text = '', array $options = []): QrCodeInterface
{
$options = $this->getOptionsResolver()->resolve($options);
$accessor = PropertyAccess::createPropertyAccessor();
$qrCode = new QrCode($text);
if ($this->writerRegistry instanceof WriterRegistryInterface) {
$qrCode->setWriterRegistry($this->writerRegistry);
}
foreach ($this->definedOptions as $option) {
if (isset($options[$option])) {
if ('writer' === $option) {
$options['writer_by_name'] = $options[$option];
$option = 'writer_by_name';
}
if ('error_correction_level' === $option) {
$options[$option] = new ErrorCorrectionLevel($options[$option]);
}
$accessor->setValue($qrCode, $option, $options[$option]);
}
}
if (!$qrCode instanceof QrCodeInterface) {
throw new ValidationException('QR Code was messed up by property accessor');
}
return $qrCode;
}
private function getOptionsResolver(): OptionsResolver
{
if (!$this->optionsResolver instanceof OptionsResolver) {
$this->optionsResolver = $this->createOptionsResolver();
}
return $this->optionsResolver;
}
private function createOptionsResolver(): OptionsResolver
{
$optionsResolver = new OptionsResolver();
$optionsResolver
->setDefaults($this->defaultOptions)
->setDefined($this->definedOptions)
;
return $optionsResolver;
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Factory;
use Endroid\QrCode\QrCodeInterface;
interface QrCodeFactoryInterface
{
/** @param array<string, mixed> $options */
public function create(string $text = '', array $options = []): QrCodeInterface;
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode;
use MyCLabs\Enum\Enum;
/**
* @method static LabelAlignment LEFT()
* @method static LabelAlignment CENTER()
* @method static LabelAlignment RIGHT()
*
* @extends Enum<string>
* @psalm-immutable
*/
class LabelAlignment extends Enum
{
const LEFT = 'left';
const CENTER = 'center';
const RIGHT = 'right';
}

478
vendor/endroid/qr-code/src/QrCode.php vendored Normal file
View File

@@ -0,0 +1,478 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode;
use BaconQrCode\Encoder\Encoder;
use Endroid\QrCode\Exception\InvalidFontException;
use Endroid\QrCode\Exception\UnsupportedExtensionException;
use Endroid\QrCode\Exception\ValidationException;
use Endroid\QrCode\Writer\WriterInterface;
class QrCode implements QrCodeInterface
{
const LABEL_FONT_PATH_DEFAULT = __DIR__.'/../assets/fonts/noto_sans.otf';
const ROUND_BLOCK_SIZE_MODE_MARGIN = 'margin';
const ROUND_BLOCK_SIZE_MODE_SHRINK = 'shrink';
const ROUND_BLOCK_SIZE_MODE_ENLARGE = 'enlarge';
private $text;
/** @var int */
private $size = 300;
/** @var int */
private $margin = 10;
/** @var array<int> */
private $foregroundColor = [
'r' => 0,
'g' => 0,
'b' => 0,
'a' => 0,
];
/** @var array<int> */
private $backgroundColor = [
'r' => 255,
'g' => 255,
'b' => 255,
'a' => 0,
];
/** @var string */
private $encoding = 'UTF-8';
/** @var bool */
private $roundBlockSize = true;
/** @var string */
private $roundBlockSizeMode = self::ROUND_BLOCK_SIZE_MODE_MARGIN;
private $errorCorrectionLevel;
/** @var string */
private $logoPath;
/** @var int|null */
private $logoWidth;
/** @var int|null */
private $logoHeight;
/** @var string */
private $label;
/** @var int */
private $labelFontSize = 16;
/** @var string */
private $labelFontPath = self::LABEL_FONT_PATH_DEFAULT;
private $labelAlignment;
/** @var array<string, int> */
private $labelMargin = [
't' => 0,
'r' => 10,
'b' => 10,
'l' => 10,
];
/** @var WriterRegistryInterface */
private $writerRegistry;
/** @var WriterInterface|null */
private $writer;
/** @var array<mixed> */
private $writerOptions = [];
/** @var bool */
private $validateResult = false;
public function __construct(string $text = '')
{
$this->text = $text;
$this->errorCorrectionLevel = ErrorCorrectionLevel::LOW();
$this->labelAlignment = LabelAlignment::CENTER();
$this->createWriterRegistry();
}
public function setText(string $text): void
{
$this->text = $text;
}
public function getText(): string
{
return $this->text;
}
public function setSize(int $size): void
{
$this->size = $size;
}
public function getSize(): int
{
return $this->size;
}
public function setMargin(int $margin): void
{
$this->margin = $margin;
}
public function getMargin(): int
{
return $this->margin;
}
/** @param array<int> $foregroundColor */
public function setForegroundColor(array $foregroundColor): void
{
if (!isset($foregroundColor['a'])) {
$foregroundColor['a'] = 0;
}
foreach ($foregroundColor as &$color) {
$color = intval($color);
}
$this->foregroundColor = $foregroundColor;
}
public function getForegroundColor(): array
{
return $this->foregroundColor;
}
/** @param array<int> $backgroundColor */
public function setBackgroundColor(array $backgroundColor): void
{
if (!isset($backgroundColor['a'])) {
$backgroundColor['a'] = 0;
}
foreach ($backgroundColor as &$color) {
$color = intval($color);
}
$this->backgroundColor = $backgroundColor;
}
public function getBackgroundColor(): array
{
return $this->backgroundColor;
}
public function setEncoding(string $encoding): void
{
$this->encoding = $encoding;
}
public function getEncoding(): string
{
return $this->encoding;
}
public function setRoundBlockSize(bool $roundBlockSize, string $roundBlockSizeMode = self::ROUND_BLOCK_SIZE_MODE_MARGIN): void
{
$this->roundBlockSize = $roundBlockSize;
$this->setRoundBlockSizeMode($roundBlockSizeMode);
}
public function getRoundBlockSize(): bool
{
return $this->roundBlockSize;
}
public function setRoundBlockSizeMode(string $roundBlockSizeMode): void
{
if (!in_array($roundBlockSizeMode, [
self::ROUND_BLOCK_SIZE_MODE_ENLARGE,
self::ROUND_BLOCK_SIZE_MODE_MARGIN,
self::ROUND_BLOCK_SIZE_MODE_SHRINK,
])) {
throw new ValidationException('Invalid round block size mode: '.$roundBlockSizeMode);
}
$this->roundBlockSizeMode = $roundBlockSizeMode;
}
public function setErrorCorrectionLevel(ErrorCorrectionLevel $errorCorrectionLevel): void
{
$this->errorCorrectionLevel = $errorCorrectionLevel;
}
public function getErrorCorrectionLevel(): ErrorCorrectionLevel
{
return $this->errorCorrectionLevel;
}
public function setLogoPath(string $logoPath): void
{
$this->logoPath = $logoPath;
}
public function getLogoPath(): ?string
{
return $this->logoPath;
}
public function setLogoSize(int $logoWidth, int $logoHeight = null): void
{
$this->logoWidth = $logoWidth;
$this->logoHeight = $logoHeight;
}
public function setLogoWidth(int $logoWidth): void
{
$this->logoWidth = $logoWidth;
}
public function getLogoWidth(): ?int
{
return $this->logoWidth;
}
public function setLogoHeight(int $logoHeight): void
{
$this->logoHeight = $logoHeight;
}
public function getLogoHeight(): ?int
{
return $this->logoHeight;
}
/** @param array<string, int> $labelMargin */
public function setLabel(string $label, int $labelFontSize = null, string $labelFontPath = null, string $labelAlignment = null, array $labelMargin = null): void
{
$this->label = $label;
if (null !== $labelFontSize) {
$this->setLabelFontSize($labelFontSize);
}
if (null !== $labelFontPath) {
$this->setLabelFontPath($labelFontPath);
}
if (null !== $labelAlignment) {
$this->setLabelAlignment($labelAlignment);
}
if (null !== $labelMargin) {
$this->setLabelMargin($labelMargin);
}
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabelFontSize(int $labelFontSize): void
{
$this->labelFontSize = $labelFontSize;
}
public function getLabelFontSize(): int
{
return $this->labelFontSize;
}
public function setLabelFontPath(string $labelFontPath): void
{
$resolvedLabelFontPath = (string) realpath($labelFontPath);
if (!is_file($resolvedLabelFontPath)) {
throw new InvalidFontException('Invalid label font path: '.$labelFontPath);
}
$this->labelFontPath = $resolvedLabelFontPath;
}
public function getLabelFontPath(): string
{
return $this->labelFontPath;
}
public function setLabelAlignment(string $labelAlignment): void
{
$this->labelAlignment = new LabelAlignment($labelAlignment);
}
public function getLabelAlignment(): string
{
return $this->labelAlignment->getValue();
}
/** @param array<string, int> $labelMargin */
public function setLabelMargin(array $labelMargin): void
{
$this->labelMargin = array_merge($this->labelMargin, $labelMargin);
}
public function getLabelMargin(): array
{
return $this->labelMargin;
}
public function setWriterRegistry(WriterRegistryInterface $writerRegistry): void
{
$this->writerRegistry = $writerRegistry;
}
public function setWriter(WriterInterface $writer): void
{
$this->writer = $writer;
}
public function getWriter(string $name = null): WriterInterface
{
if (!is_null($name)) {
return $this->writerRegistry->getWriter($name);
}
if ($this->writer instanceof WriterInterface) {
return $this->writer;
}
return $this->writerRegistry->getDefaultWriter();
}
/** @param array<string, mixed> $writerOptions */
public function setWriterOptions(array $writerOptions): void
{
$this->writerOptions = $writerOptions;
}
public function getWriterOptions(): array
{
return $this->writerOptions;
}
private function createWriterRegistry(): void
{
$this->writerRegistry = new WriterRegistry();
$this->writerRegistry->loadDefaultWriters();
}
public function setWriterByName(string $name): void
{
$this->writer = $this->getWriter($name);
}
public function setWriterByPath(string $path): void
{
$extension = pathinfo($path, PATHINFO_EXTENSION);
$this->setWriterByExtension($extension);
}
public function setWriterByExtension(string $extension): void
{
foreach ($this->writerRegistry->getWriters() as $writer) {
if ($writer->supportsExtension($extension)) {
$this->writer = $writer;
return;
}
}
throw new UnsupportedExtensionException('Missing writer for extension "'.$extension.'"');
}
public function writeString(): string
{
return $this->getWriter()->writeString($this);
}
public function writeDataUri(): string
{
return $this->getWriter()->writeDataUri($this);
}
public function writeFile(string $path): void
{
$this->getWriter()->writeFile($this, $path);
}
public function getContentType(): string
{
return $this->getWriter()->getContentType();
}
public function setValidateResult(bool $validateResult): void
{
$this->validateResult = $validateResult;
}
public function getValidateResult(): bool
{
return $this->validateResult;
}
public function getData(): array
{
$baconErrorCorrectionLevel = $this->errorCorrectionLevel->toBaconErrorCorrectionLevel();
$baconQrCode = Encoder::encode($this->text, $baconErrorCorrectionLevel, $this->encoding);
$baconMatrix = $baconQrCode->getMatrix();
$matrix = [];
$columnCount = $baconMatrix->getWidth();
$rowCount = $baconMatrix->getHeight();
for ($rowIndex = 0; $rowIndex < $rowCount; ++$rowIndex) {
$matrix[$rowIndex] = [];
for ($columnIndex = 0; $columnIndex < $columnCount; ++$columnIndex) {
$matrix[$rowIndex][$columnIndex] = $baconMatrix->get($columnIndex, $rowIndex);
}
}
$data = ['matrix' => $matrix];
$data['block_count'] = count($matrix[0]);
$data['block_size'] = $this->size / $data['block_count'];
if ($this->roundBlockSize) {
switch ($this->roundBlockSizeMode) {
case self::ROUND_BLOCK_SIZE_MODE_ENLARGE:
$data['block_size'] = intval(ceil($data['block_size']));
$this->size = $data['block_size'] * $data['block_count'];
break;
case self::ROUND_BLOCK_SIZE_MODE_SHRINK:
$data['block_size'] = intval(floor($data['block_size']));
$this->size = $data['block_size'] * $data['block_count'];
break;
case self::ROUND_BLOCK_SIZE_MODE_MARGIN:
default:
$data['block_size'] = intval(floor($data['block_size']));
}
}
$data['inner_width'] = $data['block_size'] * $data['block_count'];
$data['inner_height'] = $data['block_size'] * $data['block_count'];
$data['outer_width'] = $this->size + 2 * $this->margin;
$data['outer_height'] = $this->size + 2 * $this->margin;
$data['margin_left'] = ($data['outer_width'] - $data['inner_width']) / 2;
if ($this->roundBlockSize) {
$data['margin_left'] = intval(floor($data['margin_left']));
}
$data['margin_right'] = $data['outer_width'] - $data['inner_width'] - $data['margin_left'];
return $data;
}
}

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode;
interface QrCodeInterface
{
public function getText(): string;
public function getSize(): int;
public function getMargin(): int;
/** @return array<int> */
public function getForegroundColor(): array;
/** @return array<int> */
public function getBackgroundColor(): array;
public function getEncoding(): string;
public function getRoundBlockSize(): bool;
public function getErrorCorrectionLevel(): ErrorCorrectionLevel;
public function getLogoPath(): ?string;
public function getLogoWidth(): ?int;
public function getLogoHeight(): ?int;
public function getLabel(): ?string;
public function getLabelFontPath(): string;
public function getLabelFontSize(): int;
public function getLabelAlignment(): string;
/** @return array<int> */
public function getLabelMargin(): array;
public function getValidateResult(): bool;
/** @return array<mixed> */
public function getWriterOptions(): array;
public function getContentType(): string;
public function setWriterRegistry(WriterRegistryInterface $writerRegistry): void;
public function writeString(): string;
public function writeDataUri(): string;
public function writeFile(string $path): void;
/** @return array<mixed> */
public function getData(): array;
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\Exception\GenerateImageException;
use Endroid\QrCode\Exception\InvalidLogoException;
use Endroid\QrCode\Exception\MissingExtensionException;
use Endroid\QrCode\QrCodeInterface;
abstract class AbstractWriter implements WriterInterface
{
protected function getMimeType(string $path): string
{
if (false !== filter_var($path, FILTER_VALIDATE_URL)) {
return $this->getMimeTypeFromUrl($path);
}
return $this->getMimeTypeFromPath($path);
}
private function getMimeTypeFromUrl(string $url): string
{
/** @var mixed $format */
$format = PHP_VERSION > 80000 ? true : 1;
$headers = get_headers($url, $format);
if (!is_array($headers) || !isset($headers['Content-Type'])) {
throw new InvalidLogoException(sprintf('Content type could not be determined for logo URL "%s"', $url));
}
return $headers['Content-Type'];
}
private function getMimeTypeFromPath(string $path): string
{
if (!function_exists('mime_content_type')) {
throw new MissingExtensionException('You need the ext-fileinfo extension to determine logo mime type');
}
$mimeType = mime_content_type($path);
if (!is_string($mimeType)) {
throw new InvalidLogoException('Could not determine mime type');
}
if (!preg_match('#^image/#', $mimeType)) {
throw new GenerateImageException('Logo path is not an image');
}
// Passing mime type image/svg results in invisible images
if ('image/svg' === $mimeType) {
return 'image/svg+xml';
}
return $mimeType;
}
public function writeDataUri(QrCodeInterface $qrCode): string
{
$dataUri = 'data:'.$this->getContentType().';base64,'.base64_encode($this->writeString($qrCode));
return $dataUri;
}
public function writeFile(QrCodeInterface $qrCode, string $path): void
{
$string = $this->writeString($qrCode);
file_put_contents($path, $string);
}
public static function supportsExtension(string $extension): bool
{
return in_array($extension, static::getSupportedExtensions());
}
public static function getSupportedExtensions(): array
{
return [];
}
abstract public function getName(): string;
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\QrCodeInterface;
class BinaryWriter extends AbstractWriter
{
public function writeString(QrCodeInterface $qrCode): string
{
$rows = [];
$data = $qrCode->getData();
foreach ($data['matrix'] as $row) {
$values = '';
foreach ($row as $value) {
$values .= $value;
}
$rows[] = $values;
}
return implode("\n", $rows);
}
public static function getContentType(): string
{
return 'text/plain';
}
public static function getSupportedExtensions(): array
{
return ['bin', 'txt'];
}
public function getName(): string
{
return 'binary';
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\QrCodeInterface;
use Exception;
use ReflectionClass;
class DebugWriter extends AbstractWriter
{
public function writeString(QrCodeInterface $qrCode): string
{
$data = [];
$skip = ['getData'];
$reflectionClass = new ReflectionClass($qrCode);
foreach ($reflectionClass->getMethods() as $method) {
$methodName = $method->getShortName();
if (0 === strpos($methodName, 'get') && 0 == $method->getNumberOfParameters() && !in_array($methodName, $skip)) {
$value = $qrCode->{$methodName}();
if (is_array($value) && !is_object(current($value))) {
$value = '['.implode(', ', $value).']';
} elseif (is_bool($value)) {
$value = $value ? 'true' : 'false';
} elseif (is_string($value)) {
$value = '"'.$value.'"';
} elseif (is_null($value)) {
$value = 'null';
}
try {
$data[] = $methodName.': '.$value;
} catch (Exception $exception) {
}
}
}
$string = implode(" \n", $data);
return $string;
}
public static function getContentType(): string
{
return 'text/plain';
}
public function getName(): string
{
return 'debug';
}
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\QrCodeInterface;
class EpsWriter extends AbstractWriter
{
public function writeString(QrCodeInterface $qrCode): string
{
$data = $qrCode->getData();
$epsData = [];
$epsData[] = '%!PS-Adobe-3.0 EPSF-3.0';
$epsData[] = '%%BoundingBox: 0 0 '.$data['outer_width'].' '.$data['outer_height'];
$epsData[] = '/F { rectfill } def';
$epsData[] = number_format($qrCode->getBackgroundColor()['r'] / 100, 2, '.', ',').' '.number_format($qrCode->getBackgroundColor()['g'] / 100, 2, '.', ',').' '.number_format($qrCode->getBackgroundColor()['b'] / 100, 2, '.', ',').' setrgbcolor';
$epsData[] = '0 0 '.$data['outer_width'].' '.$data['outer_height'].' F';
$epsData[] = number_format($qrCode->getForegroundColor()['r'] / 100, 2, '.', ',').' '.number_format($qrCode->getForegroundColor()['g'] / 100, 2, '.', ',').' '.number_format($qrCode->getForegroundColor()['b'] / 100, 2, '.', ',').' setrgbcolor';
// Please note an EPS has a reversed Y axis compared to PNG and SVG
$data['matrix'] = array_reverse($data['matrix']);
foreach ($data['matrix'] as $row => $values) {
foreach ($values as $column => $value) {
if (1 === $value) {
$x = $data['margin_left'] + $data['block_size'] * $column;
$y = $data['margin_left'] + $data['block_size'] * $row;
$epsData[] = $x.' '.$y.' '.$data['block_size'].' '.$data['block_size'].' F';
}
}
}
return implode("\n", $epsData);
}
public static function getContentType(): string
{
return 'image/eps';
}
public static function getSupportedExtensions(): array
{
return ['eps'];
}
public function getName(): string
{
return 'eps';
}
}

View File

@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\Exception\ValidationException;
use Endroid\QrCode\QrCodeInterface;
class FpdfWriter extends AbstractWriter
{
/**
* Defines as which unit the size is handled. Default is: "mm".
*
* Allowed values: 'mm', 'pt', 'cm', 'in'
*/
public const WRITER_OPTION_MEASURE_UNIT = 'fpdf_measure_unit';
public function writeString(QrCodeInterface $qrCode): string
{
if (!\class_exists(\FPDF::class)) {
throw new \BadMethodCallException('The Fpdf writer requires FPDF as dependency but the class "\\FPDF" couldn\'t be found.');
}
if ($qrCode->getValidateResult()) {
throw new ValidationException('Built-in validation reader can not check fpdf qr codes: please disable via setValidateResult(false)');
}
$foregroundColor = $qrCode->getForegroundColor();
if (0 !== $foregroundColor['a']) {
throw new \InvalidArgumentException('The foreground color has an alpha channel, but the fpdf qr writer doesn\'t support alpha channels.');
}
$backgroundColor = $qrCode->getBackgroundColor();
if (0 !== $backgroundColor['a']) {
throw new \InvalidArgumentException('The foreground color has an alpha channel, but the fpdf qr writer doesn\'t support alpha channels.');
}
$label = $qrCode->getLabel();
$labelHeight = null !== $label ? 30 : 0;
$data = $qrCode->getData();
$options = $qrCode->getWriterOptions();
$fpdf = new \FPDF(
'P',
$options[self::WRITER_OPTION_MEASURE_UNIT] ?? 'mm',
[$data['outer_width'], $data['outer_height'] + $labelHeight]
);
$fpdf->AddPage();
$fpdf->SetFillColor($backgroundColor['r'], $backgroundColor['g'], $backgroundColor['b']);
$fpdf->Rect(0, 0, $data['outer_width'], $data['outer_height'], 'F');
$fpdf->SetFillColor($foregroundColor['r'], $foregroundColor['g'], $foregroundColor['b']);
foreach ($data['matrix'] as $row => $values) {
foreach ($values as $column => $value) {
if (1 === $value) {
$fpdf->Rect(
$data['margin_left'] + ($column * $data['block_size']),
$data['margin_left'] + ($row * $data['block_size']),
$data['block_size'],
$data['block_size'],
'F'
);
}
}
}
$logoPath = $qrCode->getLogoPath();
if (null !== $logoPath) {
$this->addLogo(
$fpdf,
$logoPath,
$qrCode->getLogoWidth(),
$qrCode->getLogoHeight(),
$data['outer_width'],
$data['outer_height']
);
}
if (null !== $label) {
$fpdf->setY($data['outer_height'] + 5);
$fpdf->SetFont('Helvetica', null, $qrCode->getLabelFontSize());
$fpdf->Cell(0, 0, $label, 0, 0, strtoupper($qrCode->getLabelAlignment()[0]));
}
return $fpdf->Output('S');
}
protected function addLogo(\FPDF $fpdf, string $logoPath, ?int $logoWidth, ?int $logoHeight, int $imageWidth, int $imageHeight): void
{
if (null === $logoHeight || null === $logoWidth) {
[$logoSourceWidth, $logoSourceHeight] = \getimagesize($logoPath);
if (null === $logoWidth) {
$logoWidth = (int) $logoSourceWidth;
}
if (null === $logoHeight) {
$aspectRatio = $logoWidth / $logoSourceWidth;
$logoHeight = (int) ($logoSourceHeight * $aspectRatio);
}
}
$logoX = $imageWidth / 2 - (int) $logoWidth / 2;
$logoY = $imageHeight / 2 - (int) $logoHeight / 2;
$fpdf->Image($logoPath, $logoX, $logoY, $logoWidth, $logoHeight);
}
public static function getContentType(): string
{
return 'application/pdf';
}
public static function getSupportedExtensions(): array
{
return ['pdf'];
}
public function getName(): string
{
return 'fpdf';
}
}

View File

@@ -0,0 +1,281 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\Exception\GenerateImageException;
use Endroid\QrCode\Exception\MissingFunctionException;
use Endroid\QrCode\Exception\MissingLogoHeightException;
use Endroid\QrCode\Exception\ValidationException;
use Endroid\QrCode\LabelAlignment;
use Endroid\QrCode\QrCodeInterface;
use Zxing\QrReader;
class PngWriter extends AbstractWriter
{
public function writeString(QrCodeInterface $qrCode): string
{
if (!extension_loaded('gd')) {
throw new GenerateImageException('Unable to generate image: check your GD installation');
}
$image = $this->createImage($qrCode->getData(), $qrCode);
$logoPath = $qrCode->getLogoPath();
if (null !== $logoPath) {
$image = $this->addLogo($image, $logoPath, $qrCode->getLogoWidth(), $qrCode->getLogoHeight());
}
$label = $qrCode->getLabel();
if (null !== $label) {
$image = $this->addLabel($image, $label, $qrCode->getLabelFontPath(), $qrCode->getLabelFontSize(), $qrCode->getLabelAlignment(), $qrCode->getLabelMargin(), $qrCode->getForegroundColor(), $qrCode->getBackgroundColor());
}
$string = $this->imageToString($image);
if (PHP_VERSION_ID < 80000) {
imagedestroy($image);
}
if ($qrCode->getValidateResult()) {
$reader = new QrReader($string, QrReader::SOURCE_TYPE_BLOB);
if ($reader->text() !== $qrCode->getText()) {
throw new ValidationException('Built-in validation reader read "'.$reader->text().'" instead of "'.$qrCode->getText().'".
Adjust your parameters to increase readability or disable built-in validation.');
}
}
return $string;
}
/**
* @param array<mixed> $data
*
* @return mixed
*/
private function createImage(array $data, QrCodeInterface $qrCode)
{
$baseSize = $qrCode->getRoundBlockSize() ? $data['block_size'] : 25;
$baseImage = $this->createBaseImage($baseSize, $data, $qrCode);
$interpolatedImage = $this->createInterpolatedImage($baseImage, $data, $qrCode);
if (PHP_VERSION_ID < 80000) {
imagedestroy($baseImage);
}
return $interpolatedImage;
}
/**
* @param array<mixed> $data
*
* @return mixed
*/
private function createBaseImage(int $baseSize, array $data, QrCodeInterface $qrCode)
{
$image = imagecreatetruecolor($data['block_count'] * $baseSize, $data['block_count'] * $baseSize);
if (!$image) {
throw new GenerateImageException('Unable to generate image: check your GD installation');
}
$foregroundColor = imagecolorallocatealpha($image, $qrCode->getForegroundColor()['r'], $qrCode->getForegroundColor()['g'], $qrCode->getForegroundColor()['b'], $qrCode->getForegroundColor()['a']);
if (!is_int($foregroundColor)) {
throw new GenerateImageException('Foreground color could not be allocated');
}
$backgroundColor = imagecolorallocatealpha($image, $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b'], $qrCode->getBackgroundColor()['a']);
if (!is_int($backgroundColor)) {
throw new GenerateImageException('Background color could not be allocated');
}
imagefill($image, 0, 0, $backgroundColor);
foreach ($data['matrix'] as $row => $values) {
foreach ($values as $column => $value) {
if (1 === $value) {
imagefilledrectangle($image, $column * $baseSize, $row * $baseSize, intval(($column + 1) * $baseSize), intval(($row + 1) * $baseSize), $foregroundColor);
}
}
}
return $image;
}
/**
* @param mixed $baseImage
* @param array<mixed> $data
*
* @return mixed
*/
private function createInterpolatedImage($baseImage, array $data, QrCodeInterface $qrCode)
{
$image = imagecreatetruecolor($data['outer_width'], $data['outer_height']);
if (!$image) {
throw new GenerateImageException('Unable to generate image: check your GD installation');
}
$backgroundColor = imagecolorallocatealpha($image, $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b'], $qrCode->getBackgroundColor()['a']);
if (!is_int($backgroundColor)) {
throw new GenerateImageException('Background color could not be allocated');
}
imagefill($image, 0, 0, $backgroundColor);
imagecopyresampled($image, $baseImage, (int) $data['margin_left'], (int) $data['margin_left'], 0, 0, (int) $data['inner_width'], (int) $data['inner_height'], imagesx($baseImage), imagesy($baseImage));
if ($qrCode->getBackgroundColor()['a'] > 0) {
imagesavealpha($image, true);
}
return $image;
}
/**
* @param mixed $sourceImage
*
* @return mixed
*/
private function addLogo($sourceImage, string $logoPath, int $logoWidth = null, int $logoHeight = null)
{
$mimeType = $this->getMimeType($logoPath);
$logoImage = imagecreatefromstring(strval(file_get_contents($logoPath)));
if ('image/svg+xml' === $mimeType && (null === $logoHeight || null === $logoWidth)) {
throw new MissingLogoHeightException('SVG Logos require an explicit height set via setLogoSize($width, $height)');
}
if (!$logoImage) {
throw new GenerateImageException('Unable to generate image: check your GD installation or logo path');
}
$logoSourceWidth = imagesx($logoImage);
$logoSourceHeight = imagesy($logoImage);
if (null === $logoWidth) {
$logoWidth = $logoSourceWidth;
}
if (null === $logoHeight) {
$aspectRatio = $logoWidth / $logoSourceWidth;
$logoHeight = intval($logoSourceHeight * $aspectRatio);
}
$logoX = imagesx($sourceImage) / 2 - $logoWidth / 2;
$logoY = imagesy($sourceImage) / 2 - $logoHeight / 2;
imagecopyresampled($sourceImage, $logoImage, intval($logoX), intval($logoY), 0, 0, $logoWidth, $logoHeight, $logoSourceWidth, $logoSourceHeight);
if (PHP_VERSION_ID < 80000) {
imagedestroy($logoImage);
}
return $sourceImage;
}
/**
* @param mixed $sourceImage
* @param array<int> $labelMargin
* @param array<int> $foregroundColor
* @param array<int> $backgroundColor
*
* @return mixed
*/
private function addLabel($sourceImage, string $label, string $labelFontPath, int $labelFontSize, string $labelAlignment, array $labelMargin, array $foregroundColor, array $backgroundColor)
{
if (!function_exists('imagettfbbox')) {
throw new MissingFunctionException('Missing function "imagettfbbox", please make sure you installed the FreeType library');
}
$labelBox = imagettfbbox($labelFontSize, 0, $labelFontPath, $label);
if (!$labelBox) {
throw new GenerateImageException('Unable to add label: check your GD installation');
}
$labelBoxWidth = intval($labelBox[2] - $labelBox[0]);
$labelBoxHeight = intval($labelBox[0] - $labelBox[7]);
$sourceWidth = imagesx($sourceImage);
$sourceHeight = imagesy($sourceImage);
$targetWidth = $sourceWidth;
$targetHeight = $sourceHeight + $labelBoxHeight + $labelMargin['t'] + $labelMargin['b'];
// Create empty target image
$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
if (!$targetImage) {
throw new GenerateImageException('Unable to generate image: check your GD installation');
}
$foregroundColor = imagecolorallocate($targetImage, $foregroundColor['r'], $foregroundColor['g'], $foregroundColor['b']);
if (!is_int($foregroundColor)) {
throw new GenerateImageException('Foreground color could not be allocated');
}
$backgroundColor = imagecolorallocate($targetImage, $backgroundColor['r'], $backgroundColor['g'], $backgroundColor['b']);
if (!is_int($backgroundColor)) {
throw new GenerateImageException('Background color could not be allocated');
}
imagefill($targetImage, 0, 0, $backgroundColor);
// Copy source image to target image
imagecopyresampled($targetImage, $sourceImage, 0, 0, 0, 0, $sourceWidth, $sourceHeight, $sourceWidth, $sourceHeight);
if (PHP_VERSION_ID < 80000) {
imagedestroy($sourceImage);
}
switch ($labelAlignment) {
case LabelAlignment::LEFT:
$labelX = $labelMargin['l'];
break;
case LabelAlignment::RIGHT:
$labelX = $targetWidth - $labelBoxWidth - $labelMargin['r'];
break;
default:
$labelX = intval($targetWidth / 2 - $labelBoxWidth / 2);
break;
}
$labelY = $targetHeight - $labelMargin['b'];
imagettftext($targetImage, $labelFontSize, 0, $labelX, $labelY, $foregroundColor, $labelFontPath, $label);
return $targetImage;
}
/**
* @param mixed $image
*/
private function imageToString($image): string
{
ob_start();
imagepng($image);
return (string) ob_get_clean();
}
public static function getContentType(): string
{
return 'image/png';
}
public static function getSupportedExtensions(): array
{
return ['png'];
}
public function getName(): string
{
return 'png';
}
}

View File

@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\Exception\GenerateImageException;
use Endroid\QrCode\Exception\MissingLogoHeightException;
use Endroid\QrCode\Exception\ValidationException;
use Endroid\QrCode\QrCodeInterface;
use SimpleXMLElement;
class SvgWriter extends AbstractWriter
{
public function writeString(QrCodeInterface $qrCode): string
{
$options = $qrCode->getWriterOptions();
if ($qrCode->getValidateResult()) {
throw new ValidationException('Built-in validation reader can not check SVG images: please disable via setValidateResult(false)');
}
$data = $qrCode->getData();
$svg = new SimpleXMLElement('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"/>');
$svg->addAttribute('version', '1.1');
$svg->addAttribute('width', $data['outer_width'].'px');
$svg->addAttribute('height', $data['outer_height'].'px');
$svg->addAttribute('viewBox', '0 0 '.$data['outer_width'].' '.$data['outer_height']);
$svg->addChild('defs');
// Block definition
$block_id = isset($options['rect_id']) && $options['rect_id'] ? $options['rect_id'] : 'block';
$blockDefinition = $svg->defs->addChild('rect');
$blockDefinition->addAttribute('id', $block_id);
$blockDefinition->addAttribute('width', strval($data['block_size']));
$blockDefinition->addAttribute('height', strval($data['block_size']));
$blockDefinition->addAttribute('fill', '#'.sprintf('%02x%02x%02x', $qrCode->getForegroundColor()['r'], $qrCode->getForegroundColor()['g'], $qrCode->getForegroundColor()['b']));
$blockDefinition->addAttribute('fill-opacity', strval($this->getOpacity($qrCode->getForegroundColor()['a'])));
// Background
$background = $svg->addChild('rect');
$background->addAttribute('x', '0');
$background->addAttribute('y', '0');
$background->addAttribute('width', strval($data['outer_width']));
$background->addAttribute('height', strval($data['outer_height']));
$background->addAttribute('fill', '#'.sprintf('%02x%02x%02x', $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b']));
$background->addAttribute('fill-opacity', strval($this->getOpacity($qrCode->getBackgroundColor()['a'])));
foreach ($data['matrix'] as $row => $values) {
foreach ($values as $column => $value) {
if (1 === $value) {
$block = $svg->addChild('use');
$block->addAttribute('x', strval($data['margin_left'] + $data['block_size'] * $column));
$block->addAttribute('y', strval($data['margin_left'] + $data['block_size'] * $row));
$block->addAttribute('xlink:href', '#'.$block_id, 'http://www.w3.org/1999/xlink');
}
}
}
$logoPath = $qrCode->getLogoPath();
if (is_string($logoPath)) {
$forceXlinkHref = false;
if (isset($options['force_xlink_href']) && $options['force_xlink_href']) {
$forceXlinkHref = true;
}
$this->addLogo($svg, $data['outer_width'], $data['outer_height'], $logoPath, $qrCode->getLogoWidth(), $qrCode->getLogoHeight(), $forceXlinkHref);
}
$xml = $svg->asXML();
if (!is_string($xml)) {
throw new GenerateImageException('Unable to save SVG XML');
}
if (isset($options['exclude_xml_declaration']) && $options['exclude_xml_declaration']) {
$xml = str_replace("<?xml version=\"1.0\"?>\n", '', $xml);
}
return $xml;
}
private function addLogo(SimpleXMLElement $svg, int $imageWidth, int $imageHeight, string $logoPath, int $logoWidth = null, int $logoHeight = null, bool $forceXlinkHref = false): void
{
$mimeType = $this->getMimeType($logoPath);
$imageData = file_get_contents($logoPath);
if (!is_string($imageData)) {
throw new GenerateImageException('Unable to read image data: check your logo path');
}
if ('image/svg+xml' === $mimeType && (null === $logoHeight || null === $logoWidth)) {
throw new MissingLogoHeightException('SVG Logos require an explicit height set via setLogoSize($width, $height)');
}
if (null === $logoHeight || null === $logoWidth) {
$logoImage = imagecreatefromstring(strval($imageData));
if (!$logoImage) {
throw new GenerateImageException('Unable to generate image: check your GD installation or logo path');
}
/** @var mixed $logoImage */
$logoSourceWidth = imagesx($logoImage);
$logoSourceHeight = imagesy($logoImage);
if (PHP_VERSION_ID < 80000) {
imagedestroy($logoImage);
}
if (null === $logoWidth) {
$logoWidth = $logoSourceWidth;
}
if (null === $logoHeight) {
$aspectRatio = $logoWidth / $logoSourceWidth;
$logoHeight = intval($logoSourceHeight * $aspectRatio);
}
}
$logoX = $imageWidth / 2 - $logoWidth / 2;
$logoY = $imageHeight / 2 - $logoHeight / 2;
$imageDefinition = $svg->addChild('image');
$imageDefinition->addAttribute('x', strval($logoX));
$imageDefinition->addAttribute('y', strval($logoY));
$imageDefinition->addAttribute('width', strval($logoWidth));
$imageDefinition->addAttribute('height', strval($logoHeight));
$imageDefinition->addAttribute('preserveAspectRatio', 'none');
// xlink:href is actually deprecated, but still required when placing the qr code in a pdf.
// SimpleXML strips out the xlink part by using addAttribute(), so it must be set directly.
if ($forceXlinkHref) {
$imageDefinition['xlink:href'] = 'data:'.$mimeType.';base64,'.base64_encode($imageData);
} else {
$imageDefinition->addAttribute('href', 'data:'.$mimeType.';base64,'.base64_encode($imageData));
}
}
private function getOpacity(int $alpha): float
{
$opacity = 1 - $alpha / 127;
return $opacity;
}
public static function getContentType(): string
{
return 'image/svg+xml';
}
public static function getSupportedExtensions(): array
{
return ['svg'];
}
public function getName(): string
{
return 'svg';
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Writer;
use Endroid\QrCode\QrCodeInterface;
interface WriterInterface
{
public function writeString(QrCodeInterface $qrCode): string;
public function writeDataUri(QrCodeInterface $qrCode): string;
public function writeFile(QrCodeInterface $qrCode, string $path): void;
public static function getContentType(): string;
public static function supportsExtension(string $extension): bool;
/** @return array<string> */
public static function getSupportedExtensions(): array;
public function getName(): string;
}

View File

@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode;
use Endroid\QrCode\Exception\InvalidWriterException;
use Endroid\QrCode\Writer\BinaryWriter;
use Endroid\QrCode\Writer\DebugWriter;
use Endroid\QrCode\Writer\EpsWriter;
use Endroid\QrCode\Writer\FpdfWriter;
use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\Writer\SvgWriter;
use Endroid\QrCode\Writer\WriterInterface;
class WriterRegistry implements WriterRegistryInterface
{
/** @var WriterInterface[] */
private $writers = [];
/** @var WriterInterface|null */
private $defaultWriter;
public function loadDefaultWriters(): void
{
if (count($this->writers) > 0) {
return;
}
$this->addWriters([
new BinaryWriter(),
new DebugWriter(),
new EpsWriter(),
new PngWriter(),
new SvgWriter(),
new FpdfWriter(),
]);
$this->setDefaultWriter('png');
}
public function addWriters(iterable $writers): void
{
foreach ($writers as $writer) {
$this->addWriter($writer);
}
}
public function addWriter(WriterInterface $writer): void
{
$this->writers[$writer->getName()] = $writer;
}
public function getWriter(string $name): WriterInterface
{
$this->assertValidWriter($name);
return $this->writers[$name];
}
public function getDefaultWriter(): WriterInterface
{
if ($this->defaultWriter instanceof WriterInterface) {
return $this->defaultWriter;
}
throw new InvalidWriterException('Please set the default writer via the second argument of addWriter');
}
public function setDefaultWriter(string $name): void
{
$this->defaultWriter = $this->writers[$name];
}
public function getWriters(): array
{
return $this->writers;
}
private function assertValidWriter(string $name): void
{
if (!isset($this->writers[$name])) {
throw new InvalidWriterException('Invalid writer "'.$name.'"');
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode;
use Endroid\QrCode\Writer\WriterInterface;
interface WriterRegistryInterface
{
/** @param WriterInterface[] $writers */
public function addWriters(iterable $writers): void;
public function addWriter(WriterInterface $writer): void;
public function getWriter(string $name): WriterInterface;
public function getDefaultWriter(): WriterInterface;
/** @return WriterInterface[] */
public function getWriters(): array;
}