Total Complexity | 50 |
Total Lines | 438 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like ImageBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ImageBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class ImageBuilder { |
||
24 | |||
25 | /** |
||
26 | * Reference media |
||
27 | * @var Media $media |
||
28 | */ |
||
29 | protected $media; |
||
30 | |||
31 | /** |
||
32 | * Use TTF font |
||
33 | * @var bool $use_ttf |
||
34 | */ |
||
35 | protected $use_ttf; |
||
36 | |||
37 | /** |
||
38 | * Expiration offset. Default is one day. |
||
39 | * @var int $expire_offset |
||
40 | */ |
||
41 | protected $expire_offset; |
||
42 | |||
43 | /** |
||
44 | * Should the certificate display a watermark |
||
45 | * @var bool $show_watermark |
||
46 | */ |
||
47 | protected $show_watermark; |
||
48 | |||
49 | /** |
||
50 | * Maximum watermark font size. Default is 18. |
||
51 | * @var int $font_max_size |
||
52 | */ |
||
53 | protected $font_max_size; |
||
54 | |||
55 | /** |
||
56 | * Watermark font color, in hexadecimal. Default is #4D6DF3. |
||
57 | * @var string $font_color |
||
58 | */ |
||
59 | protected $font_color; |
||
60 | |||
61 | /** |
||
62 | * Contructor for ImageBuilder |
||
63 | * |
||
64 | * @param Media|null $media Reference media object |
||
65 | */ |
||
66 | public function __construct(Media $media = null){ |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * Get the expiration offset. |
||
77 | * |
||
78 | * @return int |
||
79 | */ |
||
80 | public function getExpireOffset() { |
||
81 | return $this->expire_offset; |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Set the expiration offset. |
||
86 | * |
||
87 | * @param int $expireOffset |
||
88 | * @return ImageBuilder |
||
89 | */ |
||
90 | public function setExpireOffset($expireOffset) { |
||
91 | if($expireOffset) $this->expire_offset = $expireOffset; |
||
92 | return $this; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Gets whether the watermark should be shown. |
||
97 | * |
||
98 | * @return bool |
||
99 | */ |
||
100 | public function isShowWatermark() { |
||
101 | return $this->show_watermark; |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Set whether the watermark should be shown. |
||
106 | * |
||
107 | * @param bool $show_watermark |
||
108 | * @return ImageBuilder |
||
109 | */ |
||
110 | public function setShowWatermark($show_watermark) { |
||
111 | if(!is_null($show_watermark)) $this->show_watermark = $show_watermark; |
||
|
|||
112 | return $this; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Set the watermark maximum font size. |
||
117 | * |
||
118 | * @param int $font_max_size |
||
119 | * @return ImageBuilder |
||
120 | */ |
||
121 | public function setFontMaxSize($font_max_size) { |
||
122 | if($font_max_size) $this->font_max_size = $font_max_size; |
||
123 | return $this; |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Set the watermark font color |
||
128 | * |
||
129 | * @param int $font_color |
||
130 | * @return ImageBuilder |
||
131 | */ |
||
132 | public function setFontColor($font_color) { |
||
133 | if($font_color) $this->font_color = $font_color; |
||
134 | return $this; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Render the image to the output. |
||
139 | */ |
||
140 | public function render(){ |
||
249 | } |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Render an error as an image. |
||
254 | */ |
||
255 | protected function renderError() { |
||
256 | $error = I18N::translate('The media file was not found in this family tree.'); |
||
257 | |||
258 | $width = (mb_strlen($error) * 6.5 + 50) * 1.15; |
||
259 | $height = 60; |
||
260 | $im = imagecreatetruecolor($width, $height); /* Create a black image */ |
||
261 | $bgc = imagecolorallocate($im, 255, 255, 255); /* set background color */ |
||
262 | imagefilledrectangle($im, 2, 2, $width - 4, $height - 4, $bgc); /* create a rectangle, leaving 2 px border */ |
||
263 | |||
264 | $this->embedText($im, $error, 100, '255, 0, 0', WT_ROOT . Config::FONT_DEJAVU_SANS_TTF, 'top', 'left'); |
||
265 | |||
266 | http_response_code(404); |
||
267 | header('Content-Type: image/png'); |
||
268 | imagepng($im); |
||
269 | imagedestroy($im); |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Returns the entered image with a watermark printed. |
||
274 | * Similar to the the media firewall function. |
||
275 | * |
||
276 | * @param resource $im Certificate image to watermark |
||
277 | * @return resource Watermarked image |
||
278 | */ |
||
279 | protected function applyWatermark($im) { |
||
280 | |||
281 | // text to watermark with |
||
282 | if(method_exists($this->media, 'getWatermarkText')) { |
||
283 | $word1_text = $this->media->getWatermarkText(); |
||
284 | } |
||
285 | else { |
||
286 | $word1_text = $this->media->getTitle(); |
||
287 | } |
||
288 | |||
289 | $this->embedText( |
||
290 | $im, |
||
291 | $word1_text, |
||
292 | $this->font_max_size, |
||
293 | $this->font_color, |
||
294 | WT_ROOT . Config::FONT_DEJAVU_SANS_TTF, |
||
295 | 'top', |
||
296 | 'left' |
||
297 | ); |
||
298 | |||
299 | return ($im); |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * Embed a text in an image. |
||
304 | * Similar to the the media firewall function. |
||
305 | * |
||
306 | * @param resource $im Image to watermark |
||
307 | * @param string $text Text to display |
||
308 | * @param int $maxsize Maximum size for the font |
||
309 | * @param string $color Font color |
||
310 | * @param string $font Font to be used |
||
311 | * @param string $vpos Description of the vertical position (top, middle, bottom, accross) |
||
312 | * @param string $hpos Description of the horizontal position (right, left, top2bottom, bottom2top) |
||
313 | */ |
||
314 | protected function embedText($im, $text, $maxsize, $color, $font, $vpos, $hpos) { |
||
315 | |||
316 | // there are two ways to embed text with PHP |
||
317 | // (preferred) using GD and FreeType you can embed text using any True Type font |
||
318 | // (fall back) if that is not available, you can insert basic monospaced text |
||
319 | |||
320 | $col = $this->hexrgb($color); |
||
321 | $textcolor = imagecolorallocate($im, $col['red'], $col['green'], $col['blue']); |
||
322 | |||
323 | // make adjustments to settings that imagestring and imagestringup can’t handle |
||
324 | if (!$this->use_ttf) { |
||
325 | // imagestringup only writes up, can’t use top2bottom |
||
326 | if ($hpos === 'top2bottom') { |
||
327 | $hpos = 'bottom2top'; |
||
328 | } |
||
329 | } |
||
330 | |||
331 | $text = I18N::reverseText($text); |
||
332 | $height = imagesy($im); |
||
333 | $width = imagesx($im); |
||
334 | $calc_angle = rad2deg(atan($height / $width)); |
||
335 | $hypoth = $height / sin(deg2rad($calc_angle)); |
||
336 | |||
337 | // vertical and horizontal position of the text |
||
338 | switch ($vpos) { |
||
339 | default: |
||
340 | case 'top': |
||
341 | $taille = $this->textLength($maxsize, $width, $text); |
||
342 | $pos_y = $height * 0.15 + $taille; |
||
343 | $pos_x = $width * 0.15; |
||
344 | $rotation = 0; |
||
345 | break; |
||
346 | case 'middle': |
||
347 | $taille = $this->textLength($maxsize, $width, $text); |
||
348 | $pos_y = ($height + $taille) / 2; |
||
349 | $pos_x = $width * 0.15; |
||
350 | $rotation = 0; |
||
351 | break; |
||
352 | case 'bottom': |
||
353 | $taille = $this->textLength($maxsize, $width, $text); |
||
354 | $pos_y = ($height * .85 - $taille); |
||
355 | $pos_x = $width * 0.15; |
||
356 | $rotation = 0; |
||
357 | break; |
||
358 | case 'across': |
||
359 | switch ($hpos) { |
||
360 | default: |
||
361 | case 'left': |
||
362 | $taille = $this->textLength($maxsize, $hypoth, $text); |
||
363 | $pos_y = ($height * .85 - $taille); |
||
364 | $pos_x = $width * 0.15; |
||
365 | $rotation = $calc_angle; |
||
366 | break; |
||
367 | case 'right': |
||
368 | $taille = $this->textLength($maxsize, $hypoth, $text); |
||
369 | $pos_y = ($height * .15 - $taille); |
||
370 | $pos_x = $width * 0.85; |
||
371 | $rotation = $calc_angle + 180; |
||
372 | break; |
||
373 | case 'top2bottom': |
||
374 | $taille = $this->textLength($maxsize, $height, $text); |
||
375 | $pos_y = ($height * .15 - $taille); |
||
376 | $pos_x = ($width * .90 - $taille); |
||
377 | $rotation = -90; |
||
378 | break; |
||
379 | case 'bottom2top': |
||
380 | $taille = $this->textLength($maxsize, $height, $text); |
||
381 | $pos_y = $height * 0.85; |
||
382 | $pos_x = $width * 0.15; |
||
383 | $rotation = 90; |
||
384 | break; |
||
385 | } |
||
386 | break; |
||
387 | } |
||
388 | |||
389 | // apply the text |
||
390 | if ($this->use_ttf) { |
||
391 | // if imagettftext throws errors, catch them with a custom error handler |
||
392 | set_error_handler(array($this, 'imageTtfTextErrorHandler')); |
||
393 | imagettftext($im, $taille, $rotation, $pos_x, $pos_y, $textcolor, $font, $text); |
||
394 | restore_error_handler(); |
||
395 | } |
||
396 | // Don’t use an ‘else’ here since imagettftextErrorHandler may have changed the value of $useTTF from true to false |
||
397 | if (!$this->use_ttf) { |
||
398 | if ($rotation !== 90) { |
||
399 | imagestring($im, 5, $pos_x, $pos_y, $text, $textcolor); |
||
400 | } else { |
||
401 | imagestringup($im, 5, $pos_x, $pos_y, $text, $textcolor); |
||
402 | } |
||
403 | } |
||
404 | |||
405 | } |
||
406 | |||
407 | /** |
||
408 | * Convert an hexadecimal color to its RGB equivalent. |
||
409 | * |
||
410 | * @param string $hexstr |
||
411 | * @return int[] |
||
412 | */ |
||
413 | protected function hexrgb ($hexstr) |
||
414 | { |
||
415 | $int = hexdec($hexstr); |
||
416 | |||
417 | return array('red' => 0xFF & ($int >> 0x10), |
||
418 | 'green' => 0xFF & ($int >> 0x8), |
||
419 | 'blue' => 0xFF & $int); |
||
420 | } |
||
421 | |||
422 | /** |
||
423 | * Generate an approximate length of text, in pixels. |
||
424 | * |
||
425 | * @param int $t |
||
426 | * @param int $mxl |
||
427 | * @param string $text |
||
428 | * |
||
429 | * @return int |
||
430 | */ |
||
431 | function textLength($t, $mxl, $text) { |
||
432 | $taille_c = $t; |
||
433 | $len = mb_strlen($text); |
||
434 | while (($taille_c - 2) * $len > $mxl) { |
||
435 | $taille_c--; |
||
436 | if ($taille_c == 2) { |
||
437 | break; |
||
438 | } |
||
439 | } |
||
440 | |||
441 | return $taille_c; |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * imagettftext is the function that is most likely to throw an error |
||
446 | * use this custom error handler to catch and log it |
||
447 | * |
||
448 | * @param int $errno |
||
449 | * @param string $errstr |
||
450 | * |
||
451 | * @return bool |
||
452 | */ |
||
453 | function imageTtfTextErrorHandler($errno, $errstr) { |
||
461 | } |
||
462 | |||
463 | } |
||
464 | |||
465 | ?> |
||