Module-Values.md.txt 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # Module values
  2. ## Basics
  3. The QR Code matrix is a 2-dimensional array of numerical values that hold a bit mask for
  4. each QR pixel ("module" as per specification), the so-called "module type" or `$M_TYPE`, which is represented by
  5. [the `QRMatrix::M_*` constants](https://chillerlan.github.io/php-qrcode/classes/chillerlan-QRCode-Data-QRMatrix.html#toc-constants).
  6. You can assign different values for the several [function patterns](../Appendix/Terminology.md#function-patterns) to colorize them or even draw pixel-art.
  7. ## Assigning values
  8. To map the values and properly render the modules for the given `QROutputInterface`, it may be necessary to overwrite the
  9. [default values](https://chillerlan.github.io/php-qrcode/classes/chillerlan-QRCode-Output-QROutputInterface.html#constant_DEFAULT_MODULE_VALUES),
  10. that are replaced by the user defined values in `QROptions::$moduleValues` during the render process.
  11. The map of `QRMatrix::M_*` constants => default values looks similar to the following:
  12. ```php
  13. $options->moduleValues = [
  14. // light
  15. QRMatrix::M_NULL => false,
  16. QRMatrix::M_DARKMODULE_LIGHT => false,
  17. QRMatrix::M_DATA => false,
  18. QRMatrix::M_FINDER => false,
  19. QRMatrix::M_SEPARATOR => false,
  20. QRMatrix::M_ALIGNMENT => false,
  21. QRMatrix::M_TIMING => false,
  22. QRMatrix::M_FORMAT => false,
  23. QRMatrix::M_VERSION => false,
  24. QRMatrix::M_QUIETZONE => false,
  25. QRMatrix::M_LOGO => false,
  26. QRMatrix::M_FINDER_DOT_LIGHT => false,
  27. QRMatrix::M_TEST => false,
  28. // dark
  29. QRMatrix::M_DARKMODULE => true,
  30. QRMatrix::M_DATA_DARK => true,
  31. QRMatrix::M_FINDER_DARK => true,
  32. QRMatrix::M_SEPARATOR_DARK => true,
  33. QRMatrix::M_ALIGNMENT_DARK => true,
  34. QRMatrix::M_TIMING_DARK => true,
  35. QRMatrix::M_FORMAT_DARK => true,
  36. QRMatrix::M_VERSION_DARK => true,
  37. QRMatrix::M_QUIETZONE_DARK => true,
  38. QRMatrix::M_LOGO_DARK => true,
  39. QRMatrix::M_FINDER_DOT => true,
  40. QRMatrix::M_TEST_DARK => true,
  41. ];
  42. ```
  43. Not all the module values need to be specified - missing values will be filled with the internal default values
  44. for `true` (dark) and `false` (light) respectively. The `QROutputInterface` inheritors implement a `moduleValueIsValid()`
  45. method that checks if the given value is valid for that particular class:
  46. ```php
  47. // set an initial value that acts as default
  48. $dark = 'rgba(0, 0, 0, 0.5)';
  49. // try to receive user input
  50. if(QRMarkupSVG::moduleValueIsValid($_GET['qr_dark'])){
  51. // module values for HTML, SVG and other markup may need special treatment,
  52. // e.g. only accept hexadecimal values from user input
  53. // as moduleValueIsValid() just checks for the general syntax
  54. $dark = sanitize_user_input($_GET['qr_dark']);
  55. }
  56. $options->moduleValues = [
  57. QRMatrix::M_DATA_DARK => $dark,
  58. QRMatrix::M_FINDER_DARK => $dark,
  59. QRMatrix::M_ALIGNMENT_DARK => $dark,
  60. QRMatrix::M_FINDER_DOT => $dark,
  61. ];
  62. ```
  63. 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)):
  64. ```php
  65. // for HTML, SVG and ImageMagick
  66. $options->moduleValues = [
  67. QRMatrix::M_DATA => '#ffffff',
  68. QRMatrix::M_DATA_DARK => '#000000',
  69. // ...
  70. ];
  71. // for the GdImage, EPS and FPDF output types
  72. $options->moduleValues = [
  73. QRMatrix::M_DATA => [255, 255, 255],
  74. QRMatrix::M_DATA_DARK => [0, 0, 0],
  75. // ...
  76. ];
  77. // for string/text output
  78. $options->moduleValues = [
  79. QRMatrix::M_DATA => '░░',
  80. QRMatrix::M_DATA_DARK => '██',
  81. // ...
  82. ];
  83. ```
  84. ## Handling in your own `QROutputInterface`
  85. ### Setting module values
  86. [`QROutputAbstract::setModuleValues()`](https://chillerlan.github.io/php-qrcode/classes/chillerlan-QRCode-Output-QROutputAbstract.html#method_setModuleValues)
  87. calls the 3 abstract methods `moduleValueIsValid()`, `getModuleValue()` and `getDefaultModuleValue()` to fill the internal
  88. module value map with the values given via `QROptions::$moduleValues`:
  89. ```php
  90. protected function setModuleValues():void{
  91. foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
  92. $value = ($this->options->moduleValues[$M_TYPE] ?? null);
  93. $this->moduleValues[$M_TYPE] = $this->moduleValueIsValid($value)
  94. ? $this->getModuleValue($value)
  95. : $this->getDefaultModuleValue($defaultValue);
  96. }
  97. }
  98. ```
  99. In the following example we'll create these methods for the `GdImage` output.
  100. Since [`imagecolorallocate()`](https://www.php.net/manual/function.imagecolorallocate) and other GD functions accept 3 values
  101. 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)]`).
  102. First we need to validate the input:
  103. ```php
  104. protected function moduleValueIsValid($value):bool{
  105. // nowhere near valid
  106. if(!is_array($value) || count($value) !== 3){
  107. return false;
  108. }
  109. // now iterate over the values
  110. foreach($value as $color){
  111. // non-integers won't work
  112. if(!is_int($color)){
  113. return false;
  114. }
  115. // a strict check - we could also just ignore outliers and clamp the values instead
  116. if($color < 0 || $color > 255){
  117. return false;
  118. }
  119. }
  120. return true; // yay!
  121. }
  122. ```
  123. Now we can prepare the value:
  124. ```php
  125. protected function getModuleValue($value):array{
  126. // we call array_values() so we don't run into string-key related issues
  127. return array_map(fn(int $val):int => max(0, min(255, $val)), array_values($value));
  128. }
  129. ```
  130. And finally we need to provide default values:
  131. ```php
  132. protected function getDefaultModuleValue(bool $isDark):array{
  133. return $isDark ? [0, 0, 0] : [255, 255, 255];
  134. }
  135. ```
  136. Now that everything is ready and set, we can use the values in our GD functions:
  137. ```php
  138. $color = imagecolorallocate($this->image, ...$this->moduleValues[$M_TYPE]);
  139. ```
  140. ### Using the module values
  141. The state of the `$M_TYPE` is set with the `QRMatrix::IS_DARK` constant:
  142. ```php
  143. // set to dark (true) with bitwise OR:
  144. $M_TYPE = ($M_TYPE | QRMatrix::IS_DARK);
  145. // set to light (false) with bitwise AND NOT
  146. $M_TYPE = ($M_TYPE & ~QRMatrix::IS_DARK);
  147. // toggle the opposite state with bitwise XOR
  148. $M_TYPE = ($M_TYPE ^ QRMatrix::IS_DARK);
  149. ```
  150. You can manually check whether the module is dark:
  151. ```php
  152. ($value & QRMatrix::IS_DARK) === QRMatrix::IS_DARK;
  153. ```
  154. However it is much more convenient to use the `QRMatrix` methods for that:
  155. ```php
  156. for($y = 0; $y < $this->moduleCount; $y++){ // rows
  157. for($x = 0; $x < $this->moduleCount; $x++){ // columns
  158. // sets current module as dark (true) with the M_DATA type
  159. $this->matrix->set($x, $y, true, QRMatrix::M_DATA);
  160. // -> true (shortcut for checkType($x, $y, QRMatrix::IS_DARK))
  161. $this->matrix->check($x, $y);
  162. // -> true (current module is of type M_DATA)
  163. $this->matrix->checkType($x, $y, QRMatrix::M_DATA);
  164. // -> true (current module is of type IS_DARK)
  165. $this->matrix->checkType($x, $y, QRMatrix::IS_DARK);
  166. // -> false, type is M_DATA
  167. $this->matrix->checkTypeIn($x, $y, [QRMatrix::M_FINDER_DARK, QRMatrix::M_ALIGNMENT]);
  168. }
  169. }
  170. ```