Completed
Push — master ( 9cef2c...9cef2c )
by Maxence
02:35 queued 44s
created
lib/private/Avatar/Avatar.php 2 patches
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -46,231 +46,231 @@
 block discarded – undo
46 46
  * This class gets and sets users avatars.
47 47
  */
48 48
 abstract class Avatar implements IAvatar {
49
-	protected LoggerInterface $logger;
50
-
51
-	/**
52
-	 * https://github.com/sebdesign/cap-height -- for 500px height
53
-	 * Automated check: https://codepen.io/skjnldsv/pen/PydLBK/
54
-	 * Noto Sans cap-height is 0.715 and we want a 200px caps height size
55
-	 * (0.4 letter-to-total-height ratio, 500*0.4=200), so: 200/0.715 = 280px.
56
-	 * Since we start from the baseline (text-anchor) we need to
57
-	 * shift the y axis by 100px (half the caps height): 500/2+100=350
58
-	 */
59
-	private string $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
49
+    protected LoggerInterface $logger;
50
+
51
+    /**
52
+     * https://github.com/sebdesign/cap-height -- for 500px height
53
+     * Automated check: https://codepen.io/skjnldsv/pen/PydLBK/
54
+     * Noto Sans cap-height is 0.715 and we want a 200px caps height size
55
+     * (0.4 letter-to-total-height ratio, 500*0.4=200), so: 200/0.715 = 280px.
56
+     * Since we start from the baseline (text-anchor) we need to
57
+     * shift the y axis by 100px (half the caps height): 500/2+100=350
58
+     */
59
+    private string $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
60 60
 		<svg width="{size}" height="{size}" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
61 61
 			<rect width="100%" height="100%" fill="#{fill}"></rect>
62 62
 			<text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#{fgFill}">{letter}</text>
63 63
 		</svg>';
64 64
 
65
-	public function __construct(LoggerInterface $logger) {
66
-		$this->logger = $logger;
67
-	}
68
-
69
-	/**
70
-	 * Returns the user display name.
71
-	 */
72
-	abstract public function getDisplayName(): string;
73
-
74
-	/**
75
-	 * Returns the first letter of the display name, or "?" if no name given.
76
-	 */
77
-	private function getAvatarText(): string {
78
-		$displayName = $this->getDisplayName();
79
-		if (empty($displayName) === true) {
80
-			return '?';
81
-		}
82
-		$firstTwoLetters = array_map(function ($namePart) {
83
-			return mb_strtoupper(mb_substr($namePart, 0, 1), 'UTF-8');
84
-		}, explode(' ', $displayName, 2));
85
-		return implode('', $firstTwoLetters);
86
-	}
87
-
88
-	/**
89
-	 * @inheritdoc
90
-	 */
91
-	public function get(int $size = 64, bool $darkTheme = false) {
92
-		try {
93
-			$file = $this->getFile($size, $darkTheme);
94
-		} catch (NotFoundException $e) {
95
-			return false;
96
-		}
97
-
98
-		$avatar = new \OCP\Image();
99
-		$avatar->loadFromData($file->getContent());
100
-		return $avatar;
101
-	}
102
-
103
-	/**
104
-	 * {size} = 500
105
-	 * {fill} = hex color to fill
106
-	 * {letter} = Letter to display
107
-	 *
108
-	 * Generate SVG avatar
109
-	 *
110
-	 * @param int $size The requested image size in pixel
111
-	 * @return string
112
-	 *
113
-	 */
114
-	protected function getAvatarVector(int $size, bool $darkTheme): string {
115
-		$userDisplayName = $this->getDisplayName();
116
-		$fgRGB = $this->avatarBackgroundColor($userDisplayName);
117
-		$bgRGB = $fgRGB->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255));
118
-		$fill = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue());
119
-		$fgFill = sprintf("%02x%02x%02x", $fgRGB->red(), $fgRGB->green(), $fgRGB->blue());
120
-		$text = $this->getAvatarText();
121
-		$toReplace = ['{size}', '{fill}', '{fgFill}', '{letter}'];
122
-		return str_replace($toReplace, [$size, $fill, $fgFill, $text], $this->svgTemplate);
123
-	}
124
-
125
-	/**
126
-	 * Generate png avatar from svg with Imagick
127
-	 */
128
-	protected function generateAvatarFromSvg(int $size, bool $darkTheme): ?string {
129
-		if (!extension_loaded('imagick')) {
130
-			return null;
131
-		}
132
-		try {
133
-			$font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
134
-			$svg = $this->getAvatarVector($size, $darkTheme);
135
-			$avatar = new Imagick();
136
-			$avatar->setFont($font);
137
-			$avatar->readImageBlob($svg);
138
-			$avatar->setImageFormat('png');
139
-			$image = new \OCP\Image();
140
-			$image->loadFromData((string)$avatar);
141
-			return $image->data();
142
-		} catch (\Exception $e) {
143
-			return null;
144
-		}
145
-	}
146
-
147
-	/**
148
-	 * Generate png avatar with GD
149
-	 */
150
-	protected function generateAvatar(string $userDisplayName, int $size, bool $darkTheme): string {
151
-		$text = $this->getAvatarText();
152
-		$textColor = $this->avatarBackgroundColor($userDisplayName);
153
-		$backgroundColor = $textColor->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255));
154
-
155
-		$im = imagecreatetruecolor($size, $size);
156
-		$background = imagecolorallocate(
157
-			$im,
158
-			$backgroundColor->red(),
159
-			$backgroundColor->green(),
160
-			$backgroundColor->blue()
161
-		);
162
-		$textColor = imagecolorallocate($im,
163
-			$textColor->red(),
164
-			$textColor->green(),
165
-			$textColor->blue()
166
-		);
167
-		imagefilledrectangle($im, 0, 0, $size, $size, $background);
168
-
169
-		$font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
170
-
171
-		$fontSize = $size * 0.4;
172
-		[$x, $y] = $this->imageTTFCenter(
173
-			$im, $text, $font, (int)$fontSize
174
-		);
175
-
176
-		imagettftext($im, $fontSize, 0, $x, $y, $textColor, $font, $text);
177
-
178
-		ob_start();
179
-		imagepng($im);
180
-		$data = ob_get_contents();
181
-		ob_end_clean();
182
-
183
-		return $data;
184
-	}
185
-
186
-	/**
187
-	 * Calculate real image ttf center
188
-	 *
189
-	 * @param resource $image
190
-	 * @param string $text text string
191
-	 * @param string $font font path
192
-	 * @param int $size font size
193
-	 * @param int $angle
194
-	 * @return array
195
-	 */
196
-	protected function imageTTFCenter(
197
-		$image,
198
-		string $text,
199
-		string $font,
200
-		int $size,
201
-		int $angle = 0
202
-	): array {
203
-		// Image width & height
204
-		$xi = imagesx($image);
205
-		$yi = imagesy($image);
206
-
207
-		// bounding box
208
-		$box = imagettfbbox($size, $angle, $font, $text);
209
-
210
-		// imagettfbbox can return negative int
211
-		$xr = abs(max($box[2], $box[4]));
212
-		$yr = abs(max($box[5], $box[7]));
213
-
214
-		// calculate bottom left placement
215
-		$x = intval(($xi - $xr) / 2);
216
-		$y = intval(($yi + $yr) / 2);
217
-
218
-		return [$x, $y];
219
-	}
220
-
221
-
222
-	/**
223
-	 * Convert a string to an integer evenly
224
-	 * @param string $hash the text to parse
225
-	 * @param int $maximum the maximum range
226
-	 * @return int between 0 and $maximum
227
-	 */
228
-	private function hashToInt(string $hash, int $maximum): int {
229
-		$final = 0;
230
-		$result = [];
231
-
232
-		// Splitting evenly the string
233
-		for ($i = 0; $i < strlen($hash); $i++) {
234
-			// chars in md5 goes up to f, hex:16
235
-			$result[] = intval(substr($hash, $i, 1), 16) % 16;
236
-		}
237
-		// Adds up all results
238
-		foreach ($result as $value) {
239
-			$final += $value;
240
-		}
241
-		// chars in md5 goes up to f, hex:16
242
-		return intval($final % $maximum);
243
-	}
244
-
245
-	/**
246
-	 * @return Color Object containing r g b int in the range [0, 255]
247
-	 */
248
-	public function avatarBackgroundColor(string $hash): Color {
249
-		// Normalize hash
250
-		$hash = strtolower($hash);
251
-
252
-		// Already a md5 hash?
253
-		if (preg_match('/^([0-9a-f]{4}-?){8}$/', $hash, $matches) !== 1) {
254
-			$hash = md5($hash);
255
-		}
256
-
257
-		// Remove unwanted char
258
-		$hash = preg_replace('/[^0-9a-f]+/', '', $hash);
259
-
260
-		$red = new Color(182, 70, 157);
261
-		$yellow = new Color(221, 203, 85);
262
-		$blue = new Color(0, 130, 201); // Nextcloud blue
263
-
264
-		// Number of steps to go from a color to another
265
-		// 3 colors * 6 will result in 18 generated colors
266
-		$steps = 6;
267
-
268
-		$palette1 = Color::mixPalette($steps, $red, $yellow);
269
-		$palette2 = Color::mixPalette($steps, $yellow, $blue);
270
-		$palette3 = Color::mixPalette($steps, $blue, $red);
271
-
272
-		$finalPalette = array_merge($palette1, $palette2, $palette3);
273
-
274
-		return $finalPalette[$this->hashToInt($hash, $steps * 3)];
275
-	}
65
+    public function __construct(LoggerInterface $logger) {
66
+        $this->logger = $logger;
67
+    }
68
+
69
+    /**
70
+     * Returns the user display name.
71
+     */
72
+    abstract public function getDisplayName(): string;
73
+
74
+    /**
75
+     * Returns the first letter of the display name, or "?" if no name given.
76
+     */
77
+    private function getAvatarText(): string {
78
+        $displayName = $this->getDisplayName();
79
+        if (empty($displayName) === true) {
80
+            return '?';
81
+        }
82
+        $firstTwoLetters = array_map(function ($namePart) {
83
+            return mb_strtoupper(mb_substr($namePart, 0, 1), 'UTF-8');
84
+        }, explode(' ', $displayName, 2));
85
+        return implode('', $firstTwoLetters);
86
+    }
87
+
88
+    /**
89
+     * @inheritdoc
90
+     */
91
+    public function get(int $size = 64, bool $darkTheme = false) {
92
+        try {
93
+            $file = $this->getFile($size, $darkTheme);
94
+        } catch (NotFoundException $e) {
95
+            return false;
96
+        }
97
+
98
+        $avatar = new \OCP\Image();
99
+        $avatar->loadFromData($file->getContent());
100
+        return $avatar;
101
+    }
102
+
103
+    /**
104
+     * {size} = 500
105
+     * {fill} = hex color to fill
106
+     * {letter} = Letter to display
107
+     *
108
+     * Generate SVG avatar
109
+     *
110
+     * @param int $size The requested image size in pixel
111
+     * @return string
112
+     *
113
+     */
114
+    protected function getAvatarVector(int $size, bool $darkTheme): string {
115
+        $userDisplayName = $this->getDisplayName();
116
+        $fgRGB = $this->avatarBackgroundColor($userDisplayName);
117
+        $bgRGB = $fgRGB->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255));
118
+        $fill = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue());
119
+        $fgFill = sprintf("%02x%02x%02x", $fgRGB->red(), $fgRGB->green(), $fgRGB->blue());
120
+        $text = $this->getAvatarText();
121
+        $toReplace = ['{size}', '{fill}', '{fgFill}', '{letter}'];
122
+        return str_replace($toReplace, [$size, $fill, $fgFill, $text], $this->svgTemplate);
123
+    }
124
+
125
+    /**
126
+     * Generate png avatar from svg with Imagick
127
+     */
128
+    protected function generateAvatarFromSvg(int $size, bool $darkTheme): ?string {
129
+        if (!extension_loaded('imagick')) {
130
+            return null;
131
+        }
132
+        try {
133
+            $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
134
+            $svg = $this->getAvatarVector($size, $darkTheme);
135
+            $avatar = new Imagick();
136
+            $avatar->setFont($font);
137
+            $avatar->readImageBlob($svg);
138
+            $avatar->setImageFormat('png');
139
+            $image = new \OCP\Image();
140
+            $image->loadFromData((string)$avatar);
141
+            return $image->data();
142
+        } catch (\Exception $e) {
143
+            return null;
144
+        }
145
+    }
146
+
147
+    /**
148
+     * Generate png avatar with GD
149
+     */
150
+    protected function generateAvatar(string $userDisplayName, int $size, bool $darkTheme): string {
151
+        $text = $this->getAvatarText();
152
+        $textColor = $this->avatarBackgroundColor($userDisplayName);
153
+        $backgroundColor = $textColor->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255));
154
+
155
+        $im = imagecreatetruecolor($size, $size);
156
+        $background = imagecolorallocate(
157
+            $im,
158
+            $backgroundColor->red(),
159
+            $backgroundColor->green(),
160
+            $backgroundColor->blue()
161
+        );
162
+        $textColor = imagecolorallocate($im,
163
+            $textColor->red(),
164
+            $textColor->green(),
165
+            $textColor->blue()
166
+        );
167
+        imagefilledrectangle($im, 0, 0, $size, $size, $background);
168
+
169
+        $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
170
+
171
+        $fontSize = $size * 0.4;
172
+        [$x, $y] = $this->imageTTFCenter(
173
+            $im, $text, $font, (int)$fontSize
174
+        );
175
+
176
+        imagettftext($im, $fontSize, 0, $x, $y, $textColor, $font, $text);
177
+
178
+        ob_start();
179
+        imagepng($im);
180
+        $data = ob_get_contents();
181
+        ob_end_clean();
182
+
183
+        return $data;
184
+    }
185
+
186
+    /**
187
+     * Calculate real image ttf center
188
+     *
189
+     * @param resource $image
190
+     * @param string $text text string
191
+     * @param string $font font path
192
+     * @param int $size font size
193
+     * @param int $angle
194
+     * @return array
195
+     */
196
+    protected function imageTTFCenter(
197
+        $image,
198
+        string $text,
199
+        string $font,
200
+        int $size,
201
+        int $angle = 0
202
+    ): array {
203
+        // Image width & height
204
+        $xi = imagesx($image);
205
+        $yi = imagesy($image);
206
+
207
+        // bounding box
208
+        $box = imagettfbbox($size, $angle, $font, $text);
209
+
210
+        // imagettfbbox can return negative int
211
+        $xr = abs(max($box[2], $box[4]));
212
+        $yr = abs(max($box[5], $box[7]));
213
+
214
+        // calculate bottom left placement
215
+        $x = intval(($xi - $xr) / 2);
216
+        $y = intval(($yi + $yr) / 2);
217
+
218
+        return [$x, $y];
219
+    }
220
+
221
+
222
+    /**
223
+     * Convert a string to an integer evenly
224
+     * @param string $hash the text to parse
225
+     * @param int $maximum the maximum range
226
+     * @return int between 0 and $maximum
227
+     */
228
+    private function hashToInt(string $hash, int $maximum): int {
229
+        $final = 0;
230
+        $result = [];
231
+
232
+        // Splitting evenly the string
233
+        for ($i = 0; $i < strlen($hash); $i++) {
234
+            // chars in md5 goes up to f, hex:16
235
+            $result[] = intval(substr($hash, $i, 1), 16) % 16;
236
+        }
237
+        // Adds up all results
238
+        foreach ($result as $value) {
239
+            $final += $value;
240
+        }
241
+        // chars in md5 goes up to f, hex:16
242
+        return intval($final % $maximum);
243
+    }
244
+
245
+    /**
246
+     * @return Color Object containing r g b int in the range [0, 255]
247
+     */
248
+    public function avatarBackgroundColor(string $hash): Color {
249
+        // Normalize hash
250
+        $hash = strtolower($hash);
251
+
252
+        // Already a md5 hash?
253
+        if (preg_match('/^([0-9a-f]{4}-?){8}$/', $hash, $matches) !== 1) {
254
+            $hash = md5($hash);
255
+        }
256
+
257
+        // Remove unwanted char
258
+        $hash = preg_replace('/[^0-9a-f]+/', '', $hash);
259
+
260
+        $red = new Color(182, 70, 157);
261
+        $yellow = new Color(221, 203, 85);
262
+        $blue = new Color(0, 130, 201); // Nextcloud blue
263
+
264
+        // Number of steps to go from a color to another
265
+        // 3 colors * 6 will result in 18 generated colors
266
+        $steps = 6;
267
+
268
+        $palette1 = Color::mixPalette($steps, $red, $yellow);
269
+        $palette2 = Color::mixPalette($steps, $yellow, $blue);
270
+        $palette3 = Color::mixPalette($steps, $blue, $red);
271
+
272
+        $finalPalette = array_merge($palette1, $palette2, $palette3);
273
+
274
+        return $finalPalette[$this->hashToInt($hash, $steps * 3)];
275
+    }
276 276
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -79,7 +79,7 @@  discard block
 block discarded – undo
79 79
 		if (empty($displayName) === true) {
80 80
 			return '?';
81 81
 		}
82
-		$firstTwoLetters = array_map(function ($namePart) {
82
+		$firstTwoLetters = array_map(function($namePart) {
83 83
 			return mb_strtoupper(mb_substr($namePart, 0, 1), 'UTF-8');
84 84
 		}, explode(' ', $displayName, 2));
85 85
 		return implode('', $firstTwoLetters);
@@ -130,14 +130,14 @@  discard block
 block discarded – undo
130 130
 			return null;
131 131
 		}
132 132
 		try {
133
-			$font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
133
+			$font = __DIR__.'/../../../core/fonts/NotoSans-Regular.ttf';
134 134
 			$svg = $this->getAvatarVector($size, $darkTheme);
135 135
 			$avatar = new Imagick();
136 136
 			$avatar->setFont($font);
137 137
 			$avatar->readImageBlob($svg);
138 138
 			$avatar->setImageFormat('png');
139 139
 			$image = new \OCP\Image();
140
-			$image->loadFromData((string)$avatar);
140
+			$image->loadFromData((string) $avatar);
141 141
 			return $image->data();
142 142
 		} catch (\Exception $e) {
143 143
 			return null;
@@ -166,11 +166,11 @@  discard block
 block discarded – undo
166 166
 		);
167 167
 		imagefilledrectangle($im, 0, 0, $size, $size, $background);
168 168
 
169
-		$font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf';
169
+		$font = __DIR__.'/../../../core/fonts/NotoSans-Regular.ttf';
170 170
 
171 171
 		$fontSize = $size * 0.4;
172 172
 		[$x, $y] = $this->imageTTFCenter(
173
-			$im, $text, $font, (int)$fontSize
173
+			$im, $text, $font, (int) $fontSize
174 174
 		);
175 175
 
176 176
 		imagettftext($im, $fontSize, 0, $x, $y, $textColor, $font, $text);
Please login to merge, or discard this patch.