Completed
Push — master ( 5685ed...70dab0 )
by Roeland
16:15
created
lib/private/Avatar.php 1 patch
Indentation   +428 added lines, -428 removed lines patch added patch discarded remove patch
@@ -49,450 +49,450 @@
 block discarded – undo
49 49
  */
50 50
 
51 51
 class Avatar implements IAvatar {
52
-	/** @var ISimpleFolder */
53
-	private $folder;
54
-	/** @var IL10N */
55
-	private $l;
56
-	/** @var User */
57
-	private $user;
58
-	/** @var ILogger  */
59
-	private $logger;
60
-	/** @var IConfig */
61
-	private $config;
62
-
63
-	/**
64
-	 * https://github.com/sebdesign/cap-height -- for 500px height
65
-	 * Open Sans cap-height is 0.72 and we want a 200px caps height size (0.4 letter-to-total-height ratio, 500*0.4=200). 200/0.72 = 278px.
66
-	 * Since we start from the baseline (text-anchor) we need to shift the y axis by 100px (half the caps height): 500/2+100=350
67
-	 * 
68
-	 * @var string 
69
-	 */
70
-	private $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
52
+    /** @var ISimpleFolder */
53
+    private $folder;
54
+    /** @var IL10N */
55
+    private $l;
56
+    /** @var User */
57
+    private $user;
58
+    /** @var ILogger  */
59
+    private $logger;
60
+    /** @var IConfig */
61
+    private $config;
62
+
63
+    /**
64
+     * https://github.com/sebdesign/cap-height -- for 500px height
65
+     * Open Sans cap-height is 0.72 and we want a 200px caps height size (0.4 letter-to-total-height ratio, 500*0.4=200). 200/0.72 = 278px.
66
+     * Since we start from the baseline (text-anchor) we need to shift the y axis by 100px (half the caps height): 500/2+100=350
67
+     * 
68
+     * @var string 
69
+     */
70
+    private $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
71 71
 		<svg width="{size}" height="{size}" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
72 72
 			<rect width="100%" height="100%" fill="#{fill}"></rect>
73 73
 			<text x="50%" y="350" style="font-weight:600;font-size:278px;font-family:\'Open Sans\';text-anchor:middle;fill:#fff">{letter}</text>
74 74
 		</svg>';
75 75
 
76
-	/**
77
-	 * constructor
78
-	 *
79
-	 * @param ISimpleFolder $folder The folder where the avatars are
80
-	 * @param IL10N $l
81
-	 * @param User $user
82
-	 * @param ILogger $logger
83
-	 * @param IConfig $config
84
-	 */
85
-	public function __construct(ISimpleFolder $folder,
86
-		IL10N $l,
87
-		$user,
88
-		ILogger $logger,
89
-		IConfig $config) {
90
-		$this->folder = $folder;
91
-		$this->l = $l;
92
-		$this->user = $user;
93
-		$this->logger = $logger;
94
-		$this->config = $config;
95
-	}
96
-
97
-	/**
98
-	 * @inheritdoc
99
-	 */
100
-	public function get($size = 64) {
101
-		try {
102
-			$file = $this->getFile($size);
103
-		} catch (NotFoundException $e) {
104
-			return false;
105
-		}
106
-
107
-		$avatar = new OC_Image();
108
-		$avatar->loadFromData($file->getContent());
109
-		return $avatar;
110
-	}
111
-
112
-	/**
113
-	 * Check if an avatar exists for the user
114
-	 *
115
-	 * @return bool
116
-	 */
117
-	public function exists() {
118
-
119
-		return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
120
-	}
121
-
122
-	/**
123
-	 * Check if the avatar of a user is a custom uploaded one
124
-	 *
125
-	 * @return bool
126
-	 */
127
-	public function isCustomAvatar(): bool {
128
-		return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true';
129
-	}
130
-
131
-	/**
132
-	 * sets the users avatar
133
-	 * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar
134
-	 * @throws \Exception if the provided file is not a jpg or png image
135
-	 * @throws \Exception if the provided image is not valid
136
-	 * @throws NotSquareException if the image is not square
137
-	 * @return void
138
-	 */
139
-	public function set($data) {
140
-
141
-		if ($data instanceof IImage) {
142
-			$img = $data;
143
-			$data = $img->data();
144
-		} else {
145
-			$img = new OC_Image();
146
-			if (is_resource($data) && get_resource_type($data) === "gd") {
147
-				$img->setResource($data);
148
-			} elseif (is_resource($data)) {
149
-				$img->loadFromFileHandle($data);
150
-			} else {
151
-				try {
152
-					// detect if it is a path or maybe the images as string
153
-					$result = @realpath($data);
154
-					if ($result === false || $result === null) {
155
-						$img->loadFromData($data);
156
-					} else {
157
-						$img->loadFromFile($data);
158
-					}
159
-				} catch (\Error $e) {
160
-					$img->loadFromData($data);
161
-				}
162
-			}
163
-		}
164
-		$type = substr($img->mimeType(), -3);
165
-		if ($type === 'peg') {
166
-			$type = 'jpg';
167
-		}
168
-		if ($type !== 'jpg' && $type !== 'png') {
169
-			throw new \Exception($this->l->t('Unknown filetype'));
170
-		}
171
-
172
-		if (!$img->valid()) {
173
-			throw new \Exception($this->l->t('Invalid image'));
174
-		}
175
-
176
-		if (!($img->height() === $img->width())) {
177
-			throw new NotSquareException($this->l->t('Avatar image is not square'));
178
-		}
179
-
180
-		$this->remove();
181
-		$file = $this->folder->newFile('avatar.' . $type);
182
-		$file->putContent($data);
183
-
184
-		try {
185
-			$generated = $this->folder->getFile('generated');
186
-			$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'false');
187
-			$generated->delete();
188
-		} catch (NotFoundException $e) {
189
-			//
190
-		}
191
-		$this->user->triggerChange('avatar', $file);
192
-	}	
193
-
194
-	/**
195
-	 * remove the users avatar
196
-	 * @return void
197
-	 */
198
-	public function remove() {
199
-		$avatars = $this->folder->getDirectoryListing();
200
-
201
-		$this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
202
-			(int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
203
-
204
-		foreach ($avatars as $avatar) {
205
-			$avatar->delete();
206
-		}
207
-		$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
208
-		$this->user->triggerChange('avatar', '');
209
-	}
210
-
211
-	/**
212
-	 * @inheritdoc
213
-	 */
214
-	public function getFile($size) {
215
-		try {
216
-			$ext = $this->getExtension();
217
-		} catch (NotFoundException $e) {
218
-			if (!$data = $this->generateAvatarFromSvg(1024)) {
219
-				$data = $this->generateAvatar($this->user->getDisplayName(), 1024);
220
-			}
221
-			$avatar = $this->folder->newFile('avatar.png');
222
-			$avatar->putContent($data);
223
-			$ext = 'png';
224
-
225
-			$this->folder->newFile('generated');
226
-			$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
227
-		}
228
-
229
-		if ($size === -1) {
230
-			$path = 'avatar.' . $ext;
231
-		} else {
232
-			$path = 'avatar.' . $size . '.' . $ext;
233
-		}
234
-
235
-		try {
236
-			$file = $this->folder->getFile($path);
237
-		} catch (NotFoundException $e) {
238
-			if ($size <= 0) {
239
-				throw new NotFoundException;
240
-			}
241
-
242
-			if ($this->folder->fileExists('generated')) {
243
-				if (!$data = $this->generateAvatarFromSvg($size)) {
244
-					$data = $this->generateAvatar($this->user->getDisplayName(), $size);
245
-				}
246
-
247
-			} else {
248
-				$avatar = new OC_Image();
249
-				/** @var ISimpleFile $file */
250
-				$file = $this->folder->getFile('avatar.' . $ext);
251
-				$avatar->loadFromData($file->getContent());
252
-				$avatar->resize($size);
253
-				$data = $avatar->data();
254
-			}
255
-
256
-			try {
257
-				$file = $this->folder->newFile($path);
258
-				$file->putContent($data);
259
-			} catch (NotPermittedException $e) {
260
-				$this->logger->error('Failed to save avatar for ' . $this->user->getUID());
261
-				throw new NotFoundException();
262
-			}
263
-
264
-		}
265
-
266
-		if ($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) {
267
-			$generated = $this->folder->fileExists('generated') ? 'true' : 'false';
268
-			$this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated);
269
-		}
270
-
271
-		return $file;
272
-	}
273
-
274
-	/**
275
-	 * Get the extension of the avatar. If there is no avatar throw Exception
276
-	 *
277
-	 * @return string
278
-	 * @throws NotFoundException
279
-	 */
280
-	private function getExtension() {
281
-		if ($this->folder->fileExists('avatar.jpg')) {
282
-			return 'jpg';
283
-		} elseif ($this->folder->fileExists('avatar.png')) {
284
-			return 'png';
285
-		}
286
-		throw new NotFoundException;
287
-	}
76
+    /**
77
+     * constructor
78
+     *
79
+     * @param ISimpleFolder $folder The folder where the avatars are
80
+     * @param IL10N $l
81
+     * @param User $user
82
+     * @param ILogger $logger
83
+     * @param IConfig $config
84
+     */
85
+    public function __construct(ISimpleFolder $folder,
86
+        IL10N $l,
87
+        $user,
88
+        ILogger $logger,
89
+        IConfig $config) {
90
+        $this->folder = $folder;
91
+        $this->l = $l;
92
+        $this->user = $user;
93
+        $this->logger = $logger;
94
+        $this->config = $config;
95
+    }
96
+
97
+    /**
98
+     * @inheritdoc
99
+     */
100
+    public function get($size = 64) {
101
+        try {
102
+            $file = $this->getFile($size);
103
+        } catch (NotFoundException $e) {
104
+            return false;
105
+        }
106
+
107
+        $avatar = new OC_Image();
108
+        $avatar->loadFromData($file->getContent());
109
+        return $avatar;
110
+    }
111
+
112
+    /**
113
+     * Check if an avatar exists for the user
114
+     *
115
+     * @return bool
116
+     */
117
+    public function exists() {
118
+
119
+        return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
120
+    }
121
+
122
+    /**
123
+     * Check if the avatar of a user is a custom uploaded one
124
+     *
125
+     * @return bool
126
+     */
127
+    public function isCustomAvatar(): bool {
128
+        return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true';
129
+    }
130
+
131
+    /**
132
+     * sets the users avatar
133
+     * @param IImage|resource|string $data An image object, imagedata or path to set a new avatar
134
+     * @throws \Exception if the provided file is not a jpg or png image
135
+     * @throws \Exception if the provided image is not valid
136
+     * @throws NotSquareException if the image is not square
137
+     * @return void
138
+     */
139
+    public function set($data) {
140
+
141
+        if ($data instanceof IImage) {
142
+            $img = $data;
143
+            $data = $img->data();
144
+        } else {
145
+            $img = new OC_Image();
146
+            if (is_resource($data) && get_resource_type($data) === "gd") {
147
+                $img->setResource($data);
148
+            } elseif (is_resource($data)) {
149
+                $img->loadFromFileHandle($data);
150
+            } else {
151
+                try {
152
+                    // detect if it is a path or maybe the images as string
153
+                    $result = @realpath($data);
154
+                    if ($result === false || $result === null) {
155
+                        $img->loadFromData($data);
156
+                    } else {
157
+                        $img->loadFromFile($data);
158
+                    }
159
+                } catch (\Error $e) {
160
+                    $img->loadFromData($data);
161
+                }
162
+            }
163
+        }
164
+        $type = substr($img->mimeType(), -3);
165
+        if ($type === 'peg') {
166
+            $type = 'jpg';
167
+        }
168
+        if ($type !== 'jpg' && $type !== 'png') {
169
+            throw new \Exception($this->l->t('Unknown filetype'));
170
+        }
171
+
172
+        if (!$img->valid()) {
173
+            throw new \Exception($this->l->t('Invalid image'));
174
+        }
175
+
176
+        if (!($img->height() === $img->width())) {
177
+            throw new NotSquareException($this->l->t('Avatar image is not square'));
178
+        }
179
+
180
+        $this->remove();
181
+        $file = $this->folder->newFile('avatar.' . $type);
182
+        $file->putContent($data);
183
+
184
+        try {
185
+            $generated = $this->folder->getFile('generated');
186
+            $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'false');
187
+            $generated->delete();
188
+        } catch (NotFoundException $e) {
189
+            //
190
+        }
191
+        $this->user->triggerChange('avatar', $file);
192
+    }	
193
+
194
+    /**
195
+     * remove the users avatar
196
+     * @return void
197
+     */
198
+    public function remove() {
199
+        $avatars = $this->folder->getDirectoryListing();
200
+
201
+        $this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
202
+            (int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
203
+
204
+        foreach ($avatars as $avatar) {
205
+            $avatar->delete();
206
+        }
207
+        $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
208
+        $this->user->triggerChange('avatar', '');
209
+    }
210
+
211
+    /**
212
+     * @inheritdoc
213
+     */
214
+    public function getFile($size) {
215
+        try {
216
+            $ext = $this->getExtension();
217
+        } catch (NotFoundException $e) {
218
+            if (!$data = $this->generateAvatarFromSvg(1024)) {
219
+                $data = $this->generateAvatar($this->user->getDisplayName(), 1024);
220
+            }
221
+            $avatar = $this->folder->newFile('avatar.png');
222
+            $avatar->putContent($data);
223
+            $ext = 'png';
224
+
225
+            $this->folder->newFile('generated');
226
+            $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true');
227
+        }
228
+
229
+        if ($size === -1) {
230
+            $path = 'avatar.' . $ext;
231
+        } else {
232
+            $path = 'avatar.' . $size . '.' . $ext;
233
+        }
234
+
235
+        try {
236
+            $file = $this->folder->getFile($path);
237
+        } catch (NotFoundException $e) {
238
+            if ($size <= 0) {
239
+                throw new NotFoundException;
240
+            }
241
+
242
+            if ($this->folder->fileExists('generated')) {
243
+                if (!$data = $this->generateAvatarFromSvg($size)) {
244
+                    $data = $this->generateAvatar($this->user->getDisplayName(), $size);
245
+                }
246
+
247
+            } else {
248
+                $avatar = new OC_Image();
249
+                /** @var ISimpleFile $file */
250
+                $file = $this->folder->getFile('avatar.' . $ext);
251
+                $avatar->loadFromData($file->getContent());
252
+                $avatar->resize($size);
253
+                $data = $avatar->data();
254
+            }
255
+
256
+            try {
257
+                $file = $this->folder->newFile($path);
258
+                $file->putContent($data);
259
+            } catch (NotPermittedException $e) {
260
+                $this->logger->error('Failed to save avatar for ' . $this->user->getUID());
261
+                throw new NotFoundException();
262
+            }
263
+
264
+        }
265
+
266
+        if ($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) {
267
+            $generated = $this->folder->fileExists('generated') ? 'true' : 'false';
268
+            $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated);
269
+        }
270
+
271
+        return $file;
272
+    }
273
+
274
+    /**
275
+     * Get the extension of the avatar. If there is no avatar throw Exception
276
+     *
277
+     * @return string
278
+     * @throws NotFoundException
279
+     */
280
+    private function getExtension() {
281
+        if ($this->folder->fileExists('avatar.jpg')) {
282
+            return 'jpg';
283
+        } elseif ($this->folder->fileExists('avatar.png')) {
284
+            return 'png';
285
+        }
286
+        throw new NotFoundException;
287
+    }
288 288
 	
289
-	/**
290
-	 * {size} = 500
291
-	 * {fill} = hex color to fill
292
-	 * {letter} = Letter to display
293
-	 * 
294
-	 * Generate SVG avatar
295
-	 * @return string
296
-	 * 
297
-	 */
298
-	private function getAvatarVector(int $size): string {
299
-		$userDisplayName = $this->user->getDisplayName();
300
-
301
-		$bgRGB = $this->avatarBackgroundColor($userDisplayName);
302
-		$bgHEX = sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b);
303
-		$letter = mb_strtoupper(mb_substr($userDisplayName, 0, 1), 'UTF-8');
289
+    /**
290
+     * {size} = 500
291
+     * {fill} = hex color to fill
292
+     * {letter} = Letter to display
293
+     * 
294
+     * Generate SVG avatar
295
+     * @return string
296
+     * 
297
+     */
298
+    private function getAvatarVector(int $size): string {
299
+        $userDisplayName = $this->user->getDisplayName();
300
+
301
+        $bgRGB = $this->avatarBackgroundColor($userDisplayName);
302
+        $bgHEX = sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b);
303
+        $letter = mb_strtoupper(mb_substr($userDisplayName, 0, 1), 'UTF-8');
304 304
 		
305
-		$toReplace = ['{size}', '{fill}', '{letter}'];
306
-		return str_replace($toReplace, [$size, $bgHEX, $letter], $this->svgTemplate);
307
-	}
308
-
309
-	/**
310
-	 * Generate png avatar from svg with Imagick
311
-	 * 
312
-	 * @param int $size
313
-	 * @return string|boolean
314
-	 */
315
-	private function generateAvatarFromSvg(int $size) {
316
-		if (!extension_loaded('imagick')) {
317
-			return false;
318
-		}
319
-		try {
320
-			$font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.ttf';
321
-			$svg = $this->getAvatarVector($size);
322
-			$avatar = new Imagick();
323
-			$avatar->setFont($font);
324
-			$avatar->readImageBlob($svg);
325
-			$avatar->setImageFormat('png');
326
-			$image = new OC_Image();
327
-			$image->loadFromData($avatar);
328
-			return $image->data();
329
-		} catch (\Exception $e) {
330
-			return false;
331
-		}
332
-	}
333
-
334
-	/**
335
-	 * Generate png avatar with GD
336
-	 * 
337
-	 * @param string $userDisplayName
338
-	 * @param int $size
339
-	 * @return string
340
-	 */
341
-	private function generateAvatar($userDisplayName, $size) {
342
-		$text = mb_strtoupper(mb_substr($userDisplayName, 0, 1), 'UTF-8');
343
-		$backgroundColor = $this->avatarBackgroundColor($userDisplayName);
344
-
345
-		$im = imagecreatetruecolor($size, $size);
346
-		$background = imagecolorallocate($im, $backgroundColor->r, $backgroundColor->g, $backgroundColor->b);
347
-		$white = imagecolorallocate($im, 255, 255, 255);
348
-		imagefilledrectangle($im, 0, 0, $size, $size, $background);
349
-
350
-		$font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.ttf';
351
-
352
-		$fontSize = $size * 0.4;
353
-
354
-		list($x, $y) = $this->imageTTFCenter($im, $text, $font, $fontSize);
355
-
356
-		imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text);
357
-
358
-		ob_start();
359
-		imagepng($im);
360
-		$data = ob_get_contents();
361
-		ob_end_clean();
362
-
363
-		return $data;
364
-	}
365
-
366
-	/**
367
-	 * Calculate real image ttf center
368
-	 *
369
-	 * @param resource $image
370
-	 * @param string $text text string
371
-	 * @param string $font font path
372
-	 * @param int $size font size
373
-	 * @param int $angle
374
-	 * @return array
375
-	 */
376
-	protected function imageTTFCenter($image, string $text, string $font, int $size, $angle = 0): array {
377
-		// Image width & height
378
-		$xi = imagesx($image);
379
-		$yi = imagesy($image);
380
-
381
-		// bounding box
382
-		$box = imagettfbbox($size, $angle, $font, $text);
383
-
384
-		// imagettfbbox can return negative int
385
-		$xr = abs(max($box[2], $box[4]));
386
-		$yr = abs(max($box[5], $box[7]));
387
-
388
-		// calculate bottom left placement
389
-		$x = intval(($xi - $xr) / 2);
390
-		$y = intval(($yi + $yr) / 2);
391
-
392
-		return array($x, $y);
393
-	}
394
-
395
-	/**
396
-	 * Calculate steps between two Colors
397
-	 * @param object Color $steps start color
398
-	 * @param object Color $ends end color
399
-	 * @return array [r,g,b] steps for each color to go from $steps to $ends
400
-	 */
401
-	private function stepCalc($steps, $ends) {
402
-		$step = array();
403
-		$step[0] = ($ends[1]->r - $ends[0]->r) / $steps;
404
-		$step[1] = ($ends[1]->g - $ends[0]->g) / $steps;
405
-		$step[2] = ($ends[1]->b - $ends[0]->b) / $steps;
406
-		return $step;
407
-	}
408
-
409
-	/**
410
-	 * Convert a string to an integer evenly
411
-	 * @param string $hash the text to parse
412
-	 * @param int $maximum the maximum range
413
-	 * @return int between 0 and $maximum
414
-	 */
415
-	private function mixPalette($steps, $color1, $color2) {
416
-		$count = $steps + 1;
417
-		$palette = array($color1);
418
-		$step = $this->stepCalc($steps, [$color1, $color2]);
419
-		for ($i = 1; $i < $steps; $i++) {
420
-			$r = intval($color1->r + ($step[0] * $i));
421
-			$g = intval($color1->g + ($step[1] * $i));
422
-			$b = intval($color1->b + ($step[2] * $i));
423
-			$palette[] = new Color($r, $g, $b);
424
-		}
425
-		return $palette;
426
-	}
427
-
428
-	/**
429
-	 * Convert a string to an integer evenly
430
-	 * @param string $hash the text to parse
431
-	 * @param int $maximum the maximum range
432
-	 * @return int between 0 and $maximum
433
-	 */
434
-	private function hashToInt($hash, $maximum) {
435
-		$final = 0;
436
-		$result = array();
437
-
438
-		// Splitting evenly the string
439
-		for ($i = 0; $i < strlen($hash); $i++) {
440
-			// chars in md5 goes up to f, hex:16
441
-			$result[] = intval(substr($hash, $i, 1), 16) % 16;
442
-		}
443
-		// Adds up all results
444
-		foreach ($result as $value) {
445
-			$final += $value;
446
-		}
447
-		// chars in md5 goes up to f, hex:16
448
-		return intval($final % $maximum);
449
-	}
450
-
451
-	/**
452
-	 * @param string $hash
453
-	 * @return Color Object containting r g b int in the range [0, 255]
454
-	 */
455
-	public function avatarBackgroundColor(string $hash) {
456
-		// Normalize hash
457
-		$hash = strtolower($hash);
305
+        $toReplace = ['{size}', '{fill}', '{letter}'];
306
+        return str_replace($toReplace, [$size, $bgHEX, $letter], $this->svgTemplate);
307
+    }
308
+
309
+    /**
310
+     * Generate png avatar from svg with Imagick
311
+     * 
312
+     * @param int $size
313
+     * @return string|boolean
314
+     */
315
+    private function generateAvatarFromSvg(int $size) {
316
+        if (!extension_loaded('imagick')) {
317
+            return false;
318
+        }
319
+        try {
320
+            $font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.ttf';
321
+            $svg = $this->getAvatarVector($size);
322
+            $avatar = new Imagick();
323
+            $avatar->setFont($font);
324
+            $avatar->readImageBlob($svg);
325
+            $avatar->setImageFormat('png');
326
+            $image = new OC_Image();
327
+            $image->loadFromData($avatar);
328
+            return $image->data();
329
+        } catch (\Exception $e) {
330
+            return false;
331
+        }
332
+    }
333
+
334
+    /**
335
+     * Generate png avatar with GD
336
+     * 
337
+     * @param string $userDisplayName
338
+     * @param int $size
339
+     * @return string
340
+     */
341
+    private function generateAvatar($userDisplayName, $size) {
342
+        $text = mb_strtoupper(mb_substr($userDisplayName, 0, 1), 'UTF-8');
343
+        $backgroundColor = $this->avatarBackgroundColor($userDisplayName);
344
+
345
+        $im = imagecreatetruecolor($size, $size);
346
+        $background = imagecolorallocate($im, $backgroundColor->r, $backgroundColor->g, $backgroundColor->b);
347
+        $white = imagecolorallocate($im, 255, 255, 255);
348
+        imagefilledrectangle($im, 0, 0, $size, $size, $background);
349
+
350
+        $font = __DIR__ . '/../../core/fonts/OpenSans-Semibold.ttf';
351
+
352
+        $fontSize = $size * 0.4;
353
+
354
+        list($x, $y) = $this->imageTTFCenter($im, $text, $font, $fontSize);
355
+
356
+        imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text);
357
+
358
+        ob_start();
359
+        imagepng($im);
360
+        $data = ob_get_contents();
361
+        ob_end_clean();
362
+
363
+        return $data;
364
+    }
365
+
366
+    /**
367
+     * Calculate real image ttf center
368
+     *
369
+     * @param resource $image
370
+     * @param string $text text string
371
+     * @param string $font font path
372
+     * @param int $size font size
373
+     * @param int $angle
374
+     * @return array
375
+     */
376
+    protected function imageTTFCenter($image, string $text, string $font, int $size, $angle = 0): array {
377
+        // Image width & height
378
+        $xi = imagesx($image);
379
+        $yi = imagesy($image);
380
+
381
+        // bounding box
382
+        $box = imagettfbbox($size, $angle, $font, $text);
383
+
384
+        // imagettfbbox can return negative int
385
+        $xr = abs(max($box[2], $box[4]));
386
+        $yr = abs(max($box[5], $box[7]));
387
+
388
+        // calculate bottom left placement
389
+        $x = intval(($xi - $xr) / 2);
390
+        $y = intval(($yi + $yr) / 2);
391
+
392
+        return array($x, $y);
393
+    }
394
+
395
+    /**
396
+     * Calculate steps between two Colors
397
+     * @param object Color $steps start color
398
+     * @param object Color $ends end color
399
+     * @return array [r,g,b] steps for each color to go from $steps to $ends
400
+     */
401
+    private function stepCalc($steps, $ends) {
402
+        $step = array();
403
+        $step[0] = ($ends[1]->r - $ends[0]->r) / $steps;
404
+        $step[1] = ($ends[1]->g - $ends[0]->g) / $steps;
405
+        $step[2] = ($ends[1]->b - $ends[0]->b) / $steps;
406
+        return $step;
407
+    }
408
+
409
+    /**
410
+     * Convert a string to an integer evenly
411
+     * @param string $hash the text to parse
412
+     * @param int $maximum the maximum range
413
+     * @return int between 0 and $maximum
414
+     */
415
+    private function mixPalette($steps, $color1, $color2) {
416
+        $count = $steps + 1;
417
+        $palette = array($color1);
418
+        $step = $this->stepCalc($steps, [$color1, $color2]);
419
+        for ($i = 1; $i < $steps; $i++) {
420
+            $r = intval($color1->r + ($step[0] * $i));
421
+            $g = intval($color1->g + ($step[1] * $i));
422
+            $b = intval($color1->b + ($step[2] * $i));
423
+            $palette[] = new Color($r, $g, $b);
424
+        }
425
+        return $palette;
426
+    }
427
+
428
+    /**
429
+     * Convert a string to an integer evenly
430
+     * @param string $hash the text to parse
431
+     * @param int $maximum the maximum range
432
+     * @return int between 0 and $maximum
433
+     */
434
+    private function hashToInt($hash, $maximum) {
435
+        $final = 0;
436
+        $result = array();
437
+
438
+        // Splitting evenly the string
439
+        for ($i = 0; $i < strlen($hash); $i++) {
440
+            // chars in md5 goes up to f, hex:16
441
+            $result[] = intval(substr($hash, $i, 1), 16) % 16;
442
+        }
443
+        // Adds up all results
444
+        foreach ($result as $value) {
445
+            $final += $value;
446
+        }
447
+        // chars in md5 goes up to f, hex:16
448
+        return intval($final % $maximum);
449
+    }
450
+
451
+    /**
452
+     * @param string $hash
453
+     * @return Color Object containting r g b int in the range [0, 255]
454
+     */
455
+    public function avatarBackgroundColor(string $hash) {
456
+        // Normalize hash
457
+        $hash = strtolower($hash);
458 458
 		
459
-		// Already a md5 hash?
460
-		if( preg_match('/^([0-9a-f]{4}-?){8}$/', $hash, $matches) !== 1 ) {
461
-			$hash = md5($hash);
462
-		}
459
+        // Already a md5 hash?
460
+        if( preg_match('/^([0-9a-f]{4}-?){8}$/', $hash, $matches) !== 1 ) {
461
+            $hash = md5($hash);
462
+        }
463 463
 
464
-		// Remove unwanted char
465
-		$hash = preg_replace('/[^0-9a-f]+/', '', $hash);
464
+        // Remove unwanted char
465
+        $hash = preg_replace('/[^0-9a-f]+/', '', $hash);
466 466
 
467
-		$red = new Color(182, 70, 157);
468
-		$yellow = new Color(221, 203, 85);
469
-		$blue = new Color(0, 130, 201); // Nextcloud blue
467
+        $red = new Color(182, 70, 157);
468
+        $yellow = new Color(221, 203, 85);
469
+        $blue = new Color(0, 130, 201); // Nextcloud blue
470 470
 
471
-		// Number of steps to go from a color to another
472
-		// 3 colors * 6 will result in 18 generated colors
473
-		$steps = 6;
471
+        // Number of steps to go from a color to another
472
+        // 3 colors * 6 will result in 18 generated colors
473
+        $steps = 6;
474 474
 
475
-		$palette1 = $this->mixPalette($steps, $red, $yellow);
476
-		$palette2 = $this->mixPalette($steps, $yellow, $blue);
477
-		$palette3 = $this->mixPalette($steps, $blue, $red);
475
+        $palette1 = $this->mixPalette($steps, $red, $yellow);
476
+        $palette2 = $this->mixPalette($steps, $yellow, $blue);
477
+        $palette3 = $this->mixPalette($steps, $blue, $red);
478 478
 
479
-		$finalPalette = array_merge($palette1, $palette2, $palette3);
479
+        $finalPalette = array_merge($palette1, $palette2, $palette3);
480 480
 
481
-		return $finalPalette[$this->hashToInt($hash, $steps * 3)];
482
-	}
481
+        return $finalPalette[$this->hashToInt($hash, $steps * 3)];
482
+    }
483 483
 
484
-	public function userChanged($feature, $oldValue, $newValue) {
485
-		// We only change the avatar on display name changes
486
-		if ($feature !== 'displayName') {
487
-			return;
488
-		}
484
+    public function userChanged($feature, $oldValue, $newValue) {
485
+        // We only change the avatar on display name changes
486
+        if ($feature !== 'displayName') {
487
+            return;
488
+        }
489 489
 
490
-		// If the avatar is not generated (so an uploaded image) we skip this
491
-		if (!$this->folder->fileExists('generated')) {
492
-			return;
493
-		}
490
+        // If the avatar is not generated (so an uploaded image) we skip this
491
+        if (!$this->folder->fileExists('generated')) {
492
+            return;
493
+        }
494 494
 
495
-		$this->remove();
496
-	}
495
+        $this->remove();
496
+    }
497 497
 
498 498
 }
Please login to merge, or discard this patch.