diff options
Diffstat (limited to 'lib/private/Avatar')
-rw-r--r-- | lib/private/Avatar/Avatar.php | 92 | ||||
-rw-r--r-- | lib/private/Avatar/AvatarManager.php | 41 | ||||
-rw-r--r-- | lib/private/Avatar/GuestAvatar.php | 26 | ||||
-rw-r--r-- | lib/private/Avatar/PlaceholderAvatar.php | 31 | ||||
-rw-r--r-- | lib/private/Avatar/UserAvatar.php | 70 |
5 files changed, 97 insertions, 163 deletions
diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php index 020c509b86c..dc65c9d5743 100644 --- a/lib/private/Avatar/Avatar.php +++ b/lib/private/Avatar/Avatar.php @@ -3,51 +3,24 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright 2018 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sergey Shliakhov <husband.sergey@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Avatar; use Imagick; +use OC\User\User; use OCP\Color; use OCP\Files\NotFoundException; use OCP\IAvatar; +use OCP\IConfig; use Psr\Log\LoggerInterface; /** * This class gets and sets users avatars. */ abstract class Avatar implements IAvatar { - protected LoggerInterface $logger; - /** * https://github.com/sebdesign/cap-height -- for 500px height * Automated check: https://codepen.io/skjnldsv/pen/PydLBK/ @@ -62,8 +35,10 @@ abstract class Avatar implements IAvatar { <text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#{fgFill}">{letter}</text> </svg>'; - public function __construct(LoggerInterface $logger) { - $this->logger = $logger; + public function __construct( + protected IConfig $config, + protected LoggerInterface $logger, + ) { } /** @@ -111,32 +86,53 @@ abstract class Avatar implements IAvatar { * @return string * */ - protected function getAvatarVector(int $size, bool $darkTheme): string { - $userDisplayName = $this->getDisplayName(); + protected function getAvatarVector(string $userDisplayName, int $size, bool $darkTheme): string { $fgRGB = $this->avatarBackgroundColor($userDisplayName); $bgRGB = $fgRGB->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255)); - $fill = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue()); - $fgFill = sprintf("%02x%02x%02x", $fgRGB->red(), $fgRGB->green(), $fgRGB->blue()); + $fill = sprintf('%02x%02x%02x', $bgRGB->red(), $bgRGB->green(), $bgRGB->blue()); + $fgFill = sprintf('%02x%02x%02x', $fgRGB->red(), $fgRGB->green(), $fgRGB->blue()); $text = $this->getAvatarText(); $toReplace = ['{size}', '{fill}', '{fgFill}', '{letter}']; return str_replace($toReplace, [$size, $fill, $fgFill, $text], $this->svgTemplate); } /** + * Select the rendering font based on the user's display name and language + */ + private function getFont(string $userDisplayName): string { + if (preg_match('/\p{Han}/u', $userDisplayName) === 1) { + switch ($this->getAvatarLanguage()) { + case 'zh_TW': + return __DIR__ . '/../../../core/fonts/NotoSansTC-Regular.ttf'; + case 'zh_HK': + return __DIR__ . '/../../../core/fonts/NotoSansHK-Regular.ttf'; + case 'ja': + return __DIR__ . '/../../../core/fonts/NotoSansJP-Regular.ttf'; + case 'ko': + return __DIR__ . '/../../../core/fonts/NotoSansKR-Regular.ttf'; + default: + return __DIR__ . '/../../../core/fonts/NotoSansSC-Regular.ttf'; + } + } + return __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; + } + + /** * Generate png avatar from svg with Imagick */ - protected function generateAvatarFromSvg(int $size, bool $darkTheme): ?string { + protected function generateAvatarFromSvg(string $userDisplayName, int $size, bool $darkTheme): ?string { if (!extension_loaded('imagick')) { return null; } $formats = Imagick::queryFormats(); // Avatar generation breaks if RSVG format is enabled. Fall back to gd in that case - if (in_array("RSVG", $formats, true)) { + if (in_array('RSVG', $formats, true)) { return null; } + $text = $this->getAvatarText(); try { - $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; - $svg = $this->getAvatarVector($size, $darkTheme); + $font = $this->getFont($text); + $svg = $this->getAvatarVector($userDisplayName, $size, $darkTheme); $avatar = new Imagick(); $avatar->setFont($font); $avatar->readImageBlob($svg); @@ -178,7 +174,7 @@ abstract class Avatar implements IAvatar { } imagefilledrectangle($im, 0, 0, $size, $size, $background); - $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; + $font = $this->getFont($text); $fontSize = $size * 0.4; [$x, $y] = $this->imageTTFCenter( @@ -210,7 +206,7 @@ abstract class Avatar implements IAvatar { string $text, string $font, int $size, - int $angle = 0 + int $angle = 0, ): array { // Image width & height $xi = imagesx($image); @@ -285,4 +281,12 @@ abstract class Avatar implements IAvatar { return $finalPalette[$this->hashToInt($hash, $steps * 3)]; } + + /** + * Get the language to be used for avatar generation. + * This is used to determine the font to use for the avatar text (e.g. CJK characters). + */ + protected function getAvatarLanguage(): string { + return $this->config->getSystemValueString('default_language', 'en'); + } } diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php index 1584e02ee42..c68467085f0 100644 --- a/lib/private/Avatar/AvatarManager.php +++ b/lib/private/Avatar/AvatarManager.php @@ -1,37 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Avatar; @@ -119,10 +92,10 @@ class AvatarManager implements IAvatarManager { return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config); default: // use a placeholder avatar which caches the generated images - return new PlaceholderAvatar($folder, $user, $this->logger); + return new PlaceholderAvatar($folder, $user, $this->config, $this->logger); } - return new PlaceholderAvatar($folder, $user, $this->logger); + return new PlaceholderAvatar($folder, $user, $this->config, $this->logger); } /** @@ -142,7 +115,7 @@ class AvatarManager implements IAvatarManager { $folder->delete(); } catch (NotFoundException $e) { $this->logger->debug("No cache for the user $userId. Ignoring avatar deletion"); - } catch (NotPermittedException | StorageNotAvailableException $e) { + } catch (NotPermittedException|StorageNotAvailableException $e) { $this->logger->error("Unable to delete user avatars for $userId. gnoring avatar deletion"); } catch (NoUserException $e) { $this->logger->debug("Account $userId not found. Ignoring avatar deletion"); @@ -156,6 +129,6 @@ class AvatarManager implements IAvatarManager { * @param string $name The guest name, e.g. "Albert". */ public function getGuestAvatar(string $name): IAvatar { - return new GuestAvatar($name, $this->logger); + return new GuestAvatar($name, $this->config, $this->logger); } } diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php index 106e159d192..c0c7de0c078 100644 --- a/lib/private/Avatar/GuestAvatar.php +++ b/lib/private/Avatar/GuestAvatar.php @@ -3,31 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Avatar; use OCP\Files\SimpleFS\InMemoryFile; use OCP\Files\SimpleFS\ISimpleFile; +use OCP\IConfig; use Psr\Log\LoggerInterface; /** @@ -41,9 +24,10 @@ class GuestAvatar extends Avatar { */ public function __construct( private string $userDisplayName, + IConfig $config, LoggerInterface $logger, ) { - parent::__construct($logger); + parent::__construct($config, $logger); } /** diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php index d420ebe574a..f5f49fb7cb2 100644 --- a/lib/private/Avatar/PlaceholderAvatar.php +++ b/lib/private/Avatar/PlaceholderAvatar.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Avatar; @@ -32,6 +14,7 @@ use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\IConfig; use OCP\IImage; use Psr\Log\LoggerInterface; @@ -45,9 +28,10 @@ class PlaceholderAvatar extends Avatar { public function __construct( private ISimpleFolder $folder, private User $user, + IConfig $config, LoggerInterface $logger, ) { - parent::__construct($logger); + parent::__construct($config, $logger); } /** @@ -105,8 +89,9 @@ class PlaceholderAvatar extends Avatar { throw new NotFoundException; } - if (!$data = $this->generateAvatarFromSvg($size, $darkTheme)) { - $data = $this->generateAvatar($this->getDisplayName(), $size, $darkTheme); + $userDisplayName = $this->getDisplayName(); + if (!$data = $this->generateAvatarFromSvg($userDisplayName, $size, $darkTheme)) { + $data = $this->generateAvatar($userDisplayName, $size, $darkTheme); } try { diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php index f96259641f3..aca2aa574bc 100644 --- a/lib/private/Avatar/UserAvatar.php +++ b/lib/private/Avatar/UserAvatar.php @@ -3,29 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Avatar; @@ -47,11 +26,11 @@ class UserAvatar extends Avatar { public function __construct( private ISimpleFolder $folder, private IL10N $l, - private User $user, + protected User $user, LoggerInterface $logger, - private IConfig $config, + IConfig $config, ) { - parent::__construct($logger); + parent::__construct($config, $logger); } /** @@ -103,8 +82,8 @@ class UserAvatar extends Avatar { $img = new \OCP\Image(); if ( - (is_resource($data) && get_resource_type($data) === 'gd') || - (is_object($data) && get_class($data) === \GdImage::class) + (is_resource($data) && get_resource_type($data) === 'gd') + || (is_object($data) && get_class($data) === \GdImage::class) ) { $img->setResource($data); } elseif (is_resource($data)) { @@ -190,18 +169,20 @@ class UserAvatar extends Avatar { * @throws NotFoundException */ private function getExtension(bool $generated, bool $darkTheme): string { - if ($darkTheme && !$generated) { - if ($this->folder->fileExists('avatar-dark.jpg')) { - return 'jpg'; - } elseif ($this->folder->fileExists('avatar-dark.png')) { - return 'png'; - } + if ($darkTheme && $generated) { + $name = 'avatar-dark.'; + } else { + $name = 'avatar.'; } - if ($this->folder->fileExists('avatar.jpg')) { + + if ($this->folder->fileExists($name . 'jpg')) { return 'jpg'; - } elseif ($this->folder->fileExists('avatar.png')) { + } + + if ($this->folder->fileExists($name . 'png')) { return 'png'; } + throw new NotFoundException; } @@ -220,8 +201,9 @@ class UserAvatar extends Avatar { try { $ext = $this->getExtension($generated, $darkTheme); } catch (NotFoundException $e) { - if (!$data = $this->generateAvatarFromSvg(1024, $darkTheme)) { - $data = $this->generateAvatar($this->getDisplayName(), 1024, $darkTheme); + $userDisplayName = $this->getDisplayName(); + if (!$data = $this->generateAvatarFromSvg($userDisplayName, 1024, $darkTheme)) { + $data = $this->generateAvatar($userDisplayName, 1024, $darkTheme); } $avatar = $this->folder->newFile($darkTheme ? 'avatar-dark.png' : 'avatar.png'); $avatar->putContent($data); @@ -236,7 +218,7 @@ class UserAvatar extends Avatar { if ($size === -1) { $path = 'avatar' . ($darkTheme ? '-dark' : '') . '.' . $ext; } else { - $path = 'avatar' . ($darkTheme ? '-dark' : '') . '.' . $size . '.' . $ext; + $path = 'avatar' . ($darkTheme ? '-dark' : '') . '.' . $size . '.' . $ext; } } else { if ($size === -1) { @@ -253,8 +235,9 @@ class UserAvatar extends Avatar { throw new NotFoundException; } if ($generated) { - if (!$data = $this->generateAvatarFromSvg($size, $darkTheme)) { - $data = $this->generateAvatar($this->getDisplayName(), $size, $darkTheme); + $userDisplayName = $this->getDisplayName(); + if (!$data = $this->generateAvatarFromSvg($userDisplayName, $size, $darkTheme)) { + $data = $this->generateAvatar($userDisplayName, $size, $darkTheme); } } else { $avatar = new \OCP\Image(); @@ -312,4 +295,9 @@ class UserAvatar extends Avatar { public function isCustomAvatar(): bool { return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true'; } + + #[\Override] + protected function getAvatarLanguage(): string { + return $this->config->getUserValue($this->user->getUID(), 'core', 'lang', parent::getAvatarLanguage()); + } } |