QRImage.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. /**
  3. * Class QRImage
  4. *
  5. * @filesource QRImage.php
  6. * @created 05.12.2015
  7. * @package chillerlan\QRCode\Output
  8. * @author Smiley <smiley@chillerlan.net>
  9. * @copyright 2015 Smiley
  10. * @license MIT
  11. */
  12. namespace chillerlan\QRCode\Output;
  13. use chillerlan\QRCode\{QRCode, Data\QRMatrix};
  14. /**
  15. * Converts the matrix into images, raw or base64 output
  16. */
  17. class QRImage extends QROutputAbstract{
  18. const transparencyTypes = [
  19. QRCode::OUTPUT_IMAGE_PNG,
  20. QRCode::OUTPUT_IMAGE_GIF,
  21. ];
  22. protected $moduleValues = [
  23. // light
  24. QRMatrix::M_DATA => [255, 255, 255],
  25. QRMatrix::M_FINDER => [255, 255, 255],
  26. QRMatrix::M_SEPARATOR => [255, 255, 255],
  27. QRMatrix::M_ALIGNMENT => [255, 255, 255],
  28. QRMatrix::M_TIMING => [255, 255, 255],
  29. QRMatrix::M_FORMAT => [255, 255, 255],
  30. QRMatrix::M_VERSION => [255, 255, 255],
  31. QRMatrix::M_QUIETZONE => [255, 255, 255],
  32. QRMatrix::M_TEST => [255, 255, 255],
  33. // dark
  34. QRMatrix::M_DARKMODULE << 8 => [0, 0, 0],
  35. QRMatrix::M_DATA << 8 => [0, 0, 0],
  36. QRMatrix::M_FINDER << 8 => [0, 0, 0],
  37. QRMatrix::M_ALIGNMENT << 8 => [0, 0, 0],
  38. QRMatrix::M_TIMING << 8 => [0, 0, 0],
  39. QRMatrix::M_FORMAT << 8 => [0, 0, 0],
  40. QRMatrix::M_VERSION << 8 => [0, 0, 0],
  41. QRMatrix::M_TEST << 8 => [0, 0, 0],
  42. ];
  43. /**
  44. * @see imagecreatetruecolor()
  45. * @var resource
  46. */
  47. protected $image;
  48. /**
  49. * @var int
  50. */
  51. protected $scale;
  52. /**
  53. * @var int
  54. */
  55. protected $length;
  56. /**
  57. * @see imagecolorallocate()
  58. * @var int
  59. */
  60. protected $background;
  61. /**
  62. * @return string
  63. * @throws \chillerlan\QRCode\Output\QRCodeOutputException
  64. */
  65. public function dump():string{
  66. if($this->options->cachefile !== null && !is_writable(dirname($this->options->cachefile))){
  67. throw new QRCodeOutputException('Could not write data to cache file: '.$this->options->cachefile);
  68. }
  69. $this->setImage();
  70. $moduleValues = is_array($this->options->moduleValues[$this->matrix::M_DATA])
  71. ? $this->options->moduleValues // @codeCoverageIgnore
  72. : $this->moduleValues;
  73. foreach($this->matrix->matrix() as $y => $row){
  74. foreach($row as $x => $pixel){
  75. $this->setPixel($x, $y, imagecolorallocate($this->image, ...$moduleValues[$pixel]));
  76. }
  77. }
  78. $imageData = $this->dumpImage();
  79. if((bool)$this->options->imageBase64){
  80. $imageData = 'data:image/'.$this->options->outputType.';base64,'.base64_encode($imageData);
  81. }
  82. return $imageData;
  83. }
  84. /**
  85. * @return void
  86. */
  87. protected function setImage(){
  88. $this->scale = $this->options->scale;
  89. $this->length = $this->moduleCount * $this->scale;
  90. $this->image = imagecreatetruecolor($this->length, $this->length);
  91. $this->background = imagecolorallocate($this->image, ...$this->options->imageTransparencyBG);
  92. if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::transparencyTypes, true)){
  93. imagecolortransparent($this->image, $this->background);
  94. }
  95. imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $this->background);
  96. }
  97. /**
  98. * @param $x
  99. * @param $y
  100. * @param $color
  101. * @return void
  102. */
  103. protected function setPixel($x, $y, $color){
  104. imagefilledrectangle(
  105. $this->image,
  106. $x * $this->scale,
  107. $y * $this->scale,
  108. ($x + 1) * $this->scale - 1,
  109. ($y + 1) * $this->scale - 1,
  110. $color
  111. );
  112. }
  113. /**
  114. * @return string
  115. * @throws \chillerlan\QRCode\Output\QRCodeOutputException
  116. */
  117. protected function dumpImage():string {
  118. ob_start();
  119. try{
  120. call_user_func([$this, $this->options->outputType ?? QRCode::OUTPUT_IMAGE_PNG]);
  121. }
  122. // not going to cover edge cases
  123. // @codeCoverageIgnoreStart
  124. catch(\Exception $e){
  125. throw new QRCodeOutputException($e->getMessage());
  126. }
  127. // @codeCoverageIgnoreEnd
  128. $imageData = ob_get_contents();
  129. imagedestroy($this->image);
  130. ob_end_clean();
  131. return $imageData;
  132. }
  133. /**
  134. * @return void
  135. */
  136. protected function png(){
  137. imagepng(
  138. $this->image,
  139. $this->options->cachefile,
  140. in_array($this->options->pngCompression, range(-1, 9), true)
  141. ? $this->options->pngCompression
  142. : -1
  143. );
  144. }
  145. /**
  146. * Jiff - like... JitHub!
  147. * @return void
  148. */
  149. protected function gif(){
  150. imagegif($this->image, $this->options->cachefile);
  151. }
  152. /**
  153. * @return void
  154. */
  155. protected function jpg(){
  156. imagejpeg(
  157. $this->image,
  158. $this->options->cachefile,
  159. in_array($this->options->jpegQuality, range(0, 100), true)
  160. ? $this->options->jpegQuality
  161. : 85
  162. );
  163. }
  164. }