svgRandomColoredDots.php 3.7 KB

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