From 8054bc813f20c7523546d01586da187175594353 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 25 Nov 2023 09:38:20 +0100 Subject: [PATCH] Refactor font system --- docker-compose.yml | 2 +- src/Drivers/AbstractFontProcessor.php | 93 +++++++++++++++++++ src/Drivers/Gd/Driver.php | 7 ++ .../Gd/{Font.php => FontProcessor.php} | 26 +++--- .../{TextWriter.php => TextModifier.php} | 34 ++++--- src/Drivers/Imagick/Driver.php | 7 ++ .../Imagick/{Font.php => FontProcessor.php} | 50 +++++----- .../Imagick/Modifiers/TextModifier.php | 42 +++++++++ src/Drivers/Imagick/Modifiers/TextWriter.php | 29 ------ src/Image.php | 15 +++ src/Interfaces/DriverInterface.php | 1 + src/Interfaces/FontInterface.php | 6 -- src/Interfaces/FontProcessorInterface.php | 17 ++++ src/Modifiers/TextModifier.php | 16 ++++ src/Modifiers/TextWriter.php | 52 ----------- .../AbstractFont.php => Typography/Font.php} | 48 ++++------ src/Typography/FontFactory.php | 73 +++++++++++++++ src/Typography/Line.php | 6 -- src/Typography/TextBlock.php | 26 ------ tests/Drivers/Abstract/AbstractFontTest.php | 49 ---------- tests/Drivers/AbstractFontProcessorTest.php | 64 +++++++++++++ tests/Drivers/Gd/FontProcessorTest.php | 38 ++++++++ tests/Typography/FontFactoryTest.php | 42 +++++++++ tests/Typography/FontTest.php | 82 ++++++++++++++++ tests/Typography/TextBlockTest.php | 33 ------- 25 files changed, 575 insertions(+), 283 deletions(-) create mode 100644 src/Drivers/AbstractFontProcessor.php rename src/Drivers/Gd/{Font.php => FontProcessor.php} (77%) rename src/Drivers/Gd/Modifiers/{TextWriter.php => TextModifier.php} (52%) rename src/Drivers/Imagick/{Font.php => FontProcessor.php} (67%) create mode 100644 src/Drivers/Imagick/Modifiers/TextModifier.php delete mode 100644 src/Drivers/Imagick/Modifiers/TextWriter.php create mode 100644 src/Interfaces/FontProcessorInterface.php create mode 100644 src/Modifiers/TextModifier.php delete mode 100644 src/Modifiers/TextWriter.php rename src/{Drivers/AbstractFont.php => Typography/Font.php} (68%) create mode 100644 src/Typography/FontFactory.php delete mode 100644 tests/Drivers/Abstract/AbstractFontTest.php create mode 100644 tests/Drivers/AbstractFontProcessorTest.php create mode 100644 tests/Drivers/Gd/FontProcessorTest.php create mode 100644 tests/Typography/FontFactoryTest.php create mode 100644 tests/Typography/FontTest.php diff --git a/docker-compose.yml b/docker-compose.yml index 8f09241c..8d90fbff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,7 +4,7 @@ services: tests: build: ./ working_dir: /project - command: bash -c "composer install && ./vendor/bin/phpunit -vvv --filter=FontTest" + command: bash -c "composer install && ./vendor/bin/phpunit -vvv" volumes: - ./:/project analysis: diff --git a/src/Drivers/AbstractFontProcessor.php b/src/Drivers/AbstractFontProcessor.php new file mode 100644 index 00000000..987b55ea --- /dev/null +++ b/src/Drivers/AbstractFontProcessor.php @@ -0,0 +1,93 @@ +fontSizeInPixels() * $this->font->lineHeight())); + } + + public function capHeight(): int + { + return $this->boxSize('T')->height(); + } + + public function fontSizeInPixels(): int + { + return $this->boxSize('Hy')->height(); + } + + /** + * Build TextBlock object from text string and align every line + * according to text writers font object and position. + * + * @return TextBlock + */ + public function alignedTextBlock(Point $position, string $text): TextBlock + { + $lines = new TextBlock($text); + $boundingBox = $this->boundingBox($lines, $position); + $pivot = $boundingBox->last(); + + $leading = $this->leadingInPixels(); + $blockWidth = $this->lineWidth($lines->longestLine()); + + $x = $pivot->x(); + $y = $this->font->hasFilename() ? $pivot->y() + $this->capHeight() : $pivot->y(); + $x_adjustment = 0; + + foreach ($lines as $line) { + $line_width = $this->lineWidth($line); + $x_adjustment = $this->font->alignment() == 'left' ? 0 : $blockWidth - $line_width; + $x_adjustment = $this->font->alignment() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; + $x_adjustment = $this->font->alignment() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; + $position = new Point($x + $x_adjustment, $y); + $position->rotate($this->font->angle(), $pivot); + $line->setPosition($position); + $y += $leading; + } + + return $lines; + } + + public function boundingBox(TextBlock $block, Point $pivot = null): Polygon + { + $pivot = $pivot ? $pivot : new Point(); + + // bounding box + $box = (new Rectangle( + $this->lineWidth($block->longestLine()), + $this->leadingInPixels() * ($block->count() - 1) + $this->capHeight() + )); + + // set pivot + $box->setPivot($pivot); + + // align + $box->align($this->font->alignment()); + $box->valign($this->font->valignment()); + + $box->rotate($this->font->angle()); + + return $box; + } + + private function lineWidth(Line $line): int + { + return $this->boxSize((string) $line)->width(); + } +} diff --git a/src/Drivers/Gd/Driver.php b/src/Drivers/Gd/Driver.php index 75cb00d6..3781089e 100644 --- a/src/Drivers/Gd/Driver.php +++ b/src/Drivers/Gd/Driver.php @@ -7,6 +7,8 @@ use Intervention\Image\Image; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorProcessorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; +use Intervention\Image\Interfaces\FontInterface; +use Intervention\Image\Interfaces\FontProcessorInterface; use Intervention\Image\Interfaces\ImageInterface; class Driver extends AbstractDriver @@ -44,6 +46,11 @@ class Driver extends AbstractDriver return new ColorProcessor($colorspace); } + public function fontProcessor(FontInterface $font): FontProcessorInterface + { + return new FontProcessor($font); + } + public function colorToNative(ColorInterface $color, ColorspaceInterface $colorspace): mixed { return (new ColorProcessor($colorspace))->colorToNative($color); diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/FontProcessor.php similarity index 77% rename from src/Drivers/Gd/Font.php rename to src/Drivers/Gd/FontProcessor.php index dab733ba..4ed6e979 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/FontProcessor.php @@ -2,26 +2,21 @@ namespace Intervention\Image\Drivers\Gd; -use Intervention\Image\Drivers\AbstractFont; +use Intervention\Image\Drivers\AbstractFontProcessor; use Intervention\Image\Geometry\Point; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; -class Font extends AbstractFont +class FontProcessor extends AbstractFontProcessor { - public function size(): float - { - return floatval(ceil(parent::size() * 0.75)); - } - /** * Calculate size of bounding box of given text * * @return Polygon */ - public function getBoxSize(string $text): Polygon + public function boxSize(string $text): Polygon { - if (!$this->hasFilename()) { + if (!$this->font->hasFilename()) { // calculate box size from gd font $box = new Rectangle(0, 0); $chars = mb_strlen($text); @@ -34,9 +29,9 @@ class Font extends AbstractFont // calculate box size from font file with angle 0 $box = imageftbbox( - $this->size(), + $this->adjustedSize(), 0, - $this->filename(), + $this->font->filename(), $text ); @@ -50,10 +45,15 @@ class Font extends AbstractFont return $polygon; } + public function adjustedSize(): int + { + return floatval(ceil($this->font->size() * .75)); + } + public function getGdFont(): int { - if (is_numeric($this->filename)) { - return $this->filename; + if (is_numeric($this->font->filename())) { + return $this->font->filename(); } return 1; diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextModifier.php similarity index 52% rename from src/Drivers/Gd/Modifiers/TextWriter.php rename to src/Drivers/Gd/Modifiers/TextModifier.php index 4f07484e..9d51e6aa 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextModifier.php @@ -2,31 +2,34 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; +use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Drivers\DriverModifier; -use Intervention\Image\Drivers\Gd\Traits\CanHandleColors; +use Intervention\Image\Drivers\Gd\FontProcessor; use Intervention\Image\Interfaces\ImageInterface; -class TextWriter extends DriverModifier +class TextModifier extends DriverModifier { - use CanHandleColors; - public function apply(ImageInterface $image): ImageInterface { - $lines = $this->getAlignedTextBlock(); - $font = $this->font; - $color = $this->colorToInteger($font->getColor()); + $processor = $this->fontProcessor(); + $lines = $processor->getAlignedTextBlock($this->position, $this->text); + + $color = $this->driver()->colorToNative( + $this->driver()->handleInput($this->font->color()), + new Colorspace() + ); foreach ($image as $frame) { if ($this->font->hasFilename()) { foreach ($lines as $line) { imagettftext( $frame->native(), - $font->getSize(), - $font->getAngle() * (-1), - $line->getPosition()->x(), - $line->getPosition()->y(), + $processor->adjustedSize(), + $this->font->angle() * -1, + $line->position()->x(), + $line->position()->y(), $color, - $font->getFilename(), + $this->font->filename(), $line ); } @@ -34,7 +37,7 @@ class TextWriter extends DriverModifier foreach ($lines as $line) { imagestring( $frame->native(), - $font->getGdFont(), + $processor->getGdFont(), $line->getPosition()->x(), $line->getPosition()->y(), $line, @@ -46,4 +49,9 @@ class TextWriter extends DriverModifier return $image; } + + private function fontProcessor(): FontProcessor + { + return $this->driver()->fontProcessor($this->font); + } } diff --git a/src/Drivers/Imagick/Driver.php b/src/Drivers/Imagick/Driver.php index 682d2d45..353dab70 100644 --- a/src/Drivers/Imagick/Driver.php +++ b/src/Drivers/Imagick/Driver.php @@ -9,6 +9,8 @@ use Intervention\Image\Image; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorProcessorInterface; use Intervention\Image\Interfaces\ColorspaceInterface; +use Intervention\Image\Interfaces\FontInterface; +use Intervention\Image\Interfaces\FontProcessorInterface; use Intervention\Image\Interfaces\ImageInterface; class Driver extends AbstractDriver @@ -42,6 +44,11 @@ class Driver extends AbstractDriver return new ColorProcessor($colorspace); } + public function fontProcessor(FontInterface $font): FontProcessorInterface + { + return new FontProcessor($font); + } + public function colorToNative(ColorInterface $color, ColorspaceInterface $colorspace): mixed { return (new ColorProcessor($colorspace))->colorToNative($color); diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/FontProcessor.php similarity index 67% rename from src/Drivers/Imagick/Font.php rename to src/Drivers/Imagick/FontProcessor.php index 26780604..387ba4e3 100644 --- a/src/Drivers/Imagick/Font.php +++ b/src/Drivers/Imagick/FontProcessor.php @@ -4,42 +4,20 @@ namespace Intervention\Image\Drivers\Imagick; use Imagick; use ImagickDraw; -use Intervention\Image\Drivers\AbstractFont; +use ImagickPixel; +use Intervention\Image\Drivers\AbstractFontProcessor; use Intervention\Image\Exceptions\FontException; use Intervention\Image\Geometry\Polygon; use Intervention\Image\Geometry\Rectangle; -use Intervention\Image\Interfaces\ColorspaceInterface; -class Font extends AbstractFont +class FontProcessor extends AbstractFontProcessor { - public function toImagickDraw(?ColorspaceInterface $colorspace = null): ImagickDraw - { - if (!$this->hasFilename()) { - throw new FontException('No font file specified.'); - } - - $draw = new ImagickDraw(); - $draw->setStrokeAntialias(true); - $draw->setTextAntialias(true); - $draw->setFont($this->filename()); - $draw->setFontSize($this->size()); - $draw->setTextAlignment(Imagick::ALIGN_LEFT); - - if ($colorspace) { - $draw->setFillColor( - $this->colorToPixel($this->color(), $colorspace) - ); - } - - return $draw; - } - /** * Calculate box size of current font * * @return Polygon */ - public function getBoxSize(string $text): Polygon + public function boxSize(string $text): Polygon { // no text - no box size if (mb_strlen($text) === 0) { @@ -56,4 +34,24 @@ class Font extends AbstractFont intval(round($dimensions['ascender'] + $dimensions['descender'])), )); } + + public function toImagickDraw(?ImagickPixel $color = null): ImagickDraw + { + if (!$this->font->hasFilename()) { + throw new FontException('No font file specified.'); + } + + $draw = new ImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $draw->setFont($this->font->filename()); + $draw->setFontSize($this->font->size()); + $draw->setTextAlignment(Imagick::ALIGN_LEFT); + + if ($color) { + $draw->setFillColor($color); + } + + return $draw; + } } diff --git a/src/Drivers/Imagick/Modifiers/TextModifier.php b/src/Drivers/Imagick/Modifiers/TextModifier.php new file mode 100644 index 00000000..8f7dfa7d --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/TextModifier.php @@ -0,0 +1,42 @@ +fontProcessor(); + $lines = $processor->getAlignedTextBlock($this->position, $this->text); + + $color = $this->driver()->colorToNative( + $this->driver()->handleInput($this->font->color()), + $image->colorspace() + ); + + $draw = $processor->toImagickDraw($color); + + foreach ($image as $frame) { + foreach ($lines as $line) { + $frame->native()->annotateImage( + $draw, + $line->position()->x(), + $line->position()->y(), + $this->font->angle(), + $line + ); + } + } + + return $image; + } + + private function fontProcessor(): FontProcessor + { + return $this->driver()->fontProcessor($this->font); + } +} diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php deleted file mode 100644 index f7ae408a..00000000 --- a/src/Drivers/Imagick/Modifiers/TextWriter.php +++ /dev/null @@ -1,29 +0,0 @@ -getAlignedTextBlock(); - $font = $this->font; - - foreach ($image as $frame) { - foreach ($lines as $line) { - $frame->native()->annotateImage( - $font->toImagickDraw($image->colorspace()), - $line->getPosition()->x(), - $line->getPosition()->y(), - $font->getAngle(), - $line - ); - } - } - - return $image; - } -} diff --git a/src/Image.php b/src/Image.php index a63627f2..65057008 100644 --- a/src/Image.php +++ b/src/Image.php @@ -10,6 +10,7 @@ use Intervention\Image\Analyzers\PixelColorsAnalyzer; use Intervention\Image\Analyzers\ProfileAnalyzer; use Intervention\Image\Analyzers\ResolutionAnalyzer; use Intervention\Image\Analyzers\WidthAnalyzer; +use Intervention\Image\Geometry\Point; use Traversable; use Intervention\Image\Geometry\Rectangle; use Intervention\Image\Interfaces\AnalyzerInterface; @@ -19,12 +20,15 @@ use Intervention\Image\Interfaces\ColorspaceInterface; use Intervention\Image\Interfaces\CoreInterface; use Intervention\Image\Interfaces\DriverInterface; use Intervention\Image\Interfaces\EncoderInterface; +use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ImageInterface; use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\ProfileInterface; use Intervention\Image\Interfaces\ResolutionInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Modifiers\SharpenModifier; +use Intervention\Image\Modifiers\TextModifier; +use Intervention\Image\Typography\FontFactory; class Image implements ImageInterface, Countable { @@ -129,4 +133,15 @@ class Image implements ImageInterface, Countable { return $this->modify(new SharpenModifier($amount)); } + + public function text(string $text, int $x, int $y, callable|FontInterface $font): ImageInterface + { + return $this->modify( + new TextModifier( + $text, + new Point($x, $y), + call_user_func(new FontFactory($font)), + ), + ); + } } diff --git a/src/Interfaces/DriverInterface.php b/src/Interfaces/DriverInterface.php index 0693ed26..fefb3dbd 100644 --- a/src/Interfaces/DriverInterface.php +++ b/src/Interfaces/DriverInterface.php @@ -9,5 +9,6 @@ interface DriverInterface public function createImage(int $width, int $height): ImageInterface; public function handleInput(mixed $input): ImageInterface|ColorInterface; public function colorProcessor(ColorspaceInterface $colorspace): ColorProcessorInterface; + public function fontProcessor(FontInterface $font): FontProcessorInterface; public function colorToNative(ColorInterface $color, ColorspaceInterface $colorspace): mixed; } diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 58491135..d34e9ed2 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -2,8 +2,6 @@ namespace Intervention\Image\Interfaces; -use Intervention\Image\Geometry\Polygon; - interface FontInterface { public function setColor(mixed $color): self; @@ -21,8 +19,4 @@ interface FontInterface public function valignment(): string; public function setLineHeight(float $value): self; public function lineHeight(): float; - public function leadingInPixels(): int; - public function fontSizeInPixels(): int; - public function capHeight(): int; - public function getBoxSize(string $text): Polygon; } diff --git a/src/Interfaces/FontProcessorInterface.php b/src/Interfaces/FontProcessorInterface.php new file mode 100644 index 00000000..12981091 --- /dev/null +++ b/src/Interfaces/FontProcessorInterface.php @@ -0,0 +1,17 @@ +text); - $position = $this->position; - $font = $this->font; - - $boundingBox = $lines->boundingBox($font, $position); - $pivot = $boundingBox->last(); - - $leading = $font->leadingInPixels(); - $blockWidth = $lines->longestLine()->widthInFont($font); - - $x = $pivot->x(); - $y = $font->hasFilename() ? $pivot->y() + $font->capHeight() : $pivot->y(); - $x_adjustment = 0; - - foreach ($lines as $line) { - $x_adjustment = $font->alignment() == 'left' ? 0 : $blockWidth - $line->widthInFont($font); - $x_adjustment = $font->alignment() == 'right' ? intval(round($x_adjustment)) : $x_adjustment; - $x_adjustment = $font->alignment() == 'center' ? intval(round($x_adjustment / 2)) : $x_adjustment; - $position = new Point($x + $x_adjustment, $y); - $position->rotate($font->angle(), $pivot); - $line->setPosition($position); - $y += $leading; - } - - return $lines; - } -} diff --git a/src/Drivers/AbstractFont.php b/src/Typography/Font.php similarity index 68% rename from src/Drivers/AbstractFont.php rename to src/Typography/Font.php index 04445a5f..94d5b2ad 100644 --- a/src/Drivers/AbstractFont.php +++ b/src/Typography/Font.php @@ -1,19 +1,24 @@ filename = $filename; + } + public function setSize(float $size): FontInterface { $this->size = $size; @@ -67,30 +72,30 @@ abstract class AbstractFont implements FontInterface return $this->color; } - public function setAlignment(string $align): FontInterface + public function alignment(): string { - $this->align = $align; + return $this->alignment; + } + + public function setAlignment(string $value): FontInterface + { + $this->alignment = $value; return $this; } public function valignment(): string { - return $this->valign; + return $this->valignment; } - public function setValignment(string $valign): FontInterface + public function setValignment(string $value): FontInterface { - $this->valign = $valign; + $this->valignment = $value; return $this; } - public function alignment(): string - { - return $this->align; - } - public function setLineHeight(float $height): FontInterface { $this->lineHeight = $height; @@ -102,19 +107,4 @@ abstract class AbstractFont implements FontInterface { return $this->lineHeight; } - - public function leadingInPixels(): int - { - return intval(round($this->fontSizeInPixels() * $this->lineHeight())); - } - - public function capHeight(): int - { - return $this->getBoxSize('T')->height(); - } - - public function fontSizeInPixels(): int - { - return $this->getBoxSize('Hy')->height(); - } } diff --git a/src/Typography/FontFactory.php b/src/Typography/FontFactory.php new file mode 100644 index 00000000..8e942713 --- /dev/null +++ b/src/Typography/FontFactory.php @@ -0,0 +1,73 @@ +font = is_a($init, FontInterface::class) ? $init : new Font(); + + if (is_callable($init)) { + $init($this); + } + } + + public function __invoke(): FontInterface + { + return $this->font; + } + + public function filename(string $value): self + { + $this->font->setFilename($value); + + return $this; + } + + public function color(mixed $value): self + { + $this->font->setColor($value); + + return $this; + } + + public function size(float $value): self + { + $this->font->setSize($value); + + return $this; + } + + public function align(string $value): self + { + $this->font->setAlignment($value); + + return $this; + } + + public function valign(string $value): self + { + $this->font->setValignment($value); + + return $this; + } + + public function lineHeight(float $value): self + { + $this->font->setLineHeight($value); + + return $this; + } + + public function angle(float $value): self + { + $this->font->setAngle($value); + + return $this; + } +} diff --git a/src/Typography/Line.php b/src/Typography/Line.php index d8becbe7..87223a1f 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -2,7 +2,6 @@ namespace Intervention\Image\Typography; -use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\PointInterface; @@ -26,11 +25,6 @@ class Line return $this; } - public function widthInFont(FontInterface $font): int - { - return $font->getBoxSize($this->text)->width(); - } - public function __toString(): string { return $this->text; diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index 7f7122e8..f83a7dc4 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -3,10 +3,6 @@ namespace Intervention\Image\Typography; use Intervention\Image\Collection; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Geometry\Rectangle; -use Intervention\Image\Interfaces\FontInterface; class TextBlock extends Collection { @@ -17,28 +13,6 @@ class TextBlock extends Collection } } - public function boundingBox(FontInterface $font, Point $pivot = null): Polygon - { - $pivot = $pivot ? $pivot : new Point(); - - // bounding box - $box = (new Rectangle( - $this->longestLine()->widthInFont($font), - $font->leadingInPixels() * ($this->count() - 1) + $font->capHeight() - )); - - // set pivot - $box->setPivot($pivot); - - // align - $box->align($font->alignment()); - $box->valign($font->valignment()); - - $box->rotate($font->angle()); - - return $box; - } - /** * Return array of lines in text block * diff --git a/tests/Drivers/Abstract/AbstractFontTest.php b/tests/Drivers/Abstract/AbstractFontTest.php deleted file mode 100644 index e5a13677..00000000 --- a/tests/Drivers/Abstract/AbstractFontTest.php +++ /dev/null @@ -1,49 +0,0 @@ -shouldAllowMockingProtectedMethods() - ->makePartial(); - - $mock->shouldReceive('getBoxSize')->with('Hy')->andReturn(new Rectangle(123, 456)); - $mock->shouldReceive('getBoxSize')->with('T')->andReturn(new Rectangle(12, 34)); - - // settings - $mock->setSize(24); - $mock->setAngle(30); - $mock->setFilename(__DIR__ . '/AbstractFontTest.php'); - $mock->setColor('ccc'); - $mock->setAlignment('center'); - $mock->setValignment('top'); - $mock->setLineHeight(1.5); - - return $mock; - } - - public function testConstructor(): void - { - $mock = $this->getAbstractFontMock(); - $this->assertEquals(24.0, $mock->size()); - $this->assertEquals(30, $mock->angle()); - $this->assertEquals(__DIR__ . '/AbstractFontTest.php', $mock->filename()); - $this->assertEquals('ccc', $mock->color()); - $this->assertEquals('center', $mock->alignment()); - $this->assertEquals('top', $mock->valignment()); - $this->assertEquals(1.5, $mock->lineHeight()); - $this->assertEquals(456, $mock->fontSizeInPixels()); - $this->assertEquals(34, $mock->capHeight()); - $this->assertEquals(684, $mock->leadingInPixels()); - $this->assertTrue($mock->hasFilename()); - } -} diff --git a/tests/Drivers/AbstractFontProcessorTest.php b/tests/Drivers/AbstractFontProcessorTest.php new file mode 100644 index 00000000..ccb0e5bf --- /dev/null +++ b/tests/Drivers/AbstractFontProcessorTest.php @@ -0,0 +1,64 @@ +shouldAllowMockingProtectedMethods() + ->makePartial(); + + $mock->shouldReceive('boxSize')->with('Hy')->andReturn(new Rectangle(123, 456)); + $mock->shouldReceive('boxSize')->with('T')->andReturn(new Rectangle(12, 34)); + $mock->shouldReceive('boxSize')->with('foobar')->andReturn(new Rectangle(4, 8)); + + return $mock; + } + + public function testLeadingInPixels(): void + { + $mock = $this->getMock((new Font())->setLineHeight(2)); + $this->assertEquals(912, $mock->leadingInPixels()); + } + + public function testCapHeight(): void + { + $mock = $this->getMock((new Font())->setLineHeight(2)); + $this->assertEquals(34, $mock->capHeight()); + } + + public function testFontSizeInPixels(): void + { + $mock = $this->getMock((new Font())->setLineHeight(2)); + $this->assertEquals(456, $mock->fontSizeInPixels()); + } + + public function testAlignedTextBlock(): void + { + $mock = $this->getMock((new Font())->setLineHeight(2)); + $block = $mock->alignedTextBlock(new Point(0, 0), 'foobar'); + $this->assertInstanceOf(TextBlock::class, $block); + } + + public function testBoundingBox(): void + { + $mock = $this->getMock((new Font())->setLineHeight(2)); + $box = $mock->boundingBox(new TextBlock('foobar')); + $this->assertInstanceOf(Polygon::class, $box); + $this->assertEquals(4, $box->width()); + $this->assertEquals(34, $box->height()); + } +} diff --git a/tests/Drivers/Gd/FontProcessorTest.php b/tests/Drivers/Gd/FontProcessorTest.php new file mode 100644 index 00000000..2f62d030 --- /dev/null +++ b/tests/Drivers/Gd/FontProcessorTest.php @@ -0,0 +1,38 @@ +boxSize('test'); + $this->assertInstanceOf(Polygon::class, $result); + $this->assertEquals(20, $result->width()); + $this->assertEquals(8, $result->height()); + } + + public function testAdjustedSize(): void + { + $processor = new FontProcessor((new Font())->setSize(100)); + $this->assertEquals(75, $processor->adjustedSize()); + } + + public function testGetGdFont(): void + { + $processor = new FontProcessor(new Font()); + $this->assertEquals(1, $processor->getGdFont()); + + $processor = new FontProcessor((new Font())->setFilename(100)); + $this->assertEquals(100, $processor->getGdFont()); + + $processor = new FontProcessor((new Font())->setFilename('foo')); + $this->assertEquals(1, $processor->getGdFont()); + } +} diff --git a/tests/Typography/FontFactoryTest.php b/tests/Typography/FontFactoryTest.php new file mode 100644 index 00000000..7f319227 --- /dev/null +++ b/tests/Typography/FontFactoryTest.php @@ -0,0 +1,42 @@ +assertInstanceOf(FontInterface::class, $result); + $this->assertEquals('foo.ttf', $result->filename()); + } + + public function testBuildWithCallback(): void + { + $factory = new FontFactory(function ($font) { + $font->filename('foo.ttf'); + $font->color('#b01735'); + $font->size(70); + $font->align('center'); + $font->valign('middle'); + $font->lineHeight(1.6); + $font->angle(10); + }); + + $result = $factory(); + $this->assertInstanceOf(FontInterface::class, $result); + $this->assertEquals('foo.ttf', $result->filename()); + $this->assertEquals('#b01735', $result->color()); + $this->assertEquals(70, $result->size()); + $this->assertEquals('center', $result->alignment()); + $this->assertEquals('middle', $result->valignment()); + $this->assertEquals(1.6, $result->lineHeight()); + $this->assertEquals(10, $result->angle()); + } +} diff --git a/tests/Typography/FontTest.php b/tests/Typography/FontTest.php new file mode 100644 index 00000000..311bc52a --- /dev/null +++ b/tests/Typography/FontTest.php @@ -0,0 +1,82 @@ +assertInstanceOf(Font::class, $font); + $this->assertEquals('foo.ttf', $font->filename()); + } + + public function testSetGetSize(): void + { + $font = new Font(); + $this->assertEquals(12, $font->size()); + $result = $font->setSize(123); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals(123, $font->size()); + } + + public function testSetGetAngle(): void + { + $font = new Font(); + $this->assertEquals(0, $font->angle()); + $result = $font->setAngle(123); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals(123, $font->angle()); + } + + public function testSetGetFilename(): void + { + $font = new Font(); + $this->assertEquals(null, $font->filename()); + $this->assertFalse($font->hasFilename()); + $filename = $this->getTestImagePath(); + $result = $font->setFilename($filename); + $this->assertTrue($font->hasFilename()); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals($filename, $font->filename()); + } + + public function testSetGetColor(): void + { + $font = new Font(); + $this->assertEquals('000000', $font->color()); + $result = $font->setColor('fff'); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals('fff', $font->color()); + } + + public function testSetGetAlignment(): void + { + $font = new Font(); + $this->assertEquals('left', $font->alignment()); + $result = $font->setAlignment('center'); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals('center', $font->alignment()); + } + + public function testSetGetValignment(): void + { + $font = new Font(); + $this->assertEquals('bottom', $font->valignment()); + $result = $font->setValignment('center'); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals('center', $font->valignment()); + } + + public function testSetGetLineHeight(): void + { + $font = new Font(); + $this->assertEquals(1.25, $font->lineHeight()); + $result = $font->setLineHeight(3.2); + $this->assertInstanceOf(Font::class, $result); + $this->assertEquals(3.2, $font->lineHeight()); + } +} diff --git a/tests/Typography/TextBlockTest.php b/tests/Typography/TextBlockTest.php index d11c9e4e..60f7cf35 100644 --- a/tests/Typography/TextBlockTest.php +++ b/tests/Typography/TextBlockTest.php @@ -2,12 +2,8 @@ namespace Intervention\Image\Tests\Typography; -use Intervention\Image\Geometry\Point; -use Intervention\Image\Geometry\Polygon; -use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Tests\TestCase; use Intervention\Image\Typography\TextBlock; -use Mockery; class TextBlockTest extends TestCase { @@ -47,33 +43,4 @@ class TextBlockTest extends TestCase $result = $block->longestLine(); $this->assertEquals('FooBar', (string) $result); } - - public function testGetBoundingBox(): void - { - $block = $this->getTestBlock(); - $font = Mockery::mock(FontInterface::class) - ->shouldAllowMockingProtectedMethods() - ->makePartial(); - - $font->shouldReceive('getBoxSize')->andReturn( - new Polygon([ - new Point(0, 0), - new Point(300, 0), - new Point(300, 150), - new Point(0, 150), - ]) - ); - - $font->shouldReceive('leadingInPixels')->andReturn(30); - $font->shouldReceive('alignment')->andReturn('left'); - $font->shouldReceive('valignment')->andReturn('bottom'); - $font->shouldReceive('angle')->andReturn(0); - $font->shouldReceive('capHeight')->andReturn(22); - - $box = $block->boundingBox($font, new Point(10, 15)); - $this->assertEquals(300, $box->width()); - $this->assertEquals(82, $box->height()); - $this->assertEquals(10, $box->pivot()->x()); - $this->assertEquals(15, $box->pivot()->y()); - } }