From 0a069c3866a0f330af4538c253fa0b40ed296924 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Thu, 11 Dec 2025 19:58:13 +0100 Subject: [PATCH 01/17] Add QRPbm output format for Portable BitMap outputs --- src/Output/QRPbm.php | 42 +++++++++++++++++++++++++++ tests/Output/QRPbmTest.php | 59 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/Output/QRPbm.php create mode 100644 tests/Output/QRPbmTest.php diff --git a/src/Output/QRPbm.php b/src/Output/QRPbm.php new file mode 100644 index 000000000..f9ba5c82e --- /dev/null +++ b/src/Output/QRPbm.php @@ -0,0 +1,42 @@ +matrix->getSize(); + $result = "P1\n" + .$size.' '.$size."\n"; + foreach($this->matrix->getBooleanMatrix() as $row) { + foreach ($row as $isDark) { + $result .= $this->getDefaultModuleValue( $isDark ); + } + $result .= "\n"; + } + if ( is_string($file) ) { + file_put_contents($file,$result); + } + return $result; + } +} diff --git a/tests/Output/QRPbmTest.php b/tests/Output/QRPbmTest.php new file mode 100644 index 000000000..6dca7f59b --- /dev/null +++ b/tests/Output/QRPbmTest.php @@ -0,0 +1,59 @@ + + */ + public static function moduleValueProvider():array{ + return [ + 'invalid: wrong type' => [[], false], + 'valid: string' => ['abc', true], + 'valid: zero length string' => ['', true], + 'valid: empty string' => [' ', true], + ]; + } + + #[Test] + public function setModuleValues():void{ + + $this->options->moduleValues = [ + // data + QRMatrix::M_DATA_DARK => '1', + QRMatrix::M_DATA => '0', + ]; + + $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); + $data = $this->outputInterface->dump(); + + $this::assertStringContainsString('1', $data); + $this::assertStringContainsString('0', $data); + } +} From e70a744838b2a2bf2f4b61386e61ff9469115138 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:01:55 +0100 Subject: [PATCH 02/17] Add pbm output to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b97c5e300..085291fd1 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana - String types: JSON, plain text, etc. - Encapsulated Postscript (EPS) - PDF via [FPDF](https://github.com/setasign/fpdf) + - Portable Bitmap ([PBM](https://en.wikipedia.org/wiki/Netpbm)) - QR Code reader (via GD and ImageMagick) From f5a2d2bb5ddabc51de45acc9e1a3ecf2e3269668 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Fri, 12 Dec 2025 08:17:46 +0100 Subject: [PATCH 03/17] Actually run tests against new class --- src/Output/QRPbm.php | 28 ++++++++++++++++++---------- tests/Output/QRPbmTest.php | 13 +++++++------ 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/Output/QRPbm.php b/src/Output/QRPbm.php index f9ba5c82e..bcaae64f7 100644 --- a/src/Output/QRPbm.php +++ b/src/Output/QRPbm.php @@ -14,29 +14,37 @@ use chillerlan\QRCode\Output\QROutputAbstract; class QRPbm extends QROutputAbstract { + private const LIGHT_DARK = [ '0', '1' ]; - protected function prepareModuleValue(mixed $value):mixed{ + + protected function prepareModuleValue( mixed $value ): mixed { return $value; } - protected function getDefaultModuleValue(bool $isDark):mixed{ + + protected function getDefaultModuleValue( bool $isDark ): mixed { return $isDark ? self::LIGHT_DARK[1] : self::LIGHT_DARK[0]; } - public static function moduleValueIsValid(mixed $value):bool{ - return is_string($value) && in_array( $value, self::LIGHT_DARK, true ); + + public static function moduleValueIsValid( mixed $value): bool { + return is_string( $value ) && in_array( $value, self::LIGHT_DARK, true ); } - public function dump(string|null $file = null):mixed { + + public function dump( string|null $file = null ): mixed { $size = $this->matrix->getSize(); - $result = "P1\n" + $qrString = "P1\n" .$size.' '.$size."\n"; foreach($this->matrix->getBooleanMatrix() as $row) { foreach ($row as $isDark) { - $result .= $this->getDefaultModuleValue( $isDark ); + $qrString .= $this->getDefaultModuleValue( $isDark ); } - $result .= "\n"; + $qrString .= "\n"; } if ( is_string($file) ) { - file_put_contents($file,$result); + $writeResult = file_put_contents($file,$qrString); + if ( $writeResult === false ) { + throw new QRCodeOutputException('Cannot write data to cache file: '.$file); + } } - return $result; + return $qrString; } } diff --git a/tests/Output/QRPbmTest.php b/tests/Output/QRPbmTest.php index 6dca7f59b..8524a81ce 100644 --- a/tests/Output/QRPbmTest.php +++ b/tests/Output/QRPbmTest.php @@ -13,7 +13,7 @@ use chillerlan\QRCode\QROptions; use chillerlan\QRCode\Data\QRMatrix; -use chillerlan\QRCode\Output\{QROutputInterface, QRStringText}; +use chillerlan\QRCode\Output\{QROutputInterface, QRPbm}; use chillerlan\Settings\SettingsContainerInterface; use PHPUnit\Framework\Attributes\Test; @@ -26,7 +26,7 @@ protected function getOutputInterface( SettingsContainerInterface|QROptions $options, QRMatrix $matrix, ):QROutputInterface{ - return new QRStringText($options, $matrix); + return new QRPbm($options, $matrix); } /** @@ -34,10 +34,11 @@ protected function getOutputInterface( */ public static function moduleValueProvider():array{ return [ - 'invalid: wrong type' => [[], false], - 'valid: string' => ['abc', true], - 'valid: zero length string' => ['', true], - 'valid: empty string' => [' ', true], + 'invalid: wrong type' => [[], false], + 'invalid: Not 0 or 1' => ['abc', false], + 'invalid: empty string' => ['', false], + 'invalid: space string' => [' ', false], + 'valid: zero' => ['0', true], ]; } From aa748d204cfd9dde4456bd21eebce482f30acac7 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Fri, 12 Dec 2025 08:33:46 +0100 Subject: [PATCH 04/17] Formatting --- tests/Output/QRPbmTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Output/QRPbmTest.php b/tests/Output/QRPbmTest.php index 8524a81ce..35262dfb1 100644 --- a/tests/Output/QRPbmTest.php +++ b/tests/Output/QRPbmTest.php @@ -38,17 +38,17 @@ public static function moduleValueProvider():array{ 'invalid: Not 0 or 1' => ['abc', false], 'invalid: empty string' => ['', false], 'invalid: space string' => [' ', false], - 'valid: zero' => ['0', true], + 'valid: zero' => ['0', true], ]; } - #[Test] + #[Test] public function setModuleValues():void{ $this->options->moduleValues = [ - // data - QRMatrix::M_DATA_DARK => '1', - QRMatrix::M_DATA => '0', + // data + QRMatrix::M_DATA_DARK => '1', + QRMatrix::M_DATA => '0', ]; $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); From 9b94cfe94d93875de799d6e1bd533b3a2f7e889e Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Fri, 12 Dec 2025 08:34:58 +0100 Subject: [PATCH 05/17] Call instance methods non-statically --- tests/Output/QRPbmTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Output/QRPbmTest.php b/tests/Output/QRPbmTest.php index 35262dfb1..c12c539a8 100644 --- a/tests/Output/QRPbmTest.php +++ b/tests/Output/QRPbmTest.php @@ -54,7 +54,7 @@ public function setModuleValues():void{ $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); $data = $this->outputInterface->dump(); - $this::assertStringContainsString('1', $data); - $this::assertStringContainsString('0', $data); + $this->assertStringContainsString('1', $data); + $this->assertStringContainsString('0', $data); } } From aff03ccec137053299d4a1035c31108cd7e89877 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:04:22 +0100 Subject: [PATCH 06/17] Also support binary bitmaps --- docs/Usage/Overview.md | 1 + src/Output/QRNetpbmBitmapAbstract.php | 55 ++++++++++++++++++ src/Output/QRNetpbmBitmapAscii.php | 20 +++++++ src/Output/QRNetpbmBitmapBinary.php | 38 +++++++++++++ ...bmTest.php => QRNetpbmBitmapAsciiTest.php} | 26 ++++----- tests/Output/QRNetpbmBitmapBinaryTest.php | 57 +++++++++++++++++++ 6 files changed, 183 insertions(+), 14 deletions(-) create mode 100644 src/Output/QRNetpbmBitmapAbstract.php create mode 100644 src/Output/QRNetpbmBitmapAscii.php create mode 100644 src/Output/QRNetpbmBitmapBinary.php rename tests/Output/{QRPbmTest.php => QRNetpbmBitmapAsciiTest.php} (61%) create mode 100644 tests/Output/QRNetpbmBitmapBinaryTest.php diff --git a/docs/Usage/Overview.md b/docs/Usage/Overview.md index fbbc6467e..e80a81e67 100644 --- a/docs/Usage/Overview.md +++ b/docs/Usage/Overview.md @@ -23,6 +23,7 @@ It also features a QR Code reader based on a [PHP port](https://github.com/khana - String types: JSON, plain text, etc. - Encapsulated Postscript (EPS) - PDF via [FPDF](https://github.com/setasign/fpdf) + - Portable Bitmap ([PBM](https://en.wikipedia.org/wiki/Netpbm)) - QR Code reader (via GD and ImageMagick) diff --git a/src/Output/QRNetpbmBitmapAbstract.php b/src/Output/QRNetpbmBitmapAbstract.php new file mode 100644 index 000000000..613484676 --- /dev/null +++ b/src/Output/QRNetpbmBitmapAbstract.php @@ -0,0 +1,55 @@ +matrix->getBooleanMatrix() as $row ) { + $line = ''; + foreach ($row as $isDark) { + $line .= str_repeat( $isDark ? '1' : '0', $this->scale ); + } + $line .= "\n"; + $body .= str_repeat( $line, $this->scale ); + } + return trim($body,"\n"); + } + + public function dump( string|null $file = null ): mixed { + $qrString = $this->getHeader() . "\n" + .$this->length.' '.$this->length."\n".$this->getBody(); + $this->saveToFile( $qrString, $file ); + + return $qrString; + } +} diff --git a/src/Output/QRNetpbmBitmapAscii.php b/src/Output/QRNetpbmBitmapAscii.php new file mode 100644 index 000000000..ad4217735 --- /dev/null +++ b/src/Output/QRNetpbmBitmapAscii.php @@ -0,0 +1,20 @@ +asciiBinToBinary( $row ); + } + return $body; + } + + private function asciiBinToBinary( string $asciiBin ): string { + $binaryString = ''; + foreach( str_split( $asciiBin, 8 ) as $currentChunk ) { + $currentChunk = str_pad( $currentChunk, 8, '0' ); + $binaryString .= pack( "C", bindec( $currentChunk ) ); + } + return $binaryString; + } +} diff --git a/tests/Output/QRPbmTest.php b/tests/Output/QRNetpbmBitmapAsciiTest.php similarity index 61% rename from tests/Output/QRPbmTest.php rename to tests/Output/QRNetpbmBitmapAsciiTest.php index c12c539a8..21c81280a 100644 --- a/tests/Output/QRPbmTest.php +++ b/tests/Output/QRNetpbmBitmapAsciiTest.php @@ -13,20 +13,20 @@ use chillerlan\QRCode\QROptions; use chillerlan\QRCode\Data\QRMatrix; -use chillerlan\QRCode\Output\{QROutputInterface, QRPbm}; +use chillerlan\QRCode\Output\{QROutputInterface, QRNetpbmBitmapAscii}; use chillerlan\Settings\SettingsContainerInterface; use PHPUnit\Framework\Attributes\Test; /** - * Tests the QRPbm output class + * Tests the QRNetpbmBitmapAscii output class */ -final class QRPbmTest extends QROutputTestAbstract{ +final class QRNetpbmBitmapAsciiTest extends QROutputTestAbstract { protected function getOutputInterface( SettingsContainerInterface|QROptions $options, QRMatrix $matrix, ):QROutputInterface{ - return new QRPbm($options, $matrix); + return new QRNetpbmBitmapAscii($options, $matrix); } /** @@ -35,26 +35,24 @@ protected function getOutputInterface( public static function moduleValueProvider():array{ return [ 'invalid: wrong type' => [[], false], - 'invalid: Not 0 or 1' => ['abc', false], - 'invalid: empty string' => ['', false], - 'invalid: space string' => [' ', false], - 'valid: zero' => ['0', true], + 'invalid: wrong type' => ['abc', false], + 'valid: true' => [true, true], + 'valid: false' => [false, true], ]; } #[Test] public function setModuleValues():void{ - $this->options->moduleValues = [ // data - QRMatrix::M_DATA_DARK => '1', - QRMatrix::M_DATA => '0', + QRMatrix::M_DATA_DARK => true, + QRMatrix::M_DATA => false, ]; $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); - $data = $this->outputInterface->dump(); + $data = $this->outputInterface->dump(); - $this->assertStringContainsString('1', $data); - $this->assertStringContainsString('0', $data); + $this::assertStringContainsString('1', $data); + $this::assertStringContainsString('0', $data); } } diff --git a/tests/Output/QRNetpbmBitmapBinaryTest.php b/tests/Output/QRNetpbmBitmapBinaryTest.php new file mode 100644 index 000000000..39536aad7 --- /dev/null +++ b/tests/Output/QRNetpbmBitmapBinaryTest.php @@ -0,0 +1,57 @@ + + */ + public static function moduleValueProvider():array{ + return [ + 'invalid: wrong type' => [[], false], + 'invalid: wrong type' => ['abc', false], + 'valid: true' => [true, true], + 'valid: false' => [false, true], + ]; + } + + #[Test] + public function setModuleValues():void{ + $this->options->moduleValues = [ + // data + QRMatrix::M_DATA_DARK => true, + QRMatrix::M_DATA => false, + ]; + + $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); + $data = $this->outputInterface->dump(); + + $this::assertStringContainsString("\0", $data); + } +} From 78ae3a32e9c5809d2bc99126e2fbe4c551f08c17 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:05:26 +0100 Subject: [PATCH 07/17] Missing import --- src/Output/QRNetpbmBitmapAbstract.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Output/QRNetpbmBitmapAbstract.php b/src/Output/QRNetpbmBitmapAbstract.php index 613484676..4b2be95e5 100644 --- a/src/Output/QRNetpbmBitmapAbstract.php +++ b/src/Output/QRNetpbmBitmapAbstract.php @@ -12,6 +12,7 @@ namespace chillerlan\QRCode\Output; use chillerlan\QRCode\Output\QROutputAbstract; +use UnexpectedValueException; abstract class QRNetpbmBitmapAbstract extends QROutputAbstract { From 03802e89fa5d2f49bff0590f70916b46d85db8e4 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:06:02 +0100 Subject: [PATCH 08/17] Formatting --- src/Output/QRNetpbmBitmapAbstract.php | 6 +++--- src/Output/QRNetpbmBitmapBinary.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Output/QRNetpbmBitmapAbstract.php b/src/Output/QRNetpbmBitmapAbstract.php index 4b2be95e5..e49eb0552 100644 --- a/src/Output/QRNetpbmBitmapAbstract.php +++ b/src/Output/QRNetpbmBitmapAbstract.php @@ -18,7 +18,7 @@ abstract class QRNetpbmBitmapAbstract extends QROutputAbstract { protected function prepareModuleValue( mixed $value ): mixed { if ( !is_bool( $value ) ) { - throw new UnexpectedValueException( "Expected boolean module value" ); + throw new UnexpectedValueException( 'Expected boolean module value' ); } return $value; } @@ -35,7 +35,7 @@ abstract protected function getHeader(): string; protected function getBody(): string { $body = ''; - foreach ( $this->matrix->getBooleanMatrix() as $row ) { + foreach ($this->matrix->getBooleanMatrix() as $row) { $line = ''; foreach ($row as $isDark) { $line .= str_repeat( $isDark ? '1' : '0', $this->scale ); @@ -47,7 +47,7 @@ protected function getBody(): string { } public function dump( string|null $file = null ): mixed { - $qrString = $this->getHeader() . "\n" + $qrString = $this->getHeader()."\n" .$this->length.' '.$this->length."\n".$this->getBody(); $this->saveToFile( $qrString, $file ); diff --git a/src/Output/QRNetpbmBitmapBinary.php b/src/Output/QRNetpbmBitmapBinary.php index 7102b1f19..0cf74d493 100644 --- a/src/Output/QRNetpbmBitmapBinary.php +++ b/src/Output/QRNetpbmBitmapBinary.php @@ -21,7 +21,7 @@ protected function getHeader(): string { protected function getBody(): string { $asciiBody = parent::getBody(); $body = ''; - foreach ( explode("\n", $asciiBody) as $row ) { + foreach (explode("\n", $asciiBody) as $row) { $body .= $this->asciiBinToBinary( $row ); } return $body; @@ -29,9 +29,9 @@ protected function getBody(): string { private function asciiBinToBinary( string $asciiBin ): string { $binaryString = ''; - foreach( str_split( $asciiBin, 8 ) as $currentChunk ) { + foreach(str_split( $asciiBin, 8 ) as $currentChunk) { $currentChunk = str_pad( $currentChunk, 8, '0' ); - $binaryString .= pack( "C", bindec( $currentChunk ) ); + $binaryString .= pack( 'C', bindec( $currentChunk ) ); } return $binaryString; } From ddef97de371d6bd8ae417437996f616808408e88 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:08:16 +0100 Subject: [PATCH 09/17] Formatting --- tests/Output/QRNetpbmBitmapAsciiTest.php | 4 ++-- tests/Output/QRNetpbmBitmapBinaryTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Output/QRNetpbmBitmapAsciiTest.php b/tests/Output/QRNetpbmBitmapAsciiTest.php index 21c81280a..8fedfedc2 100644 --- a/tests/Output/QRNetpbmBitmapAsciiTest.php +++ b/tests/Output/QRNetpbmBitmapAsciiTest.php @@ -34,8 +34,8 @@ protected function getOutputInterface( */ public static function moduleValueProvider():array{ return [ - 'invalid: wrong type' => [[], false], - 'invalid: wrong type' => ['abc', false], + 'invalid: wrong type: array' => [[], false], + 'invalid: wrong type: string' => ['abc', false], 'valid: true' => [true, true], 'valid: false' => [false, true], ]; diff --git a/tests/Output/QRNetpbmBitmapBinaryTest.php b/tests/Output/QRNetpbmBitmapBinaryTest.php index 39536aad7..8a74cb0a7 100644 --- a/tests/Output/QRNetpbmBitmapBinaryTest.php +++ b/tests/Output/QRNetpbmBitmapBinaryTest.php @@ -34,8 +34,8 @@ protected function getOutputInterface( */ public static function moduleValueProvider():array{ return [ - 'invalid: wrong type' => [[], false], - 'invalid: wrong type' => ['abc', false], + 'invalid: wrong type: array' => [[], false], + 'invalid: wrong type: string' => ['abc', false], 'valid: true' => [true, true], 'valid: false' => [false, true], ]; From e5f60fdf968d73338d8697b2a7e91b58f994d456 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:14:41 +0100 Subject: [PATCH 10/17] Remove superseded QRPbm class --- src/Output/QRPbm.php | 50 -------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/Output/QRPbm.php diff --git a/src/Output/QRPbm.php b/src/Output/QRPbm.php deleted file mode 100644 index bcaae64f7..000000000 --- a/src/Output/QRPbm.php +++ /dev/null @@ -1,50 +0,0 @@ -matrix->getSize(); - $qrString = "P1\n" - .$size.' '.$size."\n"; - foreach($this->matrix->getBooleanMatrix() as $row) { - foreach ($row as $isDark) { - $qrString .= $this->getDefaultModuleValue( $isDark ); - } - $qrString .= "\n"; - } - if ( is_string($file) ) { - $writeResult = file_put_contents($file,$qrString); - if ( $writeResult === false ) { - throw new QRCodeOutputException('Cannot write data to cache file: '.$file); - } - } - return $qrString; - } -} From a520c0f6593377c569c4fd66f1d43adfea616c9b Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:30:55 +0100 Subject: [PATCH 11/17] Add mime type and return base64 if needed --- src/Output/QRNetpbmBitmapAbstract.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Output/QRNetpbmBitmapAbstract.php b/src/Output/QRNetpbmBitmapAbstract.php index e49eb0552..7ce4ab818 100644 --- a/src/Output/QRNetpbmBitmapAbstract.php +++ b/src/Output/QRNetpbmBitmapAbstract.php @@ -15,6 +15,7 @@ use UnexpectedValueException; abstract class QRNetpbmBitmapAbstract extends QROutputAbstract { + public const MIME_TYPE = 'image/x-portable-bitmap'; protected function prepareModuleValue( mixed $value ): mixed { if ( !is_bool( $value ) ) { @@ -49,8 +50,13 @@ protected function getBody(): string { public function dump( string|null $file = null ): mixed { $qrString = $this->getHeader()."\n" .$this->length.' '.$this->length."\n".$this->getBody(); + $this->saveToFile( $qrString, $file ); + if ( $this->options->outputBase64 ) { + $qrString = $this->toBase64DataURI( $qrString ); + } + return $qrString; } } From b2f1286cb53d62432e20791623fa4444c22f3a7b Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:18:59 +0100 Subject: [PATCH 12/17] Add grayscale, and incorporate suggestions from review --- src/Output/QRNetpbmAbstract.php | 63 +++++++++++++++++++++ src/Output/QRNetpbmBitmap.php | 98 +++++++++++++++++++++++++++++++++ src/Output/QRNetpbmGraymap.php | 89 ++++++++++++++++++++++++++++++ src/QROptionsTrait.php | 20 +++++++ 4 files changed, 270 insertions(+) create mode 100644 src/Output/QRNetpbmAbstract.php create mode 100644 src/Output/QRNetpbmBitmap.php create mode 100644 src/Output/QRNetpbmGraymap.php diff --git a/src/Output/QRNetpbmAbstract.php b/src/Output/QRNetpbmAbstract.php new file mode 100644 index 000000000..aac851eee --- /dev/null +++ b/src/Output/QRNetpbmAbstract.php @@ -0,0 +1,63 @@ +options->netpbmPlain ? static::HEADER_ASCII : static::HEADER_BINARY; + } + + protected function getHeader():string{ + $comment = 'created by https://github.com/chillerlan/php-qrcode'; + + return sprintf("%s\n# %s\n%s %s\n%s", $this->getMagicNumber(), $comment, $this->length, $this->length, $this->getMaxValueHeaderString()); + } + + protected function getMaxValueHeaderString():string { + if ( $this->options->netpbmMaxValue >= 65536 || $this->options->netpbmMaxValue <= 0 ) { + throw new UnexpectedValueException( "NetpbmMaxValue should be between 0 and 65536" ); + } + return $this->options->netpbmMaxValue . "\n"; + } + + abstract protected function getBodyASCII():string; + abstract protected function getBodyBinary():string; + + public function dump(string|null $file = null):string{ + $qrString = $this->getHeader(); + + $qrString .= $this->options->netpbmPlain + ? $this->getBodyASCII() + : $this->getBodyBinary(); + + $this->saveToFile($qrString, $file); + + if($this->options->outputBase64){ + $qrString = $this->toBase64DataURI($qrString); + } + + return $qrString; + } + +} diff --git a/src/Output/QRNetpbmBitmap.php b/src/Output/QRNetpbmBitmap.php new file mode 100644 index 000000000..f5fd9deb9 --- /dev/null +++ b/src/Output/QRNetpbmBitmap.php @@ -0,0 +1,98 @@ +matrix->getBooleanMatrix() as $row){ + $line = ''; + + foreach($row as $isDark){ + $line .= str_repeat($isDark ? '1' : '0', $this->scale); + } + // Lines should not be longer than 70 chars + $line = implode("\n", str_split($line,70))."\n"; + + $body .= str_repeat($line, $this->scale); + } + + return $body; + } + + protected function getBodyBinary():string{ + $body = ''; + + foreach($this->matrix->getBooleanMatrix() as $row){ + $rowdata = array_fill(0, (int)ceil($this->length / 8), 0); + $byte = 0; + $bit = 0b10000000; + + foreach($row as $isDark){ + for($i = 0; $i < $this->scale; $i++){ + if($bit <= 0){ + $bit = 0b10000000; + $byte++; + } + if($isDark){ + $rowdata[$byte] |= $bit; + } + $bit >>= 1; + } + } + + $rowdataString = pack('C*', ...$rowdata); + + $body .= str_repeat($rowdataString, $this->scale); + } + return $body; + } +} diff --git a/src/Output/QRNetpbmGraymap.php b/src/Output/QRNetpbmGraymap.php new file mode 100644 index 000000000..cf3eb1ade --- /dev/null +++ b/src/Output/QRNetpbmGraymap.php @@ -0,0 +1,89 @@ + $this->options->netpbmMaxValue ) + return $this->options->netpbmMaxValue; + return $value; + } + + protected function getDefaultModuleValue(bool $isDark):mixed{ + return $isDark ? 0 : $this->options->netpbmMaxValue; + } + + public static function moduleValueIsValid(mixed $value):bool{ + // Since this is called statically, we cannot know what $this->options->netpbmMaxValue will be. + return is_int($value) && 0<$value && $value<65536; + } + + protected function getBodyASCII():string{ + $body = ''; + $maxLength = 70 - strlen(' ' . (string)$this->options->netpbmMaxValue) + 1; + + foreach($this->matrix->getMatrix() as $row){ + $line = ''; + $rowString = ''; + foreach ($row as $module) { + for ($i=0;$i<$this->scale;$i++) { + $line .= $this->getModuleValue($module); + if (strlen($line) >= $maxLength) { + $rowString .= $line . "\n"; + $line = ''; + } else { + $line .= ' '; + } + } + } + $body .= str_repeat(trim($rowString . $line)."\n", $this->scale); + } + + return $body; + } + + protected function getBodyBinary():string{ + $body = ''; + foreach ($this->matrix->getMatrix() as $row) { + $line = ''; + foreach($row as $module) { + $line .= str_repeat($this->pack($this->getModuleValue($module)), $this->scale); + } + $body .= str_repeat($line, $this->scale); + } + return $body; + } + protected function pack( int $moduleValue ) { + if ( $this->options->netpbmMaxValue > 255 ) { + return pack('n', $moduleValue); + } + return pack('C', $moduleValue); + } +} diff --git a/src/QROptionsTrait.php b/src/QROptionsTrait.php index 6b7c2a73f..18baad3d9 100644 --- a/src/QROptionsTrait.php +++ b/src/QROptionsTrait.php @@ -395,6 +395,26 @@ trait QROptionsTrait{ */ protected string $imagickFormat = 'png32'; + /* + * QRNetpbm settings + */ + + /** + * Use netpbm plain (ascii) output instead of binary output + * + * @see https://netpbm.sourceforge.net/doc/#commonoptions + * @see https://github.com/chillerlan/php-qrcode/pull/323 + */ + protected bool $netpbmPlain = false; + + /** + * Netpbm max value ( For graymap and pixmap output formats ) + * Should be between 0 and 65536. + * + * @see https://netpbm.sourceforge.net/doc/pgm.html + * @see https://netpbm.sourceforge.net/doc/ppm.html + */ + protected int $netpbmMaxValue = 255; /* * Common markup output settings (QRMarkupSVG, QRMarkupHTML) From a9eda61e5ff05fbbd75b685fed66ba5220a4691f Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:27:44 +0100 Subject: [PATCH 13/17] Add support for Pixmap and fix codestyle --- src/Output/QRNetpbmAbstract.php | 17 +++--- src/Output/QRNetpbmBitmap.php | 5 +- src/Output/QRNetpbmGraymap.php | 28 ++++----- src/Output/QRNetpbmPixmap.php | 100 ++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 src/Output/QRNetpbmPixmap.php diff --git a/src/Output/QRNetpbmAbstract.php b/src/Output/QRNetpbmAbstract.php index aac851eee..e38b38c9b 100644 --- a/src/Output/QRNetpbmAbstract.php +++ b/src/Output/QRNetpbmAbstract.php @@ -14,10 +14,6 @@ use chillerlan\QRCode\Output\QROutputAbstract; use UnexpectedValueException; use function sprintf; -use function str_split; -use function pack; -use function bindec; -use function str_repeat; abstract class QRNetpbmAbstract extends QROutputAbstract{ @@ -31,14 +27,21 @@ protected function getMagicNumber():string{ protected function getHeader():string{ $comment = 'created by https://github.com/chillerlan/php-qrcode'; - return sprintf("%s\n# %s\n%s %s\n%s", $this->getMagicNumber(), $comment, $this->length, $this->length, $this->getMaxValueHeaderString()); + return sprintf( + "%s\n# %s\n%s %s\n%s", + $this->getMagicNumber(), + $comment, + $this->length, + $this->length, + $this->getMaxValueHeaderString(), + ); } protected function getMaxValueHeaderString():string { if ( $this->options->netpbmMaxValue >= 65536 || $this->options->netpbmMaxValue <= 0 ) { - throw new UnexpectedValueException( "NetpbmMaxValue should be between 0 and 65536" ); + throw new UnexpectedValueException( 'NetpbmMaxValue should be between 0 and 65536' ); } - return $this->options->netpbmMaxValue . "\n"; + return $this->options->netpbmMaxValue."\n"; } abstract protected function getBodyASCII():string; diff --git a/src/Output/QRNetpbmBitmap.php b/src/Output/QRNetpbmBitmap.php index f5fd9deb9..db133264b 100644 --- a/src/Output/QRNetpbmBitmap.php +++ b/src/Output/QRNetpbmBitmap.php @@ -11,13 +11,10 @@ namespace chillerlan\QRCode\Output; -use chillerlan\QRCode\Output\QROutputAbstract; use UnexpectedValueException; use function is_bool; -use function str_pad; use function str_split; use function pack; -use function bindec; use function str_repeat; class QRNetpbmBitmap extends QRNetpbmAbstract{ @@ -29,7 +26,7 @@ class QRNetpbmBitmap extends QRNetpbmAbstract{ protected function prepareModuleValue(mixed $value):mixed{ if ( !is_bool( $value ) ) { - throw new UnexpectedValueException( "Bitmap expected bool modules" ); + throw new UnexpectedValueException( 'Bitmap expected bool modules' ); } return $value; } diff --git a/src/Output/QRNetpbmGraymap.php b/src/Output/QRNetpbmGraymap.php index cf3eb1ade..f5c26e5a8 100644 --- a/src/Output/QRNetpbmGraymap.php +++ b/src/Output/QRNetpbmGraymap.php @@ -11,13 +11,7 @@ namespace chillerlan\QRCode\Output; -use chillerlan\QRCode\Output\QROutputAbstract; -use UnexpectedValueException; -use function is_bool; -use function str_pad; -use function str_split; use function pack; -use function bindec; use function str_repeat; class QRNetpbmGraymap extends QRNetpbmAbstract{ @@ -29,11 +23,13 @@ class QRNetpbmGraymap extends QRNetpbmAbstract{ protected function prepareModuleValue(mixed $value):mixed{ $value = intval( $value ); - if ( $value < 0 ) + if ( $value < 0 ) { return 0; - if ( $value > $this->options->netpbmMaxValue ) + } + if ( $value > $this->options->netpbmMaxValue ) { return $this->options->netpbmMaxValue; - return $value; + } + return $value; } protected function getDefaultModuleValue(bool $isDark):mixed{ @@ -42,29 +38,29 @@ protected function getDefaultModuleValue(bool $isDark):mixed{ public static function moduleValueIsValid(mixed $value):bool{ // Since this is called statically, we cannot know what $this->options->netpbmMaxValue will be. - return is_int($value) && 0<$value && $value<65536; + return is_int($value) && 0 < $value && $value < 65536; } protected function getBodyASCII():string{ $body = ''; - $maxLength = 70 - strlen(' ' . (string)$this->options->netpbmMaxValue) + 1; + $maxLength = (70 - strlen(' '.(string)$this->options->netpbmMaxValue) + 1); - foreach($this->matrix->getMatrix() as $row){ + foreach($this->matrix->getMatrix() as $row){ $line = ''; $rowString = ''; foreach ($row as $module) { - for ($i=0;$i<$this->scale;$i++) { + for ($i = 0; $i < $this->scale; $i++) { $line .= $this->getModuleValue($module); if (strlen($line) >= $maxLength) { - $rowString .= $line . "\n"; + $rowString .= $line."\n"; $line = ''; } else { $line .= ' '; } } } - $body .= str_repeat(trim($rowString . $line)."\n", $this->scale); - } + $body .= str_repeat(trim($rowString.$line)."\n", $this->scale); + } return $body; } diff --git a/src/Output/QRNetpbmPixmap.php b/src/Output/QRNetpbmPixmap.php new file mode 100644 index 000000000..6f156f095 --- /dev/null +++ b/src/Output/QRNetpbmPixmap.php @@ -0,0 +1,100 @@ + $this->options->netpbmMaxValue ) { + $rgbValue = $this->options->netpbmMaxValue; + } + } + return $value; + } + + protected function getDefaultModuleValue(bool $isDark):mixed{ + return array_fill(0, 3, $isDark ? 0 : $this->options->netpbmMaxValue); + } + + public static function moduleValueIsValid(mixed $value):bool{ + if ( !is_array($value) || count($value) !== 3 ) { + return false; + } + foreach ($value as $rgbVal) { + // Since this is called statically, we cannot know what $this->options->netpbmMaxValue will be. + if (!is_int($value) || 0 >= $value || $value >= 65536) { + return false; + } + } + return true; + } + + protected function getBodyASCII():string{ + $body = ''; + $maxLength = (70 - strlen(' '.(string)$this->options->netpbmMaxValue) + 1); + + foreach($this->matrix->getMatrix() as $row){ + $line = ''; + $rowString = ''; + foreach ($row as $module) { + for ($i = 0; $i < $this->scale; $i++) { + foreach($this->getModuleValue($module) as $rgbValue) { + $line .= $rgbValue; + if (strlen($line) >= $maxLength) { + $rowString .= $line."\n"; + $line = ''; + } else { + $line .= ' '; + } + } + } + } + $body .= str_repeat(trim($rowString.$line)."\n", $this->scale); + } + + return $body; + } + + protected function getBodyBinary():string{ + $body = ''; + foreach ($this->matrix->getMatrix() as $row) { + $line = ''; + foreach($row as $module) { + $line .= str_repeat($this->pack($this->getModuleValue($module)), $this->scale); + } + $body .= str_repeat($line, $this->scale); + } + return $body; + } + protected function pack( array $moduleValue ) { + if ( $this->options->netpbmMaxValue > 255 ) { + return pack('n*', ...$moduleValue); + } + return pack('C*', ...$moduleValue); + } +} From ce85a5020e855b84682fb759e91a6bb3cef22409 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:29:19 +0100 Subject: [PATCH 14/17] Remove now obsolete classes --- src/Output/QRNetpbmBitmapAbstract.php | 62 ----------------------- src/Output/QRNetpbmBitmapAscii.php | 20 -------- src/Output/QRNetpbmBitmapBinary.php | 38 -------------- tests/Output/QRNetpbmBitmapAsciiTest.php | 58 --------------------- tests/Output/QRNetpbmBitmapBinaryTest.php | 57 --------------------- 5 files changed, 235 deletions(-) delete mode 100644 src/Output/QRNetpbmBitmapAbstract.php delete mode 100644 src/Output/QRNetpbmBitmapAscii.php delete mode 100644 src/Output/QRNetpbmBitmapBinary.php delete mode 100644 tests/Output/QRNetpbmBitmapAsciiTest.php delete mode 100644 tests/Output/QRNetpbmBitmapBinaryTest.php diff --git a/src/Output/QRNetpbmBitmapAbstract.php b/src/Output/QRNetpbmBitmapAbstract.php deleted file mode 100644 index 7ce4ab818..000000000 --- a/src/Output/QRNetpbmBitmapAbstract.php +++ /dev/null @@ -1,62 +0,0 @@ -matrix->getBooleanMatrix() as $row) { - $line = ''; - foreach ($row as $isDark) { - $line .= str_repeat( $isDark ? '1' : '0', $this->scale ); - } - $line .= "\n"; - $body .= str_repeat( $line, $this->scale ); - } - return trim($body,"\n"); - } - - public function dump( string|null $file = null ): mixed { - $qrString = $this->getHeader()."\n" - .$this->length.' '.$this->length."\n".$this->getBody(); - - $this->saveToFile( $qrString, $file ); - - if ( $this->options->outputBase64 ) { - $qrString = $this->toBase64DataURI( $qrString ); - } - - return $qrString; - } -} diff --git a/src/Output/QRNetpbmBitmapAscii.php b/src/Output/QRNetpbmBitmapAscii.php deleted file mode 100644 index ad4217735..000000000 --- a/src/Output/QRNetpbmBitmapAscii.php +++ /dev/null @@ -1,20 +0,0 @@ -asciiBinToBinary( $row ); - } - return $body; - } - - private function asciiBinToBinary( string $asciiBin ): string { - $binaryString = ''; - foreach(str_split( $asciiBin, 8 ) as $currentChunk) { - $currentChunk = str_pad( $currentChunk, 8, '0' ); - $binaryString .= pack( 'C', bindec( $currentChunk ) ); - } - return $binaryString; - } -} diff --git a/tests/Output/QRNetpbmBitmapAsciiTest.php b/tests/Output/QRNetpbmBitmapAsciiTest.php deleted file mode 100644 index 8fedfedc2..000000000 --- a/tests/Output/QRNetpbmBitmapAsciiTest.php +++ /dev/null @@ -1,58 +0,0 @@ - - */ - public static function moduleValueProvider():array{ - return [ - 'invalid: wrong type: array' => [[], false], - 'invalid: wrong type: string' => ['abc', false], - 'valid: true' => [true, true], - 'valid: false' => [false, true], - ]; - } - - #[Test] - public function setModuleValues():void{ - $this->options->moduleValues = [ - // data - QRMatrix::M_DATA_DARK => true, - QRMatrix::M_DATA => false, - ]; - - $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); - $data = $this->outputInterface->dump(); - - $this::assertStringContainsString('1', $data); - $this::assertStringContainsString('0', $data); - } -} diff --git a/tests/Output/QRNetpbmBitmapBinaryTest.php b/tests/Output/QRNetpbmBitmapBinaryTest.php deleted file mode 100644 index 8a74cb0a7..000000000 --- a/tests/Output/QRNetpbmBitmapBinaryTest.php +++ /dev/null @@ -1,57 +0,0 @@ - - */ - public static function moduleValueProvider():array{ - return [ - 'invalid: wrong type: array' => [[], false], - 'invalid: wrong type: string' => ['abc', false], - 'valid: true' => [true, true], - 'valid: false' => [false, true], - ]; - } - - #[Test] - public function setModuleValues():void{ - $this->options->moduleValues = [ - // data - QRMatrix::M_DATA_DARK => true, - QRMatrix::M_DATA => false, - ]; - - $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); - $data = $this->outputInterface->dump(); - - $this::assertStringContainsString("\0", $data); - } -} From 5182a50dfe46263bafe31a07255f3a4222b946a4 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Wed, 24 Dec 2025 17:09:55 +0100 Subject: [PATCH 15/17] Bugfixes and review comment changes --- src/Output/QRNetpbmGraymap.php | 18 ++++++++---------- src/Output/QRNetpbmPixmap.php | 23 ++++++++++++----------- src/QROptionsTrait.php | 5 +++++ 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/Output/QRNetpbmGraymap.php b/src/Output/QRNetpbmGraymap.php index f5c26e5a8..c9d67a8cc 100644 --- a/src/Output/QRNetpbmGraymap.php +++ b/src/Output/QRNetpbmGraymap.php @@ -37,8 +37,7 @@ protected function getDefaultModuleValue(bool $isDark):mixed{ } public static function moduleValueIsValid(mixed $value):bool{ - // Since this is called statically, we cannot know what $this->options->netpbmMaxValue will be. - return is_int($value) && 0 < $value && $value < 65536; + return is_int($value) && $value >= 0 && $value < 65536; } protected function getBodyASCII():string{ @@ -59,27 +58,26 @@ protected function getBodyASCII():string{ } } } - $body .= str_repeat(trim($rowString.$line)."\n", $this->scale); + $body .= str_repeat(trim($rowString.$line)."\n", $this->scale); } return $body; } protected function getBodyBinary():string{ + $format = $this->options->netpbmMaxValue > 255 ? 'n' : 'C'; + $body = ''; foreach ($this->matrix->getMatrix() as $row) { $line = ''; foreach($row as $module) { - $line .= str_repeat($this->pack($this->getModuleValue($module)), $this->scale); + $line .= str_repeat( + pack($format,$this->getModuleValue($module)), + $this->scale + ); } $body .= str_repeat($line, $this->scale); } return $body; } - protected function pack( int $moduleValue ) { - if ( $this->options->netpbmMaxValue > 255 ) { - return pack('n', $moduleValue); - } - return pack('C', $moduleValue); - } } diff --git a/src/Output/QRNetpbmPixmap.php b/src/Output/QRNetpbmPixmap.php index 6f156f095..af258cb2b 100644 --- a/src/Output/QRNetpbmPixmap.php +++ b/src/Output/QRNetpbmPixmap.php @@ -25,7 +25,8 @@ protected function prepareModuleValue(mixed $value):mixed{ if (!is_array($value) || count($value) !== 3){ throw new UnexpectedValueException( 'Module value should be array of length 3 for NetpbmPixmap' ); } - foreach($value as &$rgbValue) { + $newValue = []; + foreach($value as $rgbValue) { $rgbValue = intval( $rgbValue ); if ( $rgbValue < 0 ) { $rgbValue = 0; @@ -33,8 +34,9 @@ protected function prepareModuleValue(mixed $value):mixed{ if ( $rgbValue > $this->options->netpbmMaxValue ) { $rgbValue = $this->options->netpbmMaxValue; } + $newValue []= $rgbValue; } - return $value; + return $newValue; } protected function getDefaultModuleValue(bool $isDark):mixed{ @@ -46,8 +48,7 @@ public static function moduleValueIsValid(mixed $value):bool{ return false; } foreach ($value as $rgbVal) { - // Since this is called statically, we cannot know what $this->options->netpbmMaxValue will be. - if (!is_int($value) || 0 >= $value || $value >= 65536) { + if (!is_int($rgbVal) || $rgbVal < 0 || $rgbVal >= 65536) { return false; } } @@ -81,20 +82,20 @@ protected function getBodyASCII():string{ } protected function getBodyBinary():string{ + $format = $this->options->netpbmMaxValue > 255 ? 'n*' : 'C*'; $body = ''; foreach ($this->matrix->getMatrix() as $row) { $line = ''; foreach($row as $module) { - $line .= str_repeat($this->pack($this->getModuleValue($module)), $this->scale); + $m = $this->getModuleValue($module); + $f = pack($format, ...$m); + $line .= str_repeat( + $f, + $this->scale + ); } $body .= str_repeat($line, $this->scale); } return $body; } - protected function pack( array $moduleValue ) { - if ( $this->options->netpbmMaxValue > 255 ) { - return pack('n*', ...$moduleValue); - } - return pack('C*', ...$moduleValue); - } } diff --git a/src/QROptionsTrait.php b/src/QROptionsTrait.php index 18baad3d9..520ce2365 100644 --- a/src/QROptionsTrait.php +++ b/src/QROptionsTrait.php @@ -395,6 +395,7 @@ trait QROptionsTrait{ */ protected string $imagickFormat = 'png32'; + /* * QRNetpbm settings */ @@ -636,4 +637,8 @@ protected function set_circleRadius(float $circleRadius):void{ $this->circleRadius = max(0.1, min(0.75, $circleRadius)); } + protected function set_netpbmMaxValue(int $netpbmMaxValue):void{ + $this->netpbmMaxValue = min(65535,max(1,$netpbmMaxValue)); + } + } From 2bb89f881df05a291c144ecff39fe94e5879fc90 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Wed, 24 Dec 2025 18:11:40 +0100 Subject: [PATCH 16/17] Add tests --- tests/Output/QRNetpbmBitmapTest.php | 66 ++++++++++++++++++++++++ tests/Output/QRNetpbmGraymapTest.php | 69 +++++++++++++++++++++++++ tests/Output/QRNetpbmPixmapTest.php | 77 ++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 tests/Output/QRNetpbmBitmapTest.php create mode 100644 tests/Output/QRNetpbmGraymapTest.php create mode 100644 tests/Output/QRNetpbmPixmapTest.php diff --git a/tests/Output/QRNetpbmBitmapTest.php b/tests/Output/QRNetpbmBitmapTest.php new file mode 100644 index 000000000..4ea34acc4 --- /dev/null +++ b/tests/Output/QRNetpbmBitmapTest.php @@ -0,0 +1,66 @@ + + */ + public static function moduleValueProvider():array{ + return [ + 'invalid: wrong type: array' => [[], false], + 'invalid: wrong type: string' => ['abc', false], + 'valid: true' => [true, true], + 'valid: false' => [false, true], + ]; + } + + #[Test] + public function setModuleValues():void{ + $this->options->moduleValues = [ + // data + QRMatrix::M_DATA_DARK => true, + QRMatrix::M_DATA => false, + ]; + + $this->options->outputBase64 = false; + $this->options->netpbmPlain = true; + $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); + $data = $this->outputInterface->dump(); + + $this::assertStringContainsString('1', $data); + $this::assertStringContainsString('0', $data); + } + + #[Test] + public function renderToCacheFilePlain() { + $this->options->netpbmPlain = true; + $this->renderToCacheFile(); + } +} diff --git a/tests/Output/QRNetpbmGraymapTest.php b/tests/Output/QRNetpbmGraymapTest.php new file mode 100644 index 000000000..93c630b2d --- /dev/null +++ b/tests/Output/QRNetpbmGraymapTest.php @@ -0,0 +1,69 @@ + + */ + public static function moduleValueProvider():array{ + return [ + 'invalid: wrong type: array' => [[], false], + 'invalid: wrong type: string' => ['abc', false], + 'invalid: wrong type: bool' => [true, false], + 'invalid: out of bounds: negative' => [-1, false], + 'invalid: out of bounds: too big' => [70000, false], + 'valid: dark' => [0, true], + 'valid: light' => [65000, true], + ]; + } + + #[Test] + public function setModuleValues():void{ + $this->options->moduleValues = [ + // data + QRMatrix::M_DATA_DARK => 33, + QRMatrix::M_DATA => 99, + ]; + + $this->options->netpbmPlain = true; + $this->options->outputBase64 = false; + $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); + $data = $this->outputInterface->dump(); + + $this::assertStringContainsString('33', $data); + $this::assertStringContainsString('99', $data); + } + + #[Test] + public function renderToCacheFilePlain() { + $this->options->netpbmPlain = true; + $this->renderToCacheFile(); + } +} diff --git a/tests/Output/QRNetpbmPixmapTest.php b/tests/Output/QRNetpbmPixmapTest.php new file mode 100644 index 000000000..edb418933 --- /dev/null +++ b/tests/Output/QRNetpbmPixmapTest.php @@ -0,0 +1,77 @@ + + */ + public static function moduleValueProvider():array{ + return [ + 'invalid: wrong type: string' => ['abc', false], + 'invalid: wrong type: bool' => [true, false], + 'invalid: wrong type: integer' => [0, false], + 'invalid: wrong size: empty' => [[], false], + 'invalid: wrong size: 1' => [[1], false], + 'invalid: wrong size: too big' => [[1,2,3,4], false], + 'invalid: value out of bounds: negative' => [[-1,0,0], false], + 'invalid: value out of bounds: too big' => [[0,70000,0], false], + 'valid: dark' => [[0,0,0], true], + 'valid: colored' => [[0,150,3000], true], + 'valid: white' => [[255,255,255], true], + ]; + } + + #[Test] + public function setModuleValues():void{ + $this->options->moduleValues = [ + // data + QRMatrix::M_DATA_DARK => [11,12,13], + QRMatrix::M_DATA => [250,251,252], + ]; + + $this->options->netpbmPlain = true; + $this->options->outputBase64 = false; + $this->outputInterface = $this->getOutputInterface($this->options, $this->matrix); + $data = $this->outputInterface->dump(); + + $this::assertStringContainsString('11', $data); + $this::assertStringContainsString('12', $data); + $this::assertStringContainsString('13', $data); + $this::assertStringContainsString('250', $data); + $this::assertStringContainsString('251', $data); + $this::assertStringContainsString('252', $data); + } + + #[Test] + public function renderToCacheFilePlain() { + $this->options->netpbmPlain = true; + $this->renderToCacheFile(); + } +} From e6f33195696c03b0d7878c9eec34df88f4687e14 Mon Sep 17 00:00:00 2001 From: wgevaert <56224691+wgevaert@users.noreply.github.com> Date: Wed, 24 Dec 2025 23:33:16 +0100 Subject: [PATCH 17/17] Remove superfluous check on maxvalue --- src/Output/QRNetpbmAbstract.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Output/QRNetpbmAbstract.php b/src/Output/QRNetpbmAbstract.php index e38b38c9b..af4074527 100644 --- a/src/Output/QRNetpbmAbstract.php +++ b/src/Output/QRNetpbmAbstract.php @@ -38,9 +38,6 @@ protected function getHeader():string{ } protected function getMaxValueHeaderString():string { - if ( $this->options->netpbmMaxValue >= 65536 || $this->options->netpbmMaxValue <= 0 ) { - throw new UnexpectedValueException( 'NetpbmMaxValue should be between 0 and 65536' ); - } return $this->options->netpbmMaxValue."\n"; }