-
-
Notifications
You must be signed in to change notification settings - Fork 323
Add QRPbm output format for Portable BitMap outputs #323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Hey, I haven't had a close look at the specification yet, but I see that there are multiple formats, so I'm thinking what the best way to abstract is ( |
src/Output/QRPbm.php
Outdated
|
|
||
| class QRPbm extends QROutputAbstract { | ||
|
|
||
| private const LIGHT_DARK = [ '0', '1' ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary when we handle integers instead.
src/Output/QRPbm.php
Outdated
| private const LIGHT_DARK = [ '0', '1' ]; | ||
|
|
||
| protected function prepareModuleValue( mixed $value ): mixed { | ||
| return $value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method should cast/clamp the allowed values, and the return type can be narrowed to int then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I will use bool instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking beyond the implementation of the b/w PBM format - int is certainly the more flexible and consistent choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The b/w PBM format is fundamentally different from the other formats: In PBM, 0/false is white, in PGM/PPM, 0 is black. To prevent confusion, I think I will handle PBM as a special case and use booleans for those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case you can just completely ignore the module values setting. If you're going to abstract the classes as I recommended, you can override the setModuleValues() method from the parent to do nothing and be done with it for this particular format.
src/Output/QRPbm.php
Outdated
| } | ||
|
|
||
| protected function getDefaultModuleValue( bool $isDark ): mixed { | ||
| return $isDark ? self::LIGHT_DARK[1] : self::LIGHT_DARK[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can return (int)$isDark for the b/w PBM, 0 and 255 (or 65535?) for grayscale PGM, and for the RGB formats PPM/PAM it could return an array similar to the GD output class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not entirely true; For P1 image format, 0 is white so we'd have to return (int)!$isDark instead. Confusingly, for P2 image format, 0 is black so we'd have to return $isDark ? 0 : $this->getMaxValue().
src/Output/QRPbm.php
Outdated
| } | ||
|
|
||
| public static function moduleValueIsValid( mixed $value): bool { | ||
| return is_string( $value ) && in_array( $value, self::LIGHT_DARK, true ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A simple is_int() here would suffice for grayscale, for RGB formats see
php-qrcode/src/Output/RGBArrayModuleValueTrait.php
Lines 28 to 48 in c55c64d
| public static function moduleValueIsValid(mixed $value):bool{ | |
| if(!is_array($value) || count($value) < 3){ | |
| return false; | |
| } | |
| // check the first 3 values of the array | |
| foreach(array_values($value) as $i => $val){ | |
| if($i > 2){ | |
| break; | |
| } | |
| if(!is_numeric($val)){ | |
| return false; | |
| } | |
| } | |
| return true; | |
| } |
src/Output/QRPbm.php
Outdated
| } | ||
|
|
||
| public function dump( string|null $file = null ): mixed { | ||
| $size = $this->matrix->getSize(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This call is unnecessary, the size is already available in the parent class as $this->size.
src/Output/QRPbm.php
Outdated
| public function dump( string|null $file = null ): mixed { | ||
| $size = $this->matrix->getSize(); | ||
| $qrString = "P1\n" | ||
| .$size.' '.$size."\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation does not yet take into account the module scaling. From what I understand, the Netpbm formats are raster images, so the output size would be 1 pixel per QR module, which is very small. The parent class also holds the calculated matrix size as $this->length as well as the current scale as $this->scale, which could be used to repeat the rows/columns accordingly. The return type can be narrowed to string.
src/Output/QRPbm.php
Outdated
| .$size.' '.$size."\n"; | ||
| foreach($this->matrix->getBooleanMatrix() as $row) { | ||
| foreach ($row as $isDark) { | ||
| $qrString .= $this->getDefaultModuleValue( $isDark ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method getDefaultModuleValue() is supposed to only be called when you really need the default value - while it doesn't matter in this case, it can lead to unexpected results in extended classes. In the case of the b/w PBM format, a simple (int)$isDark will suffice.
I'm also thinking of adding an option like netpbmBinary or so, to allow saving in the binary format as the raw plaintext format can grow excessively large.
src/Output/QRPbm.php
Outdated
| if ( is_string($file) ) { | ||
| $writeResult = file_put_contents($file,$qrString); | ||
| if ( $writeResult === false ) { | ||
| throw new QRCodeOutputException('Cannot write data to cache file: '.$file); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire block can be replaced by $this->saveToFile($qrString, $file); from the parent - it does all necessary error handling.
tests/Output/QRPbmTest.php
Outdated
| $this->assertStringContainsString('1', $data); | ||
| $this->assertStringContainsString('0', $data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You had this right before, we do call static methods statically :)
|
|
||
| namespace chillerlan\QRCode\Output; | ||
|
|
||
| use chillerlan\QRCode\Output\QRNetpbmBitmapAbstract; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need separate classes for ASCII and binary here - it's quite excessive for a file format that's barely used. My initial recommendation was an abstract class for all netpbm based formats with child classes for each PBM, PGM, PPM and PAM. The conversion to binary can be done in the loop that creates the body (avoiding to loop over the same data 3 times), with a switch in the options, same goes for the header.
Proposed changes
See #322
Types of changes
What types of changes does your code introduce?
Checklist:
Any dependent changes have been merged and published in downstream modulesThere are none