Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
356 changes: 0 additions & 356 deletions .reuse/dep5

This file was deleted.

526 changes: 526 additions & 0 deletions REUSE.toml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/files-checker.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
'.npmignore',
'.php-cs-fixer.dist.php',
'.pre-commit-config.yaml',
'.reuse',
'.tag',
'.tx',
'.user.ini',
Expand All @@ -42,6 +41,7 @@
'DESIGN.md',
'Makefile',
'README.md',
'REUSE.toml',
'SECURITY.md',
'apps',
'autotest-checkers.sh',
Expand Down
92 changes: 0 additions & 92 deletions core/fonts/LICENSE_OFL.txt

This file was deleted.

Binary file added core/fonts/NotoSansHK-Regular.ttf
Binary file not shown.
Binary file added core/fonts/NotoSansJP-Regular.ttf
Binary file not shown.
Binary file added core/fonts/NotoSansKR-Regular.ttf
Binary file not shown.
Binary file added core/fonts/NotoSansSC-Regular.ttf
Binary file not shown.
Binary file added core/fonts/NotoSansTC-Regular.ttf
Binary file not shown.
51 changes: 41 additions & 10 deletions lib/private/Avatar/Avatar.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
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/
Expand All @@ -35,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,
) {
}

/**
Expand Down Expand Up @@ -84,8 +86,7 @@ public function get(int $size = 64, bool $darkTheme = false) {
* @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());
Expand All @@ -95,10 +96,31 @@ protected function getAvatarVector(int $size, bool $darkTheme): string {
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;
}
Expand All @@ -107,9 +129,10 @@ protected function generateAvatarFromSvg(int $size, bool $darkTheme): ?string {
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);
Expand Down Expand Up @@ -151,7 +174,7 @@ protected function generateAvatar(string $userDisplayName, int $size, bool $dark
}
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(
Expand Down Expand Up @@ -258,4 +281,12 @@ public function avatarBackgroundColor(string $hash): Color {

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');
}
}
6 changes: 3 additions & 3 deletions lib/private/Avatar/AvatarManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ public function getAvatar(string $userId): IAvatar {
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);
}

/**
Expand Down Expand Up @@ -129,6 +129,6 @@ public function deleteUserAvatar(string $userId): void {
* @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);
}
}
4 changes: 3 additions & 1 deletion lib/private/Avatar/GuestAvatar.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use OCP\Files\SimpleFS\InMemoryFile;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IConfig;
use Psr\Log\LoggerInterface;

/**
Expand All @@ -23,9 +24,10 @@ class GuestAvatar extends Avatar {
*/
public function __construct(
private string $userDisplayName,
IConfig $config,
LoggerInterface $logger,
) {
parent::__construct($logger);
parent::__construct($config, $logger);
}

/**
Expand Down
9 changes: 6 additions & 3 deletions lib/private/Avatar/PlaceholderAvatar.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\IImage;
use Psr\Log\LoggerInterface;

Expand All @@ -27,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);
}

/**
Expand Down Expand Up @@ -87,8 +89,9 @@ public function getFile(int $size, bool $darkTheme = false): ISimpleFile {
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 {
Expand Down
21 changes: 14 additions & 7 deletions lib/private/Avatar/UserAvatar.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,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);
}

/**
Expand Down Expand Up @@ -201,8 +201,9 @@ public function getFile(int $size, bool $darkTheme = false): ISimpleFile {
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);
Expand Down Expand Up @@ -234,8 +235,9 @@ public function getFile(int $size, bool $darkTheme = false): ISimpleFile {
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();
Expand Down Expand Up @@ -293,4 +295,9 @@ public function userChanged(string $feature, $oldValue, $newValue): void {
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());
}
}
2 changes: 1 addition & 1 deletion tests/lib/Avatar/AvatarManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ public function testGetAvatarScopes($avatarScope, $isPublicCall, $isKnownUser, $
}

if ($expectedPlaceholder) {
$expected = new PlaceholderAvatar($folder, $user, $this->createMock(LoggerInterface::class));
$expected = new PlaceholderAvatar($folder, $user, $this->config, $this->logger);
} else {
$expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
}
Expand Down
5 changes: 3 additions & 2 deletions tests/lib/Avatar/GuestAvatarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ class GuestAvatarTest extends TestCase {
*/
public function setupGuestAvatar() {
/* @var MockObject|LoggerInterface $logger */
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$this->guestAvatar = new GuestAvatar('einstein', $logger);
$logger = $this->createMock(LoggerInterface::class);
$config = $this->createMock(\OCP\IConfig::class);
$this->guestAvatar = new GuestAvatar('einstein', $config, $logger);
}

/**
Expand Down
Loading
Loading