Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
+18
@@ -37,3 +37,21 @@ BaconQrCode comes with multiple back ends for rendering images. Currently includ
|
||||
- `ImagickImageBackEnd`: renders raster images using the Imagick library
|
||||
- `SvgImageBackEnd`: renders SVG files using XMLWriter
|
||||
- `EpsImageBackEnd`: renders EPS files
|
||||
|
||||
### GDLib Renderer
|
||||
GD library has so many limitations, that GD support is not added as backend, but as separated renderer.
|
||||
Use `GDLibRenderer` instead of `ImageRenderer`. These are the limitations:
|
||||
|
||||
- Does not support gradient.
|
||||
- Does not support any curves, so you QR code is always squared.
|
||||
|
||||
Example usage:
|
||||
|
||||
```php
|
||||
use BaconQrCode\Renderer\GDLibRenderer;
|
||||
use BaconQrCode\Writer;
|
||||
|
||||
$renderer = new GDLibRenderer(400);
|
||||
$writer = new Writer($renderer);
|
||||
$writer->writeFile('Hello World!', 'qrcode.png');
|
||||
```
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
"description": "BaconQrCode is a QR code generator for PHP.",
|
||||
"license": "BSD-2-Clause",
|
||||
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"ext-iconv": "*",
|
||||
"dasprid/enum": "^1.0.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-imagick": "to generate QR code images"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BaconQrCode\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"BaconQrCodeTest\\": "test/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5.11 || 11.0.4",
|
||||
"spatie/phpunit-snapshot-assertions": "^5.1.5",
|
||||
"squizlabs/php_codesniffer": "^3.9",
|
||||
"phly/keep-a-changelog": "^2.12"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"ocramius/package-versions": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"exclude": [
|
||||
"/test",
|
||||
"/phpunit.xml.dist"
|
||||
]
|
||||
}
|
||||
}
|
||||
+4
-12
@@ -16,21 +16,13 @@ final class BitArray
|
||||
*
|
||||
* @var SplFixedArray<int>
|
||||
*/
|
||||
private $bits;
|
||||
|
||||
/**
|
||||
* Size of the bit array in bits.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $size;
|
||||
private SplFixedArray $bits;
|
||||
|
||||
/**
|
||||
* Creates a new bit array with a given size.
|
||||
*/
|
||||
public function __construct(int $size = 0)
|
||||
public function __construct(private int $size = 0)
|
||||
{
|
||||
$this->size = $size;
|
||||
$this->bits = SplFixedArray::fromArray(array_fill(0, ($this->size + 31) >> 3, 0));
|
||||
}
|
||||
|
||||
@@ -107,7 +99,7 @@ final class BitArray
|
||||
}
|
||||
|
||||
$result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
return min($result, $this->size);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +125,7 @@ final class BitArray
|
||||
}
|
||||
|
||||
$result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
return min($result, $this->size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-12
@@ -17,36 +17,30 @@ class BitMatrix
|
||||
{
|
||||
/**
|
||||
* Width of the bit matrix.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
private int $width;
|
||||
|
||||
/**
|
||||
* Height of the bit matrix.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
private ?int $height;
|
||||
|
||||
/**
|
||||
* Size in bits of each individual row.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $rowSize;
|
||||
private int $rowSize;
|
||||
|
||||
/**
|
||||
* Bits representation.
|
||||
*
|
||||
* @var SplFixedArray<int>
|
||||
*/
|
||||
private $bits;
|
||||
private SplFixedArray $bits;
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if a dimension is smaller than zero
|
||||
*/
|
||||
public function __construct(int $width, int $height = null)
|
||||
public function __construct(int $width, ?int $height = null)
|
||||
{
|
||||
if (null === $height) {
|
||||
$height = $width;
|
||||
@@ -138,7 +132,7 @@ class BitMatrix
|
||||
/**
|
||||
* A fast method to retrieve one row of data from the matrix as a BitArray.
|
||||
*/
|
||||
public function getRow(int $y, BitArray $row = null) : BitArray
|
||||
public function getRow(int $y, ?BitArray $row = null) : BitArray
|
||||
{
|
||||
if (null === $row || $row->getSize() < $this->width) {
|
||||
$row = new BitArray($this->width);
|
||||
|
||||
+7
-10
@@ -69,29 +69,26 @@ final class CharacterSetEci extends AbstractEnum
|
||||
protected const GB18030 = [[29], 'GB2312', 'EUC_CN', 'GBK'];
|
||||
protected const EUC_KR = [[30], 'EUC-KR'];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $values;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $otherEncodingNames;
|
||||
private array $otherEncodingNames;
|
||||
|
||||
/**
|
||||
* @var array<int, self>|null
|
||||
*/
|
||||
private static $valueToEci;
|
||||
private static ?array $valueToEci;
|
||||
|
||||
/**
|
||||
* @var array<string, self>|null
|
||||
*/
|
||||
private static $nameToEci;
|
||||
private static ?array $nameToEci = null;
|
||||
|
||||
public function __construct(array $values, string ...$otherEncodingNames)
|
||||
/**
|
||||
* @param int[] $values
|
||||
*/
|
||||
public function __construct(private readonly array $values, string ...$otherEncodingNames)
|
||||
{
|
||||
$this->values = $values;
|
||||
$this->otherEncodingNames = $otherEncodingNames;
|
||||
}
|
||||
|
||||
|
||||
+1
-17
@@ -11,24 +11,8 @@ namespace BaconQrCode\Common;
|
||||
*/
|
||||
final class EcBlock
|
||||
{
|
||||
/**
|
||||
* How many times the block is used.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* Number of data codewords.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $dataCodewords;
|
||||
|
||||
public function __construct(int $count, int $dataCodewords)
|
||||
public function __construct(private readonly int $count, private readonly int $dataCodewords)
|
||||
{
|
||||
$this->count = $count;
|
||||
$this->dataCodewords = $dataCodewords;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-10
@@ -12,23 +12,15 @@ namespace BaconQrCode\Common;
|
||||
*/
|
||||
final class EcBlocks
|
||||
{
|
||||
/**
|
||||
* Number of EC codewords per block.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $ecCodewordsPerBlock;
|
||||
|
||||
/**
|
||||
* List of EC blocks.
|
||||
*
|
||||
* @var EcBlock[]
|
||||
*/
|
||||
private $ecBlocks;
|
||||
private array $ecBlocks;
|
||||
|
||||
public function __construct(int $ecCodewordsPerBlock, EcBlock ...$ecBlocks)
|
||||
public function __construct(private readonly int $ecCodewordsPerBlock, EcBlock ...$ecBlocks)
|
||||
{
|
||||
$this->ecCodewordsPerBlock = $ecCodewordsPerBlock;
|
||||
$this->ecBlocks = $ecBlocks;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,14 +21,8 @@ final class ErrorCorrectionLevel extends AbstractEnum
|
||||
protected const Q = [0x03];
|
||||
protected const H = [0x02];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $bits;
|
||||
|
||||
protected function __construct(int $bits)
|
||||
protected function __construct(private readonly int $bits)
|
||||
{
|
||||
$this->bits = $bits;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,23 +62,16 @@ class FormatInformation
|
||||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i.
|
||||
*
|
||||
* @var array
|
||||
* @var int[]
|
||||
*/
|
||||
private const BITS_SET_IN_HALF_BYTE = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
|
||||
|
||||
/**
|
||||
* Error correction level.
|
||||
*
|
||||
* @var ErrorCorrectionLevel
|
||||
*/
|
||||
private $ecLevel;
|
||||
private ErrorCorrectionLevel $ecLevel;
|
||||
|
||||
/**
|
||||
* Data mask.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $dataMask;
|
||||
private int $dataMask;
|
||||
|
||||
protected function __construct(int $formatInfo)
|
||||
{
|
||||
|
||||
+5
-12
@@ -33,19 +33,12 @@ final class Mode extends AbstractEnum
|
||||
protected const HANZI = [[8, 10, 12], 0x0d];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
* @param int[] $characterCountBitsForVersions
|
||||
*/
|
||||
private $characterCountBitsForVersions;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $bits;
|
||||
|
||||
protected function __construct(array $characterCountBitsForVersions, int $bits)
|
||||
{
|
||||
$this->characterCountBitsForVersions = $characterCountBitsForVersions;
|
||||
$this->bits = $bits;
|
||||
protected function __construct(
|
||||
private readonly array $characterCountBitsForVersions,
|
||||
private readonly int $bits
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-25
@@ -16,73 +16,59 @@ final class ReedSolomonCodec
|
||||
{
|
||||
/**
|
||||
* Symbol size in bits.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $symbolSize;
|
||||
private int $symbolSize;
|
||||
|
||||
/**
|
||||
* Block size in symbols.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $blockSize;
|
||||
private int $blockSize;
|
||||
|
||||
/**
|
||||
* First root of RS code generator polynomial, index form.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $firstRoot;
|
||||
private int $firstRoot;
|
||||
|
||||
/**
|
||||
* Primitive element to generate polynomial roots, index form.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $primitive;
|
||||
private int $primitive;
|
||||
|
||||
/**
|
||||
* Prim-th root of 1, index form.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $iPrimitive;
|
||||
private int $iPrimitive;
|
||||
|
||||
/**
|
||||
* RS code generator polynomial degree (number of roots).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $numRoots;
|
||||
private int $numRoots;
|
||||
|
||||
/**
|
||||
* Padding bytes at front of shortened block.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $padding;
|
||||
private int $padding;
|
||||
|
||||
/**
|
||||
* Log lookup table.
|
||||
*
|
||||
* @var SplFixedArray
|
||||
*/
|
||||
private $alphaTo;
|
||||
private SplFixedArray $alphaTo;
|
||||
|
||||
/**
|
||||
* Anti-Log lookup table.
|
||||
*
|
||||
* @var SplFixedArray
|
||||
*/
|
||||
private $indexOf;
|
||||
private SplFixedArray $indexOf;
|
||||
|
||||
/**
|
||||
* Generator polynomial.
|
||||
*
|
||||
* @var SplFixedArray
|
||||
*/
|
||||
private $generatorPoly;
|
||||
private SplFixedArray $generatorPoly;
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if symbol size ist not between 0 and 8
|
||||
@@ -222,7 +208,7 @@ final class ReedSolomonCodec
|
||||
/**
|
||||
* Decodes received data.
|
||||
*/
|
||||
public function decode(SplFixedArray $data, SplFixedArray $erasures = null) : ?int
|
||||
public function decode(SplFixedArray $data, ?SplFixedArray $erasures = null) : ?int
|
||||
{
|
||||
// This speeds up the initialization a bit.
|
||||
$numRootsPlusOne = SplFixedArray::fromArray(array_fill(0, $this->numRoots + 1, 0), false);
|
||||
|
||||
+7
-11
@@ -50,38 +50,34 @@ final class Version
|
||||
|
||||
/**
|
||||
* Version number of this version.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $versionNumber;
|
||||
private int $versionNumber;
|
||||
|
||||
/**
|
||||
* Alignment pattern centers.
|
||||
*
|
||||
* @var SplFixedArray
|
||||
* @var SplFixedArray|array
|
||||
*/
|
||||
private $alignmentPatternCenters;
|
||||
private SplFixedArray|array $alignmentPatternCenters;
|
||||
|
||||
/**
|
||||
* Error correction blocks.
|
||||
*
|
||||
* @var EcBlocks[]
|
||||
*/
|
||||
private $ecBlocks;
|
||||
private array $ecBlocks;
|
||||
|
||||
/**
|
||||
* Total number of codewords.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $totalCodewords;
|
||||
private null|int|float $totalCodewords;
|
||||
|
||||
/**
|
||||
* Cached version instances.
|
||||
*
|
||||
* @var array<int, self>|null
|
||||
*/
|
||||
private static $versions;
|
||||
private static ?array $versions = null;
|
||||
|
||||
/**
|
||||
* @param int[] $alignmentPatternCenters
|
||||
@@ -300,7 +296,7 @@ final class Version
|
||||
[6, 26],
|
||||
new EcBlocks(20, new EcBlock(1, 80)),
|
||||
new EcBlocks(18, new EcBlock(2, 32)),
|
||||
new EcBlocks(26, new EcBlock(3, 24)),
|
||||
new EcBlocks(26, new EcBlock(2, 24)),
|
||||
new EcBlocks(16, new EcBlock(4, 9))
|
||||
),
|
||||
new self(
|
||||
|
||||
+6
-20
@@ -10,30 +10,16 @@ use SplFixedArray;
|
||||
*/
|
||||
final class BlockPair
|
||||
{
|
||||
/**
|
||||
* Data bytes in the block.
|
||||
*
|
||||
* @var SplFixedArray<int>
|
||||
*/
|
||||
private $dataBytes;
|
||||
|
||||
/**
|
||||
* Error correction bytes in the block.
|
||||
*
|
||||
* @var SplFixedArray<int>
|
||||
*/
|
||||
private $errorCorrectionBytes;
|
||||
|
||||
/**
|
||||
* Creates a new block pair.
|
||||
*
|
||||
* @param SplFixedArray<int> $data
|
||||
* @param SplFixedArray<int> $errorCorrection
|
||||
* @param SplFixedArray<int> $dataBytes Data bytes in the block.
|
||||
* @param SplFixedArray<int> $errorCorrectionBytes Error correction bytes in the block.
|
||||
*/
|
||||
public function __construct(SplFixedArray $data, SplFixedArray $errorCorrection)
|
||||
{
|
||||
$this->dataBytes = $data;
|
||||
$this->errorCorrectionBytes = $errorCorrection;
|
||||
public function __construct(
|
||||
private readonly SplFixedArray $dataBytes,
|
||||
private readonly SplFixedArray $errorCorrectionBytes
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-18
@@ -16,26 +16,10 @@ final class ByteMatrix
|
||||
*
|
||||
* @var SplFixedArray<SplFixedArray<int>>
|
||||
*/
|
||||
private $bytes;
|
||||
private SplFixedArray $bytes;
|
||||
|
||||
/**
|
||||
* Width of the matrix.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* Height of the matrix.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
|
||||
public function __construct(int $width, int $height)
|
||||
public function __construct(private readonly int $width, private readonly int $height)
|
||||
{
|
||||
$this->height = $height;
|
||||
$this->width = $width;
|
||||
$this->bytes = new SplFixedArray($height);
|
||||
|
||||
for ($y = 0; $y < $height; ++$y) {
|
||||
|
||||
+23
-12
@@ -20,7 +20,10 @@ final class Encoder
|
||||
/**
|
||||
* Default byte encoding.
|
||||
*/
|
||||
public const DEFAULT_BYTE_MODE_ECODING = 'ISO-8859-1';
|
||||
public const DEFAULT_BYTE_MODE_ENCODING = 'ISO-8859-1';
|
||||
|
||||
/** @deprecated use DEFAULT_BYTE_MODE_ENCODING */
|
||||
public const DEFAULT_BYTE_MODE_ECODING = self::DEFAULT_BYTE_MODE_ENCODING;
|
||||
|
||||
/**
|
||||
* The original table is defined in the table 5 of JISX0510:2004 (p.19).
|
||||
@@ -37,9 +40,9 @@ final class Encoder
|
||||
/**
|
||||
* Codec cache.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string,ReedSolomonCodec>
|
||||
*/
|
||||
private static $codecs = [];
|
||||
private static array $codecs = [];
|
||||
|
||||
/**
|
||||
* Encodes "content" with the error correction level "ecLevel".
|
||||
@@ -47,8 +50,10 @@ final class Encoder
|
||||
public static function encode(
|
||||
string $content,
|
||||
ErrorCorrectionLevel $ecLevel,
|
||||
string $encoding = self::DEFAULT_BYTE_MODE_ECODING,
|
||||
?Version $forcedVersion = null
|
||||
string $encoding = self::DEFAULT_BYTE_MODE_ENCODING,
|
||||
?Version $forcedVersion = null,
|
||||
// Barcode scanner might not be able to read the encoded message of the QR code with the prefix ECI of UTF-8
|
||||
bool $prefixEci = true
|
||||
) : QrCode {
|
||||
// Pick an encoding mode appropriate for the content. Note that this
|
||||
// will not attempt to use multiple modes / segments even if that were
|
||||
@@ -60,7 +65,7 @@ final class Encoder
|
||||
$headerBits = new BitArray();
|
||||
|
||||
// Append ECI segment if applicable
|
||||
if (Mode::BYTE() === $mode && self::DEFAULT_BYTE_MODE_ECODING !== $encoding) {
|
||||
if ($prefixEci && Mode::BYTE() === $mode && self::DEFAULT_BYTE_MODE_ENCODING !== $encoding) {
|
||||
$eci = CharacterSetEci::getCharacterSetEciByName($encoding);
|
||||
|
||||
if (null !== $eci) {
|
||||
@@ -155,7 +160,7 @@ final class Encoder
|
||||
/**
|
||||
* Chooses the best mode for a given content.
|
||||
*/
|
||||
private static function chooseMode(string $content, string $encoding = null) : Mode
|
||||
private static function chooseMode(string $content, ?string $encoding = null) : Mode
|
||||
{
|
||||
if (null !== $encoding && 0 === strcasecmp($encoding, 'SHIFT-JIS')) {
|
||||
return self::isOnlyDoubleByteKanji($content) ? Mode::KANJI() : Mode::BYTE();
|
||||
@@ -217,7 +222,7 @@ final class Encoder
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $length; $i += 2) {
|
||||
$byte = $bytes[$i] & 0xff;
|
||||
$byte = ord($bytes[$i]) & 0xff;
|
||||
|
||||
if (($byte < 0x81 || $byte > 0x9f) && $byte < 0xe0 || $byte > 0xeb) {
|
||||
return false;
|
||||
@@ -629,17 +634,23 @@ final class Encoder
|
||||
*/
|
||||
private static function appendKanjiBytes(string $content, BitArray $bits) : void
|
||||
{
|
||||
if (strlen($content) % 2 > 0) {
|
||||
$bytes = @iconv('utf-8', 'SHIFT-JIS', $content);
|
||||
|
||||
if (false === $bytes) {
|
||||
throw new WriterException('Content could not be converted to SHIFT-JIS');
|
||||
}
|
||||
|
||||
if (strlen($bytes) % 2 > 0) {
|
||||
// We just do a simple length check here. The for loop will check
|
||||
// individual characters.
|
||||
throw new WriterException('Content does not seem to be encoded in SHIFT-JIS');
|
||||
}
|
||||
|
||||
$length = strlen($content);
|
||||
$length = strlen($bytes);
|
||||
|
||||
for ($i = 0; $i < $length; $i += 2) {
|
||||
$byte1 = ord($content[$i]) & 0xff;
|
||||
$byte2 = ord($content[$i + 1]) & 0xff;
|
||||
$byte1 = ord($bytes[$i]) & 0xff;
|
||||
$byte2 = ord($bytes[$i + 1]) & 0xff;
|
||||
$code = ($byte1 << 8) | $byte2;
|
||||
|
||||
if ($code >= 0x8140 && $code <= 0x9ffc) {
|
||||
|
||||
+4
-4
@@ -14,10 +14,10 @@ final class MaskUtil
|
||||
/**#@+
|
||||
* Penalty weights from section 6.8.2.1
|
||||
*/
|
||||
const N1 = 3;
|
||||
const N2 = 3;
|
||||
const N3 = 40;
|
||||
const N4 = 10;
|
||||
public const N1 = 3;
|
||||
public const N2 = 3;
|
||||
public const N3 = 40;
|
||||
public const N4 = 10;
|
||||
/**#@-*/
|
||||
|
||||
private function __construct()
|
||||
|
||||
+8
-41
@@ -17,51 +17,23 @@ final class QrCode
|
||||
*/
|
||||
public const NUM_MASK_PATTERNS = 8;
|
||||
|
||||
/**
|
||||
* Mode of the QR code.
|
||||
*
|
||||
* @var Mode
|
||||
*/
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* EC level of the QR code.
|
||||
*
|
||||
* @var ErrorCorrectionLevel
|
||||
*/
|
||||
private $errorCorrectionLevel;
|
||||
|
||||
/**
|
||||
* Version of the QR code.
|
||||
*
|
||||
* @var Version
|
||||
*/
|
||||
private $version;
|
||||
|
||||
/**
|
||||
* Mask pattern of the QR code.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $maskPattern = -1;
|
||||
private int $maskPattern = -1;
|
||||
|
||||
/**
|
||||
* Matrix of the QR code.
|
||||
*
|
||||
* @var ByteMatrix
|
||||
*/
|
||||
private $matrix;
|
||||
private ByteMatrix $matrix;
|
||||
|
||||
public function __construct(
|
||||
Mode $mode,
|
||||
ErrorCorrectionLevel $errorCorrectionLevel,
|
||||
Version $version,
|
||||
int $maskPattern,
|
||||
ByteMatrix $matrix
|
||||
private readonly Mode $mode,
|
||||
private readonly ErrorCorrectionLevel $errorCorrectionLevel,
|
||||
private readonly Version $version,
|
||||
int $maskPattern,
|
||||
ByteMatrix $matrix
|
||||
) {
|
||||
$this->mode = $mode;
|
||||
$this->errorCorrectionLevel = $errorCorrectionLevel;
|
||||
$this->version = $version;
|
||||
$this->maskPattern = $maskPattern;
|
||||
$this->matrix = $matrix;
|
||||
}
|
||||
@@ -98,12 +70,7 @@ final class QrCode
|
||||
return $this->maskPattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the matrix.
|
||||
*
|
||||
* @return ByteMatrix
|
||||
*/
|
||||
public function getMatrix()
|
||||
public function getMatrix(): ByteMatrix
|
||||
{
|
||||
return $this->matrix;
|
||||
}
|
||||
|
||||
+1
-14
@@ -7,27 +7,14 @@ use BaconQrCode\Exception;
|
||||
|
||||
final class Alpha implements ColorInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $alpha;
|
||||
|
||||
/**
|
||||
* @var ColorInterface
|
||||
*/
|
||||
private $baseColor;
|
||||
|
||||
/**
|
||||
* @param int $alpha the alpha value, 0 to 100
|
||||
*/
|
||||
public function __construct(int $alpha, ColorInterface $baseColor)
|
||||
public function __construct(private readonly int $alpha, private readonly ColorInterface $baseColor)
|
||||
{
|
||||
if ($alpha < 0 || $alpha > 100) {
|
||||
throw new Exception\InvalidArgumentException('Alpha must be between 0 and 100');
|
||||
}
|
||||
|
||||
$this->alpha = $alpha;
|
||||
$this->baseColor = $baseColor;
|
||||
}
|
||||
|
||||
public function getAlpha() : int
|
||||
|
||||
+6
-27
@@ -7,34 +7,18 @@ use BaconQrCode\Exception;
|
||||
|
||||
final class Cmyk implements ColorInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $cyan;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $magenta;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $yellow;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $black;
|
||||
|
||||
/**
|
||||
* @param int $cyan the cyan amount, 0 to 100
|
||||
* @param int $magenta the magenta amount, 0 to 100
|
||||
* @param int $yellow the yellow amount, 0 to 100
|
||||
* @param int $black the black amount, 0 to 100
|
||||
*/
|
||||
public function __construct(int $cyan, int $magenta, int $yellow, int $black)
|
||||
{
|
||||
public function __construct(
|
||||
private readonly int $cyan,
|
||||
private readonly int $magenta,
|
||||
private readonly int $yellow,
|
||||
private readonly int $black
|
||||
) {
|
||||
if ($cyan < 0 || $cyan > 100) {
|
||||
throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100');
|
||||
}
|
||||
@@ -50,11 +34,6 @@ final class Cmyk implements ColorInterface
|
||||
if ($black < 0 || $black > 100) {
|
||||
throw new Exception\InvalidArgumentException('Black must be between 0 and 100');
|
||||
}
|
||||
|
||||
$this->cyan = $cyan;
|
||||
$this->magenta = $magenta;
|
||||
$this->yellow = $yellow;
|
||||
$this->black = $black;
|
||||
}
|
||||
|
||||
public function getCyan() : int
|
||||
|
||||
@@ -7,21 +7,14 @@ use BaconQrCode\Exception;
|
||||
|
||||
final class Gray implements ColorInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $gray;
|
||||
|
||||
/**
|
||||
* @param int $gray the gray value between 0 (black) and 100 (white)
|
||||
*/
|
||||
public function __construct(int $gray)
|
||||
public function __construct(private readonly int $gray)
|
||||
{
|
||||
if ($gray < 0 || $gray > 100) {
|
||||
throw new Exception\InvalidArgumentException('Gray must be between 0 and 100');
|
||||
}
|
||||
|
||||
$this->gray = (int) $gray;
|
||||
}
|
||||
|
||||
public function getGray() : int
|
||||
|
||||
+5
-20
@@ -7,27 +7,12 @@ use BaconQrCode\Exception;
|
||||
|
||||
final class Rgb implements ColorInterface
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $red;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $green;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $blue;
|
||||
|
||||
/**
|
||||
* @param int $red the red amount of the color, 0 to 255
|
||||
* @param int $green the green amount of the color, 0 to 255
|
||||
* @param int $blue the blue amount of the color, 0 to 255
|
||||
*/
|
||||
public function __construct(int $red, int $green, int $blue)
|
||||
public function __construct(private readonly int $red, private readonly int $green, private readonly int $blue)
|
||||
{
|
||||
if ($red < 0 || $red > 255) {
|
||||
throw new Exception\InvalidArgumentException('Red must be between 0 and 255');
|
||||
@@ -40,10 +25,6 @@ final class Rgb implements ColorInterface
|
||||
if ($blue < 0 || $blue > 255) {
|
||||
throw new Exception\InvalidArgumentException('Blue must be between 0 and 255');
|
||||
}
|
||||
|
||||
$this->red = $red;
|
||||
$this->green = $green;
|
||||
$this->blue = $blue;
|
||||
}
|
||||
|
||||
public function getRed() : int
|
||||
@@ -73,6 +54,10 @@ final class Rgb implements ColorInterface
|
||||
$y = 1 - ($this->blue / 255);
|
||||
$k = min($c, $m, $y);
|
||||
|
||||
if ($k === 0) {
|
||||
return new Cmyk(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
return new Cmyk(
|
||||
(int) (100 * ($c - $k) / (1 - $k)),
|
||||
(int) (100 * ($m - $k) / (1 - $k)),
|
||||
|
||||
@@ -10,20 +10,8 @@ use BaconQrCode\Renderer\Path\Path;
|
||||
*/
|
||||
final class CompositeEye implements EyeInterface
|
||||
{
|
||||
/**
|
||||
* @var EyeInterface
|
||||
*/
|
||||
private $externalEye;
|
||||
|
||||
/**
|
||||
* @var EyeInterface
|
||||
*/
|
||||
private $internalEye;
|
||||
|
||||
public function __construct(EyeInterface $externalEye, EyeInterface $internalEye)
|
||||
public function __construct(private readonly EyeInterface $externalEye, private readonly EyeInterface $internalEye)
|
||||
{
|
||||
$this->externalEye = $externalEye;
|
||||
$this->internalEye = $internalEye;
|
||||
}
|
||||
|
||||
public function getExternalPath() : Path
|
||||
|
||||
@@ -12,14 +12,8 @@ use BaconQrCode\Renderer\Path\Path;
|
||||
*/
|
||||
final class ModuleEye implements EyeInterface
|
||||
{
|
||||
/**
|
||||
* @var ModuleInterface
|
||||
*/
|
||||
private $module;
|
||||
|
||||
public function __construct(ModuleInterface $module)
|
||||
public function __construct(private readonly ModuleInterface $module)
|
||||
{
|
||||
$this->module = $module;
|
||||
}
|
||||
|
||||
public function getExternalPath() : Path
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace BaconQrCode\Renderer\Eye;
|
||||
|
||||
use BaconQrCode\Renderer\Path\Path;
|
||||
|
||||
/**
|
||||
* Renders the outer eye as solid with a curved corner and inner eye as a circle.
|
||||
*/
|
||||
final class PointyEye implements EyeInterface
|
||||
{
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function instance() : self
|
||||
{
|
||||
return self::$instance ?: self::$instance = new self();
|
||||
}
|
||||
|
||||
public function getExternalPath() : Path
|
||||
{
|
||||
return (new Path())
|
||||
->move(-3.5, 3.5)
|
||||
->line(-3.5, 0)
|
||||
->ellipticArc(3.5, 3.5, 0, false, true, 0, -3.5)
|
||||
->line(3.5, -3.5)
|
||||
->line(3.5, 3.5)
|
||||
->close()
|
||||
->move(2.5, 0)
|
||||
->ellipticArc(2.5, 2.5, 0, false, true, 0, 2.5)
|
||||
->ellipticArc(2.5, 2.5, 0, false, true, -2.5, 0)
|
||||
->ellipticArc(2.5, 2.5, 0, false, true, 0, -2.5)
|
||||
->ellipticArc(2.5, 2.5, 0, false, true, 2.5, 0)
|
||||
->close()
|
||||
;
|
||||
}
|
||||
|
||||
public function getInternalPath() : Path
|
||||
{
|
||||
return (new Path())
|
||||
->move(1.5, 0)
|
||||
->ellipticArc(1.5, 1.5, 0., false, true, 0., 1.5)
|
||||
->ellipticArc(1.5, 1.5, 0., false, true, -1.5, 0.)
|
||||
->ellipticArc(1.5, 1.5, 0., false, true, 0., -1.5)
|
||||
->ellipticArc(1.5, 1.5, 0., false, true, 1.5, 0.)
|
||||
->close()
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,7 @@ use BaconQrCode\Renderer\Path\Path;
|
||||
*/
|
||||
final class SimpleCircleEye implements EyeInterface
|
||||
{
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $instance;
|
||||
private static ?SimpleCircleEye $instance = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
|
||||
@@ -10,10 +10,7 @@ use BaconQrCode\Renderer\Path\Path;
|
||||
*/
|
||||
final class SquareEye implements EyeInterface
|
||||
{
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $instance;
|
||||
private static ?SquareEye $instance = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BaconQrCode\Renderer;
|
||||
|
||||
use BaconQrCode\Encoder\ByteMatrix;
|
||||
use BaconQrCode\Encoder\MatrixUtil;
|
||||
use BaconQrCode\Encoder\QrCode;
|
||||
use BaconQrCode\Exception\InvalidArgumentException;
|
||||
use BaconQrCode\Exception\RuntimeException;
|
||||
use BaconQrCode\Renderer\Color\Alpha;
|
||||
use BaconQrCode\Renderer\Color\ColorInterface;
|
||||
use BaconQrCode\Renderer\RendererStyle\EyeFill;
|
||||
use BaconQrCode\Renderer\RendererStyle\Fill;
|
||||
use GdImage;
|
||||
|
||||
final class GDLibRenderer implements RendererInterface
|
||||
{
|
||||
private ?GdImage $image;
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
private array $colors;
|
||||
|
||||
public function __construct(
|
||||
private int $size,
|
||||
private int $margin = 4,
|
||||
private string $imageFormat = 'png',
|
||||
private int $compressionQuality = 9,
|
||||
private ?Fill $fill = null
|
||||
) {
|
||||
if (! extension_loaded('gd') || ! function_exists('gd_info')) {
|
||||
throw new RuntimeException('You need to install the GD extension to use this back end');
|
||||
}
|
||||
|
||||
if ($this->fill === null) {
|
||||
$this->fill = Fill::default();
|
||||
}
|
||||
if ($this->fill->hasGradientFill()) {
|
||||
throw new InvalidArgumentException('GDLibRenderer does not support gradients');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException if matrix width doesn't match height
|
||||
*/
|
||||
public function render(QrCode $qrCode): string
|
||||
{
|
||||
$matrix = $qrCode->getMatrix();
|
||||
$matrixSize = $matrix->getWidth();
|
||||
|
||||
if ($matrixSize !== $matrix->getHeight()) {
|
||||
throw new InvalidArgumentException('Matrix must have the same width and height');
|
||||
}
|
||||
|
||||
MatrixUtil::removePositionDetectionPatterns($matrix);
|
||||
$this->newImage();
|
||||
$this->draw($matrix);
|
||||
|
||||
return $this->renderImage();
|
||||
}
|
||||
|
||||
private function newImage(): void
|
||||
{
|
||||
$img = imagecreatetruecolor($this->size, $this->size);
|
||||
if ($img === false) {
|
||||
throw new RuntimeException('Failed to create image of that size');
|
||||
}
|
||||
|
||||
$this->image = $img;
|
||||
imagealphablending($this->image, false);
|
||||
imagesavealpha($this->image, true);
|
||||
|
||||
|
||||
$bg = $this->getColor($this->fill->getBackgroundColor());
|
||||
imagefilledrectangle($this->image, 0, 0, $this->size, $this->size, $bg);
|
||||
imagealphablending($this->image, true);
|
||||
}
|
||||
|
||||
private function draw(ByteMatrix $matrix): void
|
||||
{
|
||||
$matrixSize = $matrix->getWidth();
|
||||
|
||||
$pointsOnSide = $matrix->getWidth() + $this->margin * 2;
|
||||
$pointInPx = $this->size / $pointsOnSide;
|
||||
|
||||
$this->drawEye(0, 0, $pointInPx, $this->fill->getTopLeftEyeFill());
|
||||
$this->drawEye($matrixSize - 7, 0, $pointInPx, $this->fill->getTopRightEyeFill());
|
||||
$this->drawEye(0, $matrixSize - 7, $pointInPx, $this->fill->getBottomLeftEyeFill());
|
||||
|
||||
$rows = $matrix->getArray()->toArray();
|
||||
$color = $this->getColor($this->fill->getForegroundColor());
|
||||
for ($y = 0; $y < $matrixSize; $y += 1) {
|
||||
for ($x = 0; $x < $matrixSize; $x += 1) {
|
||||
if (! $rows[$y][$x]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$points = $this->normalizePoints([
|
||||
($this->margin + $x) * $pointInPx, ($this->margin + $y) * $pointInPx,
|
||||
($this->margin + $x + 1) * $pointInPx, ($this->margin + $y) * $pointInPx,
|
||||
($this->margin + $x + 1) * $pointInPx, ($this->margin + $y + 1) * $pointInPx,
|
||||
($this->margin + $x) * $pointInPx, ($this->margin + $y + 1) * $pointInPx,
|
||||
]);
|
||||
imagefilledpolygon($this->image, $points, $color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function drawEye(int $xOffset, int $yOffset, float $pointInPx, EyeFill $eyeFill): void
|
||||
{
|
||||
$internalColor = $this->getColor($eyeFill->inheritsInternalColor()
|
||||
? $this->fill->getForegroundColor()
|
||||
: $eyeFill->getInternalColor());
|
||||
|
||||
$externalColor = $this->getColor($eyeFill->inheritsExternalColor()
|
||||
? $this->fill->getForegroundColor()
|
||||
: $eyeFill->getExternalColor());
|
||||
|
||||
for ($y = 0; $y < 7; $y += 1) {
|
||||
for ($x = 0; $x < 7; $x += 1) {
|
||||
if ((($y === 1 || $y === 5) && $x > 0 && $x < 6) || (($x === 1 || $x === 5) && $y > 0 && $y < 6)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$points = $this->normalizePoints([
|
||||
($this->margin + $x + $xOffset) * $pointInPx, ($this->margin + $y + $yOffset) * $pointInPx,
|
||||
($this->margin + $x + $xOffset + 1) * $pointInPx, ($this->margin + $y + $yOffset) * $pointInPx,
|
||||
($this->margin + $x + $xOffset + 1) * $pointInPx, ($this->margin + $y + $yOffset + 1) * $pointInPx,
|
||||
($this->margin + $x + $xOffset) * $pointInPx, ($this->margin + $y + $yOffset + 1) * $pointInPx,
|
||||
]);
|
||||
|
||||
if ($y > 1 && $y < 5 && $x > 1 && $x < 5) {
|
||||
imagefilledpolygon($this->image, $points, $internalColor);
|
||||
} else {
|
||||
imagefilledpolygon($this->image, $points, $externalColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize points will trim right and bottom line by 1 pixel.
|
||||
* Otherwise pixels of neighbors are overlapping which leads to issue with transparency and small QR codes.
|
||||
*/
|
||||
private function normalizePoints(array $points): array
|
||||
{
|
||||
$maxX = $maxY = 0;
|
||||
for ($i = 0; $i < count($points); $i += 2) {
|
||||
// Do manual round as GD just removes decimal part
|
||||
$points[$i] = $newX = round($points[$i]);
|
||||
$points[$i + 1] = $newY = round($points[$i + 1]);
|
||||
|
||||
$maxX = max($maxX, $newX);
|
||||
$maxY = max($maxY, $newY);
|
||||
}
|
||||
|
||||
// Do trimming only if there are 4 points (8 coordinates), assumes this is square.
|
||||
|
||||
for ($i = 0; $i < count($points); $i += 2) {
|
||||
$points[$i] = min($points[$i], $maxX - 1);
|
||||
$points[$i + 1] = min($points[$i + 1], $maxY - 1);
|
||||
}
|
||||
|
||||
return $points;
|
||||
}
|
||||
|
||||
private function renderImage(): string
|
||||
{
|
||||
ob_start();
|
||||
$quality = $this->compressionQuality;
|
||||
switch ($this->imageFormat) {
|
||||
case 'png':
|
||||
if ($quality > 9 || $quality < 0) {
|
||||
$quality = 9;
|
||||
}
|
||||
imagepng($this->image, null, $quality);
|
||||
break;
|
||||
|
||||
case 'gif':
|
||||
imagegif($this->image, null);
|
||||
break;
|
||||
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
if ($quality > 100 || $quality < 0) {
|
||||
$quality = 85;
|
||||
}
|
||||
imagejpeg($this->image, null, $quality);
|
||||
break;
|
||||
default:
|
||||
ob_end_clean();
|
||||
throw new InvalidArgumentException(
|
||||
'Supported image formats are jpeg, png and gif, got: ' . $this->imageFormat
|
||||
);
|
||||
}
|
||||
|
||||
imagedestroy($this->image);
|
||||
$this->colors = [];
|
||||
$this->image = null;
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
private function getColor(ColorInterface $color): int
|
||||
{
|
||||
$alpha = 100;
|
||||
|
||||
if ($color instanceof Alpha) {
|
||||
$alpha = $color->getAlpha();
|
||||
$color = $color->getBaseColor();
|
||||
}
|
||||
|
||||
$rgb = $color->toRgb();
|
||||
|
||||
$colorKey = sprintf('%02X%02X%02X%02X', $rgb->getRed(), $rgb->getGreen(), $rgb->getBlue(), $alpha);
|
||||
|
||||
if (! isset($this->colors[$colorKey])) {
|
||||
$colorId = imagecolorallocatealpha(
|
||||
$this->image,
|
||||
$rgb->getRed(),
|
||||
$rgb->getGreen(),
|
||||
$rgb->getBlue(),
|
||||
(int)((100 - $alpha) / 100 * 127) // Alpha for GD is in range 0 (opaque) - 127 (transparent)
|
||||
);
|
||||
|
||||
if ($colorId === false) {
|
||||
throw new RuntimeException('Failed to create color: #' . $colorKey);
|
||||
}
|
||||
|
||||
$this->colors[$colorKey] = $colorId;
|
||||
}
|
||||
|
||||
return $this->colors[$colorKey];
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,7 @@ final class EpsImageBackEnd implements ImageBackEndInterface
|
||||
{
|
||||
private const PRECISION = 3;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $eps;
|
||||
private ?string $eps;
|
||||
|
||||
public function new(int $size, ColorInterface $backgroundColor) : void
|
||||
{
|
||||
|
||||
@@ -23,40 +23,22 @@ use ImagickPixel;
|
||||
|
||||
final class ImagickImageBackEnd implements ImageBackEndInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $imageFormat;
|
||||
private string $imageFormat;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $compressionQuality;
|
||||
private int $compressionQuality;
|
||||
|
||||
/**
|
||||
* @var Imagick|null
|
||||
*/
|
||||
private $image;
|
||||
private ?Imagick $image;
|
||||
|
||||
/**
|
||||
* @var ImagickDraw|null
|
||||
*/
|
||||
private $draw;
|
||||
private ?ImagickDraw $draw;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $gradientCount;
|
||||
private ?int $gradientCount;
|
||||
|
||||
/**
|
||||
* @var TransformationMatrix[]|null
|
||||
*/
|
||||
private $matrices;
|
||||
private ?array $matrices;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $matrixIndex;
|
||||
private ?int $matrixIndex;
|
||||
|
||||
public function __construct(string $imageFormat = 'png', int $compressionQuality = 100)
|
||||
{
|
||||
|
||||
@@ -19,28 +19,16 @@ use XMLWriter;
|
||||
final class SvgImageBackEnd implements ImageBackEndInterface
|
||||
{
|
||||
private const PRECISION = 3;
|
||||
private const SCALE_FORMAT = 'scale(%.' . self::PRECISION . 'F)';
|
||||
private const TRANSLATE_FORMAT = 'translate(%.' . self::PRECISION . 'F,%.' . self::PRECISION . 'F)';
|
||||
private const SCALE_FORMAT = 'scale(%.' . self::PRECISION . 'F)';
|
||||
private const TRANSLATE_FORMAT = 'translate(%.' . self::PRECISION . 'F,%.' . self::PRECISION . 'F)';
|
||||
|
||||
/**
|
||||
* @var XMLWriter|null
|
||||
*/
|
||||
private $xmlWriter;
|
||||
private ?XMLWriter $xmlWriter;
|
||||
|
||||
/**
|
||||
* @var int[]|null
|
||||
*/
|
||||
private $stack;
|
||||
private ?array $stack;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $currentStack;
|
||||
private ?int $currentStack;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $gradientCount;
|
||||
private ?int $gradientCount;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -99,7 +87,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
|
||||
$this->xmlWriter->startElement('g');
|
||||
$this->xmlWriter->writeAttribute(
|
||||
'transform',
|
||||
sprintf(self::SCALE_FORMAT, round($size, self::PRECISION))
|
||||
sprintf(self::SCALE_FORMAT, round($size, self::PRECISION))
|
||||
);
|
||||
++$this->stack[$this->currentStack];
|
||||
}
|
||||
@@ -113,7 +101,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
|
||||
$this->xmlWriter->startElement('g');
|
||||
$this->xmlWriter->writeAttribute(
|
||||
'transform',
|
||||
sprintf(self::TRANSLATE_FORMAT, round($x, self::PRECISION), round($y, self::PRECISION))
|
||||
sprintf(self::TRANSLATE_FORMAT, round($x, self::PRECISION), round($y, self::PRECISION))
|
||||
);
|
||||
++$this->stack[$this->currentStack];
|
||||
}
|
||||
@@ -336,7 +324,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
|
||||
$this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor));
|
||||
|
||||
if ($startColor instanceof Alpha) {
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', $startColor->getAlpha());
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha());
|
||||
}
|
||||
|
||||
$this->xmlWriter->endElement();
|
||||
@@ -346,7 +334,7 @@ final class SvgImageBackEnd implements ImageBackEndInterface
|
||||
$this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor));
|
||||
|
||||
if ($endColor instanceof Alpha) {
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', $endColor->getAlpha());
|
||||
$this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha());
|
||||
}
|
||||
|
||||
$this->xmlWriter->endElement();
|
||||
|
||||
@@ -8,7 +8,7 @@ final class TransformationMatrix
|
||||
/**
|
||||
* @var float[]
|
||||
*/
|
||||
private $values;
|
||||
private array $values;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
+16
-18
@@ -13,20 +13,10 @@ use BaconQrCode\Renderer\RendererStyle\RendererStyle;
|
||||
|
||||
final class ImageRenderer implements RendererInterface
|
||||
{
|
||||
/**
|
||||
* @var RendererStyle
|
||||
*/
|
||||
private $rendererStyle;
|
||||
|
||||
/**
|
||||
* @var ImageBackEndInterface
|
||||
*/
|
||||
private $imageBackEnd;
|
||||
|
||||
public function __construct(RendererStyle $rendererStyle, ImageBackEndInterface $imageBackEnd)
|
||||
{
|
||||
$this->rendererStyle = $rendererStyle;
|
||||
$this->imageBackEnd = $imageBackEnd;
|
||||
public function __construct(
|
||||
private readonly RendererStyle $rendererStyle,
|
||||
private readonly ImageBackEndInterface $imageBackEnd
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,8 +112,12 @@ final class ImageRenderer implements RendererInterface
|
||||
) : Path {
|
||||
if ($fill->inheritsBothColors()) {
|
||||
return $modulePath
|
||||
->append($externalPath->translate($xTranslation, $yTranslation))
|
||||
->append($internalPath->translate($xTranslation, $yTranslation));
|
||||
->append(
|
||||
$externalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
|
||||
)
|
||||
->append(
|
||||
$internalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
|
||||
);
|
||||
}
|
||||
|
||||
$this->imageBackEnd->push();
|
||||
@@ -134,13 +128,17 @@ final class ImageRenderer implements RendererInterface
|
||||
}
|
||||
|
||||
if ($fill->inheritsExternalColor()) {
|
||||
$modulePath = $modulePath->append($externalPath->translate($xTranslation, $yTranslation));
|
||||
$modulePath = $modulePath->append(
|
||||
$externalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
|
||||
);
|
||||
} else {
|
||||
$this->imageBackEnd->drawPathWithColor($externalPath, $fill->getExternalColor());
|
||||
}
|
||||
|
||||
if ($fill->inheritsInternalColor()) {
|
||||
$modulePath = $modulePath->append($internalPath->translate($xTranslation, $yTranslation));
|
||||
$modulePath = $modulePath->append(
|
||||
$internalPath->rotate($rotation)->translate($xTranslation, $yTranslation)
|
||||
);
|
||||
} else {
|
||||
$this->imageBackEnd->drawPathWithColor($internalPath, $fill->getInternalColor());
|
||||
}
|
||||
|
||||
@@ -16,18 +16,11 @@ final class DotsModule implements ModuleInterface
|
||||
public const MEDIUM = .8;
|
||||
public const SMALL = .6;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $size;
|
||||
|
||||
public function __construct(float $size)
|
||||
public function __construct(private readonly float $size)
|
||||
{
|
||||
if ($size <= 0 || $size > 1) {
|
||||
throw new InvalidArgumentException('Size must between 0 (exclusive) and 1 (inclusive)');
|
||||
}
|
||||
|
||||
$this->size = $size;
|
||||
}
|
||||
|
||||
public function createPath(ByteMatrix $matrix) : Path
|
||||
|
||||
@@ -5,44 +5,26 @@ namespace BaconQrCode\Renderer\Module\EdgeIterator;
|
||||
|
||||
final class Edge
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $positive;
|
||||
|
||||
/**
|
||||
* @var array<int[]>
|
||||
*/
|
||||
private $points = [];
|
||||
private array $points = [];
|
||||
|
||||
/**
|
||||
* @var array<int[]>|null
|
||||
*/
|
||||
private $simplifiedPoints;
|
||||
private ?array $simplifiedPoints = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $minX = PHP_INT_MAX;
|
||||
private int $minX = PHP_INT_MAX;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $minY = PHP_INT_MAX;
|
||||
private int $minY = PHP_INT_MAX;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxX = -1;
|
||||
private int $maxX = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $maxY = -1;
|
||||
private int $maxY = -1;
|
||||
|
||||
public function __construct(bool $positive)
|
||||
public function __construct(private readonly bool $positive)
|
||||
{
|
||||
$this->positive = $positive;
|
||||
}
|
||||
|
||||
public function addPoint(int $x, int $y) : void
|
||||
|
||||
+5
-14
@@ -15,22 +15,13 @@ final class EdgeIterator implements IteratorAggregate
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $bytes = [];
|
||||
private array $bytes = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $size;
|
||||
private ?int $size;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
private int $width;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
private int $height;
|
||||
|
||||
public function __construct(ByteMatrix $matrix)
|
||||
{
|
||||
@@ -41,7 +32,7 @@ final class EdgeIterator implements IteratorAggregate
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Edge[]
|
||||
* @return Traversable<Edge>
|
||||
*/
|
||||
public function getIterator() : Traversable
|
||||
{
|
||||
|
||||
@@ -17,12 +17,7 @@ final class RoundnessModule implements ModuleInterface
|
||||
public const MEDIUM = .5;
|
||||
public const SOFT = .25;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $intensity;
|
||||
|
||||
public function __construct(float $intensity)
|
||||
public function __construct(private float $intensity)
|
||||
{
|
||||
if ($intensity <= 0 || $intensity > 1) {
|
||||
throw new InvalidArgumentException('Intensity must between 0 (exclusive) and 1 (inclusive)');
|
||||
|
||||
@@ -12,10 +12,7 @@ use BaconQrCode\Renderer\Path\Path;
|
||||
*/
|
||||
final class SquareModule implements ModuleInterface
|
||||
{
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $instance;
|
||||
private static ?SquareModule $instance = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
|
||||
@@ -5,10 +5,7 @@ namespace BaconQrCode\Renderer\Path;
|
||||
|
||||
final class Close implements OperationInterface
|
||||
{
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $instance;
|
||||
private static ?Close $instance = null;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
@@ -26,4 +23,12 @@ final class Close implements OperationInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public function rotate(int $degrees) : OperationInterface
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+32
-38
@@ -5,44 +5,14 @@ namespace BaconQrCode\Renderer\Path;
|
||||
|
||||
final class Curve implements OperationInterface
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $x1;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $y1;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $x2;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $y2;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $x3;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $y3;
|
||||
|
||||
public function __construct(float $x1, float $y1, float $x2, float $y2, float $x3, float $y3)
|
||||
{
|
||||
$this->x1 = $x1;
|
||||
$this->y1 = $y1;
|
||||
$this->x2 = $x2;
|
||||
$this->y2 = $y2;
|
||||
$this->x3 = $x3;
|
||||
$this->y3 = $y3;
|
||||
public function __construct(
|
||||
private readonly float $x1,
|
||||
private readonly float $y1,
|
||||
private readonly float $x2,
|
||||
private readonly float $y2,
|
||||
private readonly float $x3,
|
||||
private readonly float $y3
|
||||
) {
|
||||
}
|
||||
|
||||
public function getX1() : float
|
||||
@@ -89,4 +59,28 @@ final class Curve implements OperationInterface
|
||||
$this->y3 + $y
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public function rotate(int $degrees) : OperationInterface
|
||||
{
|
||||
$radians = deg2rad($degrees);
|
||||
$sin = sin($radians);
|
||||
$cos = cos($radians);
|
||||
$x1r = $this->x1 * $cos - $this->y1 * $sin;
|
||||
$y1r = $this->x1 * $sin + $this->y1 * $cos;
|
||||
$x2r = $this->x2 * $cos - $this->y2 * $sin;
|
||||
$y2r = $this->x2 * $sin + $this->y2 * $cos;
|
||||
$x3r = $this->x3 * $cos - $this->y3 * $sin;
|
||||
$y3r = $this->x3 * $sin + $this->y3 * $cos;
|
||||
return new self(
|
||||
$x1r,
|
||||
$y1r,
|
||||
$x2r,
|
||||
$y2r,
|
||||
$x3r,
|
||||
$y3r
|
||||
);
|
||||
}
|
||||
}
|
||||
+33
-47
@@ -7,57 +7,22 @@ final class EllipticArc implements OperationInterface
|
||||
{
|
||||
private const ZERO_TOLERANCE = 1e-05;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $xRadius;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $yRadius;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $xAxisAngle;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $largeArc;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $sweep;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $x;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $y;
|
||||
private float $xRadius;
|
||||
private float $yRadius;
|
||||
private float $xAxisAngle;
|
||||
|
||||
public function __construct(
|
||||
float $xRadius,
|
||||
float $yRadius,
|
||||
float $xAxisAngle,
|
||||
bool $largeArc,
|
||||
bool $sweep,
|
||||
float $x,
|
||||
float $y
|
||||
float $xRadius,
|
||||
float $yRadius,
|
||||
float $xAxisAngle,
|
||||
private readonly bool $largeArc,
|
||||
private readonly bool $sweep,
|
||||
private readonly float $x,
|
||||
private readonly float $y
|
||||
) {
|
||||
$this->xRadius = abs($xRadius);
|
||||
$this->yRadius = abs($yRadius);
|
||||
$this->xAxisAngle = $xAxisAngle % 360;
|
||||
$this->largeArc = $largeArc;
|
||||
$this->sweep = $sweep;
|
||||
$this->x = $x;
|
||||
$this->y = $y;
|
||||
}
|
||||
|
||||
public function getXRadius() : float
|
||||
@@ -111,6 +76,27 @@ final class EllipticArc implements OperationInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public function rotate(int $degrees) : OperationInterface
|
||||
{
|
||||
$radians = deg2rad($degrees);
|
||||
$sin = sin($radians);
|
||||
$cos = cos($radians);
|
||||
$xr = $this->x * $cos - $this->y * $sin;
|
||||
$yr = $this->x * $sin + $this->y * $cos;
|
||||
return new self(
|
||||
$this->xRadius,
|
||||
$this->yRadius,
|
||||
$this->xAxisAngle,
|
||||
$this->largeArc,
|
||||
$this->sweep,
|
||||
$xr,
|
||||
$yr
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the elliptic arc to multiple curves.
|
||||
*
|
||||
@@ -136,7 +122,7 @@ final class EllipticArc implements OperationInterface
|
||||
/**
|
||||
* @return Curve[]
|
||||
*/
|
||||
private function createCurves(float $fromX, $fromY) : array
|
||||
private function createCurves(float $fromX, float $fromY) : array
|
||||
{
|
||||
$xAngle = deg2rad($this->xAxisAngle);
|
||||
list($centerX, $centerY, $radiusX, $radiusY, $startAngle, $deltaAngle) =
|
||||
@@ -179,7 +165,7 @@ final class EllipticArc implements OperationInterface
|
||||
/**
|
||||
* @return float[]
|
||||
*/
|
||||
private function calculateCenterPointParameters(float $fromX, float $fromY, float $xAngle)
|
||||
private function calculateCenterPointParameters(float $fromX, float $fromY, float $xAngle): array
|
||||
{
|
||||
$rX = $this->xRadius;
|
||||
$rY = $this->yRadius;
|
||||
|
||||
+14
-13
@@ -5,20 +5,8 @@ namespace BaconQrCode\Renderer\Path;
|
||||
|
||||
final class Line implements OperationInterface
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $x;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $y;
|
||||
|
||||
public function __construct(float $x, float $y)
|
||||
public function __construct(private readonly float $x, private readonly float $y)
|
||||
{
|
||||
$this->x = $x;
|
||||
$this->y = $y;
|
||||
}
|
||||
|
||||
public function getX() : float
|
||||
@@ -38,4 +26,17 @@ final class Line implements OperationInterface
|
||||
{
|
||||
return new self($this->x + $x, $this->y + $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public function rotate(int $degrees) : OperationInterface
|
||||
{
|
||||
$radians = deg2rad($degrees);
|
||||
$sin = sin($radians);
|
||||
$cos = cos($radians);
|
||||
$xr = $this->x * $cos - $this->y * $sin;
|
||||
$yr = $this->x * $sin + $this->y * $cos;
|
||||
return new self($xr, $yr);
|
||||
}
|
||||
}
|
||||
+14
-13
@@ -5,20 +5,8 @@ namespace BaconQrCode\Renderer\Path;
|
||||
|
||||
final class Move implements OperationInterface
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $x;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $y;
|
||||
|
||||
public function __construct(float $x, float $y)
|
||||
public function __construct(private readonly float $x, private readonly float $y)
|
||||
{
|
||||
$this->x = $x;
|
||||
$this->y = $y;
|
||||
}
|
||||
|
||||
public function getX() : float
|
||||
@@ -38,4 +26,17 @@ final class Move implements OperationInterface
|
||||
{
|
||||
return new self($this->x + $x, $this->y + $y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public function rotate(int $degrees) : OperationInterface
|
||||
{
|
||||
$radians = deg2rad($degrees);
|
||||
$sin = sin($radians);
|
||||
$cos = cos($radians);
|
||||
$xr = $this->x * $cos - $this->y * $sin;
|
||||
$yr = $this->x * $sin + $this->y * $cos;
|
||||
return new self($xr, $yr);
|
||||
}
|
||||
}
|
||||
@@ -9,4 +9,9 @@ interface OperationInterface
|
||||
* Translates the operation's coordinates.
|
||||
*/
|
||||
public function translate(float $x, float $y) : self;
|
||||
|
||||
/**
|
||||
* Rotates the operation's coordinates.
|
||||
*/
|
||||
public function rotate(int $degrees) : self;
|
||||
}
|
||||
+13
-2
@@ -14,7 +14,7 @@ final class Path implements IteratorAggregate
|
||||
/**
|
||||
* @var OperationInterface[]
|
||||
*/
|
||||
private $operations = [];
|
||||
private array $operations = [];
|
||||
|
||||
/**
|
||||
* Moves the drawing operation to a certain position.
|
||||
@@ -94,8 +94,19 @@ final class Path implements IteratorAggregate
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function rotate(int $degrees) : self
|
||||
{
|
||||
$path = new self();
|
||||
|
||||
foreach ($this->operations as $operation) {
|
||||
$path->operations[] = $operation->rotate($degrees);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OperationInterface[]|Traversable
|
||||
* @return Traversable<int, OperationInterface>
|
||||
*/
|
||||
public function getIterator() : Traversable
|
||||
{
|
||||
|
||||
@@ -28,14 +28,8 @@ final class PlainTextRenderer implements RendererInterface
|
||||
*/
|
||||
private const EMPTY_BLOCK = "\xc2\xa0";
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $margin;
|
||||
|
||||
public function __construct(int $margin = 2)
|
||||
public function __construct(private readonly int $margin = 2)
|
||||
{
|
||||
$this->margin = $margin;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,25 +8,12 @@ use BaconQrCode\Renderer\Color\ColorInterface;
|
||||
|
||||
final class EyeFill
|
||||
{
|
||||
/**
|
||||
* @var ColorInterface|null
|
||||
*/
|
||||
private $externalColor;
|
||||
private static ?EyeFill $inherit = null;
|
||||
|
||||
/**
|
||||
* @var ColorInterface|null
|
||||
*/
|
||||
private $internalColor;
|
||||
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $inherit;
|
||||
|
||||
public function __construct(?ColorInterface $externalColor, ?ColorInterface $internalColor)
|
||||
{
|
||||
$this->externalColor = $externalColor;
|
||||
$this->internalColor = $internalColor;
|
||||
public function __construct(
|
||||
private readonly ?ColorInterface $externalColor,
|
||||
private readonly ?ColorInterface $internalColor
|
||||
) {
|
||||
}
|
||||
|
||||
public static function uniform(ColorInterface $color) : self
|
||||
|
||||
@@ -9,55 +9,16 @@ use BaconQrCode\Renderer\Color\Gray;
|
||||
|
||||
final class Fill
|
||||
{
|
||||
/**
|
||||
* @var ColorInterface
|
||||
*/
|
||||
private $backgroundColor;
|
||||
|
||||
/**
|
||||
* @var ColorInterface|null
|
||||
*/
|
||||
private $foregroundColor;
|
||||
|
||||
/**
|
||||
* @var Gradient|null
|
||||
*/
|
||||
private $foregroundGradient;
|
||||
|
||||
/**
|
||||
* @var EyeFill
|
||||
*/
|
||||
private $topLeftEyeFill;
|
||||
|
||||
/**
|
||||
* @var EyeFill
|
||||
*/
|
||||
private $topRightEyeFill;
|
||||
|
||||
/**
|
||||
* @var EyeFill
|
||||
*/
|
||||
private $bottomLeftEyeFill;
|
||||
|
||||
/**
|
||||
* @var self|null
|
||||
*/
|
||||
private static $default;
|
||||
private static ?Fill $default = null;
|
||||
|
||||
private function __construct(
|
||||
ColorInterface $backgroundColor,
|
||||
?ColorInterface $foregroundColor,
|
||||
?Gradient $foregroundGradient,
|
||||
EyeFill $topLeftEyeFill,
|
||||
EyeFill $topRightEyeFill,
|
||||
EyeFill $bottomLeftEyeFill
|
||||
private readonly ColorInterface $backgroundColor,
|
||||
private readonly ?ColorInterface $foregroundColor,
|
||||
private readonly ?Gradient $foregroundGradient,
|
||||
private readonly EyeFill $topLeftEyeFill,
|
||||
private readonly EyeFill $topRightEyeFill,
|
||||
private readonly EyeFill $bottomLeftEyeFill
|
||||
) {
|
||||
$this->backgroundColor = $backgroundColor;
|
||||
$this->foregroundColor = $foregroundColor;
|
||||
$this->foregroundGradient = $foregroundGradient;
|
||||
$this->topLeftEyeFill = $topLeftEyeFill;
|
||||
$this->topRightEyeFill = $topRightEyeFill;
|
||||
$this->bottomLeftEyeFill = $bottomLeftEyeFill;
|
||||
}
|
||||
|
||||
public static function default() : self
|
||||
|
||||
@@ -7,26 +7,11 @@ use BaconQrCode\Renderer\Color\ColorInterface;
|
||||
|
||||
final class Gradient
|
||||
{
|
||||
/**
|
||||
* @var ColorInterface
|
||||
*/
|
||||
private $startColor;
|
||||
|
||||
/**
|
||||
* @var ColorInterface
|
||||
*/
|
||||
private $endColor;
|
||||
|
||||
/**
|
||||
* @var GradientType
|
||||
*/
|
||||
private $type;
|
||||
|
||||
public function __construct(ColorInterface $startColor, ColorInterface $endColor, GradientType $type)
|
||||
{
|
||||
$this->startColor = $startColor;
|
||||
$this->endColor = $endColor;
|
||||
$this->type = $type;
|
||||
public function __construct(
|
||||
private readonly ColorInterface $startColor,
|
||||
private readonly ColorInterface $endColor,
|
||||
private readonly GradientType $type
|
||||
) {
|
||||
}
|
||||
|
||||
public function getStartColor() : ColorInterface
|
||||
|
||||
@@ -10,40 +10,19 @@ use BaconQrCode\Renderer\Module\SquareModule;
|
||||
|
||||
final class RendererStyle
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $size;
|
||||
private ModuleInterface $module;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $margin;
|
||||
private EyeInterface|null $eye;
|
||||
|
||||
/**
|
||||
* @var ModuleInterface
|
||||
*/
|
||||
private $module;
|
||||
|
||||
/**
|
||||
* @var EyeInterface|null
|
||||
*/
|
||||
private $eye;
|
||||
|
||||
/**
|
||||
* @var Fill
|
||||
*/
|
||||
private $fill;
|
||||
private Fill $fill;
|
||||
|
||||
public function __construct(
|
||||
int $size,
|
||||
int $margin = 4,
|
||||
private int $size,
|
||||
private int $margin = 4,
|
||||
?ModuleInterface $module = null,
|
||||
?EyeInterface $eye = null,
|
||||
?Fill $fill = null
|
||||
) {
|
||||
$this->margin = $margin;
|
||||
$this->size = $size;
|
||||
$this->module = $module ?: SquareModule::instance();
|
||||
$this->eye = $eye ?: new ModuleEye($this->module);
|
||||
$this->fill = $fill ?: Fill::default();
|
||||
|
||||
+3
-11
@@ -14,19 +14,11 @@ use BaconQrCode\Renderer\RendererInterface;
|
||||
*/
|
||||
final class Writer
|
||||
{
|
||||
/**
|
||||
* Renderer instance.
|
||||
*
|
||||
* @var RendererInterface
|
||||
*/
|
||||
private $renderer;
|
||||
|
||||
/**
|
||||
* Creates a new writer with a specific renderer.
|
||||
*/
|
||||
public function __construct(RendererInterface $renderer)
|
||||
public function __construct(private readonly RendererInterface $renderer)
|
||||
{
|
||||
$this->renderer = $renderer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +31,7 @@ final class Writer
|
||||
*/
|
||||
public function writeString(
|
||||
string $content,
|
||||
string $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING,
|
||||
string $encoding = Encoder::DEFAULT_BYTE_MODE_ENCODING,
|
||||
?ErrorCorrectionLevel $ecLevel = null,
|
||||
?Version $forcedVersion = null
|
||||
) : string {
|
||||
@@ -62,7 +54,7 @@ final class Writer
|
||||
public function writeFile(
|
||||
string $content,
|
||||
string $filename,
|
||||
string $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING,
|
||||
string $encoding = Encoder::DEFAULT_BYTE_MODE_ENCODING,
|
||||
?ErrorCorrectionLevel $ecLevel = null,
|
||||
?Version $forcedVersion = null
|
||||
) : void {
|
||||
|
||||
Reference in new issue
Block a user