From 171f0d97dae580ac7b12e9812d01d7073ff497d5 Mon Sep 17 00:00:00 2001 From: smiley Date: Wed, 4 Oct 2023 23:17:29 +0200 Subject: [PATCH] :book: --- docs/Customizing/Module-Values.md | 221 +++++++++++++++++++++++++++ docs/Readme.md | 5 + docs/Usage/Advanced-usage.md | 2 +- docs/Usage/Configuration-settings.md | 7 +- docs/Usage/Overview.md | 2 +- docs/index.rst | 6 + src/QROptionsTrait.php | 5 +- 7 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 docs/Customizing/Module-Values.md diff --git a/docs/Customizing/Module-Values.md b/docs/Customizing/Module-Values.md new file mode 100644 index 000000000..d25de444e --- /dev/null +++ b/docs/Customizing/Module-Values.md @@ -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]); + } +} +``` diff --git a/docs/Readme.md b/docs/Readme.md index b45902819..259509ce4 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -64,6 +64,11 @@ The markdown sources for the [Read the Docs online manual](https://php-qrcode.re - [Configuration settings](./Usage/Configuration-settings.md) +### Customizing output + +- [Module values](./Customizing/Module-Values.md) + + ### Built-In Output Modules - [QREps](./Built-In-Output/QREps.md) diff --git a/docs/Usage/Advanced-usage.md b/docs/Usage/Advanced-usage.md index 7fbae1907..725bf9c41 100644 --- a/docs/Usage/Advanced-usage.md +++ b/docs/Usage/Advanced-usage.md @@ -3,7 +3,7 @@ ## Configuration via `QROptions` The [`QROptions`](https://github.com/chillerlan/php-qrcode/blob/main/src/QROptions.php) class is a container based on [chillerlan/php-settings-container](https://github.com/chillerlan/php-settings-container) that behaves similar to a [`\stdClass`](https://www.php.net/manual/class.stdclass) object, but with fixed properties. -A list with all available `QROptions` can be found under [cnfiguration settings](../Usage/Configuration-settings.md). +A list with all available `QROptions` can be found under [configuration settings](../Usage/Configuration-settings.md). ```php $options = new QROptions; diff --git a/docs/Usage/Configuration-settings.md b/docs/Usage/Configuration-settings.md index 3cb7a8104..169176ee3 100644 --- a/docs/Usage/Configuration-settings.md +++ b/docs/Usage/Configuration-settings.md @@ -389,6 +389,11 @@ Sets the value for the "preserveAspectRatio" on the `` element String substitute for dark +**See also:** + +- [en.wikipedia.org/wiki/Block_Elements](https://en.wikipedia.org/wiki/Block_Elements) +- [en.wikipedia.org/wiki/ANSI_escape_code#8-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) + ## textLight @@ -407,7 +412,7 @@ Whether to return matrix values in JSON as booleans or `$M_TYPE` integers ## fpdfMeasureUnit -Measurement unit for `FPDF` output: pt, mm, cm, in (defaults to "pt") +Measurement unit for `FPDF` output: `pt`, `mm`, `cm`, `in` (default: `pt`) **See also:** diff --git a/docs/Usage/Overview.md b/docs/Usage/Overview.md index 2b656bcb5..cbce5bdc4 100644 --- a/docs/Usage/Overview.md +++ b/docs/Usage/Overview.md @@ -1,6 +1,6 @@ # Overview -A PHP QR Code generator based on the [implementation by Kazuhiko Arase](https://github.com/kazuhikoarase/qrcode-generator), namespaced, cleaned up, improved and other stuff.
+A PHP QR Code generator based on the [implementation by Kazuhiko Arase](https://github.com/kazuhikoarase/qrcode-generator), namespaced, cleaned up, improved and other stuff. It also features a QR Code reader based on a [PHP port](https://github.com/khanamiryan/php-qrcode-detector-decoder) of the [ZXing library](https://github.com/zxing/zxing). diff --git a/docs/index.rst b/docs/index.rst index 5f4d0d185..bff629843 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,6 +22,12 @@ This work is licensed under the Creative Commons Attribution 4.0 International ( Usage/Advanced-usage.md Usage/Configuration-settings.md +.. toctree:: + :maxdepth: 3 + :caption: Customizing output + + Customizing/Module-Values.md + .. toctree:: :maxdepth: 3 :caption: Built-In Output Modules diff --git a/src/QROptionsTrait.php b/src/QROptionsTrait.php index b69e7c2a6..a86af4377 100644 --- a/src/QROptionsTrait.php +++ b/src/QROptionsTrait.php @@ -427,6 +427,9 @@ trait QROptionsTrait{ /** * String substitute for dark + * + * @see https://en.wikipedia.org/wiki/Block_Elements + * @see https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit */ protected string $textDark = '██'; @@ -450,7 +453,7 @@ trait QROptionsTrait{ */ /** - * Measurement unit for `FPDF` output: pt, mm, cm, in (defaults to "pt") + * Measurement unit for `FPDF` output: `pt`, `mm`, `cm`, `in` (default: `pt`) * * @see FPDF::__construct() */