svgRandomColoredDots.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. /**
  3. * Randomly colored modules example
  4. *
  5. * @see https://github.com/chillerlan/php-qrcode/discussions/136
  6. *
  7. * @created 09.07.2022
  8. * @author Smiley <smiley@chillerlan.net>
  9. * @copyright 2022 Smiley
  10. * @license MIT
  11. *
  12. * @noinspection PhpIllegalPsrClassPathInspection
  13. */
  14. use chillerlan\QRCode\{QRCode, QROptions};
  15. use chillerlan\QRCode\Common\EccLevel;
  16. use chillerlan\QRCode\Data\QRMatrix;
  17. use chillerlan\QRCode\Output\QRMarkupSVG;
  18. require_once __DIR__.'/../vendor/autoload.php';
  19. /*
  20. * Class definition
  21. */
  22. // the extended SVG output module
  23. class RandomDotsSVGOutput extends QRMarkupSVG{
  24. /**
  25. * @inheritDoc
  26. */
  27. protected function path(string $path, int $M_TYPE):string{
  28. // omit the "fill" and "opacity" attributes on the path element
  29. return sprintf('<path class="%s" d="%s"/>', $this->getCssClass($M_TYPE), $path);
  30. }
  31. /**
  32. * To alter the layer a module appears on, we need to re-implement the collection method
  33. *
  34. * @inheritDoc
  35. */
  36. protected function collectModules(Closure $transform):array{
  37. $paths = [];
  38. $dotColors = $this->options->dotColors; // avoid magic getter in long loops
  39. // collect the modules for each type
  40. foreach($this->matrix->getMatrix() as $y => $row){
  41. foreach($row as $x => $M_TYPE){
  42. $M_TYPE_LAYER = $M_TYPE;
  43. if($this->connectPaths && !$this->matrix->checkTypeIn($x, $y, $this->excludeFromConnect)){
  44. // to connect paths we'll redeclare the $M_TYPE_LAYER to data only
  45. $M_TYPE_LAYER = QRMatrix::M_DATA;
  46. if($this->matrix->isDark($M_TYPE)){
  47. $M_TYPE_LAYER = QRMatrix::M_DATA_DARK;
  48. }
  49. }
  50. // randomly assign another $M_TYPE_LAYER for the given types
  51. // note that the layer id has to be an integer value,
  52. // ideally outside the several bitmask values
  53. if($M_TYPE_LAYER === QRMatrix::M_DATA_DARK){
  54. $M_TYPE_LAYER = array_rand($dotColors);
  55. }
  56. // collect the modules per $M_TYPE
  57. $module = $transform($x, $y, $M_TYPE, $M_TYPE_LAYER);
  58. if(!empty($module)){
  59. $paths[$M_TYPE_LAYER][] = $module;
  60. }
  61. }
  62. }
  63. // beautify output
  64. ksort($paths);
  65. return $paths;
  66. }
  67. }
  68. /**
  69. * the extended options with the $dotColors option
  70. *
  71. * @property array<int, string> $dotColors
  72. */
  73. class RandomDotsOptions extends QROptions{
  74. /**
  75. * a map of $M_TYPE_LAYER => color
  76. *
  77. * @see \array_rand()
  78. * @var array<int, string>
  79. */
  80. protected array $dotColors = [];
  81. }
  82. /*
  83. * Runtime
  84. */
  85. // prepare the options
  86. $options = new RandomDotsOptions;
  87. // our custom dot colors
  88. // adding the IS_DARK flag so that the proper layer css class is assigned
  89. $options->dotColors = [
  90. (111 | QRMatrix::IS_DARK) => '#e2453c',
  91. (222 | QRMatrix::IS_DARK) => '#e07e39',
  92. (333 | QRMatrix::IS_DARK) => '#e5d667',
  93. (444 | QRMatrix::IS_DARK) => '#51b95b',
  94. (555 | QRMatrix::IS_DARK) => '#1e72b7',
  95. (666 | QRMatrix::IS_DARK) => '#6f5ba7',
  96. ];
  97. // generate the CSS for the several colored layers
  98. $layerColors = '';
  99. foreach($options->dotColors as $layer => $color){
  100. $layerColors .= sprintf("\n\t\t.qr-%s{ fill: %s; }", $layer, $color);
  101. }
  102. $options->svgDefs = '
  103. <style><![CDATA[
  104. .dark{ fill: #424242; }
  105. '.$layerColors.'
  106. ]]></style>';
  107. // set the custom output interface
  108. $options->outputInterface = RandomDotsSVGOutput::class;
  109. // common qrcode options
  110. $options->version = 5;
  111. $options->eccLevel = EccLevel::H;
  112. $options->addQuietzone = true;
  113. $options->outputBase64 = false;
  114. $options->drawLightModules = false;
  115. $options->connectPaths = true;
  116. $options->excludeFromConnect = [
  117. QRMatrix::M_FINDER_DARK,
  118. QRMatrix::M_FINDER_DOT,
  119. QRMatrix::M_ALIGNMENT_DARK,
  120. ];
  121. $options->drawCircularModules = true;
  122. $options->circleRadius = 0.4;
  123. $options->keepAsSquare = [
  124. QRMatrix::M_FINDER_DARK,
  125. QRMatrix::M_FINDER_DOT,
  126. QRMatrix::M_ALIGNMENT_DARK,
  127. ];
  128. $out = (new QRCode($options))->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
  129. // dump the output
  130. if(php_sapi_name() !== 'cli'){
  131. header('content-type: image/svg+xml');
  132. }
  133. echo $out;
  134. exit;