1 | <?php |
||
2 | /** |
||
3 | * Class QROutputAbstract |
||
4 | * |
||
5 | * @created 09.12.2015 |
||
6 | * @author Smiley <[email protected]> |
||
7 | * @copyright 2015 Smiley |
||
8 | * @license MIT |
||
9 | */ |
||
10 | |||
11 | namespace chillerlan\QRCode\Output; |
||
12 | |||
13 | use chillerlan\QRCode\Data\QRMatrix; |
||
14 | use chillerlan\Settings\SettingsContainerInterface; |
||
15 | use Closure; |
||
16 | use function base64_encode, dirname, file_put_contents, is_writable, ksort, sprintf; |
||
17 | |||
18 | /** |
||
19 | * common output abstract |
||
20 | */ |
||
21 | abstract class QROutputAbstract implements QROutputInterface{ |
||
22 | |||
23 | /** |
||
24 | * the current size of the QR matrix |
||
25 | * |
||
26 | * @see \chillerlan\QRCode\Data\QRMatrix::getSize() |
||
27 | */ |
||
28 | protected int $moduleCount; |
||
29 | |||
30 | /** |
||
31 | * the current scaling for a QR pixel |
||
32 | * |
||
33 | * @see \chillerlan\QRCode\QROptions::$scale |
||
34 | */ |
||
35 | protected int $scale; |
||
36 | |||
37 | /** |
||
38 | * the side length of the QR image (modules * scale) |
||
39 | */ |
||
40 | protected int $length; |
||
41 | |||
42 | /** |
||
43 | * an (optional) array of color values for the several QR matrix parts |
||
44 | */ |
||
45 | protected array $moduleValues; |
||
46 | |||
47 | /** |
||
48 | * the (filled) data matrix object |
||
49 | */ |
||
50 | protected QRMatrix $matrix; |
||
51 | |||
52 | /** |
||
53 | * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions |
||
54 | */ |
||
55 | protected SettingsContainerInterface $options; |
||
56 | |||
57 | /** |
||
58 | * QROutputAbstract constructor. |
||
59 | */ |
||
60 | public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){ |
||
61 | $this->options = $options; |
||
62 | $this->matrix = $matrix; |
||
63 | |||
64 | $this->setMatrixDimensions(); |
||
65 | $this->setModuleValues(); |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * Sets/updates the matrix dimensions |
||
70 | * |
||
71 | * Call this method if you modify the matrix from within your custom module in case the dimensions have been changed |
||
72 | */ |
||
73 | protected function setMatrixDimensions():void{ |
||
74 | $this->moduleCount = $this->matrix->getSize(); |
||
75 | $this->scale = $this->options->scale; |
||
76 | $this->length = ($this->moduleCount * $this->scale); |
||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Sets the initial module values |
||
81 | */ |
||
82 | protected function setModuleValues():void{ |
||
83 | |||
84 | foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){ |
||
85 | $value = ($this->options->moduleValues[$M_TYPE] ?? null); |
||
86 | |||
87 | $this->moduleValues[$M_TYPE] = $this::moduleValueIsValid($value) |
||
88 | ? $this->prepareModuleValue($value) |
||
89 | : $this->getDefaultModuleValue($defaultValue); |
||
90 | } |
||
91 | |||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Returns the prepared value for the given $M_TYPE |
||
96 | * |
||
97 | * @return mixed|null return value depends on the output class, null if $moduleValues[$M_TYPE] doesn't exist |
||
98 | * @throws \chillerlan\QRCode\Output\QRCodeOutputException |
||
99 | */ |
||
100 | protected function getModuleValue(int $M_TYPE){ |
||
101 | |||
102 | if(!isset($this->moduleValues[$M_TYPE])){ |
||
103 | return null; |
||
104 | # throw new QRCodeOutputException(sprintf('invalid M_TYPE: %024b', $M_TYPE)); |
||
105 | } |
||
106 | |||
107 | return $this->moduleValues[$M_TYPE]; |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Returns the prepared module value at the given coordinate [$x, $y] (convenience) |
||
112 | * |
||
113 | * @return mixed|null |
||
114 | */ |
||
115 | protected function getModuleValueAt(int $x, int $y){ |
||
116 | return $this->getModuleValue($this->matrix->get($x, $y)); |
||
117 | } |
||
118 | |||
119 | /** |
||
120 | * Prepares the value for the given input () |
||
121 | * |
||
122 | * @param mixed $value |
||
123 | * |
||
124 | * @return mixed|null return value depends on the output class |
||
125 | */ |
||
126 | abstract protected function prepareModuleValue($value); |
||
127 | |||
128 | /** |
||
129 | * Returns a default value for either dark or light modules |
||
130 | * |
||
131 | * @return mixed|null return value depends on the output class |
||
132 | */ |
||
133 | abstract protected function getDefaultModuleValue(bool $isDark); |
||
134 | |||
135 | /** |
||
136 | * Returns a base64 data URI for the given string and mime type |
||
137 | */ |
||
138 | protected function toBase64DataURI(string $data, string $mime):string{ |
||
139 | return sprintf('data:%s;base64,%s', $mime, base64_encode($data)); |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * Saves the qr $data to a $file. If $file is null, nothing happens. |
||
144 | * |
||
145 | * @see file_put_contents() |
||
146 | * @see \chillerlan\QRCode\QROptions::$cachefile |
||
147 | * |
||
148 | * @throws \chillerlan\QRCode\Output\QRCodeOutputException |
||
149 | */ |
||
150 | protected function saveToFile(string $data, string $file = null):void{ |
||
151 | |||
152 | if($file === null){ |
||
153 | return; |
||
154 | } |
||
155 | |||
156 | if(!is_writable(dirname($file))){ |
||
157 | throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s', $file)); |
||
158 | } |
||
159 | |||
160 | if(file_put_contents($file, $data) === false){ |
||
161 | throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s (file_put_contents error)', $file)); |
||
162 | } |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * collects the modules per QRMatrix::M_* type and runs a $transform function on each module and |
||
167 | * returns an array with the transformed modules |
||
168 | * |
||
169 | * The transform callback is called with the following parameters: |
||
170 | * |
||
171 | * $x - current column |
||
172 | * $y - current row |
||
173 | * $M_TYPE - field value |
||
174 | * $M_TYPE_LAYER - (possibly modified) field value that acts as layer id |
||
175 | */ |
||
176 | protected function collectModules(Closure $transform):array{ |
||
177 | $paths = []; |
||
178 | |||
179 | // collect the modules for each type |
||
180 | for($y = 0; $y < $this->moduleCount; $y++){ |
||
181 | for($x = 0; $x < $this->moduleCount; $x++){ |
||
182 | $M_TYPE = $this->matrix->get($x, $y); |
||
183 | $M_TYPE_LAYER = $M_TYPE; |
||
184 | |||
185 | if($this->options->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->options->excludeFromConnect)){ |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
186 | // to connect paths we'll redeclare the $M_TYPE_LAYER to data only |
||
187 | $M_TYPE_LAYER = QRMatrix::M_DATA; |
||
188 | |||
189 | if($this->matrix->check($x, $y)){ |
||
190 | $M_TYPE_LAYER = QRMatrix::M_DATA_DARK; |
||
191 | } |
||
192 | } |
||
193 | |||
194 | // collect the modules per $M_TYPE |
||
195 | $module = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER); |
||
196 | |||
197 | if(!empty($module)){ |
||
198 | $paths[$M_TYPE_LAYER][] = $module; |
||
199 | } |
||
200 | } |
||
201 | } |
||
202 | |||
203 | // beautify output |
||
204 | ksort($paths); |
||
205 | |||
206 | return $paths; |
||
207 | } |
||
208 | |||
209 | } |
||
210 |