Total Complexity | 53 |
Total Lines | 457 |
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 | * Should the image be rendered as attachment (vs inline) * |
||
63 | * @var bool $as_attachment |
||
64 | */ |
||
65 | protected $as_attachment; |
||
66 | |||
67 | /** |
||
68 | * Contructor for ImageBuilder |
||
69 | * |
||
70 | * @param Media|null $media Reference media object |
||
71 | */ |
||
72 | public function __construct(Media $media = null){ |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Get the expiration offset. |
||
84 | * |
||
85 | * @return int |
||
86 | */ |
||
87 | public function getExpireOffset() { |
||
88 | return $this->expire_offset; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Set the expiration offset. |
||
93 | * |
||
94 | * @param int $expireOffset |
||
95 | * @return ImageBuilder |
||
96 | */ |
||
97 | public function setExpireOffset($expireOffset) { |
||
98 | if($expireOffset) $this->expire_offset = $expireOffset; |
||
99 | return $this; |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * Gets whether the watermark should be shown. |
||
104 | * |
||
105 | * @return bool |
||
106 | */ |
||
107 | public function isShowWatermark() { |
||
108 | return $this->show_watermark; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Set whether the watermark should be shown. |
||
113 | * |
||
114 | * @param bool $show_watermark |
||
115 | * @return ImageBuilder |
||
116 | */ |
||
117 | public function setShowWatermark($show_watermark) { |
||
118 | if(!is_null($show_watermark)) $this->show_watermark = $show_watermark; |
||
|
|||
119 | return $this; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Set the watermark maximum font size. |
||
124 | * |
||
125 | * @param int $font_max_size |
||
126 | * @return ImageBuilder |
||
127 | */ |
||
128 | public function setFontMaxSize($font_max_size) { |
||
129 | if($font_max_size) $this->font_max_size = $font_max_size; |
||
130 | return $this; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Set the watermark font color |
||
135 | * |
||
136 | * @param int $font_color |
||
137 | * @return ImageBuilder |
||
138 | */ |
||
139 | public function setFontColor($font_color) { |
||
140 | if($font_color) $this->font_color = $font_color; |
||
141 | return $this; |
||
142 | } |
||
143 | |||
144 | /** |
||
145 | * Set whether the image should be rendered as attachment |
||
146 | * |
||
147 | * @param bool $is_attachement |
||
148 | * @return ImageBuilder |
||
149 | */ |
||
150 | public function setAsAttachment($is_attachement) { |
||
151 | if(is_bool($is_attachement)) $this->as_attachment = $is_attachement; |
||
152 | return $this; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * Render the image to the output. |
||
157 | */ |
||
158 | public function render(){ |
||
268 | } |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Render an error as an image. |
||
273 | */ |
||
274 | protected function renderError() { |
||
275 | $error = I18N::translate('The media file was not found in this family tree.'); |
||
276 | |||
277 | $width = (mb_strlen($error) * 6.5 + 50) * 1.15; |
||
278 | $height = 60; |
||
279 | $im = imagecreatetruecolor($width, $height); /* Create a black image */ |
||
280 | $bgc = imagecolorallocate($im, 255, 255, 255); /* set background color */ |
||
281 | imagefilledrectangle($im, 2, 2, $width - 4, $height - 4, $bgc); /* create a rectangle, leaving 2 px border */ |
||
282 | |||
283 | $this->embedText($im, $error, 100, '255, 0, 0', WT_ROOT . Config::FONT_DEJAVU_SANS_TTF, 'top', 'left'); |
||
284 | |||
285 | http_response_code(404); |
||
286 | header('Content-Type: image/png'); |
||
287 | imagepng($im); |
||
288 | imagedestroy($im); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Returns the entered image with a watermark printed. |
||
293 | * Similar to the the media firewall function. |
||
294 | * |
||
295 | * @param resource $im Certificate image to watermark |
||
296 | * @return resource Watermarked image |
||
297 | */ |
||
298 | protected function applyWatermark($im) { |
||
299 | |||
300 | // text to watermark with |
||
301 | if(method_exists($this->media, 'getWatermarkText')) { |
||
302 | $word1_text = $this->media->getWatermarkText(); |
||
303 | } |
||
304 | else { |
||
305 | $word1_text = $this->media->getTitle(); |
||
306 | } |
||
307 | |||
308 | $this->embedText( |
||
309 | $im, |
||
310 | $word1_text, |
||
311 | $this->font_max_size, |
||
312 | $this->font_color, |
||
313 | WT_ROOT . Config::FONT_DEJAVU_SANS_TTF, |
||
314 | 'top', |
||
315 | 'left' |
||
316 | ); |
||
317 | |||
318 | return ($im); |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Embed a text in an image. |
||
323 | * Similar to the the media firewall function. |
||
324 | * |
||
325 | * @param resource $im Image to watermark |
||
326 | * @param string $text Text to display |
||
327 | * @param int $maxsize Maximum size for the font |
||
328 | * @param string $color Font color |
||
329 | * @param string $font Font to be used |
||
330 | * @param string $vpos Description of the vertical position (top, middle, bottom, accross) |
||
331 | * @param string $hpos Description of the horizontal position (right, left, top2bottom, bottom2top) |
||
332 | */ |
||
333 | protected function embedText($im, $text, $maxsize, $color, $font, $vpos, $hpos) { |
||
334 | |||
335 | // there are two ways to embed text with PHP |
||
336 | // (preferred) using GD and FreeType you can embed text using any True Type font |
||
337 | // (fall back) if that is not available, you can insert basic monospaced text |
||
338 | |||
339 | $col = $this->hexrgb($color); |
||
340 | $textcolor = imagecolorallocate($im, $col['red'], $col['green'], $col['blue']); |
||
341 | |||
342 | // make adjustments to settings that imagestring and imagestringup can’t handle |
||
343 | if (!$this->use_ttf) { |
||
344 | // imagestringup only writes up, can’t use top2bottom |
||
345 | if ($hpos === 'top2bottom') { |
||
346 | $hpos = 'bottom2top'; |
||
347 | } |
||
348 | } |
||
349 | |||
350 | $text = I18N::reverseText($text); |
||
351 | $height = imagesy($im); |
||
352 | $width = imagesx($im); |
||
353 | $calc_angle = rad2deg(atan($height / $width)); |
||
354 | $hypoth = $height / sin(deg2rad($calc_angle)); |
||
355 | |||
356 | // vertical and horizontal position of the text |
||
357 | switch ($vpos) { |
||
358 | default: |
||
359 | case 'top': |
||
360 | $taille = $this->textLength($maxsize, $width, $text); |
||
361 | $pos_y = $height * 0.15 + $taille; |
||
362 | $pos_x = $width * 0.15; |
||
363 | $rotation = 0; |
||
364 | break; |
||
365 | case 'middle': |
||
366 | $taille = $this->textLength($maxsize, $width, $text); |
||
367 | $pos_y = ($height + $taille) / 2; |
||
368 | $pos_x = $width * 0.15; |
||
369 | $rotation = 0; |
||
370 | break; |
||
371 | case 'bottom': |
||
372 | $taille = $this->textLength($maxsize, $width, $text); |
||
373 | $pos_y = ($height * .85 - $taille); |
||
374 | $pos_x = $width * 0.15; |
||
375 | $rotation = 0; |
||
376 | break; |
||
377 | case 'across': |
||
378 | switch ($hpos) { |
||
379 | default: |
||
380 | case 'left': |
||
381 | $taille = $this->textLength($maxsize, $hypoth, $text); |
||
382 | $pos_y = ($height * .85 - $taille); |
||
383 | $pos_x = $width * 0.15; |
||
384 | $rotation = $calc_angle; |
||
385 | break; |
||
386 | case 'right': |
||
387 | $taille = $this->textLength($maxsize, $hypoth, $text); |
||
388 | $pos_y = ($height * .15 - $taille); |
||
389 | $pos_x = $width * 0.85; |
||
390 | $rotation = $calc_angle + 180; |
||
391 | break; |
||
392 | case 'top2bottom': |
||
393 | $taille = $this->textLength($maxsize, $height, $text); |
||
394 | $pos_y = ($height * .15 - $taille); |
||
395 | $pos_x = ($width * .90 - $taille); |
||
396 | $rotation = -90; |
||
397 | break; |
||
398 | case 'bottom2top': |
||
399 | $taille = $this->textLength($maxsize, $height, $text); |
||
400 | $pos_y = $height * 0.85; |
||
401 | $pos_x = $width * 0.15; |
||
402 | $rotation = 90; |
||
403 | break; |
||
404 | } |
||
405 | break; |
||
406 | } |
||
407 | |||
408 | // apply the text |
||
409 | if ($this->use_ttf) { |
||
410 | // if imagettftext throws errors, catch them with a custom error handler |
||
411 | set_error_handler(array($this, 'imageTtfTextErrorHandler')); |
||
412 | imagettftext($im, $taille, $rotation, $pos_x, $pos_y, $textcolor, $font, $text); |
||
413 | restore_error_handler(); |
||
414 | } |
||
415 | // Don’t use an ‘else’ here since imagettftextErrorHandler may have changed the value of $useTTF from true to false |
||
416 | if (!$this->use_ttf) { |
||
417 | if ($rotation !== 90) { |
||
418 | imagestring($im, 5, $pos_x, $pos_y, $text, $textcolor); |
||
419 | } else { |
||
420 | imagestringup($im, 5, $pos_x, $pos_y, $text, $textcolor); |
||
421 | } |
||
422 | } |
||
423 | |||
424 | } |
||
425 | |||
426 | /** |
||
427 | * Convert an hexadecimal color to its RGB equivalent. |
||
428 | * |
||
429 | * @param string $hexstr |
||
430 | * @return int[] |
||
431 | */ |
||
432 | protected function hexrgb ($hexstr) |
||
433 | { |
||
434 | $int = hexdec($hexstr); |
||
435 | |||
436 | return array('red' => 0xFF & ($int >> 0x10), |
||
437 | 'green' => 0xFF & ($int >> 0x8), |
||
438 | 'blue' => 0xFF & $int); |
||
439 | } |
||
440 | |||
441 | /** |
||
442 | * Generate an approximate length of text, in pixels. |
||
443 | * |
||
444 | * @param int $t |
||
445 | * @param int $mxl |
||
446 | * @param string $text |
||
447 | * |
||
448 | * @return int |
||
449 | */ |
||
450 | function textLength($t, $mxl, $text) { |
||
451 | $taille_c = $t; |
||
452 | $len = mb_strlen($text); |
||
453 | while (($taille_c - 2) * $len > $mxl) { |
||
454 | $taille_c--; |
||
455 | if ($taille_c == 2) { |
||
456 | break; |
||
457 | } |
||
458 | } |
||
459 | |||
460 | return $taille_c; |
||
461 | } |
||
462 | |||
463 | /** |
||
464 | * imagettftext is the function that is most likely to throw an error |
||
465 | * use this custom error handler to catch and log it |
||
466 | * |
||
467 | * @param int $errno |
||
468 | * @param string $errstr |
||
469 | * |
||
470 | * @return bool |
||
471 | */ |
||
472 | function imageTtfTextErrorHandler($errno, $errstr) { |
||
480 | } |
||
481 | |||
482 | } |
||
483 | |||
484 | ?> |
||