-
-
Notifications
You must be signed in to change notification settings - Fork 302
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
97ee549
commit 171f0d9
Showing
7 changed files
with
244 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
# Module values | ||
|
||
## Basics | ||
|
||
The QR Code matrix is a 2-dimensional array of numerical values that hold a bit mask for | ||
each QR pixel ("module" as per specification), the so-called "module type" or `$M_TYPE`, which is represented by | ||
[the `QRMatrix::M_*` constants](https://chillerlan.github.io/php-qrcode/classes/chillerlan-QRCode-Data-QRMatrix.html#toc-constants). | ||
You can assign different values for the several [function patterns](../Appendix/Terminology.md#function-patterns) to colorize them or even draw pixel-art. | ||
|
||
|
||
## Assigning values | ||
|
||
To map the values and properly render the modules for the given `QROutputInterface`, it may be necessary to overwrite the | ||
[default values](https://chillerlan.github.io/php-qrcode/classes/chillerlan-QRCode-Output-QROutputInterface.html#constant_DEFAULT_MODULE_VALUES), | ||
that are replaced by the user defined values in `QROptions::$moduleValues` during the render process. | ||
|
||
The map of `QRMatrix::M_*` constants => default values looks similar to the following: | ||
|
||
```php | ||
$options->moduleValues = [ | ||
// light | ||
QRMatrix::M_NULL => false, | ||
QRMatrix::M_DARKMODULE_LIGHT => false, | ||
QRMatrix::M_DATA => false, | ||
QRMatrix::M_FINDER => false, | ||
QRMatrix::M_SEPARATOR => false, | ||
QRMatrix::M_ALIGNMENT => false, | ||
QRMatrix::M_TIMING => false, | ||
QRMatrix::M_FORMAT => false, | ||
QRMatrix::M_VERSION => false, | ||
QRMatrix::M_QUIETZONE => false, | ||
QRMatrix::M_LOGO => false, | ||
QRMatrix::M_FINDER_DOT_LIGHT => false, | ||
QRMatrix::M_TEST => false, | ||
// dark | ||
QRMatrix::M_DARKMODULE => true, | ||
QRMatrix::M_DATA_DARK => true, | ||
QRMatrix::M_FINDER_DARK => true, | ||
QRMatrix::M_SEPARATOR_DARK => true, | ||
QRMatrix::M_ALIGNMENT_DARK => true, | ||
QRMatrix::M_TIMING_DARK => true, | ||
QRMatrix::M_FORMAT_DARK => true, | ||
QRMatrix::M_VERSION_DARK => true, | ||
QRMatrix::M_QUIETZONE_DARK => true, | ||
QRMatrix::M_LOGO_DARK => true, | ||
QRMatrix::M_FINDER_DOT => true, | ||
QRMatrix::M_TEST_DARK => true, | ||
]; | ||
``` | ||
|
||
Not all the module values need to be specified - missing values will be filled with the internal default values | ||
for `true` (dark) and `false` (light) respectively. The `QROutputInterface` inheritors implement a `moduleValueIsValid()` | ||
method that checks if the given value is valid for that particular class: | ||
|
||
```php | ||
// set an initial value that acts as default | ||
$dark = 'rgba(0, 0, 0, 0.5)'; | ||
|
||
// try to receive user input | ||
if(QRMarkupSVG::moduleValueIsValid($_GET['qr_dark'])){ | ||
// module values for HTML, SVG and other markup may need special treatment, | ||
// e.g. only accept hexadecimal values from user input | ||
// as moduleValueIsValid() just checks for the general syntax | ||
$dark = sanitize_user_input($_GET['qr_dark']); | ||
} | ||
|
||
$options->moduleValues = [ | ||
QRMatrix::M_DATA_DARK => $dark, | ||
QRMatrix::M_FINDER_DARK => $dark, | ||
QRMatrix::M_ALIGNMENT_DARK => $dark, | ||
QRMatrix::M_FINDER_DOT => $dark, | ||
]; | ||
``` | ||
|
||
The several output classes may need different substitute values (you can find examples [in the test `moduleValueProvider()` for each output class](https://github.com/chillerlan/php-qrcode/tree/main/tests/Output)): | ||
|
||
```php | ||
// for HTML, SVG and ImageMagick | ||
$options->moduleValues = [ | ||
QRMatrix::M_DATA => '#ffffff', | ||
QRMatrix::M_DATA_DARK => '#000000', | ||
// ... | ||
]; | ||
|
||
// for the GdImage, EPS and FPDF output types | ||
$options->moduleValues = [ | ||
QRMatrix::M_DATA => [255, 255, 255], | ||
QRMatrix::M_DATA_DARK => [0, 0, 0], | ||
// ... | ||
]; | ||
|
||
// for string/text output | ||
$options->moduleValues = [ | ||
QRMatrix::M_DATA => '░░', | ||
QRMatrix::M_DATA_DARK => '██', | ||
// ... | ||
]; | ||
``` | ||
|
||
|
||
## Handling in your own `QROutputInterface` | ||
|
||
### Setting module values | ||
|
||
[`QROutputAbstract::setModuleValues()`](https://chillerlan.github.io/php-qrcode/classes/chillerlan-QRCode-Output-QROutputAbstract.html#method_setModuleValues) | ||
calls the 3 abstract methods `moduleValueIsValid()`, `getModuleValue()` and `getDefaultModuleValue()` to fill the internal | ||
module value map with the values given via `QROptions::$moduleValues`: | ||
|
||
```php | ||
protected function setModuleValues():void{ | ||
|
||
foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ | ||
$value = ($this->options->moduleValues[$M_TYPE] ?? null); | ||
|
||
$this->moduleValues[$M_TYPE] = $this->moduleValueIsValid($value) | ||
? $this->getModuleValue($value) | ||
: $this->getDefaultModuleValue($defaultValue); | ||
} | ||
|
||
} | ||
``` | ||
|
||
In the following example we'll create these methods for the `GdImage` output. | ||
Since [`imagecolorallocate()`](https://www.php.net/manual/function.imagecolorallocate) and other GD functions accept 3 values | ||
for RGB color (or 4 in case of RGBA), we'll supply these as a array where each value is an integer between 0 and 255 (`[RRR, GGG, BBB, (, AAA)]`). | ||
|
||
First we need to validate the input: | ||
|
||
```php | ||
protected function moduleValueIsValid($value):bool{ | ||
|
||
// nowhere near valid | ||
if(!is_array($value) || count($value) !== 3){ | ||
return false; | ||
} | ||
|
||
// now iterate over the values | ||
foreach($value as $color){ | ||
|
||
// non-integers won't work | ||
if(!is_int($color)){ | ||
return false; | ||
} | ||
|
||
// a strict check - we could also just ignore outliers and clamp the values instead | ||
if($color < 0 || $color > 255){ | ||
return false; | ||
} | ||
} | ||
|
||
return true; // yay! | ||
} | ||
``` | ||
|
||
Now we can prepare the value: | ||
|
||
```php | ||
protected function getModuleValue($value):array{ | ||
// we call array_values() so we don't run into string-key related issues | ||
return array_map(fn(int $val):int => max(0, min(255, $val)), array_values($value)); | ||
} | ||
``` | ||
|
||
And finally we need to provide default values: | ||
|
||
```php | ||
protected function getDefaultModuleValue(bool $isDark):array{ | ||
return $isDark ? [0, 0, 0] : [255, 255, 255]; | ||
} | ||
``` | ||
|
||
Now that everything is ready and set, we can use the values in our GD functions: | ||
|
||
```php | ||
$color = imagecolorallocate($this->image, ...$this->moduleValues[$M_TYPE]); | ||
``` | ||
|
||
|
||
### Using the module values | ||
|
||
The state of the `$M_TYPE` is set with the `QRMatrix::IS_DARK` constant: | ||
|
||
```php | ||
// set to dark (true) with bitwise OR: | ||
$M_TYPE = ($M_TYPE | QRMatrix::IS_DARK); | ||
|
||
// set to light (false) with bitwise AND NOT | ||
$M_TYPE = ($M_TYPE & ~QRMatrix::IS_DARK); | ||
|
||
// toggle the opposite state with bitwise XOR | ||
$M_TYPE = ($M_TYPE ^ QRMatrix::IS_DARK); | ||
``` | ||
|
||
You can manually check whether the module is dark: | ||
|
||
```php | ||
($value & QRMatrix::IS_DARK) === QRMatrix::IS_DARK; | ||
``` | ||
|
||
However it is much more convenient to use the `QRMatrix` methods for that: | ||
|
||
```php | ||
for($y = 0; $y < $this->moduleCount; $y++){ // rows | ||
for($x = 0; $x < $this->moduleCount; $x++){ // columns | ||
// sets current module as dark (true) with the M_DATA type | ||
$this->matrix->set($x, $y, true, QRMatrix::M_DATA); | ||
|
||
// -> true (shortcut for checkType($x, $y, QRMatrix::IS_DARK)) | ||
$this->matrix->check($x, $y); | ||
|
||
// -> true (current module is of type M_DATA) | ||
$this->matrix->checkType($x, $y, QRMatrix::M_DATA); | ||
|
||
// -> true (current module is of type IS_DARK) | ||
$this->matrix->checkType($x, $y, QRMatrix::IS_DARK); | ||
|
||
// -> false, type is M_DATA | ||
$this->matrix->checkTypeIn($x, $y, [QRMatrix::M_FINDER_DARK, QRMatrix::M_ALIGNMENT]); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters