From fda458ff959ce5f05ac57e244a7c66ecd702b1ba Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 24 May 2022 19:31:53 +0200 Subject: [PATCH] Add modifier TextWriter --- src/Drivers/Abstract/AbstractFont.php | 108 +++++++++++++++++++ src/Drivers/Abstract/AbstractImage.php | 9 ++ src/Drivers/Gd/Font.php | 77 +++++++++++++ src/Drivers/Gd/Modifiers/TextWriter.php | 80 ++++++++++++++ src/Drivers/Imagick/Font.php | 73 +++++++++++++ src/Drivers/Imagick/Modifiers/TextWriter.php | 56 ++++++++++ src/Exceptions/FontException.php | 8 ++ src/Interfaces/FontInterface.php | 23 ++++ 8 files changed, 434 insertions(+) create mode 100644 src/Drivers/Abstract/AbstractFont.php create mode 100644 src/Drivers/Gd/Font.php create mode 100644 src/Drivers/Gd/Modifiers/TextWriter.php create mode 100644 src/Drivers/Imagick/Font.php create mode 100644 src/Drivers/Imagick/Modifiers/TextWriter.php create mode 100644 src/Exceptions/FontException.php create mode 100644 src/Interfaces/FontInterface.php diff --git a/src/Drivers/Abstract/AbstractFont.php b/src/Drivers/Abstract/AbstractFont.php new file mode 100644 index 00000000..6fee3a60 --- /dev/null +++ b/src/Drivers/Abstract/AbstractFont.php @@ -0,0 +1,108 @@ +text; + } + + public function size(float $size): self + { + $this->size = $size; + + return $this; + } + + public function getSize(): float + { + return $this->size; + } + + public function angle(float $angle): self + { + $this->angle = $angle; + + return $this; + } + + public function getAngle(): float + { + return $this->angle; + } + + public function filename(string $filename): self + { + $this->filename = $filename; + + return $this; + } + + public function getFilename(): string + { + return $this->filename; + } + + public function hasFilename(): bool + { + return is_file($this->filename); + } + + public function color($color): self + { + $this->color = $color; + + return $this; + } + + public function getColor(): ?ColorInterface + { + return $this->handleInput($this->color); + } + + public function align(string $align): self + { + $this->align = $align; + + return $this; + } + + public function getValign(): string + { + return $this->valign; + } + + public function valign(string $valign): self + { + $this->valign = $valign; + + return $this; + } + + public function getAlign(): string + { + return $this->align; + } +} diff --git a/src/Drivers/Abstract/AbstractImage.php b/src/Drivers/Abstract/AbstractImage.php index 7bf79761..50e6fb6a 100644 --- a/src/Drivers/Abstract/AbstractImage.php +++ b/src/Drivers/Abstract/AbstractImage.php @@ -13,6 +13,7 @@ use Intervention\Image\Interfaces\ModifierInterface; use Intervention\Image\Interfaces\SizeInterface; use Intervention\Image\Traits\CanHandleInput; use Intervention\Image\Traits\CanResolveDriverClass; +use Intervention\Image\Interfaces\FontInterface; abstract class AbstractImage implements ImageInterface { @@ -235,6 +236,14 @@ abstract class AbstractImage implements ImageInterface return $colors; } + public function text(string $text, int $x, int $y, ?callable $init = null): ImageInterface + { + $font = $this->resolveDriverClass('Font', $text, $init); + $modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font); + + return $this->modify($modifier); + } + public function resize(?int $width = null, ?int $height = null): ImageInterface { return $this->modify( diff --git a/src/Drivers/Gd/Font.php b/src/Drivers/Gd/Font.php new file mode 100644 index 00000000..90a15b0c --- /dev/null +++ b/src/Drivers/Gd/Font.php @@ -0,0 +1,77 @@ +hasFilename()) { + // calculate box size from gd font + $box = new Size(0, 0); + $chars = mb_strlen($this->getText()); + if ($chars > 0) { + $box->setWidth($chars * $this->getGdFontWidth()); + $box->setHeight($this->getGdFontHeight()); + } + return $box; + } + + // calculate box size from font file + $box = imageftbbox( + $this->getSize(), + $this->getAngle(), + $this->getFilename(), + $this->getText() + ); + + return new Size(abs($box[0] - $box[2]), abs($box[1] - $box[7])); + } + + public function getGdFont(): int + { + if (is_numeric($this->filename)) { + return $this->filename; + } + + return 1; + } + + protected function getGdFontWidth(): int + { + return $this->getGdFont() + 4; + } + + protected function getGdFontHeight(): int + { + switch ($this->getGdFont()) { + case 1: + return 8; + + case 2: + return 14; + + case 3: + return 14; + + case 4: + return 16; + + case 5: + return 16; + } + } +} diff --git a/src/Drivers/Gd/Modifiers/TextWriter.php b/src/Drivers/Gd/Modifiers/TextWriter.php new file mode 100644 index 00000000..6c6277c3 --- /dev/null +++ b/src/Drivers/Gd/Modifiers/TextWriter.php @@ -0,0 +1,80 @@ +getAlignedPosition(); + + foreach ($image as $frame) { + if ($this->font->hasFilename()) { + imagettftext( + $frame->getCore(), + $this->font->getSize(), + $this->font->getAngle(), + $position->getX(), + $position->getY(), + $this->font->getColor()->toInt(), + $this->font->getFilename(), + $this->font->getText() + ); + } else { + imagestring( + $frame->getCore(), + $this->font->getGdFont(), + $position->getX(), + $position->getY(), + $this->font->getText(), + $this->font->getColor()->toInt() + ); + } + } + + return $image; + } + + protected function getAlignedPosition(): Point + { + $position = $this->position; + $box = $this->font->getBoxSize(); + + // adjust x pos + switch ($this->font->getAlign()) { + case 'center': + $position->setX($position->getX() - round($box->getWidth() / 2)); + break; + + case 'right': + $position->setX($position->getX() - $box->getWidth()); + break; + } + + // adjust y pos + switch ($this->font->getValign()) { + case 'top': + $position->setY($position->getY() + $box->getHeight()); + break; + + case 'middle': + case 'center': + $position->setY($position->getY() + round($box->getHeight() / 2)); + break; + } + + return $position; + } +} diff --git a/src/Drivers/Imagick/Font.php b/src/Drivers/Imagick/Font.php new file mode 100644 index 00000000..df32d284 --- /dev/null +++ b/src/Drivers/Imagick/Font.php @@ -0,0 +1,73 @@ +hasFilename()) { + throw new FontException('No font file.'); + } + + $draw = new ImagickDraw(); + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $draw->setFont($this->getFilename()); + $draw->setFontSize($this->getSize()); + $draw->setFillColor($this->getColor()->getPixel()); + $draw->setTextAlignment($this->getImagickAlign()); + + return $draw; + } + + public function getAngle(): float + { + return parent::getAngle() * (-1); + } + + public function getImagickAlign(): int + { + switch (strtolower($this->getAlign())) { + case 'center': + return Imagick::ALIGN_CENTER; + break; + + case 'right': + return Imagick::ALIGN_RIGHT; + break; + } + + return Imagick::ALIGN_LEFT; + } + + /** + * Calculate box size of current font + * + * @return Size + */ + public function getBoxSize(): Size + { + $foo = null; + // no text - no box size + if (mb_strlen($this->getText()) === 0) { + return new Size(0, 0); + } + + $dimensions = (new Imagick())->queryFontMetrics( + $this->toImagickDraw(), + $this->getText() + ); + + return new Size( + intval(abs($dimensions['textWidth'])), + intval(abs($dimensions['textHeight'])) + ); + } +} diff --git a/src/Drivers/Imagick/Modifiers/TextWriter.php b/src/Drivers/Imagick/Modifiers/TextWriter.php new file mode 100644 index 00000000..52c6b7be --- /dev/null +++ b/src/Drivers/Imagick/Modifiers/TextWriter.php @@ -0,0 +1,56 @@ +font->toImagickDraw(); + $position = $this->getAlignedPosition(); + + foreach ($image as $frame) { + $frame->getCore()->annotateImage( + $draw, + $position->getX(), + $position->getY(), + $this->font->getAngle(), + $this->font->getText() + ); + } + + return $image; + } + + protected function getAlignedPosition(): Point + { + $position = $this->position; + $box = $this->font->getBoxSize(); + + // adjust y pos + switch ($this->font->getValign()) { + case 'top': + $position->setY($position->getY() + $box->getHeight()); + break; + + case 'middle': + case 'center': + $position->setY($position->getY() + round($box->getHeight() / 2)); + break; + } + + return $position; + } +} diff --git a/src/Exceptions/FontException.php b/src/Exceptions/FontException.php new file mode 100644 index 00000000..be32f177 --- /dev/null +++ b/src/Exceptions/FontException.php @@ -0,0 +1,8 @@ +