1
0
mirror of https://github.com/Intervention/image.git synced 2025-08-19 12:11:26 +02:00

Add modifier TextWriter

This commit is contained in:
Oliver Vogel
2022-05-24 19:31:53 +02:00
parent 7433c123d5
commit fda458ff95
8 changed files with 434 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
<?php
namespace Intervention\Image\Drivers\Abstract;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\FontInterface;
use Intervention\Image\Traits\CanHandleInput;
abstract class AbstractFont implements FontInterface
{
use CanHandleInput;
protected $size = 12;
protected $angle = 0;
protected $color = '000000';
protected $filename;
protected $align = 'left';
protected $valign = 'bottom';
public function __construct(protected string $text, ?callable $init = null)
{
if (is_callable($init)) {
$init($this);
}
}
public function getText(): string
{
return $this->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;
}
}

View File

@@ -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(

77
src/Drivers/Gd/Font.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
namespace Intervention\Image\Drivers\Gd;
use Intervention\Image\Drivers\Abstract\AbstractFont;
use Intervention\Image\Geometry\Size;
class Font extends AbstractFont
{
public function getSize(): float
{
return floatval(ceil(parent::getSize() * 0.75));
}
/**
* Calculate size of bounding box of current text
*
* @return Size
*/
public function getBoxSize(): Size
{
if (!$this->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;
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Intervention\Image\Drivers\Gd\Modifiers;
use Intervention\Image\Drivers\Gd\Font;
use Intervention\Image\Geometry\Point;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface;
class TextWriter implements ModifierInterface
{
public function __construct(
protected Point $position,
protected Font $font
) {
//
}
public function apply(ImageInterface $image): ImageInterface
{
$position = $this->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;
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Intervention\Image\Drivers\Imagick;
use Imagick;
use ImagickDraw;
use Intervention\Image\Drivers\Abstract\AbstractFont;
use Intervention\Image\Exceptions\FontException;
use Intervention\Image\Geometry\Size;
class Font extends AbstractFont
{
public function toImagickDraw(): ImagickDraw
{
if (!$this->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']))
);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Intervention\Image\Drivers\Imagick\Modifiers;
use Intervention\Image\Drivers\Imagick\Font;
use Intervention\Image\Geometry\Point;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface;
class TextWriter implements ModifierInterface
{
public function __construct(
protected Point $position,
protected Font $font
) {
//
}
public function apply(ImageInterface $image): ImageInterface
{
$draw = $this->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;
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Intervention\Image\Exceptions;
class FontException extends \RuntimeException
{
//
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Intervention\Image\Interfaces;
use Intervention\Image\Geometry\Size;
use Intervention\Image\Interfaces\ColorInterface;
interface FontInterface
{
public function getText(): string;
public function color($color): self;
public function getColor(): ?ColorInterface;
public function size(float $size): self;
public function getSize(): float;
public function angle(float $angle): self;
public function getAngle(): float;
public function filename(string $filename): self;
public function getFilename(): string;
public function hasFilename(): bool;
public function align(string $align): self;
public function getAlign(): string;
public function getBoxSize(): Size;
}