You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IconBuilder.php 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
  4. *
  5. * @author Julius Härtl <jus@bitgrid.net>
  6. *
  7. * @license GNU AGPL version 3 or any later version
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as
  11. * published by the Free Software Foundation, either version 3 of the
  12. * License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. namespace OCA\Theming;
  24. use Imagick;
  25. use ImagickPixel;
  26. use OCP\App\AppPathNotFoundException;
  27. class IconBuilder {
  28. /** @var ThemingDefaults */
  29. private $themingDefaults;
  30. /** @var Util */
  31. private $util;
  32. /**
  33. * IconBuilder constructor.
  34. *
  35. * @param ThemingDefaults $themingDefaults
  36. * @param Util $util
  37. */
  38. public function __construct(
  39. ThemingDefaults $themingDefaults,
  40. Util $util
  41. ) {
  42. $this->themingDefaults = $themingDefaults;
  43. $this->util = $util;
  44. }
  45. /**
  46. * @param $app string app name
  47. * @return string|false image blob
  48. */
  49. public function getFavicon($app) {
  50. $icon = $this->renderAppIcon($app, 32);
  51. if($icon === false) {
  52. return false;
  53. }
  54. $icon->setImageFormat("png24");
  55. $data = $icon->getImageBlob();
  56. $icon->destroy();
  57. return $data;
  58. }
  59. /**
  60. * @param $app string app name
  61. * @return string|false image blob
  62. */
  63. public function getTouchIcon($app) {
  64. $icon = $this->renderAppIcon($app, 512);
  65. if($icon === false) {
  66. return false;
  67. }
  68. $icon->setImageFormat("png24");
  69. $data = $icon->getImageBlob();
  70. $icon->destroy();
  71. return $data;
  72. }
  73. /**
  74. * Render app icon on themed background color
  75. * fallback to logo
  76. *
  77. * @param $app string app name
  78. * @param $size int size of the icon in px
  79. * @return Imagick|false
  80. */
  81. public function renderAppIcon($app, $size) {
  82. try {
  83. $appIcon = $this->util->getAppIcon($app);
  84. $appIconContent = file_get_contents($appIcon);
  85. } catch (AppPathNotFoundException $e) {
  86. return false;
  87. }
  88. if($appIconContent === false) {
  89. return false;
  90. }
  91. $color = $this->themingDefaults->getMailHeaderColor();
  92. $mime = mime_content_type($appIcon);
  93. // generate background image with rounded corners
  94. $background = '<?xml version="1.0" encoding="UTF-8"?>' .
  95. '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" width="512" height="512" xmlns:xlink="http://www.w3.org/1999/xlink">' .
  96. '<rect x="0" y="0" rx="100" ry="100" width="512" height="512" style="fill:' . $color . ';" />' .
  97. '</svg>';
  98. // resize svg magic as this seems broken in Imagemagick
  99. if($mime === "image/svg+xml" || substr($appIconContent, 0, 4) === "<svg") {
  100. if(substr($appIconContent, 0, 5) !== "<?xml") {
  101. $svg = "<?xml version=\"1.0\"?>".$appIconContent;
  102. } else {
  103. $svg = $appIconContent;
  104. }
  105. $tmp = new Imagick();
  106. $tmp->readImageBlob($svg);
  107. $x = $tmp->getImageWidth();
  108. $y = $tmp->getImageHeight();
  109. $res = $tmp->getImageResolution();
  110. $tmp->destroy();
  111. if($x>$y) {
  112. $max = $x;
  113. } else {
  114. $max = $y;
  115. }
  116. // convert svg to resized image
  117. $appIconFile = new Imagick();
  118. $resX = (int)(512 * $res['x'] / $max * 2.53);
  119. $resY = (int)(512 * $res['y'] / $max * 2.53);
  120. $appIconFile->setResolution($resX, $resY);
  121. $appIconFile->setBackgroundColor(new ImagickPixel('transparent'));
  122. $appIconFile->readImageBlob($svg);
  123. $appIconFile->scaleImage(512, 512, true);
  124. } else {
  125. $appIconFile = new Imagick();
  126. $appIconFile->setBackgroundColor(new ImagickPixel('transparent'));
  127. $appIconFile->readImageBlob(file_get_contents($appIcon));
  128. $appIconFile->scaleImage(512, 512, true);
  129. }
  130. // offset for icon positioning
  131. $border_w = (int)($appIconFile->getImageWidth() * 0.05);
  132. $border_h = (int)($appIconFile->getImageHeight() * 0.05);
  133. $innerWidth = (int)($appIconFile->getImageWidth() - $border_w * 2);
  134. $innerHeight = (int)($appIconFile->getImageHeight() - $border_h * 2);
  135. $appIconFile->adaptiveResizeImage($innerWidth, $innerHeight);
  136. // center icon
  137. $offset_w = 512 / 2 - $innerWidth / 2;
  138. $offset_h = 512 / 2 - $innerHeight / 2;
  139. $appIconFile->setImageFormat("png24");
  140. $finalIconFile = new Imagick();
  141. $finalIconFile->setBackgroundColor(new ImagickPixel('transparent'));
  142. $finalIconFile->readImageBlob($background);
  143. $finalIconFile->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
  144. $finalIconFile->setImageArtifact('compose:args', "1,0,-0.5,0.5");
  145. $finalIconFile->compositeImage($appIconFile, Imagick::COMPOSITE_ATOP, $offset_w, $offset_h);
  146. $finalIconFile->setImageFormat('png24');
  147. if (defined("Imagick::INTERPOLATE_BICUBIC") === true) {
  148. $filter = Imagick::INTERPOLATE_BICUBIC;
  149. } else {
  150. $filter = Imagick::FILTER_LANCZOS;
  151. }
  152. $finalIconFile->resizeImage($size, $size, $filter, 1, false);
  153. $appIconFile->destroy();
  154. return $finalIconFile;
  155. }
  156. public function colorSvg($app, $image) {
  157. try {
  158. $imageFile = $this->util->getAppImage($app, $image);
  159. } catch (AppPathNotFoundException $e) {
  160. return false;
  161. }
  162. $svg = file_get_contents($imageFile);
  163. if ($svg !== false && $svg !== "") {
  164. $color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor());
  165. $svg = $this->util->colorizeSvg($svg, $color);
  166. return $svg;
  167. } else {
  168. return false;
  169. }
  170. }
  171. }