Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Usage/Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
55 changes: 55 additions & 0 deletions src/Output/QRNetpbmBitmapAbstract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Class QRPbm
*
* @created 11.12.2025
* @author wgevaert
* @copyright 2025 wgevaert
* @license MIT
*/
declare(strict_types=1);

namespace chillerlan\QRCode\Output;

use chillerlan\QRCode\Output\QROutputAbstract;

abstract class QRNetpbmBitmapAbstract extends QROutputAbstract {

protected function prepareModuleValue( mixed $value ): mixed {
if ( !is_bool( $value ) ) {
throw new UnexpectedValueException( "Expected boolean module value" );
}
return $value;
}

protected function getDefaultModuleValue( bool $isDark ): bool {
return $isDark;
}

public static function moduleValueIsValid( mixed $value ): bool {
return is_bool( $value );
}

abstract protected function getHeader(): string;

protected function getBody(): string {
$body = '';
foreach ( $this->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;
}
}
20 changes: 20 additions & 0 deletions src/Output/QRNetpbmBitmapAscii.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php
/**
* Class QRPbm
*
* @created 11.12.2025
* @author wgevaert
* @copyright 2025 wgevaert
* @license MIT
*/
declare(strict_types=1);

namespace chillerlan\QRCode\Output;

use chillerlan\QRCode\Output\QRNetpbmBitmapAbstract;

class QRNetpbmBitmapAscii extends QRNetpbmBitmapAbstract {
protected function getHeader(): string {
return 'P1';
}
}
38 changes: 38 additions & 0 deletions src/Output/QRNetpbmBitmapBinary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
/**
* Class QRPbm
*
* @created 11.12.2025
* @author wgevaert
* @copyright 2025 wgevaert
* @license MIT
*/
declare(strict_types=1);

namespace chillerlan\QRCode\Output;

use chillerlan\QRCode\Output\QRNetpbmBitmapAbstract;

class QRNetpbmBitmapBinary extends QRNetpbmBitmapAbstract {
protected function getHeader(): string {
return 'P4';
}

protected function getBody(): string {
$asciiBody = parent::getBody();
$body = '';
foreach ( explode("\n", $asciiBody) as $row ) {
$body .= $this->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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand All @@ -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);
}
}
57 changes: 57 additions & 0 deletions tests/Output/QRNetpbmBitmapBinaryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
/**
* Class QRPbmTest
*
* @created 11.12.2025
* @author wgevaert
* @copyright 2025 wgevaert
* @license MIT
*/
declare(strict_types=1);

namespace chillerlan\QRCodeTest\Output;

use chillerlan\QRCode\QROptions;
use chillerlan\QRCode\Data\QRMatrix;
use chillerlan\QRCode\Output\{QROutputInterface, QRNetpbmBitmapBinary};
use chillerlan\Settings\SettingsContainerInterface;
use PHPUnit\Framework\Attributes\Test;

/**
* Tests the QRNetpbmBitmapBinary output class
*/
final class QRNetpbmBitmapBinaryTest extends QROutputTestAbstract {

protected function getOutputInterface(
SettingsContainerInterface|QROptions $options,
QRMatrix $matrix,
):QROutputInterface{
return new QRNetpbmBitmapBinary($options, $matrix);
}

/**
* @phpstan-return array<string, array{0: mixed, 1: bool}>
*/
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);
}
}