From 604596ec166602117aaf51e86b7138a519b62742 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Thu, 19 Oct 2017 16:22:43 +0200 Subject: [PATCH 1/6] Always generate avatar Even if no avatar is set we should just generate the image. This to not duplicate the code on all the clients. And only server images from the avtar endpoint. Signed-off-by: Roeland Jago Douma --- core/Controller/AvatarController.php | 160 ++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 5 deletions(-) diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index 36b12fbd79c16..5b762a4fd3fa1 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -111,6 +111,136 @@ public function __construct($appName, $this->timeFactory = $timeFactory; } + + /** + * @param int $r + * @param int $g + * @param int $b + * @return double[] Array containing h s l in [0, 1] range + */ + private function rgbToHsl($r, $g, $b) { + $r /= 255.0; + $g /= 255.0; + $b /= 255.0; + + $max = max($r, $g, $b); + $min = min($r, $g, $b); + + + $h = ($max + $min) / 2.0; + $l = ($max + $min) / 2.0; + + if($max === $min) { + $h = $s = 0; // Achromatic + } else { + $d = $max - $min; + $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); + switch($max) { + case $r: + $h = ($g - $b) / $d + ($g < $b ? 6 : 0); + break; + case $g: + $h = ($b - $r) / $d + 2.0; + break; + case $b: + $h = ($r - $g) / $d + 4.0; + break; + } + $h /= 6.0; + } + return [$h, $s, $l]; + + } + + /** + * @param string $text + * @return int[] Array containting r g b in the range [0, 255] + */ + private function avatarBackgroundColor($text) { + $hash = preg_replace('/[^0-9a-f]+/', '', $text); + + $hash = md5($hash); + $hashChars = str_split($hash); + + + // Init vars + $result = ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']; + $rgb = [0, 0, 0]; + $sat = 0.70; + $lum = 0.68; + $modulo = 16; + + + // Splitting evenly the string + foreach($hashChars as $i => $char) { + $result[$i % $modulo] .= intval($char, 16); + } + + // Converting our data into a usable rgb format + // Start at 1 because 16%3=1 but 15%3=0 and makes the repartition even + for($count = 1; $count < $modulo; $count++) { + $rgb[$count%3] += (int)$result[$count]; + } + + // Reduce values bigger than rgb requirements + $rgb[0] %= 255; + $rgb[1] %= 255; + $rgb[2] %= 255; + + $hsl = $this->rgbToHsl($rgb[0], $rgb[1], $rgb[2]); + + // Classic formulla to check the brigtness for our eye + // If too bright, lower the sat + $bright = sqrt(0.299 * ($rgb[0] ** 2) + 0.587 * ($rgb[1] ** 2) + 0.114 * ($rgb[2] ** 2)); + if ($bright >= 200) { + $sat = 0.60; + } + + return $this->hslToRgb($hsl[0], $sat, $lum); + } + + /** + * @param double $h Heu in range [0, 1] + * @param double $s Saturation in range [0, 1] + * @param double $l Lightness in range [0, 1] + * @return int[] Array containging r g b in the range [0, 255] + */ + private function hslToRgb($h, $s, $l){ + $hue2rgb = function ($p, $q, $t){ + if($t < 0) { + $t += 1; + } + if($t > 1) { + $t -= 1; + } + if($t < 1/6) { + return $p + ($q - $p) * 6 * $t; + } + if($t < 1/2) { + return $q; + } + if($t < 2/3) { + return $p + ($q - $p) * (2/3 - $t) * 6; + } + return $p; + }; + + if($s == 0){ + $r = $l; + $g = $l; + $b = $l; // achromatic + }else{ + $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s; + $p = 2 * $l - $q; + $r = $hue2rgb($p, $q, $h + 1/3); + $g = $hue2rgb($p, $q, $h); + $b = $hue2rgb($p, $q, $h - 1/3); + } + + return array(round($r * 255), round($g * 255), round($b * 255)); + } + + /** * @NoAdminRequired * @NoCSRFRequired @@ -135,11 +265,31 @@ public function getAvatar($userId, $size) { ['Content-Type' => $avatar->getMimeType()]); } catch (NotFoundException $e) { $user = $this->userManager->get($userId); - $resp = new JSONResponse([ - 'data' => [ - 'displayname' => $user->getDisplayName(), - ], - ]); + $userDisplayName = $user->getDisplayName(); + $text = strtoupper(substr($userDisplayName, 0, 1)); + $backgroundColor = $this->avatarBackgroundColor($userDisplayName); + + $im = imagecreatetruecolor($size, $size); + $background = imagecolorallocate($im, $backgroundColor[0], $backgroundColor[1], $backgroundColor[2]); + $white = imagecolorallocate($im, 255, 255, 255); + imagefilledrectangle($im, 0, 0, $size, $size, $background); + + $font = __DIR__ . '/../../core/fonts/OpenSans-Light.woff'; + + $fontSize = $size * 0.4; + $box = imagettfbbox($fontSize, 0, $font, $text); + + $x = ($size - ($box[2] - $box[0])) / 2; + $y = ($size - ($box[1] - $box[7])) / 2; + $y -= $box[7]; + imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text); + + header('Content-Type: image/png'); + + imagepng($im); + imagedestroy($im); + + exit(); } catch (\Exception $e) { $resp = new JSONResponse([ 'data' => [ From ba648eecdff7dcb4feff676ca2cfc9c8b4f01ef5 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Fri, 20 Oct 2017 20:06:13 +0200 Subject: [PATCH 2/6] Move avatar code to Avatar Class Signed-off-by: Roeland Jago Douma --- core/Controller/AvatarController.php | 162 +--------------------- lib/private/Avatar.php | 197 +++++++++++++++++++++++++-- 2 files changed, 190 insertions(+), 169 deletions(-) diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index 5b762a4fd3fa1..c01e81a1790c0 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -112,133 +112,6 @@ public function __construct($appName, } - /** - * @param int $r - * @param int $g - * @param int $b - * @return double[] Array containing h s l in [0, 1] range - */ - private function rgbToHsl($r, $g, $b) { - $r /= 255.0; - $g /= 255.0; - $b /= 255.0; - - $max = max($r, $g, $b); - $min = min($r, $g, $b); - - - $h = ($max + $min) / 2.0; - $l = ($max + $min) / 2.0; - - if($max === $min) { - $h = $s = 0; // Achromatic - } else { - $d = $max - $min; - $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); - switch($max) { - case $r: - $h = ($g - $b) / $d + ($g < $b ? 6 : 0); - break; - case $g: - $h = ($b - $r) / $d + 2.0; - break; - case $b: - $h = ($r - $g) / $d + 4.0; - break; - } - $h /= 6.0; - } - return [$h, $s, $l]; - - } - - /** - * @param string $text - * @return int[] Array containting r g b in the range [0, 255] - */ - private function avatarBackgroundColor($text) { - $hash = preg_replace('/[^0-9a-f]+/', '', $text); - - $hash = md5($hash); - $hashChars = str_split($hash); - - - // Init vars - $result = ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']; - $rgb = [0, 0, 0]; - $sat = 0.70; - $lum = 0.68; - $modulo = 16; - - - // Splitting evenly the string - foreach($hashChars as $i => $char) { - $result[$i % $modulo] .= intval($char, 16); - } - - // Converting our data into a usable rgb format - // Start at 1 because 16%3=1 but 15%3=0 and makes the repartition even - for($count = 1; $count < $modulo; $count++) { - $rgb[$count%3] += (int)$result[$count]; - } - - // Reduce values bigger than rgb requirements - $rgb[0] %= 255; - $rgb[1] %= 255; - $rgb[2] %= 255; - - $hsl = $this->rgbToHsl($rgb[0], $rgb[1], $rgb[2]); - - // Classic formulla to check the brigtness for our eye - // If too bright, lower the sat - $bright = sqrt(0.299 * ($rgb[0] ** 2) + 0.587 * ($rgb[1] ** 2) + 0.114 * ($rgb[2] ** 2)); - if ($bright >= 200) { - $sat = 0.60; - } - - return $this->hslToRgb($hsl[0], $sat, $lum); - } - - /** - * @param double $h Heu in range [0, 1] - * @param double $s Saturation in range [0, 1] - * @param double $l Lightness in range [0, 1] - * @return int[] Array containging r g b in the range [0, 255] - */ - private function hslToRgb($h, $s, $l){ - $hue2rgb = function ($p, $q, $t){ - if($t < 0) { - $t += 1; - } - if($t > 1) { - $t -= 1; - } - if($t < 1/6) { - return $p + ($q - $p) * 6 * $t; - } - if($t < 1/2) { - return $q; - } - if($t < 2/3) { - return $p + ($q - $p) * (2/3 - $t) * 6; - } - return $p; - }; - - if($s == 0){ - $r = $l; - $g = $l; - $b = $l; // achromatic - }else{ - $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s; - $p = 2 * $l - $q; - $r = $hue2rgb($p, $q, $h + 1/3); - $g = $hue2rgb($p, $q, $h); - $b = $hue2rgb($p, $q, $h - 1/3); - } - - return array(round($r * 255), round($g * 255), round($b * 255)); - } /** @@ -263,39 +136,10 @@ public function getAvatar($userId, $size) { $resp = new FileDisplayResponse($avatar, Http::STATUS_OK, ['Content-Type' => $avatar->getMimeType()]); - } catch (NotFoundException $e) { - $user = $this->userManager->get($userId); - $userDisplayName = $user->getDisplayName(); - $text = strtoupper(substr($userDisplayName, 0, 1)); - $backgroundColor = $this->avatarBackgroundColor($userDisplayName); - - $im = imagecreatetruecolor($size, $size); - $background = imagecolorallocate($im, $backgroundColor[0], $backgroundColor[1], $backgroundColor[2]); - $white = imagecolorallocate($im, 255, 255, 255); - imagefilledrectangle($im, 0, 0, $size, $size, $background); - - $font = __DIR__ . '/../../core/fonts/OpenSans-Light.woff'; - - $fontSize = $size * 0.4; - $box = imagettfbbox($fontSize, 0, $font, $text); - - $x = ($size - ($box[2] - $box[0])) / 2; - $y = ($size - ($box[1] - $box[7])) / 2; - $y -= $box[7]; - imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text); - - header('Content-Type: image/png'); - - imagepng($im); - imagedestroy($im); - - exit(); } catch (\Exception $e) { - $resp = new JSONResponse([ - 'data' => [ - 'displayname' => $userId, - ], - ]); + $resp = new Http\Response(); + $resp->setStatus(Http::STATUS_NOT_FOUND); + return $resp; } // Let cache this! diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index b2d1b2be3186f..47f7ffdab3749 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -124,15 +124,15 @@ public function set ($data) { $type = 'jpg'; } if ($type !== 'jpg' && $type !== 'png') { - throw new \Exception($this->l->t("Unknown filetype")); + throw new \Exception($this->l->t('Unknown filetype')); } if (!$img->valid()) { - throw new \Exception($this->l->t("Invalid image")); + throw new \Exception($this->l->t('Invalid image')); } if (!($img->height() === $img->width())) { - throw new NotSquareException($this->l->t("Avatar image is not square")); + throw new NotSquareException($this->l->t('Avatar image is not square')); } $this->remove(); @@ -164,7 +164,16 @@ public function remove () { * @inheritdoc */ public function getFile($size) { - $ext = $this->getExtension(); + try { + $ext = $this->getExtension(); + } catch (NotFoundException $e) { + $data = $this->generateAvatar($this->user->getDisplayName(), 1024); + $avatar = $this->folder->newFile('avatar.png'); + $avatar->putContent($data); + $ext = 'png'; + + $this->folder->newFile('generated'); + } if ($size === -1) { $path = 'avatar.' . $ext; @@ -179,19 +188,26 @@ public function getFile($size) { throw new NotFoundException; } - $avatar = new OC_Image(); - /** @var ISimpleFile $file */ - $file = $this->folder->getFile('avatar.' . $ext); - $avatar->loadFromData($file->getContent()); - if ($size !== -1) { + if ($this->folder->fileExists('generated')) { + $data = $this->generateAvatar($this->user->getDisplayName(), $size); + + } else { + $avatar = new OC_Image(); + /** @var ISimpleFile $file */ + $file = $this->folder->getFile('avatar.' . $ext); + $avatar->loadFromData($file->getContent()); $avatar->resize($size); + $data = $avatar->data(); } + try { $file = $this->folder->newFile($path); - $file->putContent($avatar->data()); + $file->putContent($data); } catch (NotPermittedException $e) { $this->logger->error('Failed to save avatar for ' . $this->user->getUID()); + throw new NotFoundException(); } + } return $file; @@ -211,4 +227,165 @@ private function getExtension() { } throw new NotFoundException; } + + /** + * @param string $userDisplayName + * @param int $size + * @return string + */ + private function generateAvatar($userDisplayName, $size) { + $text = strtoupper(substr($userDisplayName, 0, 1)); + $backgroundColor = $this->avatarBackgroundColor($userDisplayName); + + $im = imagecreatetruecolor($size, $size); + $background = imagecolorallocate($im, $backgroundColor[0], $backgroundColor[1], $backgroundColor[2]); + $white = imagecolorallocate($im, 255, 255, 255); + imagefilledrectangle($im, 0, 0, $size, $size, $background); + + $font = __DIR__ . '/../../core/fonts/OpenSans-Light.woff'; + + $fontSize = $size * 0.4; + $box = imagettfbbox($fontSize, 0, $font, $text); + + $x = ($size - ($box[2] - $box[0])) / 2; + $y = ($size - ($box[1] - $box[7])) / 2; + $y -= $box[7]; + imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text); + + ob_start(); + imagepng($im); + $data = ob_get_contents(); + ob_end_clean(); + + return $data; + } + + /** + * @param int $r + * @param int $g + * @param int $b + * @return double[] Array containing h s l in [0, 1] range + */ + private function rgbToHsl($r, $g, $b) { + $r /= 255.0; + $g /= 255.0; + $b /= 255.0; + + $max = max($r, $g, $b); + $min = min($r, $g, $b); + + + $h = ($max + $min) / 2.0; + $l = ($max + $min) / 2.0; + + if($max === $min) { + $h = $s = 0; // Achromatic + } else { + $d = $max - $min; + $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); + switch($max) { + case $r: + $h = ($g - $b) / $d + ($g < $b ? 6 : 0); + break; + case $g: + $h = ($b - $r) / $d + 2.0; + break; + case $b: + $h = ($r - $g) / $d + 4.0; + break; + } + $h /= 6.0; + } + return [$h, $s, $l]; + + } + + /** + * @param string $text + * @return int[] Array containting r g b in the range [0, 255] + */ + private function avatarBackgroundColor($text) { + $hash = preg_replace('/[^0-9a-f]+/', '', $text); + + $hash = md5($hash); + $hashChars = str_split($hash); + + + // Init vars + $result = ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0']; + $rgb = [0, 0, 0]; + $sat = 0.70; + $lum = 0.68; + $modulo = 16; + + + // Splitting evenly the string + foreach($hashChars as $i => $char) { + $result[$i % $modulo] .= intval($char, 16); + } + + // Converting our data into a usable rgb format + // Start at 1 because 16%3=1 but 15%3=0 and makes the repartition even + for($count = 1; $count < $modulo; $count++) { + $rgb[$count%3] += (int)$result[$count]; + } + + // Reduce values bigger than rgb requirements + $rgb[0] %= 255; + $rgb[1] %= 255; + $rgb[2] %= 255; + + $hsl = $this->rgbToHsl($rgb[0], $rgb[1], $rgb[2]); + + // Classic formula to check the brightness for our eye + // If too bright, lower the sat + $bright = sqrt(0.299 * ($rgb[0] ** 2) + 0.587 * ($rgb[1] ** 2) + 0.114 * ($rgb[2] ** 2)); + if ($bright >= 200) { + $sat = 0.60; + } + + return $this->hslToRgb($hsl[0], $sat, $lum); + } + + /** + * @param double $h Hue in range [0, 1] + * @param double $s Saturation in range [0, 1] + * @param double $l Lightness in range [0, 1] + * @return int[] Array containing r g b in the range [0, 255] + */ + private function hslToRgb($h, $s, $l){ + $hue2rgb = function ($p, $q, $t){ + if($t < 0) { + $t += 1; + } + if($t > 1) { + $t -= 1; + } + if($t < 1/6) { + return $p + ($q - $p) * 6 * $t; + } + if($t < 1/2) { + return $q; + } + if($t < 2/3) { + return $p + ($q - $p) * (2/3 - $t) * 6; + } + return $p; + }; + + if($s === 0){ + $r = $l; + $g = $l; + $b = $l; // achromatic + }else{ + $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s; + $p = 2 * $l - $q; + $r = $hue2rgb($p, $q, $h + 1/3); + $g = $hue2rgb($p, $q, $h); + $b = $hue2rgb($p, $q, $h - 1/3); + } + + return array(round($r * 255), round($g * 255), round($b * 255)); + } + } From 03d9e7e963c704eb5b4f48c495fac9310b9f2e0d Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Fri, 20 Oct 2017 20:08:45 +0200 Subject: [PATCH 3/6] Sinc we use AppData the regex is not needed anymore Signed-off-by: Roeland Jago Douma --- lib/private/Avatar.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 47f7ffdab3749..833d430ac0129 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -146,16 +146,13 @@ public function set ($data) { * @return void */ public function remove () { - $regex = '/^avatar\.([0-9]+\.)?(jpg|png)$/'; $avatars = $this->folder->getDirectoryListing(); $this->config->setUserValue($this->user->getUID(), 'avatar', 'version', (int)$this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1); foreach ($avatars as $avatar) { - if (preg_match($regex, $avatar->getName())) { - $avatar->delete(); - } + $avatar->delete(); } $this->user->triggerChange('avatar', ''); } From d5496dc435eeff931ab1d540ea4b069520e88e12 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Fri, 20 Oct 2017 20:15:26 +0200 Subject: [PATCH 4/6] Since we update the avatar in the sync service remove the generated file Signed-off-by: Roeland Jago Douma --- lib/private/Avatar.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 833d430ac0129..583a83f5166ed 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -138,6 +138,13 @@ public function set ($data) { $this->remove(); $file = $this->folder->newFile('avatar.'.$type); $file->putContent($data); + + try { + $generated = $this->folder->getFile('generated'); + $generated->delete(); + } catch (NotFoundException $e) { + // + } $this->user->triggerChange('avatar', $file); } From 8e8fe6b8ebc4b3ad32a72e1262aed82c96a52b36 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Thu, 9 Nov 2017 11:34:50 +0100 Subject: [PATCH 5/6] Fix tests Signed-off-by: Roeland Jago Douma --- .../Core/Controller/AvatarControllerTest.php | 8 +--- tests/lib/AvatarTest.php | 44 ++++++++++++++++--- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/tests/Core/Controller/AvatarControllerTest.php b/tests/Core/Controller/AvatarControllerTest.php index de568438022d2..3194d671908ac 100644 --- a/tests/Core/Controller/AvatarControllerTest.php +++ b/tests/Core/Controller/AvatarControllerTest.php @@ -134,9 +134,7 @@ public function testGetAvatarNoAvatar() { $response = $this->avatarController->getAvatar('userId', 32); //Comment out until JS is fixed - //$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); - $this->assertEquals(Http::STATUS_OK, $response->getStatus()); - $this->assertEquals('displayName', $response->getData()['data']['displayname']); + $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); } /** @@ -167,9 +165,7 @@ public function testGetAvatarNoUser() { $response = $this->avatarController->getAvatar('userDoesNotExist', 32); //Comment out until JS is fixed - //$this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); - $this->assertEquals(Http::STATUS_OK, $response->getStatus()); - $this->assertEquals('userDoesNotExist', $response->getData()['data']['displayname']); + $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus()); } /** diff --git a/tests/lib/AvatarTest.php b/tests/lib/AvatarTest.php index cea3f9bed1a09..240aecc115e95 100644 --- a/tests/lib/AvatarTest.php +++ b/tests/lib/AvatarTest.php @@ -12,6 +12,8 @@ use OC\User\User; use OCP\Files\File; use OCP\Files\Folder; +use OCP\Files\NotFoundException; +use OCP\Files\SimpleFS\ISimpleFile; use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; @@ -49,7 +51,35 @@ public function setUp() { } public function testGetNoAvatar() { - $this->assertEquals(false, $this->avatar->get()); + $file = $this->createMock(ISimpleFile::class); + $this->folder->method('newFile') + ->willReturn($file); + + $this->folder->method('getFile') + ->will($this->returnCallback(function($path) { + if ($path === 'avatar.64.png') { + throw new NotFoundException(); + } + })); + $this->folder->method('fileExists') + ->will($this->returnCallback(function($path) { + if ($path === 'generated') { + return true; + } + return false; + })); + + $data = NULL; + $file->method('putContent') + ->with($this->callback(function ($d) use (&$data) { + $data = $d; + return true; + })); + + $file->method('getContent') + ->willReturn($data); + + $this->assertEquals($data, $this->avatar->get()->data()); } public function testGetAvatarSizeMatch() { @@ -161,13 +191,13 @@ public function testSetAvatar() { ->willReturn('avatar.32.jpg'); $resizedAvatarFile->expects($this->once())->method('delete'); - $nonAvatarFile = $this->createMock(File::class); - $nonAvatarFile->method('getName') - ->willReturn('avatarX'); - $nonAvatarFile->expects($this->never())->method('delete'); - $this->folder->method('getDirectoryListing') - ->willReturn([$avatarFileJPG, $avatarFilePNG, $resizedAvatarFile, $nonAvatarFile]); + ->willReturn([$avatarFileJPG, $avatarFilePNG, $resizedAvatarFile]); + + $generated = $this->createMock(File::class); + $this->folder->method('getFile') + ->with('generated') + ->willReturn($generated); $newFile = $this->createMock(File::class); $this->folder->expects($this->once()) From 66f523e13ff98aef8cdca9f449df7353dcf60da7 Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Fri, 8 Dec 2017 16:24:31 +0100 Subject: [PATCH 6/6] Better center the letter and use semibold font type Signed-off-by: Morris Jobke --- lib/private/Avatar.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/private/Avatar.php b/lib/private/Avatar.php index 583a83f5166ed..5893daa180431 100644 --- a/lib/private/Avatar.php +++ b/lib/private/Avatar.php @@ -246,13 +246,14 @@ private function generateAvatar($userDisplayName, $size) { $white = imagecolorallocate($im, 255, 255, 255); imagefilledrectangle($im, 0, 0, $size, $size, $background); - $font = __DIR__ . '/../../core/fonts/OpenSans-Light.woff'; + $font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.woff'; $fontSize = $size * 0.4; $box = imagettfbbox($fontSize, 0, $font, $text); $x = ($size - ($box[2] - $box[0])) / 2; $y = ($size - ($box[1] - $box[7])) / 2; + $x += 1; $y -= $box[7]; imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text);