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:
108
src/Drivers/Abstract/AbstractFont.php
Normal file
108
src/Drivers/Abstract/AbstractFont.php
Normal 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;
|
||||
}
|
||||
}
|
@@ -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
77
src/Drivers/Gd/Font.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
80
src/Drivers/Gd/Modifiers/TextWriter.php
Normal file
80
src/Drivers/Gd/Modifiers/TextWriter.php
Normal 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;
|
||||
}
|
||||
}
|
73
src/Drivers/Imagick/Font.php
Normal file
73
src/Drivers/Imagick/Font.php
Normal 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']))
|
||||
);
|
||||
}
|
||||
}
|
56
src/Drivers/Imagick/Modifiers/TextWriter.php
Normal file
56
src/Drivers/Imagick/Modifiers/TextWriter.php
Normal 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;
|
||||
}
|
||||
}
|
8
src/Exceptions/FontException.php
Normal file
8
src/Exceptions/FontException.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Intervention\Image\Exceptions;
|
||||
|
||||
class FontException extends \RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
23
src/Interfaces/FontInterface.php
Normal file
23
src/Interfaces/FontInterface.php
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user