QROutputAbstract.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <?php
  2. /**
  3. * Class QROutputAbstract
  4. *
  5. * @created 09.12.2015
  6. * @author Smiley <smiley@chillerlan.net>
  7. * @copyright 2015 Smiley
  8. * @license MIT
  9. */
  10. namespace chillerlan\QRCode\Output;
  11. use chillerlan\QRCode\Data\QRMatrix;
  12. use chillerlan\Settings\SettingsContainerInterface;
  13. use Closure;
  14. use function base64_encode, dirname, file_put_contents, is_writable, ksort, sprintf;
  15. /**
  16. * common output abstract
  17. */
  18. abstract class QROutputAbstract implements QROutputInterface{
  19. /**
  20. * the current size of the QR matrix
  21. *
  22. * @see \chillerlan\QRCode\Data\QRMatrix::size()
  23. */
  24. protected int $moduleCount;
  25. /**
  26. * the current scaling for a QR pixel
  27. *
  28. * @see \chillerlan\QRCode\QROptions::$scale
  29. */
  30. protected int $scale;
  31. /**
  32. * the side length of the QR image (modules * scale)
  33. */
  34. protected int $length;
  35. /**
  36. * an (optional) array of color values for the several QR matrix parts
  37. */
  38. protected array $moduleValues;
  39. /**
  40. * the (filled) data matrix object
  41. */
  42. protected QRMatrix $matrix;
  43. /**
  44. * @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
  45. */
  46. protected SettingsContainerInterface $options;
  47. /**
  48. * QROutputAbstract constructor.
  49. */
  50. public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
  51. $this->options = $options;
  52. $this->matrix = $matrix;
  53. $this->moduleCount = $this->matrix->size();
  54. $this->scale = $this->options->scale;
  55. $this->length = $this->moduleCount * $this->scale;
  56. $this->setModuleValues();
  57. }
  58. /**
  59. * Sets the initial module values
  60. */
  61. protected function setModuleValues():void{
  62. foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
  63. $value = $this->options->moduleValues[$M_TYPE] ?? null;
  64. $this->moduleValues[$M_TYPE] = $this->moduleValueIsValid($value)
  65. ? $this->getModuleValue($value)
  66. : $this->getDefaultModuleValue($defaultValue);
  67. }
  68. }
  69. /**
  70. * Determines whether the given value is valid
  71. *
  72. * @param mixed|null $value
  73. */
  74. abstract protected function moduleValueIsValid($value):bool;
  75. /**
  76. * Returns the final value for the given input (return value depends on the output module)
  77. *
  78. * @param mixed $value
  79. *
  80. * @return mixed
  81. */
  82. abstract protected function getModuleValue($value);
  83. /**
  84. * Returns a defualt value for either dark or light modules (return value depends on the output module)
  85. *
  86. * @return mixed
  87. */
  88. abstract protected function getDefaultModuleValue(bool $isDark);
  89. /**
  90. * Returns a base64 data URI for the given string and mime type
  91. */
  92. protected function base64encode(string $data, string $mime):string{
  93. return sprintf('data:%s;base64,%s', $mime, base64_encode($data));
  94. }
  95. /**
  96. * saves the qr data to a file
  97. *
  98. * @see file_put_contents()
  99. * @see \chillerlan\QRCode\QROptions::cachefile
  100. *
  101. * @throws \chillerlan\QRCode\Output\QRCodeOutputException
  102. */
  103. protected function saveToFile(string $data, string $file):void{
  104. if(!is_writable(dirname($file))){
  105. throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s', $file));
  106. }
  107. if(file_put_contents($file, $data) === false){
  108. throw new QRCodeOutputException(sprintf('Cannot write data to cache file: %s (file_put_contents error)', $file));
  109. }
  110. }
  111. /**
  112. * collects the modules per QRMatrix::M_* type and runs a $transform functio on each module and
  113. * returns an array with the transformed modules
  114. *
  115. * The transform callback is called with the following parameters:
  116. *
  117. * $x - current column
  118. * $y - current row
  119. * $M_TYPE - field value
  120. * $M_TYPE_LAYER - (possibly modified) field value that acts as layer id
  121. */
  122. protected function collectModules(Closure $transform):array{
  123. $paths = [];
  124. // collect the modules for each type
  125. foreach($this->matrix->matrix() as $y => $row){
  126. foreach($row as $x => $M_TYPE){
  127. $M_TYPE_LAYER = $M_TYPE;
  128. if($this->options->connectPaths && $this->matrix->checkTypeNotIn($x, $y, $this->options->excludeFromConnect)){
  129. // to connect paths we'll redeclare the $M_TYPE_LAYER to data only
  130. $M_TYPE_LAYER = QRMatrix::M_DATA;
  131. if($this->matrix->check($x, $y)){
  132. $M_TYPE_LAYER |= QRMatrix::IS_DARK;
  133. }
  134. }
  135. // collect the modules per $M_TYPE
  136. $paths[$M_TYPE_LAYER][] = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER);
  137. }
  138. }
  139. // beautify output
  140. ksort($paths);
  141. return $paths;
  142. }
  143. }