Feature: Labelprint für Kistenetiketten hinzugefügt

This commit is contained in:
2025-10-27 12:14:44 +01:00
parent 43bc416554
commit 14bae6c9ef
1068 changed files with 229014 additions and 1807 deletions

View File

@@ -38,7 +38,7 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
*
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function __construct($gdImage, SettingsContainerInterface $options = null){
public function __construct($gdImage, ?SettingsContainerInterface $options = null){
/** @noinspection PhpFullyQualifiedNameUsageInspection */
if(
@@ -85,12 +85,12 @@ class GDLuminanceSource extends LuminanceSourceAbstract{
}
/** @inheritDoc */
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
}
/** @inheritDoc */
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
return new self(imagecreatefromstring($blob), $options);
}

View File

@@ -35,7 +35,7 @@ final class GenericGFPoly{
* @throws \chillerlan\QRCode\QRCodeException if argument is null or empty, or if leading coefficient is 0 and this
* is not a constant polynomial (that is, it is not the monomial "0")
*/
public function __construct(array $coefficients, int $degree = null){
public function __construct(array $coefficients, ?int $degree = null){
$degree ??= 0;
if(empty($coefficients)){

View File

@@ -28,7 +28,7 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
/**
* IMagickLuminanceSource constructor.
*/
public function __construct(Imagick $imagick, SettingsContainerInterface $options = null){
public function __construct(Imagick $imagick, ?SettingsContainerInterface $options = null){
parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
$this->imagick = $imagick;
@@ -63,12 +63,12 @@ class IMagickLuminanceSource extends LuminanceSourceAbstract{
}
/** @inheritDoc */
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
public static function fromFile(string $path, ?SettingsContainerInterface $options = null):self{
return new self(new Imagick(self::checkFile($path)), $options);
}
/** @inheritDoc */
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
public static function fromBlob(string $blob, ?SettingsContainerInterface $options = null):self{
$im = new Imagick;
$im->readImageBlob($blob);

View File

@@ -34,7 +34,7 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
/**
*
*/
public function __construct(int $width, int $height, SettingsContainerInterface $options = null){
public function __construct(int $width, int $height, ?SettingsContainerInterface $options = null){
$this->width = $width;
$this->height = $height;
$this->options = ($options ?? new QROptions);
@@ -57,7 +57,10 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
return $this->height;
}
/** @inheritDoc */
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function getRow(int $y):array{
if($y < 0 || $y >= $this->getHeight()){

View File

@@ -77,7 +77,7 @@ final class MaskPattern{
*/
public function __construct(int $maskPattern){
if((0b111 & $maskPattern) !== $maskPattern){
if(($maskPattern & 0b111) !== $maskPattern){
throw new QRCodeException('invalid mask pattern');
}

View File

@@ -11,7 +11,7 @@
namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use function array_flip, ceil, intdiv, str_split;
use function ceil, intdiv, preg_match, strpos;
/**
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
@@ -24,16 +24,9 @@ final class AlphaNum extends QRDataModeAbstract{
/**
* ISO/IEC 18004:2000 Table 5
*
* @var int[]
* @var string
*/
private const CHAR_TO_ORD = [
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7,
'8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39,
'+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44,
];
private const CHAR_MAP = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
/**
* @inheritDoc
@@ -51,18 +44,7 @@ final class AlphaNum extends QRDataModeAbstract{
* @inheritDoc
*/
public static function validateString(string $string):bool{
if($string === ''){
return false;
}
foreach(str_split($string) as $chr){
if(!isset(self::CHAR_TO_ORD[$chr])){
return false;
}
}
return true;
return (bool)preg_match('/^[A-Z\d %$*+\-.:\/]+$/', $string);
}
/**
@@ -78,12 +60,15 @@ final class AlphaNum extends QRDataModeAbstract{
// encode 2 characters in 11 bits
for($i = 0; ($i + 1) < $len; $i += 2){
$bitBuffer->put((self::CHAR_TO_ORD[$this->data[$i]] * 45 + self::CHAR_TO_ORD[$this->data[($i + 1)]]), 11);
$bitBuffer->put(
($this->ord($this->data[$i]) * 45 + $this->ord($this->data[($i + 1)])),
11,
);
}
// encode a remaining character in 6 bits
if($i < $len){
$bitBuffer->put(self::CHAR_TO_ORD[$this->data[$i]], 6);
$bitBuffer->put($this->ord($this->data[$i]), 6);
}
return $this;
@@ -95,19 +80,7 @@ final class AlphaNum extends QRDataModeAbstract{
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$charmap = array_flip(self::CHAR_TO_ORD);
// @todo
$toAlphaNumericChar = function(int $ord) use ($charmap):string{
if(isset($charmap[$ord])){
return $charmap[$ord];
}
throw new QRCodeDataException('invalid character value: '.$ord);
};
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$result = '';
// Read two characters at a time
while($length > 1){
@@ -116,9 +89,9 @@ final class AlphaNum extends QRDataModeAbstract{
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
}
$nextTwoCharsBits = $bitBuffer->read(11);
$result .= $toAlphaNumericChar(intdiv($nextTwoCharsBits, 45));
$result .= $toAlphaNumericChar($nextTwoCharsBits % 45);
$nextTwoCharsBits = $bitBuffer->read(11);
$result .= self::chr(intdiv($nextTwoCharsBits, 45));
$result .= self::chr($nextTwoCharsBits % 45);
$length -= 2;
}
@@ -128,10 +101,36 @@ final class AlphaNum extends QRDataModeAbstract{
throw new QRCodeDataException('not enough bits available'); // @codeCoverageIgnore
}
$result .= $toAlphaNumericChar($bitBuffer->read(6));
$result .= self::chr($bitBuffer->read(6));
}
return $result;
}
/**
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private function ord(string $chr):int{
/** @phan-suppress-next-line PhanParamSuspiciousOrder */
$ord = strpos(self::CHAR_MAP, $chr);
if($ord === false){
throw new QRCodeDataException('invalid character'); // @codeCoverageIgnore
}
return $ord;
}
/**
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private static function chr(int $ord):string{
if($ord < 0 || $ord > 44){
throw new QRCodeDataException('invalid character code'); // @codeCoverageIgnore
}
return self::CHAR_MAP[$ord];
}
}

View File

@@ -34,6 +34,7 @@ final class ECI extends QRDataModeAbstract{
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Data\QRCodeDataException
* @noinspection PhpMissingParentConstructorInspection
*/
public function __construct(int $encoding){
@@ -107,7 +108,7 @@ final class ECI extends QRDataModeAbstract{
$id = ((($firstByte & 0b00011111) << 16) | $bitBuffer->read(16));
}
else{
throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte)); // @codeCoverageIgnore
throw new QRCodeDataException(sprintf('error decoding ECI value first byte: %08b', $firstByte));// @codeCoverageIgnore
}
return new ECICharset($id);
@@ -128,17 +129,12 @@ final class ECI extends QRDataModeAbstract{
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
$eciCharset = self::parseValue($bitBuffer);
$nextMode = $bitBuffer->read(4);
if($nextMode !== Mode::BYTE){
throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $nextMode));
}
$data = Byte::decodeSegment($bitBuffer, $versionNumber);
$encoding = $eciCharset->getName();
$data = self::decodeModeSegment($nextMode, $bitBuffer, $versionNumber);
$encoding = $eciCharset->getName();
if($encoding === null){
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// section 6.4.5: it does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
@@ -152,4 +148,18 @@ final class ECI extends QRDataModeAbstract{
return mb_convert_encoding($data, mb_internal_encoding(), $encoding);
}
/**
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private static function decodeModeSegment(int $mode, BitBuffer $bitBuffer, int $versionNumber):string{
switch(true){
case $mode === Mode::NUMBER: return Number::decodeSegment($bitBuffer, $versionNumber);
case $mode === Mode::ALPHANUM: return AlphaNum::decodeSegment($bitBuffer, $versionNumber);
case $mode === Mode::BYTE: return Byte::decodeSegment($bitBuffer, $versionNumber);
}
throw new QRCodeDataException(sprintf('ECI designator followed by invalid mode: "%04b"', $mode));
}
}

View File

@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use Throwable;
use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
/**
* Hanzi (simplified Chinese) mode, GBT18284-2000: 13-bit double-byte characters from the GB2312/GB18030 character set
@@ -64,11 +64,15 @@ final class Hanzi extends QRDataModeAbstract{
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function convertEncoding(string $string):string{
mb_detect_order([mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ']);
$detected = mb_detect_encoding($string, null, true);
$detected = mb_detect_encoding(
$string,
[mb_internal_encoding(), 'UTF-8', 'GB2312', 'GB18030', 'CP936', 'EUC-CN', 'HZ'],
true,
);
if($detected === false){
throw new QRCodeDataException('mb_detect_encoding error');
@@ -199,7 +203,7 @@ final class Hanzi extends QRDataModeAbstract{
$length--;
}
return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
}
}

View File

@@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use Throwable;
use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding,
mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
mb_internal_encoding, mb_strlen, ord, sprintf, strlen;
/**
* Kanji mode: 13-bit double-byte characters from the Shift-JIS character set
@@ -57,11 +57,10 @@ final class Kanji extends QRDataModeAbstract{
/**
* @inheritDoc
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function convertEncoding(string $string):string{
mb_detect_order([mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004']);
$detected = mb_detect_encoding($string, null, true);
$detected = mb_detect_encoding($string, [mb_internal_encoding(), 'UTF-8', 'SJIS', 'SJIS-2004'], true);
if($detected === false){
throw new QRCodeDataException('mb_detect_encoding error');
@@ -185,7 +184,7 @@ final class Kanji extends QRDataModeAbstract{
$length--;
}
return mb_convert_encoding(implode($buffer), mb_internal_encoding(), self::ENCODING);
return mb_convert_encoding(implode('', $buffer), mb_internal_encoding(), self::ENCODING);
}
}

View File

@@ -11,7 +11,7 @@
namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\Common\{BitBuffer, Mode};
use function array_flip, ceil, intdiv, str_split, substr, unpack;
use function ceil, intdiv, substr, unpack;
/**
* Numeric mode: decimal digits 0 to 9
@@ -21,13 +21,6 @@ use function array_flip, ceil, intdiv, str_split, substr, unpack;
*/
final class Number extends QRDataModeAbstract{
/**
* @var int[]
*/
private const NUMBER_TO_ORD = [
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
];
/**
* @inheritDoc
*/
@@ -44,18 +37,7 @@ final class Number extends QRDataModeAbstract{
* @inheritDoc
*/
public static function validateString(string $string):bool{
if($string === ''){
return false;
}
foreach(str_split($string) as $chr){
if(!isset(self::NUMBER_TO_ORD[$chr])){
return false;
}
}
return true;
return (bool)preg_match('/^\d+$/', $string);
}
/**
@@ -95,12 +77,20 @@ final class Number extends QRDataModeAbstract{
/**
* get the code for the given numeric string
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
private function parseInt(string $string):int{
$num = 0;
foreach(unpack('C*', $string) as $chr){
$num = ($num * 10 + $chr - 48);
$ords = unpack('C*', $string);
if($ords === false){
throw new QRCodeDataException('unpack() error');
}
foreach($ords as $ord){
$num = ($num * 10 + $ord - 48);
}
return $num;
@@ -112,19 +102,7 @@ final class Number extends QRDataModeAbstract{
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public static function decodeSegment(BitBuffer $bitBuffer, int $versionNumber):string{
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$charmap = array_flip(self::NUMBER_TO_ORD);
// @todo
$toNumericChar = function(int $ord) use ($charmap):string{
if(isset($charmap[$ord])){
return $charmap[$ord];
}
throw new QRCodeDataException('invalid character value: '.$ord);
};
$length = $bitBuffer->read(self::getLengthBits($versionNumber));
$result = '';
// Read three digits at a time
while($length >= 3){
@@ -139,9 +117,9 @@ final class Number extends QRDataModeAbstract{
throw new QRCodeDataException('error decoding numeric value');
}
$result .= $toNumericChar(intdiv($threeDigitsBits, 100));
$result .= $toNumericChar(intdiv($threeDigitsBits, 10) % 10);
$result .= $toNumericChar($threeDigitsBits % 10);
$result .= intdiv($threeDigitsBits, 100);
$result .= (intdiv($threeDigitsBits, 10) % 10);
$result .= ($threeDigitsBits % 10);
$length -= 3;
}
@@ -158,8 +136,8 @@ final class Number extends QRDataModeAbstract{
throw new QRCodeDataException('error decoding numeric value');
}
$result .= $toNumericChar(intdiv($twoDigitsBits, 10));
$result .= $toNumericChar($twoDigitsBits % 10);
$result .= intdiv($twoDigitsBits, 10);
$result .= ($twoDigitsBits % 10);
}
elseif($length === 1){
// One digit left over to read
@@ -173,7 +151,7 @@ final class Number extends QRDataModeAbstract{
throw new QRCodeDataException('error decoding numeric value');
}
$result .= $toNumericChar($digitBits);
$result .= $digitBits;
}
return $result;

View File

@@ -195,7 +195,7 @@ final class QRData{
// guess the version number within the given range
for($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++){
if($total <= $this->maxBitsForEcc[$version]){
if($total <= ($this->maxBitsForEcc[$version] - 4)){
return new Version($version);
}
}
@@ -207,7 +207,7 @@ final class QRData{
/**
* creates a BitBuffer and writes the string data to it
*
* @throws \chillerlan\QRCode\QRCodeException on data overflow
* @throws \chillerlan\QRCode\Data\QRCodeDataException on data overflow
*/
private function writeBitBuffer():void{
$MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);

View File

@@ -175,7 +175,7 @@ class QRMatrix{
*
* @return int[][]|bool[][]
*/
public function getMatrix(bool $boolean = null):array{
public function getMatrix(?bool $boolean = null):array{
if($boolean !== true){
return $this->matrix;
@@ -195,7 +195,7 @@ class QRMatrix{
* @see \chillerlan\QRCode\Data\QRMatrix::getMatrix()
* @codeCoverageIgnore
*/
public function matrix(bool $boolean = null):array{
public function matrix(?bool $boolean = null):array{
return $this->getMatrix($boolean);
}
@@ -387,7 +387,7 @@ class QRMatrix{
* 7 # 3
* 6 5 4
*/
public function checkNeighbours(int $x, int $y, int $M_TYPE = null):int{
public function checkNeighbours(int $x, int $y, ?int $M_TYPE = null):int{
$bits = 0;
foreach($this::neighbours as $bit => [$ix, $iy]){
@@ -463,6 +463,7 @@ class QRMatrix{
for($c = 0; $c < 3; $c++){
for($i = 0; $i < 8; $i++){
// phpcs:ignore
$this->set( $h[$c][0] , ($h[$c][1] + $i), false, $this::M_SEPARATOR);
$this->set(($v[$c][0] - $i), $v[$c][1] , false, $this::M_SEPARATOR);
}
@@ -552,7 +553,7 @@ class QRMatrix{
*
* ISO/IEC 18004:2000 Section 8.9
*/
public function setFormatInfo(MaskPattern $maskPattern = null):self{
public function setFormatInfo(?MaskPattern $maskPattern = null):self{
$this->maskPattern = $maskPattern;
$bits = 0; // sets all format fields to false (test mode)
@@ -678,7 +679,7 @@ class QRMatrix{
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
$height ??= $width;
// if width and height happen to be negative or 0 (default value), just return - nothing to do

View File

@@ -415,7 +415,7 @@ final class BitMatrix extends QRMatrix{
* @codeCoverageIgnore
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setQuietZone(int $quietZoneSize = null):self{
public function setQuietZone(?int $quietZoneSize = null):self{
throw new QRCodeDataException('not supported');
}
@@ -423,7 +423,7 @@ final class BitMatrix extends QRMatrix{
* @codeCoverageIgnore
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setLogoSpace(int $width, int $height = null, int $startX = null, int $startY = null):self{
public function setLogoSpace(int $width, ?int $height = null, ?int $startX = null, ?int $startY = null):self{
throw new QRCodeDataException('not supported');
}

View File

@@ -29,6 +29,7 @@ final class Decoder{
private ?EccLevel $eccLevel = null;
private ?MaskPattern $maskPattern = null;
private BitBuffer $bitBuffer;
private Detector $detector;
/**
* Decodes a QR Code represented as a BitMatrix.
@@ -37,7 +38,8 @@ final class Decoder{
* @throws \Throwable|\chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function decode(LuminanceSourceInterface $source):DecoderResult{
$matrix = (new Detector($source))->detect();
$this->detector = new Detector($source);
$matrix = $this->detector->detect();
try{
// clone the BitMatrix to avoid errors in case we run into mirroring
@@ -148,6 +150,7 @@ final class Decoder{
'data' => $result,
'version' => $this->version,
'eccLevel' => $this->eccLevel,
'finderPatterns' => $this->detector->getFinderPatterns(),
'maskPattern' => $this->maskPattern,
'structuredAppendParity' => $parityData,
'structuredAppendSequence' => $symbolSequence,

View File

@@ -20,13 +20,14 @@ use function property_exists;
* applies to 2D barcode formats. For now, it contains the raw bytes obtained
* as well as a String interpretation of those bytes, if applicable.
*
* @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
* @property string $data
* @property \chillerlan\QRCode\Common\Version $version
* @property \chillerlan\QRCode\Common\EccLevel $eccLevel
* @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
* @property int $structuredAppendParity
* @property int $structuredAppendSequence
* @property \chillerlan\QRCode\Common\BitBuffer $rawBytes
* @property string $data
* @property \chillerlan\QRCode\Common\Version $version
* @property \chillerlan\QRCode\Common\EccLevel $eccLevel
* @property \chillerlan\QRCode\Common\MaskPattern $maskPattern
* @property int $structuredAppendParity
* @property int $structuredAppendSequence
* @property \chillerlan\QRCode\Detector\FinderPattern[] $finderPatterns
*/
final class DecoderResult{
@@ -37,11 +38,13 @@ final class DecoderResult{
private string $data = '';
private int $structuredAppendParity = -1;
private int $structuredAppendSequence = -1;
/** @var \chillerlan\QRCode\Detector\FinderPattern[] */
private array $finderPatterns = [];
/**
* DecoderResult constructor.
*/
public function __construct(iterable $properties = null){
public function __construct(?iterable $properties = null){
if(!empty($properties)){

View File

@@ -94,7 +94,7 @@ final class ReedSolomonDecoder{
while($longerBlocksStartAt >= 0){
$numCodewords = count($result[$longerBlocksStartAt][1]);
if($numCodewords == $shorterBlocksTotalCodewords){
if($numCodewords === $shorterBlocksTotalCodewords){
break;
}

View File

@@ -256,7 +256,7 @@ final class AlignmentPatternFinder{
$i++;
}
if($i == $maxI || $stateCount[1] > $maxCount){
if($i === $maxI || $stateCount[1] > $maxCount){
return null;
}
@@ -269,6 +269,7 @@ final class AlignmentPatternFinder{
return null;
}
// phpcs:ignore
if((5 * abs(($stateCount[0] + $stateCount[1] + $stateCount[2]) - $originalStateCountTotal)) >= (2 * $originalStateCountTotal)){
return null;
}

View File

@@ -25,6 +25,8 @@ use const NAN;
final class Detector{
private BitMatrix $matrix;
/** @var \chillerlan\QRCode\Detector\FinderPattern[] */
private array $finderPatterns = [];
/**
* Detector constructor.
@@ -33,11 +35,20 @@ final class Detector{
$this->matrix = (new Binarizer($source))->getBlackMatrix();
}
/**
* @return \chillerlan\QRCode\Detector\FinderPattern[]
*/
public function getFinderPatterns():array{
return $this->finderPatterns;
}
/**
* Detects a QR Code in an image.
*/
public function detect():BitMatrix{
[$bottomLeft, $topLeft, $topRight] = (new FinderPatternFinder($this->matrix))->find();
$this->finderPatterns = (new FinderPatternFinder($this->matrix))->find();
[$bottomLeft, $topLeft, $topRight] = $this->finderPatterns;
$moduleSize = $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
$dimension = $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
@@ -305,11 +316,11 @@ final class Detector{
*
*/
private function createTransform(
FinderPattern $nw,
FinderPattern $ne,
FinderPattern $sw,
int $size,
AlignmentPattern $ap = null
FinderPattern $nw,
FinderPattern $ne,
FinderPattern $sw,
int $size,
?AlignmentPattern $ap = null
):PerspectiveTransform{
$dimMinusThree = ($size - 3.5);

View File

@@ -27,7 +27,7 @@ final class FinderPattern extends ResultPoint{
/**
*
*/
public function __construct(float $posX, float $posY, float $estimatedModuleSize, int $count = null){
public function __construct(float $posX, float $posY, float $estimatedModuleSize, ?int $count = null){
parent::__construct($posX, $posY, $estimatedModuleSize);
$this->count = ($count ?? 1);

View File

@@ -290,11 +290,13 @@ final class FinderPatternFinder{
// Now also count down, right from center
$i = 1;
// phpcs:ignore
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
$stateCount[2]++;
$i++;
}
// phpcs:ignore
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && !$this->matrix->check(($centerJ + $i), ($centerI + $i))){
$stateCount[3]++;
$i++;
@@ -304,6 +306,7 @@ final class FinderPatternFinder{
return false;
}
// phpcs:ignore
while(($centerI + $i) < $dimension && ($centerJ + $i) < $dimension && $this->matrix->check(($centerJ + $i), ($centerI + $i))){
$stateCount[4]++;
$i++;

View File

@@ -152,15 +152,15 @@ final class GridSampler{
// no need to try/catch as QRMatrix::set() will silently discard out of bounds values
# try{
for($x = 0; $x < $max; $x += 2){
// Black(-ish) pixel
$bits->set(
intdiv($x, 2),
$y,
$matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
QRMatrix::M_DATA
);
}
for($x = 0; $x < $max; $x += 2){
// Black(-ish) pixel
$bits->set(
intdiv($x, 2),
$y,
$matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]),
QRMatrix::M_DATA
);
}
# }
# catch(\Throwable $aioobe){//ArrayIndexOutOfBoundsException
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting

View File

@@ -152,7 +152,7 @@ final class PerspectiveTransform{
/**
* @return array[] [$xValues, $yValues]
*/
public function transformPoints(array $xValues, array $yValues = null):array{
public function transformPoints(array $xValues, ?array $yValues = null):array{
$max = count($xValues);
if($yValues !== null){ // unused

View File

@@ -95,7 +95,7 @@ class QREps extends QROutputAbstract{
// CMYK
? '%f %f %f %f C'
// RGB
:'%f %f %f R';
: '%f %f %f R';
return sprintf($format, ...$values);
}
@@ -103,7 +103,7 @@ class QREps extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
[$width, $height] = $this->getOutputDimensions();
$eps = [

View File

@@ -110,7 +110,10 @@ class QRFpdf extends QROutputAbstract{
* Initializes an FPDF instance
*/
protected function initFPDF():FPDF{
return new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
$fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, $this->getOutputDimensions());
$fpdf->AddPage();
return $fpdf;
}
/**
@@ -118,9 +121,8 @@ class QRFpdf extends QROutputAbstract{
*
* @return string|\FPDF
*/
public function dump(string $file = null){
$this->fpdf = $this->initFPDF();
$this->fpdf->AddPage();
public function dump(?string $file = null, ?FPDF $fpdf = null){
$this->fpdf = ($fpdf ?? $this->initFPDF());
if($this::moduleValueIsValid($this->options->bgColor)){
$bgColor = $this->prepareModuleValue($this->options->bgColor);

View File

@@ -179,7 +179,7 @@ class QRGdImage extends QROutputAbstract{
* @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
* @throws \ErrorException
*/
public function dump(string $file = null){
public function dump(?string $file = null){
set_error_handler(function(int $errno, string $errstr):bool{
throw new ErrorException($errstr, $errno);

View File

@@ -112,7 +112,7 @@ class QRImagick extends QROutputAbstract{
*
* @return string|\Imagick
*/
public function dump(string $file = null){
public function dump(?string $file = null){
$this->setBgColor();
$this->imagick = $this->createImage();

View File

@@ -71,7 +71,7 @@ abstract class QRMarkup extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
$data = $this->createMarkup($file !== null);
$this->saveToFile($data, $file);

View File

@@ -146,18 +146,18 @@ abstract class QROutputAbstract implements QROutputInterface{
}
/**
* Prepares the value for the given input ()
* Prepares the value for the given input (return value depends on the output class)
*
* @param mixed $value
*
* @return mixed|null return value depends on the output class
* @return mixed|null
*/
abstract protected function prepareModuleValue($value);
/**
* Returns a default value for either dark or light modules
* Returns a default value for either dark or light modules (return value depends on the output class)
*
* @return mixed|null return value depends on the output class
* @return mixed|null
*/
abstract protected function getDefaultModuleValue(bool $isDark);
@@ -188,7 +188,7 @@ abstract class QROutputAbstract implements QROutputInterface{
/**
* Returns a base64 data URI for the given string and mime type
*/
protected function toBase64DataURI(string $data, string $mime = null):string{
protected function toBase64DataURI(string $data, ?string $mime = null):string{
return sprintf('data:%s;base64,%s', ($mime ?? $this::MIME_TYPE), base64_encode($data));
}
@@ -200,7 +200,7 @@ abstract class QROutputAbstract implements QROutputInterface{
*
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
*/
protected function saveToFile(string $data, string $file = null):void{
protected function saveToFile(string $data, ?string $file = null):void{
if($file === null){
return;

View File

@@ -221,6 +221,6 @@ interface QROutputInterface{
*
* @return mixed
*/
public function dump(string $file = null);
public function dump(?string $file = null);
}

View File

@@ -46,7 +46,7 @@ class QRString extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
switch($this->options->outputType){
case QROutputInterface::STRING_TEXT:
@@ -101,7 +101,7 @@ class QRString extends QROutputAbstract{
*
* @codeCoverageIgnore
*/
public static function ansi8(string $str, int $color, bool $background = null):string{
public static function ansi8(string $str, int $color, ?bool $background = null):string{
$color = max(0, min($color, 255));
$background = ($background === true) ? 48 : 38;

View File

@@ -25,9 +25,9 @@ class QRStringJSON extends QROutputAbstract{
* @inheritDoc
* @throws \JsonException
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
$matrix = $this->matrix->getMatrix($this->options->jsonAsBooleans);
$data = json_encode($matrix, $this->options->jsonFlags);;
$data = json_encode($matrix, $this->options->jsonFlags);
$this->saveToFile($data, $file);

View File

@@ -43,7 +43,7 @@ class QRStringText extends QROutputAbstract{
/**
* @inheritDoc
*/
public function dump(string $file = null):string{
public function dump(?string $file = null):string{
$lines = [];
$linestart = $this->options->textLineStart;
@@ -66,7 +66,7 @@ class QRStringText extends QROutputAbstract{
*
* @codeCoverageIgnore
*/
public static function ansi8(string $str, int $color, bool $background = null):string{
public static function ansi8(string $str, int $color, ?bool $background = null):string{
$color = max(0, min($color, 255));
$background = ($background === true) ? 48 : 38;

View File

@@ -182,7 +182,7 @@ class QRCode{
*
* PHP8: accept iterable
*/
public function __construct(SettingsContainerInterface $options = null){
public function __construct(?SettingsContainerInterface $options = null){
$this->setOptions(($options ?? new QROptions));
}
@@ -202,9 +202,14 @@ class QRCode{
/**
* Renders a QR Code for the given $data and QROptions, saves $file optionally
*
* Note: it is possible to add several data segments before calling this method with a valid $data string
* which will result in a mixed-mode QR Code with the given parameter as last element.
*
* @see https://github.com/chillerlan/php-qrcode/issues/246
*
* @return mixed
*/
public function render(string $data = null, string $file = null){
public function render(?string $data = null, ?string $file = null){
if($data !== null){
/** @var \chillerlan\QRCode\Data\QRDataModeInterface $dataInterface */
@@ -226,7 +231,7 @@ class QRCode{
*
* @return mixed
*/
public function renderMatrix(QRMatrix $matrix, string $file = null){
public function renderMatrix(QRMatrix $matrix, ?string $file = null){
return $this->initOutputInterface($matrix)->dump($file ?? $this->options->cachefile);
}
@@ -298,7 +303,7 @@ class QRCode{
throw new QRCodeOutputException('invalid output module');
}
if(!in_array(QROutputInterface::class, class_implements($outputInterface))){
if(!in_array(QROutputInterface::class, class_implements($outputInterface), true)){
throw new QRCodeOutputException('output module does not implement QROutputInterface');
}
@@ -457,8 +462,6 @@ class QRCode{
/**
* Reads a QR Code from a given file
*
* @noinspection PhpUndefinedMethodInspection
*/
public function readFromFile(string $path):DecoderResult{
return $this->readFromSource($this->luminanceSourceFQN::fromFile($path, $this->options));
@@ -466,8 +469,6 @@ class QRCode{
/**
* Reads a QR Code from the given data blob
*
* @noinspection PhpUndefinedMethodInspection
*/
public function readFromBlob(string $blob):DecoderResult{
return $this->readFromSource($this->luminanceSourceFQN::fromBlob($blob, $this->options));