From 0e2aa7f595fe78020e098be5a0252804c43510b5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 25 Jun 2022 16:43:34 +0200 Subject: [PATCH] Implement multiline functionality for TextWriter --- src/Drivers/Abstract/AbstractFont.php | 13 ++++ src/Drivers/Abstract/AbstractTextWriter.php | 6 ++ src/Drivers/Gd/Font.php | 15 +++++ src/Drivers/Gd/Modifiers/TextWriter.php | 72 ++++++++++++++------- src/Interfaces/FontInterface.php | 5 ++ src/Typography/Line.php | 7 ++ src/Typography/TextBlock.php | 13 ++++ 7 files changed, 109 insertions(+), 22 deletions(-) diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php index dd03a831..dc246737 100644 --- a/src/Drivers/Abstract/AbstractFont.php +++ b/src/Drivers/Abstract/AbstractFont.php @@ -16,6 +16,7 @@ abstract class AbstractFont implements FontInterface protected $filename; protected $align = 'left'; protected $valign = 'bottom'; + protected $lineHeight = 1.25; public function __construct(callable $init = null) { @@ -100,4 +101,16 @@ abstract class AbstractFont implements FontInterface { return $this->align; } + + public function lineHeight(float $height): FontInterface + { + $this->lineHeight = $height; + + return $this; + } + + public function getLineHeight(): float + { + return $this->lineHeight; + } } diff --git a/src/Drivers/Abstract/AbstractTextWriter.php b/src/Drivers/Abstract/AbstractTextWriter.php index 9b1e7a3a..7c0e196f 100644 --- a/src/Drivers/Abstract/AbstractTextWriter.php +++ b/src/Drivers/Abstract/AbstractTextWriter.php @@ -5,6 +5,7 @@ namespace Intervention\Image\Drivers\Abstract; use Intervention\Image\Geometry\Point; use Intervention\Image\Interfaces\FontInterface; use Intervention\Image\Interfaces\ModifierInterface; +use Intervention\Image\Typography\TextBlock; abstract class AbstractTextWriter implements ModifierInterface { @@ -15,4 +16,9 @@ abstract class AbstractTextWriter implements ModifierInterface ) { // } + + public function getTextBlock(): TextBlock + { + return new TextBlock($this->text); + } } diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php index 88048d61..e524dcfe 100644 --- a/src/Drivers/Gd/Font.php +++ b/src/Drivers/Gd/Font.php @@ -84,4 +84,19 @@ class Font extends AbstractFont return 8; } } + + public function capHeight(): int + { + return $this->getBoxSize('T')->height(); + } + + public function leadingInPixels(): int + { + return intval(round($this->fontSizeInPixels() * $this->getLineHeight())); + } + + public function fontSizeInPixels(): int + { + return $this->getBoxSize('Hy')->height(); + } } diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php index 486dedb2..6d2f4d4d 100644 --- a/src/Drivers/Gd/Modifiers/TextWriter.php +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -5,26 +5,39 @@ namespace Intervention\Image\Drivers\Gd\Modifiers; use Intervention\Image\Drivers\Abstract\AbstractTextWriter; use Intervention\Image\Drivers\Gd\Font; use Intervention\Image\Exceptions\FontException; -use Intervention\Image\Geometry\Point; +use Intervention\Image\Geometry\Polygon; +use Intervention\Image\Geometry\Size; use Intervention\Image\Interfaces\ImageInterface; class TextWriter extends AbstractTextWriter { public function apply(ImageInterface $image): ImageInterface { - $position = $this->getAlignedPosition(); + $box = $this->getBoundingBox(); + $position = clone $box->last(); + $leading = $this->getFont()->leadingInPixels(); + foreach ($image as $frame) { if ($this->font->hasFilename()) { - imagettftext( - $frame->getCore(), - $this->getFont()->getSize(), - $this->getFont()->getAngle() * (-1), - $position->getX(), - $position->getY(), - $this->getFont()->getColor()->toInt(), - $this->getFont()->getFilename(), - $this->text - ); + $position->moveY($this->getFont()->capHeight()); + $posx = $position->getX(); + $posy = $position->getY(); + foreach ($this->getTextBlock() as $line) { + imagettftext( + $frame->getCore(), + $this->getFont()->getSize(), + $this->getFont()->getAngle() * (-1), + $posx, + $posy, + $this->getFont()->getColor()->toInt(), + $this->getFont()->getFilename(), + $line + ); + $posy += $leading; + } + + // debug + imagepolygon($frame->getCore(), $box->toArray(), 0); } else { imagestring( $frame->getCore(), @@ -40,21 +53,36 @@ class TextWriter extends AbstractTextWriter return $image; } - private function getAlignedPosition(): Point + private function getBoundingBox(): Polygon { - $poly = $this->font->getBoxSize($this->text); + $size = new Size( + $this->getTextBlock()->longestLine()->width($this->font), + $this->getFont()->leadingInPixels() * $this->getTextBlock()->count() + ); + + $poly = $size->toPolygon(); $poly->setPivotPoint($this->position); + $poly->align($this->getFont()->getAlign()); + $poly->valign($this->getFont()->getValign()); - $poly->align($this->font->getAlign()); - $poly->valign($this->font->getValign()); - - if ($this->font->getAngle() != 0) { - $poly->rotate($this->font->getAngle()); - } - - return $poly->last(); + return $poly; } + // private function getAlignedPosition(): Point + // { + // $poly = $this->font->getBoxSize($this->text); + // $poly->setPivotPoint($this->position); + // + // $poly->align($this->font->getAlign()); + // $poly->valign($this->font->getValign()); + // + // if ($this->font->getAngle() != 0) { + // $poly->rotate($this->font->getAngle()); + // } + // + // return $poly->last(); + // } + private function getFont(): Font { if (!is_a($this->font, Font::class)) { diff --git a/src/Interfaces/FontInterface.php b/src/Interfaces/FontInterface.php index 66a78b15..6ff95e44 100644 --- a/src/Interfaces/FontInterface.php +++ b/src/Interfaces/FontInterface.php @@ -20,5 +20,10 @@ interface FontInterface public function getAlign(): string; public function valign(string $align): self; public function getValign(): string; + public function lineHeight(float $value): self; + public function getLineHeight(): float; + public function leadingInPixels(): int; + public function fontSizeInPixels(): int; + public function capHeight(): int; public function getBoxSize(string $text): Polygon; } diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 80037869..67e38571 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -2,6 +2,8 @@ namespace Intervention\Image\Typography; +use Intervention\Image\Interfaces\FontInterface; + class Line { public function __construct(protected string $text) @@ -13,4 +15,9 @@ class Line { return $this->text; } + + public function width(FontInterface $font): int + { + return $font->getBoxSize($this->text)->width(); + } } diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index c8cdadf3..c77c29c8 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -17,4 +17,17 @@ class TextBlock extends Collection { return $this->items; } + + public function longestLine(): Line + { + $lines = $this->lines(); + usort($lines, function ($a, $b) { + if (mb_strlen($a) === mb_strlen($b)) { + return 0; + } + return (mb_strlen($a) > mb_strlen($b)) ? -1 : 1; + }); + + return $lines[0]; + } }