chillerlan /
php-qrcode
| 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
Loading history...
|
|||
| 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 |