Skip to content

Commit 6a26d3a

Browse files
committed
📖
1 parent 59d8132 commit 6a26d3a

File tree

4 files changed

+264
-28
lines changed

4 files changed

+264
-28
lines changed

docs/Customizing/QROutputAbstract.md

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# `QROutputAbstract`
2+
3+
The abstract class `QROutputAbstract` contains several commonly used methods and properties and can be used as a basis for a custom output class.
4+
5+
6+
## Properties
7+
8+
### `$options` and `$matrix`
9+
10+
The `QROptions` and `QRMatrix` instances that were passed to the constructor of the output class.
11+
Both objects can be modified during runtime, for example to override settings or add matrix modifications.
12+
13+
14+
### `$moduleCount`, `$scale` and `$length`
15+
16+
These are convenience variables mostly to avoid multiple method calls to `QRMatrix::getSize()` and `QROptions::__get('scale')` inside loops,
17+
the `$length` is calculated from the aforementioned values (`$moduleCount * $scale`).
18+
The method `setMatrixDimensions()` can be called to update these 3 values after the matrix has been modified, e.g. by adding a quiet zone during output.
19+
20+
21+
### `$moduleValues`
22+
23+
The finalized map of `$M_TYPE` to value for the current output. This map is generated during invocation of the output class via `setModuleValues()`.
24+
25+
26+
### Copies of `QROptions` values
27+
28+
Some values from the `QROptions` instance are copied to properties to avoid calling the magic getters in long loops for a significant performance increase, e.g. in the module collector.
29+
Currently, the following values are copied via `copyVars()` during invocation:
30+
`$connectPaths`, `$excludeFromConnect`, `$eol`, `$drawLightModules`, `$drawCircularModules`, `$keepAsSquare`, `$circleRadius`.
31+
32+
33+
## Methods
34+
35+
### `setModuleValues()`
36+
37+
This method calls the abstract/interface methods `moduleValueIsValid()`, `prepareModuleValue()` and `getDefaultModuleValue()` to prepare the module values map.
38+
39+
40+
### `moduleValueIsValid()`
41+
42+
This method is declared in the `QROutputInterface` and needs to be implemented by the output class; it is `static` so that it can be called before invocation.
43+
The purpose is to determine whether the given `mixed` input is a valid module value for the current output class and returns `bool`.
44+
It's also useful to check values from `QROptions` such as `$bgColor` or `$transparencyColor`.
45+
46+
Below is a pseudo implementation, check the code of the several output classes for actual implementations
47+
(e.g. [`QRImagick::moduleValueIsValid()`](https://github.com/chillerlan/php-qrcode/blob/4bd4b59fdec72397f5b1f70da9cadcb76764191b/src/Output/QRImagick.php#L68-L96))
48+
49+
```php
50+
class MyOutput extends QROutputAbstract{
51+
52+
public static function moduleValueIsValid(mixed $value):bool{
53+
54+
// check the type of the input value first
55+
if(!is_expected_type($value)){
56+
return false;
57+
}
58+
59+
// do some more checks to determine the value
60+
if(!is_somehow_valid($value)){
61+
return false;
62+
}
63+
64+
// looks like we got a match
65+
return true;
66+
}
67+
68+
}
69+
```
70+
71+
72+
### `prepareModuleValue()`
73+
74+
This method prepares the final replacement value from the given input.
75+
It might still be necessary to validate the given value despite it being checked earlier by `moduleValueIsValid()` -
76+
if nothing helps, this is a good place to throw an exception.
77+
Below a pseudo implementation example (see [`QRGdImage::prepareModuleValue()`](https://github.com/chillerlan/php-qrcode/blob/4bd4b59fdec72397f5b1f70da9cadcb76764191b/src/Output/QRGdImage.php#L138-L158)):
78+
79+
```php
80+
class MyOutput extends QROutputAbstract{
81+
82+
protected function prepareModuleValue(mixed $value):mixed{
83+
84+
// extended validation to make sure the values are valid for output
85+
// e.g. examine array values, clamp etc.
86+
if(!is_valid($value)){
87+
throw new QRCodeOutputException('invalid module value');
88+
}
89+
90+
return $this->modifyValue($value);
91+
}
92+
93+
}
94+
```
95+
96+
97+
### `getDefaultModuleValue()`
98+
99+
Finally, setting a default value is required, in case a value for an `$M_TYPE` is not set or it's invalid.
100+
101+
```php
102+
class MyOutput extends QROutputAbstract{
103+
104+
protected function getDefaultModuleValue(bool $isDark):mixed{
105+
$defaultValue = ($isDark === true)
106+
? 'default value for dark'
107+
: 'default value for light';
108+
109+
return $this->prepareModuleValue($defaultValue);
110+
}
111+
112+
}
113+
```
114+
115+
116+
### `getModuleValue()` and `getModuleValueAt()`
117+
118+
Both methods return a module value, the main difference is that `getModuleValueAt()` is a convenience method
119+
that makes an extra call to retrieve the `$M_TYPE` from the given matrix coordinate to return the value via `getModuleValue()`.
120+
121+
A `foreach` loop over the matrix gives you the key (coordinate) *and* value of an array element:
122+
123+
```php
124+
class MyOutput extends QROutputAbstract{
125+
126+
public function dump(string $file = null):string{
127+
$lines = [];
128+
129+
foreach($this->matrix->getMatrix() as $y => $row){
130+
$lines[$y] = '';
131+
132+
foreach($row as $x => $M_TYPE){
133+
$lines[$y] .= $this->getModuleValue($M_TYPE);
134+
}
135+
}
136+
137+
return implode($this->options->eol, $lines);
138+
}
139+
140+
}
141+
```
142+
143+
However, sometimes you might happen to use a `for` loop instead. The `for` loop leaves you only with the matrix coordinates, so you need to call `getModuleValueAt()`:
144+
145+
```php
146+
class MyOutput extends QROutputAbstract{
147+
148+
public function dump(string $file = null):string{
149+
$lines = [];
150+
151+
for($y = 0; $y < $this->moduleCount; $y++){
152+
$lines[$y] = '';
153+
154+
for($x = 0; $x < $this->moduleCount; $x++){
155+
$lines[$y] .= $this->getModuleValueAt($x, $y);
156+
}
157+
158+
}
159+
160+
return implode($this->options->eol, $lines);
161+
}
162+
163+
}
164+
```
165+
166+
167+
### `setMatrixDimensions()`
168+
169+
As mentioned before, this method is supposed to set the values for the properties `$moduleCount`, `$scale` and `$length`.
170+
It is called in the constructor during invocation, but it might be necessary to call it again if the size of the matrix was changed in the output class
171+
(see [the round quiet zone example](https://github.com/chillerlan/php-qrcode/blob/99b1f9cf454ab1316cb643950a71caed3a6c0f5a/examples/svgRoundQuietzone.php#L38-L44) for a use case).
172+
173+
174+
### `getOutputDimensions()`
175+
176+
This method provides a simple way for consistent width/height values for the output (if applicable) which then can be changed by simply overriding this method.
177+
It returns a 2-element array that contains the values in a format that can be used by the output class, which is `QROutputAbstract::$length` (`$moduleCount * $scale`):
178+
179+
```php
180+
[$width, $height] = $this->getOutputDimensions();
181+
```
182+
183+
The output width and height can be changed in all places by simply overriding the method:
184+
185+
```php
186+
class MyOutput extends QROutputAbstract{
187+
188+
protected function getOutputDimensions():array{
189+
// adjust the height in order to add something under the QR Code
190+
return [$this->length, ($this->length + 69)];
191+
}
192+
193+
}
194+
```
195+
196+
197+
### `collectModules()`
198+
199+
The module collector is particularly useful for plain text based file formats, for example the various markup languages like SVG and HTML or other structured file formats such as EPS.
200+
This method takes a `Closure` as a parameter, which is called with 4 parameters: the module coordinates `$x` and `$y`, the `$M_TYPE` and `$M_TYPE_LAYER`.
201+
The `$M_TYPE_LAYER` is a copy of the `$M_TYPE` that represents the array key of the returned array and that may have been reassigned in the collector to another path layer, e.g. through `QROptions::$connectPaths`.
202+
203+
```php
204+
class MyOutput extends QROutputAbstract{
205+
206+
public function dump(string $file = null):string{
207+
208+
// collect the modules for the path elements
209+
$paths = $this->collectModules(fn(int $x, int $y, int $M_TYPE):string => sprintf('%d %d %012b', $x, $y, $M_TYPE));
210+
211+
// loop over the paths
212+
foreach($paths as $M_TYPE_LAYER => &$path){
213+
214+
if(empty($path)){
215+
continue;
216+
}
217+
218+
$path = implode($this->options->eol, $path);
219+
}
220+
221+
return implode($this->options->eol, $paths);
222+
}
223+
224+
}
225+
```
226+
227+
Sometimes it can be necessary to override `collectModules()` in order to apply special effects such as random colors - you can find some implementations in [the SVG examples](https://github.com/chillerlan/php-qrcode/tree/main/examples).
228+
229+
230+
### `saveToFile()` and `toBase64DataURI()`
231+
232+
The void method `saveToFile()` takes a data blob and the `$file` given in `QROutputInterface::dump()` and save to the path if it is not `null` - the file path itself is not checked except for writability.
233+
234+
The final output can be transformed to a [base64 data URI](https://en.wikipedia.org/wiki/Data_URI_scheme) with `toBase64DataURI()`, where the data blob and a valid mime type as parameters - the mime type is not checked.
235+
236+
237+
238+
```php
239+
class MyOutput extends QROutputAbstract{
240+
241+
public function dump(string $file = null):string{
242+
$output = 'qrcode data string';
243+
244+
// save the plain data to file
245+
$this->saveToFile($output, $file);
246+
247+
// base64 encoding may be called optionally
248+
if($this->options->outputBase64){
249+
$output = $this->toBase64DataURI($output, 'text/plain');
250+
}
251+
252+
return $output;
253+
}
254+
255+
}
256+
```

docs/Readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ The markdown sources for the [Read the Docs online manual](https://php-qrcode.re
6767
### Customizing output
6868

6969
- [Module values](./Customizing/Module-Values.md)
70+
- [`QROutputAbstract`](./Customizing/QROutputAbstract.md)
7071

7172

7273
### Built-In Output Modules

docs/Usage/Configuration-settings.md

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -341,32 +341,13 @@ Imagick output format
341341
A common css class
342342

343343

344-
## markupDark
345-
346-
Markup substitute for dark (CSS value)
347-
348-
349-
## markupLight
350-
351-
Markup substitute for light (CSS value)
352-
353-
354344
## svgAddXmlHeader
355345

356346
Whether to add an XML header line or not, e.g. to embed the SVG directly in HTML
357347

358348
`<?xml version="1.0" encoding="UTF-8"?>`
359349

360350

361-
## svgOpacity
362-
363-
SVG path opacity
364-
365-
Sets the value for the SVG "fill-opacity" on a `<path>` element. Only in effect when non-empty values
366-
for `QROptions::$markupDark` and `QROptions::$markupLight` are given.
367-
The opacity value is the same for all paths - please use CSS for more sophisticated implementations.
368-
369-
370351
## svgDefs
371352

372353
Anything in the SVG `<defs>` tag
@@ -385,19 +366,16 @@ Sets the value for the "preserveAspectRatio" on the `<svg>` element
385366
- [developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio)
386367

387368

388-
## textDark
369+
## svgUseFillAttributes
389370

390-
String substitute for dark
371+
Whether to use the SVG `fill` attributes
391372

392-
**See also:**
373+
If set to `true` (default), the `fill` attribute will be set with the module value for the `<path>` element's `$M_TYPE`.
374+
When set to `false`, the module values map will be ignored and the QR Code may be styled via CSS.
393375

394-
- [en.wikipedia.org/wiki/Block_Elements](https://en.wikipedia.org/wiki/Block_Elements)
395-
- [en.wikipedia.org/wiki/ANSI_escape_code#8-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit)
396-
397-
398-
## textLight
376+
**See also:**
399377

400-
String substitute for light
378+
- [developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill)
401379

402380

403381
## textLineStart

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This work is licensed under the Creative Commons Attribution 4.0 International (
2727
:caption: Customizing output
2828

2929
Customizing/Module-Values.md
30+
Customizing/QROutputAbstract.md
3031

3132
.. toctree::
3233
:maxdepth: 3

0 commit comments

Comments
 (0)