LogoImageData.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. declare(strict_types=1);
  3. namespace Endroid\QrCode\ImageData;
  4. use Endroid\QrCode\Logo\LogoInterface;
  5. class LogoImageData
  6. {
  7. private string $data;
  8. /** @var mixed */
  9. private $image;
  10. private string $mimeType;
  11. private int $width;
  12. private int $height;
  13. private bool $punchoutBackground;
  14. /** @param mixed $image */
  15. private function __construct(
  16. string $data,
  17. $image,
  18. string $mimeType,
  19. int $width,
  20. int $height,
  21. bool $punchoutBackground
  22. ) {
  23. $this->data = $data;
  24. $this->image = $image;
  25. $this->mimeType = $mimeType;
  26. $this->width = $width;
  27. $this->height = $height;
  28. $this->punchoutBackground = $punchoutBackground;
  29. }
  30. public static function createForLogo(LogoInterface $logo): self
  31. {
  32. $data = @file_get_contents($logo->getPath());
  33. if (!is_string($data)) {
  34. throw new \Exception(sprintf('Invalid data at path "%s"', $logo->getPath()));
  35. }
  36. if (false !== filter_var($logo->getPath(), FILTER_VALIDATE_URL)) {
  37. $mimeType = self::detectMimeTypeFromUrl($logo->getPath());
  38. } else {
  39. $mimeType = self::detectMimeTypeFromPath($logo->getPath());
  40. }
  41. $width = $logo->getResizeToWidth();
  42. $height = $logo->getResizeToHeight();
  43. if ('image/svg+xml' === $mimeType) {
  44. if (null === $width || null === $height) {
  45. throw new \Exception('SVG Logos require an explicitly set resize width and height');
  46. }
  47. return new self($data, null, $mimeType, $width, $height, $logo->getPunchoutBackground());
  48. }
  49. $image = @imagecreatefromstring($data);
  50. if (!$image) {
  51. throw new \Exception(sprintf('Unable to parse image data at path "%s"', $logo->getPath()));
  52. }
  53. // No target width and height specified: use from original image
  54. if (null !== $width && null !== $height) {
  55. return new self($data, $image, $mimeType, $width, $height, $logo->getPunchoutBackground());
  56. }
  57. // Only target width specified: calculate height
  58. if (null !== $width && null === $height) {
  59. return new self($data, $image, $mimeType, $width, intval(imagesy($image) * $width / imagesx($image)), $logo->getPunchoutBackground());
  60. }
  61. // Only target height specified: calculate width
  62. if (null === $width && null !== $height) {
  63. return new self($data, $image, $mimeType, intval(imagesx($image) * $height / imagesy($image)), $height, $logo->getPunchoutBackground());
  64. }
  65. return new self($data, $image, $mimeType, imagesx($image), imagesy($image), $logo->getPunchoutBackground());
  66. }
  67. public function getData(): string
  68. {
  69. return $this->data;
  70. }
  71. /** @return mixed */
  72. public function getImage()
  73. {
  74. if (null === $this->image) {
  75. throw new \Exception('SVG Images have no image resource');
  76. }
  77. return $this->image;
  78. }
  79. public function getMimeType(): string
  80. {
  81. return $this->mimeType;
  82. }
  83. public function getWidth(): int
  84. {
  85. return $this->width;
  86. }
  87. public function getHeight(): int
  88. {
  89. return $this->height;
  90. }
  91. public function getPunchoutBackground(): bool
  92. {
  93. return $this->punchoutBackground;
  94. }
  95. public function createDataUri(): string
  96. {
  97. return 'data:'.$this->mimeType.';base64,'.base64_encode($this->data);
  98. }
  99. private static function detectMimeTypeFromUrl(string $url): string
  100. {
  101. /** @var mixed $format */
  102. $format = PHP_VERSION_ID >= 80000 ? true : 1;
  103. $headers = get_headers($url, $format);
  104. if (!is_array($headers) || !isset($headers['Content-Type'])) {
  105. throw new \Exception(sprintf('Content type could not be determined for logo URL "%s"', $url));
  106. }
  107. return is_array($headers['Content-Type']) ? $headers['Content-Type'][1] : $headers['Content-Type'];
  108. }
  109. private static function detectMimeTypeFromPath(string $path): string
  110. {
  111. if (!function_exists('mime_content_type')) {
  112. throw new \Exception('You need the ext-fileinfo extension to determine logo mime type');
  113. }
  114. $mimeType = @mime_content_type($path);
  115. if (!is_string($mimeType)) {
  116. throw new \Exception('Could not determine mime type');
  117. }
  118. if (!preg_match('#^image/#', $mimeType)) {
  119. throw new \Exception('Logo path is not an image');
  120. }
  121. // Passing mime type image/svg results in invisible images
  122. if ('image/svg' === $mimeType) {
  123. return 'image/svg+xml';
  124. }
  125. return $mimeType;
  126. }
  127. }