123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- <?php
- /**
- * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
- namespace OCA\Theming;
- use Imagick;
- use ImagickPixel;
- use OCP\Files\SimpleFS\ISimpleFile;
- class IconBuilder {
- /**
- * IconBuilder constructor.
- *
- * @param ThemingDefaults $themingDefaults
- * @param Util $util
- * @param ImageManager $imageManager
- */
- public function __construct(
- private ThemingDefaults $themingDefaults,
- private Util $util,
- private ImageManager $imageManager,
- ) {
- }
- /**
- * @param $app string app name
- * @return string|false image blob
- */
- public function getFavicon($app) {
- if (!$this->imageManager->shouldReplaceIcons()) {
- return false;
- }
- try {
- $favicon = new Imagick();
- $favicon->setFormat('ico');
- $icon = $this->renderAppIcon($app, 128);
- if ($icon === false) {
- return false;
- }
- $icon->setImageFormat('png32');
- $clone = clone $icon;
- $clone->scaleImage(16, 0);
- $favicon->addImage($clone);
- $clone = clone $icon;
- $clone->scaleImage(32, 0);
- $favicon->addImage($clone);
- $clone = clone $icon;
- $clone->scaleImage(64, 0);
- $favicon->addImage($clone);
- $clone = clone $icon;
- $clone->scaleImage(128, 0);
- $favicon->addImage($clone);
- $data = $favicon->getImagesBlob();
- $favicon->destroy();
- $icon->destroy();
- $clone->destroy();
- return $data;
- } catch (\ImagickException $e) {
- return false;
- }
- }
- /**
- * @param $app string app name
- * @return string|false image blob
- */
- public function getTouchIcon($app) {
- try {
- $icon = $this->renderAppIcon($app, 512);
- if ($icon === false) {
- return false;
- }
- $icon->setImageFormat('png32');
- $data = $icon->getImageBlob();
- $icon->destroy();
- return $data;
- } catch (\ImagickException $e) {
- return false;
- }
- }
- /**
- * Render app icon on themed background color
- * fallback to logo
- *
- * @param $app string app name
- * @param $size int size of the icon in px
- * @return Imagick|false
- */
- public function renderAppIcon($app, $size) {
- $appIcon = $this->util->getAppIcon($app);
- if ($appIcon === false) {
- return false;
- }
- if ($appIcon instanceof ISimpleFile) {
- $appIconContent = $appIcon->getContent();
- $mime = $appIcon->getMimeType();
- } else {
- $appIconContent = file_get_contents($appIcon);
- $mime = mime_content_type($appIcon);
- }
- if ($appIconContent === false || $appIconContent === '') {
- return false;
- }
- $color = $this->themingDefaults->getColorPrimary();
- // generate background image with rounded corners
- $cornerRadius = 0.2 * $size;
- $background = '<?xml version="1.0" encoding="UTF-8"?>' .
- '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" width="' . $size . '" height="' . $size . '" xmlns:xlink="http://www.w3.org/1999/xlink">' .
- '<rect x="0" y="0" rx="' . $cornerRadius . '" ry="' . $cornerRadius . '" width="' . $size . '" height="' . $size . '" style="fill:' . $color . ';" />' .
- '</svg>';
- // resize svg magic as this seems broken in Imagemagick
- if ($mime === 'image/svg+xml' || substr($appIconContent, 0, 4) === '<svg') {
- if (substr($appIconContent, 0, 5) !== '<?xml') {
- $svg = '<?xml version="1.0"?>' . $appIconContent;
- } else {
- $svg = $appIconContent;
- }
- $tmp = new Imagick();
- $tmp->setBackgroundColor(new ImagickPixel('transparent'));
- $tmp->setResolution(72, 72);
- $tmp->readImageBlob($svg);
- $x = $tmp->getImageWidth();
- $y = $tmp->getImageHeight();
- $tmp->destroy();
- // convert svg to resized image
- $appIconFile = new Imagick();
- $resX = (int)(72 * $size / $x);
- $resY = (int)(72 * $size / $y);
- $appIconFile->setResolution($resX, $resY);
- $appIconFile->setBackgroundColor(new ImagickPixel('transparent'));
- $appIconFile->readImageBlob($svg);
- /**
- * invert app icons for bright primary colors
- * the default nextcloud logo will not be inverted to black
- */
- if ($this->util->isBrightColor($color)
- && !$appIcon instanceof ISimpleFile
- && $app !== 'core'
- ) {
- $appIconFile->negateImage(false);
- }
- } else {
- $appIconFile = new Imagick();
- $appIconFile->setBackgroundColor(new ImagickPixel('transparent'));
- $appIconFile->readImageBlob($appIconContent);
- }
- // offset for icon positioning
- $padding = 0.15;
- $border_w = (int)($appIconFile->getImageWidth() * $padding);
- $border_h = (int)($appIconFile->getImageHeight() * $padding);
- $innerWidth = ($appIconFile->getImageWidth() - $border_w * 2);
- $innerHeight = ($appIconFile->getImageHeight() - $border_h * 2);
- $appIconFile->adaptiveResizeImage($innerWidth, $innerHeight);
- // center icon
- $offset_w = (int)($size / 2 - $innerWidth / 2);
- $offset_h = (int)($size / 2 - $innerHeight / 2);
- $finalIconFile = new Imagick();
- $finalIconFile->setBackgroundColor(new ImagickPixel('transparent'));
- $finalIconFile->readImageBlob($background);
- $finalIconFile->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
- $finalIconFile->setImageArtifact('compose:args', '1,0,-0.5,0.5');
- $finalIconFile->compositeImage($appIconFile, Imagick::COMPOSITE_ATOP, $offset_w, $offset_h);
- $finalIconFile->setImageFormat('png24');
- if (defined('Imagick::INTERPOLATE_BICUBIC') === true) {
- $filter = Imagick::INTERPOLATE_BICUBIC;
- } else {
- $filter = Imagick::FILTER_LANCZOS;
- }
- $finalIconFile->resizeImage($size, $size, $filter, 1, false);
- $appIconFile->destroy();
- return $finalIconFile;
- }
- /**
- * @param $app string app name
- * @param $image string relative path to svg file in app directory
- * @return string|false content of a colorized svg file
- */
- public function colorSvg($app, $image) {
- $imageFile = $this->util->getAppImage($app, $image);
- if ($imageFile === false || $imageFile === '') {
- return false;
- }
- $svg = file_get_contents($imageFile);
- if ($svg !== false && $svg !== '') {
- $color = $this->util->elementColor($this->themingDefaults->getColorPrimary());
- $svg = $this->util->colorizeSvg($svg, $color);
- return $svg;
- } else {
- return false;
- }
- }
- }
|