| 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 | ?> |
||