diff --git a/composer.json b/composer.json index 34e893d..a180435 100644 --- a/composer.json +++ b/composer.json @@ -3,11 +3,12 @@ "description": "A class drawing multiline and aligned text on pictures. Uses GD extension.", "license": "MIT", "require": { - "php": ">=5.3", - "ext-gd": "*" + "php": ">=8.1", + "ext-gd": "*", + "ext-mbstring": "*" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^10.1.2" }, "autoload": { "psr-4": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..b170ac7 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 6 + paths: + - ./src \ No newline at end of file diff --git a/src/Box.php b/src/Box.php index 3721a50..92370db 100644 --- a/src/Box.php +++ b/src/Box.php @@ -1,4 +1,7 @@ im = $image; $this->fontColor = new Color(0, 0, 0); @@ -92,7 +53,7 @@ public function __construct(&$image) /** * @param Color $color Font color */ - public function setFontColor(Color $color) + public function setFontColor(Color $color): void { $this->fontColor = $color; } @@ -100,7 +61,7 @@ public function setFontColor(Color $color) /** * @param string $path Path to the font file */ - public function setFontFace($path) + public function setFontFace(string $path): void { $this->fontFace = $path; } @@ -108,7 +69,7 @@ public function setFontFace($path) /** * @param int $v Font size in *pixels* */ - public function setFontSize($v) + public function setFontSize(int $v): void { $this->fontSize = $v; } @@ -116,15 +77,15 @@ public function setFontSize($v) /** * @param Color $color Stroke color */ - public function setStrokeColor(Color $color) + public function setStrokeColor(Color $color): void { $this->strokeColor = $color; } - + /** * @param int $v Stroke size in *pixels* */ - public function setStrokeSize($v) + public function setStrokeSize(int $v): void { $this->strokeSize = $v; } @@ -134,27 +95,28 @@ public function setStrokeSize($v) * @param int $xShift Relative shadow position in pixels. Positive values move shadow to right, negative to left. * @param int $yShift Relative shadow position in pixels. Positive values move shadow to bottom, negative to up. */ - public function setTextShadow(Color $color, $xShift, $yShift) + public function setTextShadow(Color $color, int $xShift, int $yShift): void { - $this->textShadow = array( + $this->textShadow = [ 'color' => $color, - 'offset' => new Point($xShift, $yShift) - ); + 'offset' => new Point($xShift, $yShift), + ]; } /** * @param Color $color Font color */ - public function setBackgroundColor(Color $color) + public function setBackgroundColor(Color $color): void { $this->backgroundColor = $color; } /** * Allows to customize spacing between lines. + * * @param float $v Height of the single text line, in percents, proportionally to font size */ - public function setLineHeight($v) + public function setLineHeight(float $v): void { $this->lineHeight = $v; } @@ -162,41 +124,48 @@ public function setLineHeight($v) /** * @param float $v Position of baseline, in percents, proportionally to line height measuring from the bottom. */ - public function setBaseline($v) + public function setBaseline(float $v): void { $this->baseline = $v; } /** * Sets text alignment inside textbox - * @param string $x Horizontal alignment. Allowed values are: left, center, right. - * @param string $y Vertical alignment. Allowed values are: top, center, bottom. + * + * @param HorizontalAlignment|string $x Horizontal alignment. Allowed values are: left, center, right. + * @param VerticalAlignment|string $y Vertical alignment. Allowed values are: top, center, bottom. */ - public function setTextAlign($x = 'left', $y = 'top') + public function setTextAlign(HorizontalAlignment|string $x = HorizontalAlignment::Left, VerticalAlignment|string $y = VerticalAlignment::Top): void { - $xAllowed = array('left', 'right', 'center'); - $yAllowed = array('top', 'bottom', 'center'); - - if (!in_array($x, $xAllowed)) { - throw new \InvalidArgumentException('Invalid horizontal alignement value was specified.'); + if (is_string($x)) { + $x = HorizontalAlignment::from($x); } - if (!in_array($y, $yAllowed)) { - throw new \InvalidArgumentException('Invalid vertical alignement value was specified.'); + if (is_string($y)) { + $y = VerticalAlignment::from($y); } $this->alignX = $x; $this->alignY = $y; } + /** + * @param float|int $spacing Spacing between characters + */ + public function setSpacing(float|int $spacing): void + { + $this->spacing = $spacing; + } + /** * Sets textbox position and dimensions + * * @param int $x Distance in pixels from left edge of image. * @param int $y Distance in pixels from top edge of image. * @param int $width Width of texbox in pixels. * @param int $height Height of textbox in pixels. */ - public function setBox($x, $y, $width, $height) + public function setBox(int $x, int $y, int $width, int $height): void { $this->box = new Rectangle($x, $y, $width, $height); } @@ -204,38 +173,101 @@ public function setBox($x, $y, $width, $height) /** * Enables debug mode. Whole textbox and individual lines will be filled with random colors. */ - public function enableDebug() + public function enableDebug(): void { $this->debug = true; } + public function setTextWrapping(TextWrapping $textWrapping): void + { + $this->textWrapping = $textWrapping; + } + + /** - * @param int $textWrapping + * Draws the text on the picture. + * + * @param string $text Text to draw. May contain newline characters. + * + * @return Rectangle Area that cover the drawn text */ - public function setTextWrapping($textWrapping) + public function draw(string $text): Rectangle { - $this->textWrapping = $textWrapping; + return $this->drawText($text, true); + } + + /** + * Draws the text on the picture, fitting it to the current box + * + * @param string $text Text to draw. May contain newline characters. + * @param int $precision Increment or decrement of font size. The lower this value, the slower this method. + * + * @return Rectangle Area that cover the drawn text + */ + public function drawFitFontSize(string $text, int $precision = -1, int $maxFontSize = -1, int $minFontSize = -1, int &$usedFontSize = 0): Rectangle + { + $initialFontSize = $this->fontSize; + + $usedFontSize = $this->fontSize; + $rectangle = $this->calculate($text); + + if ($rectangle->getHeight() > $this->box->getHeight() || $rectangle->getWidth() > $this->box->getWidth()) { + // Decrement font size + do { + $this->setFontSize($usedFontSize); + $rectangle = $this->calculate($text); + + $usedFontSize -= $precision; + } while (($minFontSize == -1 || $usedFontSize > $minFontSize) && + ($rectangle->getHeight() > $this->box->getHeight() || $rectangle->getWidth() > $this->box->getWidth())); + + $usedFontSize += $precision; + } else { + // Increment font size + do { + $this->setFontSize($usedFontSize); + $rectangle = $this->calculate($text); + + $usedFontSize += $precision; + } while (($maxFontSize > 0 && $usedFontSize < $maxFontSize) + && $rectangle->getHeight() < $this->box->getHeight() + && $rectangle->getWidth() < $this->box->getWidth()); + + $usedFontSize -= $precision * 2; + } + $this->setFontSize($usedFontSize); + + $rectangle = $this->drawText($text, true); + + // Restore initial font size + $this->setFontSize($initialFontSize); + + return $rectangle; + } + + /** + * Get the area that will cover the given text + */ + public function calculate(string $text): Rectangle + { + return $this->drawText($text, false); } /** * Draws the text on the picture. + * * @param string $text Text to draw. May contain newline characters. */ - public function draw($text) + protected function drawText(string $text, bool $draw): Rectangle { if (!isset($this->fontFace)) { throw new \InvalidArgumentException('No path to font file has been specified.'); } - switch ($this->textWrapping) { - case TextWrapping::NoWrap: - $lines = array($text); - break; - case TextWrapping::WrapWithOverflow: - default: - $lines = $this->wrapTextWithOverflow($text); - break; - } + $lines = match ($this->textWrapping) { + TextWrapping::NoWrap => [$text], + default => $this->wrapTextWithOverflow($text), + }; if ($this->debug) { // Marks whole texbox area with color @@ -248,46 +280,41 @@ public function draw($text) $lineHeightPx = $this->lineHeight * $this->fontSize; $textHeight = count($lines) * $lineHeightPx; - switch ($this->alignY) { - case VerticalAlignment::Center: - $yAlign = ($this->box->getHeight() / 2) - ($textHeight / 2); - break; - case VerticalAlignment::Bottom: - $yAlign = $this->box->getHeight() - $textHeight; - break; - case VerticalAlignment::Top: - default: - $yAlign = 0; - } + $yAlign = match ($this->alignY) { + VerticalAlignment::Center => ($this->box->getHeight() / 2) - ($textHeight / 2), + VerticalAlignment::Bottom => $this->box->getHeight() - $textHeight, + default => 0, + }; $n = 0; + + $drawnX = $drawnY = PHP_INT_MAX; + $drawnH = $drawnW = 0; + foreach ($lines as $line) { $box = $this->calculateBox($line); - switch ($this->alignX) { - case HorizontalAlignment::Center: - $xAlign = ($this->box->getWidth() - $box->getWidth()) / 2; - break; - case HorizontalAlignment::Right: - $xAlign = ($this->box->getWidth() - $box->getWidth()); - break; - case HorizontalAlignment::Left: - default: - $xAlign = 0; - } + $xAlign = match ($this->alignX) { + HorizontalAlignment::Center => (int)round(($this->box->getWidth() - $box->getWidth()) / 2), + HorizontalAlignment::Right => ($this->box->getWidth() - $box->getWidth()), + default => 0, + }; $yShift = $lineHeightPx * (1 - $this->baseline); // current line X and Y position $xMOD = $this->box->getX() + $xAlign; - $yMOD = $this->box->getY() + $yAlign + $yShift + ($n * $lineHeightPx); + $yMOD = (int)round($this->box->getY() + $yAlign + $yShift + ($n * $lineHeightPx)); - if ($line && $this->backgroundColor) { + if ($draw && $line && $this->backgroundColor) { // Marks whole texbox area with given background-color $backgroundHeight = $this->fontSize; $this->drawFilledRectangle( new Rectangle( $xMOD, - $this->box->getY() + $yAlign + ($n * $lineHeightPx) + ($lineHeightPx - $backgroundHeight) + (1 - $this->lineHeight) * 13 * (1 / 50 * $this->fontSize), + (int)round( + $this->box->getY() + $yAlign + ($n * $lineHeightPx) + ($lineHeightPx - $backgroundHeight) + + (1 - $this->lineHeight) * 13 * (1 / 50 * $this->fontSize) + ), $box->getWidth(), $backgroundHeight ), @@ -300,47 +327,56 @@ public function draw($text) $this->drawFilledRectangle( new Rectangle( $xMOD, - $this->box->getY() + $yAlign + ($n * $lineHeightPx), + (int)round($this->box->getY() + $yAlign + ($n * $lineHeightPx)), $box->getWidth(), - $lineHeightPx + (int)round($lineHeightPx) ), new Color(rand(1, 180), rand(1, 180), rand(1, 180)) ); } - if ($this->textShadow !== false) { + if ($draw) { + if (isset($this->textShadow)) { + $this->drawInternal( + new Point( + $xMOD + $this->textShadow['offset']->getX(), + $yMOD + $this->textShadow['offset']->getY() + ), + $this->textShadow['color'], + $line + ); + } + + $this->strokeText($xMOD, $yMOD, $line); $this->drawInternal( new Point( - $xMOD + $this->textShadow['offset']->getX(), - $yMOD + $this->textShadow['offset']->getY() + $xMOD, + $yMOD ), - $this->textShadow['color'], + $this->fontColor, $line ); } - $this->strokeText($xMOD, $yMOD, $line); - $this->drawInternal( - new Point( - $xMOD, - $yMOD - ), - $this->fontColor, - $line - ); + $drawnX = min($xMOD, $drawnX); + $drawnY = min($this->box->getY() + $yAlign + ($n * $lineHeightPx), $drawnY); + $drawnW = max($drawnW, $box->getWidth()); + $drawnH += $lineHeightPx; $n++; } + + return new Rectangle((int)round($drawnX), (int)round($drawnY), $drawnW, (int)round($drawnH)); } /** * Splits overflowing text into array of strings. - * @param string $text + * * @return string[] */ - protected function wrapTextWithOverflow($text) + protected function wrapTextWithOverflow(string $text): array { - $lines = array(); + $lines = []; // Split text explicitly into lines by \n, \r\n and \r $explicitLines = preg_split('/\n|\r\n?/', $text); foreach ($explicitLines as $line) { @@ -348,12 +384,12 @@ protected function wrapTextWithOverflow($text) $words = explode(" ", $line); $line = $words[0]; for ($i = 1; $i < count($words); $i++) { - $box = $this->calculateBox($line." ".$words[$i]); + $box = $this->calculateBox($line . " " . $words[$i]); if ($box->getWidth() >= $this->box->getWidth()) { $lines[] = $line; $line = $words[$i]; } else { - $line .= " ".$words[$i]; + $line .= " " . $words[$i]; } } $lines[] = $line; @@ -361,15 +397,12 @@ protected function wrapTextWithOverflow($text) return $lines; } - /** - * @return float - */ - protected function getFontSizeInPoints() + protected function getFontSizeInPoints(): float { return 0.75 * $this->fontSize; } - protected function drawFilledRectangle(Rectangle $rect, Color $color) + protected function drawFilledRectangle(Rectangle $rect, Color $color): void { imagefilledrectangle( $this->im, @@ -383,30 +416,30 @@ protected function drawFilledRectangle(Rectangle $rect, Color $color) /** * Returns the bounding box of a text. - * @param string $text - * @return Rectangle */ - protected function calculateBox($text) + protected function calculateBox(string $text): Rectangle { $bounds = imagettfbbox($this->getFontSizeInPoints(), 0, $this->fontFace, $text); - $xLeft = $bounds[0]; // (lower|upper) left corner, X position - $xRight = $bounds[2]; // (lower|upper) right corner, X position + $xLeft = $bounds[0]; // (lower|upper) left corner, X position + $xRight = $bounds[2] + (mb_strlen($text) * $this->spacing); // (lower|upper) right corner, X position $yLower = $bounds[1]; // lower (left|right) corner, Y position $yUpper = $bounds[5]; // upper (left|right) corner, Y position return new Rectangle( $xLeft, $yUpper, - $xRight - $xLeft, + (int)round($xRight - $xLeft), $yLower - $yUpper ); } - protected function strokeText($x, $y, $text) + protected function strokeText(int $x, int $y, string $text): void { $size = $this->strokeSize; - if ($size <= 0) return; + if ($size <= 0) { + return; + } for ($c1 = $x - $size; $c1 <= $x + $size; $c1++) { for ($c2 = $y - $size; $c2 <= $y + $size; $c2++) { $this->drawInternal(new Point($c1, $c2), $this->strokeColor, $text); @@ -414,17 +447,49 @@ protected function strokeText($x, $y, $text) } } - protected function drawInternal(Point $position, Color $color, $text) + protected function drawInternal(Point $position, Color $color, string $text): void { - imagettftext( - $this->im, - $this->getFontSizeInPoints(), - 0, // no rotation - $position->getX(), - $position->getY(), - $color->getIndex($this->im), - $this->fontFace, - $text - ); + if ($this->spacing == 0) { + imagettftext( + $this->im, + $this->getFontSizeInPoints(), + 0, // no rotation + (int)round($position->getX()), + (int)round($position->getY()), + $color->getIndex($this->im), + $this->fontFace, + $text + ); + } else { // https://stackoverflow.com/a/65254013/528065 + $getBoxW = fn($bBox) => $bBox[2] - $bBox[0]; + + $x = $position->getX(); + $testStr = 'test'; + $size = $this->getFontSizeInPoints(); + $testW = $getBoxW(imagettfbbox($size, 0, $this->fontFace, $testStr)); + foreach (mb_str_split($text) as $char) { + if ($this->debug) { + $bounds = imagettfbbox($size, 0, $this->fontFace, $char); + $xLeft = $bounds[0]; // (lower|upper) left corner, X position + $xRight = $bounds[2]; // (lower|upper) right corner, X position + $yLower = $bounds[1]; // lower (left|right) corner, Y position + $yUpper = $bounds[5]; // upper (left|right) corner, Y position + + $this->drawFilledRectangle( + new Rectangle( + $x - $bounds[0], + $position->getY() - ($yLower - $yUpper), + $xRight - $xLeft, + $yLower - $yUpper + ), + new Color(rand(180, 255), rand(180, 255), rand(180, 255), 80) + ); + } + + $fullBox = imagettfbbox($size, 0, $this->fontFace, $char . $testStr); + imagettftext($this->im, $size, 0, (int)round($x - $fullBox[0]), (int)round($position->getY()), $color->getIndex($this->im), $this->fontFace, $char); + $x += $this->spacing + $getBoxW($fullBox) - $testW; + } + } } } diff --git a/src/Color.php b/src/Color.php index 5efee50..adda5f0 100644 --- a/src/Color.php +++ b/src/Color.php @@ -1,4 +1,7 @@ red = $red; - $this->green = $green; - $this->blue = $blue; - $this->alpha = $alpha; } /** * Parses string to Color object representation. + * * @param string $str String with color information, ex. #000000 + * * @return Color * @todo Add parsing of CSS-like strings: rgb(), rgba(), hsl() */ - public static function parseString($str) + public static function parseString(string $str): Color { $str = str_replace('#', '', $str); if (strlen($str) == 6) { $r = hexdec(substr($str, 0, 2)); $g = hexdec(substr($str, 2, 2)); $b = hexdec(substr($str, 4, 2)); - } else if (strlen($str) == 3) { + } elseif (strlen($str) == 3) { $r = hexdec(str_repeat(substr($str, 0, 1), 2)); $g = hexdec(str_repeat(substr($str, 1, 1), 2)); $b = hexdec(str_repeat(substr($str, 2, 1), 2)); @@ -69,14 +50,15 @@ public static function parseString($str) * @param float $h Hue * @param float $s Saturation * @param float $l Light + * * @return Color */ - public static function fromHsl($h, $s, $l) + public static function fromHsl(float $h, float $s, float $l): Color { $fromFloat = function (array $rgb) { foreach ($rgb as &$v) { $v = (int)round($v * 255); - }; + } return new Color($rgb[0], $rgb[1], $rgb[2]); }; @@ -84,55 +66,60 @@ public static function fromHsl($h, $s, $l) // If saturation is 0, the given color is grey and only // lightness is relevant. if ($s == 0) { - return $fromFloat(array($l, $l, $l)); + return $fromFloat([$l, $l, $l]); } // Else calculate r, g, b according to hue. // Check http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL for details $chroma = (1 - abs(2 * $l - 1)) * $s; - $h_ = $h * 6; - $x = $chroma * (1 - abs((fmod($h_,2)) - 1)); // Note: fmod because % (modulo) returns int value!! - $m = $l - round($chroma/2, 10); // Bugfix for strange float behaviour (e.g. $l=0.17 and $s=1) - - if ($h_ >= 0 && $h_ < 1) $rgb = array(($chroma + $m), ($x + $m), $m); - elseif ($h_ >= 1 && $h_ < 2) $rgb = array(($x + $m), ($chroma + $m), $m); - elseif ($h_ >= 2 && $h_ < 3) $rgb = array($m, ($chroma + $m), ($x + $m)); - elseif ($h_ >= 3 && $h_ < 4) $rgb = array($m, ($x + $m), ($chroma + $m)); - elseif ($h_ >= 4 && $h_ < 5) $rgb = array(($x + $m), $m, ($chroma + $m)); - elseif ($h_ >= 5 && $h_ < 6) $rgb = array(($chroma + $m), $m, ($x + $m)); - else throw new \InvalidArgumentException('Invalid hue, it should be a value between 0 and 1.'); + $h_ = $h * 6; + $x = $chroma * (1 - abs((fmod($h_, 2)) - 1)); // Note: fmod because % (modulo) returns int value!! + $m = $l - round($chroma / 2, 10); // Bugfix for strange float behaviour (e.g. $l=0.17 and $s=1) + + if ($h_ >= 0 && $h_ < 1) { + $rgb = [($chroma + $m), ($x + $m), $m]; + } elseif ($h_ >= 1 && $h_ < 2) { + $rgb = [($x + $m), ($chroma + $m), $m]; + } elseif ($h_ >= 2 && $h_ < 3) { + $rgb = [$m, ($chroma + $m), ($x + $m)]; + } elseif ($h_ >= 3 && $h_ < 4) { + $rgb = [$m, ($x + $m), ($chroma + $m)]; + } elseif ($h_ >= 4 && $h_ < 5) { + $rgb = [($x + $m), $m, ($chroma + $m)]; + } elseif ($h_ >= 5 && $h_ < 6) { + $rgb = [($chroma + $m), $m, ($x + $m)]; + } else { + throw new \InvalidArgumentException('Invalid hue, it should be a value between 0 and 1.'); + } return $fromFloat($rgb); } /** - * @param resource $image GD image resource - * @return int Returns the index of the specified color+alpha in the palette of the image, + * @param \GdImage $image GD image resource + * + * @return int|false Returns the index of the specified color+alpha in the palette of the image, * or index of allocated color if the color does not exist in the image's palette. */ - public function getIndex($image) + public function getIndex(\GdImage $image): int|false { $index = $this->hasAlphaChannel() - ? imagecolorexactalpha( - $image, $this->red, $this->green, $this->blue, $this->alpha) - : imagecolorexact( - $image, $this->red, $this->green, $this->blue); + ? imagecolorexactalpha($image, $this->red, $this->green, $this->blue, $this->alpha) + : imagecolorexact($image, $this->red, $this->green, $this->blue); if ($index !== -1) { return $index; } return $this->hasAlphaChannel() - ? imagecolorallocatealpha( - $image, $this->red, $this->green, $this->blue, $this->alpha) - : imagecolorallocate( - $image, $this->red, $this->green, $this->blue); + ? imagecolorallocatealpha($image, $this->red, $this->green, $this->blue, $this->alpha) + : imagecolorallocate($image, $this->red, $this->green, $this->blue); } /** * @return bool TRUE when alpha channel is specified, FALSE otherwise */ - public function hasAlphaChannel() + public function hasAlphaChannel(): bool { return $this->alpha !== null; } @@ -140,8 +127,8 @@ public function hasAlphaChannel() /** * @return int[] */ - public function toArray() + public function toArray(): array { - return array($this->red, $this->green, $this->blue); + return [$this->red, $this->green, $this->blue]; } } \ No newline at end of file diff --git a/src/HorizontalAlignment.php b/src/HorizontalAlignment.php index 3a4aee7..9b70897 100644 --- a/src/HorizontalAlignment.php +++ b/src/HorizontalAlignment.php @@ -1,9 +1,12 @@ x = $x; - $this->y = $y; } - /** - * @return int - */ - public function getX() + public function getX(): int { return $this->x; } - /** - * @return int - */ - public function getY() + public function getY(): int { return $this->y; } diff --git a/src/Struct/Rectangle.php b/src/Struct/Rectangle.php index c24e708..69aa279 100644 --- a/src/Struct/Rectangle.php +++ b/src/Struct/Rectangle.php @@ -1,77 +1,45 @@ width = $width; - $this->height = $height; } - /** - * @return int - */ - public function getWidth() + public function getWidth(): int { return $this->width; } - /** - * @return int - */ - public function getHeight() + public function getHeight(): int { return $this->height; } - /** - * @return int - */ - public function getLeft() + public function getLeft(): int { return $this->getX(); } - /** - * @return int - */ - public function getTop() + public function getTop(): int { return $this->getY(); } - /** - * @return int - */ - public function getRight() + public function getRight(): int { return $this->getX() + $this->width; } - /** - * @return int - */ - public function getBottom() + public function getBottom(): int { return $this->getY() + $this->height; } diff --git a/src/TextWrapping.php b/src/TextWrapping.php index a0b04bd..1d390fa 100644 --- a/src/TextWrapping.php +++ b/src/TextWrapping.php @@ -1,8 +1,11 @@ setTextAlign($x, $y); $box->draw("Owls are birds from the order Strigiformes, which includes about 200 species."); - $this->assertImageEquals("test_align_{$y}_{$x}.png", $im); + $this->assertImageEquals("test_align_{$y->value}_{$x->value}.png", $im); } } } diff --git a/tests/TextWrappingTest.php b/tests/TextWrappingTest.php index b9d2578..44e0e71 100644 --- a/tests/TextWrappingTest.php +++ b/tests/TextWrappingTest.php @@ -3,11 +3,13 @@ use GDText\Box; use GDText\Color; +use GDText\HorizontalAlignment; use GDText\TextWrapping; +use GDText\VerticalAlignment; class TextWrappingTest extends TestCase { - protected function mockBox($im) + protected function mockBox($im): Box { imagealphablending($im, true); imagesavealpha($im, true); @@ -17,7 +19,7 @@ protected function mockBox($im) $box->setFontColor(new Color(255, 75, 140)); $box->setFontSize(16); $box->setBox(0, 135, imagesx($im), 70); - $box->setTextAlign('left', 'top'); + $box->setTextAlign(HorizontalAlignment::Left, VerticalAlignment::Top); return $box; }