1
0
mirror of https://github.com/Intervention/image.git synced 2025-08-01 11:30:16 +02:00

Prepare TextWriter for multi line functionality

This commit is contained in:
Oliver Vogel
2022-06-25 12:18:46 +02:00
parent 452b91929c
commit ae3762d455
13 changed files with 167 additions and 58 deletions

View File

@@ -17,18 +17,13 @@ abstract class AbstractFont implements FontInterface
protected $align = 'left';
protected $valign = 'bottom';
public function __construct(protected string $text, ?callable $init = null)
public function __construct(callable $init = null)
{
if (is_callable($init)) {
$init($this);
}
}
public function getText(): string
{
return $this->text;
}
public function size(float $size): FontInterface
{
$this->size = $size;

View File

@@ -194,8 +194,8 @@ abstract class AbstractImage implements ImageInterface
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);
$font = $this->resolveDriverClass('Font', $init);
$modifier = $this->resolveDriverClass('Modifiers\TextWriter', new Point($x, $y), $font, $text);
return $this->modify($modifier);
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Intervention\Image\Drivers\Abstract;
use Intervention\Image\Geometry\Point;
use Intervention\Image\Interfaces\FontInterface;
use Intervention\Image\Interfaces\ModifierInterface;
abstract class AbstractTextWriter implements ModifierInterface
{
public function __construct(
protected Point $position,
protected FontInterface $font,
protected string $text
) {
//
}
}

View File

@@ -19,12 +19,12 @@ class Font extends AbstractFont
*
* @return Polygon
*/
public function getBoxSize(): Polygon
public function getBoxSize(string $text): Polygon
{
if (!$this->hasFilename()) {
// calculate box size from gd font
$box = new Size(0, 0);
$chars = mb_strlen($this->getText());
$chars = mb_strlen($text);
if ($chars > 0) {
$box->setWidth($chars * $this->getGdFontWidth());
$box->setHeight($this->getGdFontHeight());
@@ -37,7 +37,7 @@ class Font extends AbstractFont
$this->getSize(),
0,
$this->getFilename(),
$this->getText()
$text
);
// build polygon from points
@@ -47,7 +47,6 @@ class Font extends AbstractFont
$polygon->addPoint(new Point($box[2], $box[3]));
$polygon->addPoint(new Point($box[0], $box[1]));
return $polygon;
}

View File

@@ -2,20 +2,14 @@
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\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface;
class TextWriter implements ModifierInterface
class TextWriter extends AbstractTextWriter
{
public function __construct(
protected Point $position,
protected Font $font
) {
//
}
public function apply(ImageInterface $image): ImageInterface
{
$position = $this->getAlignedPosition();
@@ -23,21 +17,21 @@ class TextWriter implements ModifierInterface
if ($this->font->hasFilename()) {
imagettftext(
$frame->getCore(),
$this->font->getSize(),
$this->font->getAngle() * (-1),
$this->getFont()->getSize(),
$this->getFont()->getAngle() * (-1),
$position->getX(),
$position->getY(),
$this->font->getColor()->toInt(),
$this->font->getFilename(),
$this->font->getText()
$this->getFont()->getColor()->toInt(),
$this->getFont()->getFilename(),
$this->text
);
} else {
imagestring(
$frame->getCore(),
$this->font->getGdFont(),
$this->getFont()->getGdFont(),
$position->getX(),
$position->getY(),
$this->font->getText(),
$this->text,
$this->font->getColor()->toInt()
);
}
@@ -46,9 +40,9 @@ class TextWriter implements ModifierInterface
return $image;
}
public function getAlignedPosition(): Point
private function getAlignedPosition(): Point
{
$poly = $this->font->getBoxSize();
$poly = $this->font->getBoxSize($this->text);
$poly->setPivotPoint($this->position);
$poly->align($this->font->getAlign());
@@ -60,4 +54,12 @@ class TextWriter implements ModifierInterface
return $poly->last();
}
private function getFont(): Font
{
if (!is_a($this->font, Font::class)) {
throw new FontException('Font is not compatible to current driver.');
}
return $this->font;
}
}

View File

@@ -5,10 +5,10 @@ namespace Intervention\Image\Drivers\Imagick;
use Imagick;
use ImagickDraw;
use Intervention\Image\Drivers\Abstract\AbstractFont;
use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Exceptions\FontException;
use Intervention\Image\Geometry\Polygon;
use Intervention\Image\Geometry\Size;
use Intervention\Image\Interfaces\ColorInterface;
class Font extends AbstractFont
{
@@ -18,22 +18,28 @@ class Font extends AbstractFont
throw new FontException('No font file specified.');
}
$color = $this->getColor();
if (!is_a($color, Color::class)) {
throw new DecoderException('Unable to decode font color.');
}
$draw = new ImagickDraw();
$draw->setStrokeAntialias(true);
$draw->setTextAntialias(true);
$draw->setFont($this->getFilename());
$draw->setFontSize($this->getSize());
$draw->setFillColor($color->getPixel());
$draw->setFillColor($this->getColor()->getPixel());
$draw->setTextAlignment($this->getImagickAlign());
return $draw;
}
public function getColor(): ?ColorInterface
{
$color = parent::getColor();
if (!is_a($color, Color::class)) {
throw new FontException('Font is not compatible to current driver.');
}
return $color;
}
public function getAngle(): float
{
return parent::getAngle() * (-1);
@@ -57,16 +63,16 @@ class Font extends AbstractFont
*
* @return Polygon
*/
public function getBoxSize(): Polygon
public function getBoxSize(string $text): Polygon
{
// no text - no box size
if (mb_strlen($this->getText()) === 0) {
if (mb_strlen($text) === 0) {
return (new Size(0, 0))->toPolygon();
}
$dimensions = (new Imagick())->queryFontMetrics(
$this->toImagickDraw(),
$this->getText()
$text
);
return (new Size(

View File

@@ -2,30 +2,24 @@
namespace Intervention\Image\Drivers\Imagick\Modifiers;
use Intervention\Image\Drivers\Abstract\AbstractTextWriter;
use Intervention\Image\Drivers\Imagick\Font;
use Intervention\Image\Exceptions\FontException;
use Intervention\Image\Geometry\Point;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ModifierInterface;
class TextWriter implements ModifierInterface
class TextWriter extends AbstractTextWriter
{
public function __construct(
protected Point $position,
protected Font $font
) {
//
}
public function apply(ImageInterface $image): ImageInterface
{
$position = $this->getAlignedPosition();
foreach ($image as $frame) {
$frame->getCore()->annotateImage(
$this->font->toImagickDraw(),
$this->getFont()->toImagickDraw(),
$position->getX(),
$position->getY(),
$this->font->getAngle() * (-1),
$this->font->getText()
$this->getFont()->getAngle() * (-1),
$this->text
);
}
@@ -35,10 +29,10 @@ class TextWriter implements ModifierInterface
protected function getAlignedPosition(): Point
{
$position = $this->position;
$box = $this->font->getBoxSize();
$box = $this->getFont()->getBoxSize($this->text);
// adjust y pos
switch ($this->font->getValign()) {
switch ($this->getFont()->getValign()) {
case 'top':
$position->setY($position->getY() + $box->height());
break;
@@ -51,4 +45,12 @@ class TextWriter implements ModifierInterface
return $position;
}
private function getFont(): Font
{
if (!is_a($this->font, Font::class)) {
throw new FontException('Font is not compatible to current driver.');
}
return $this->font;
}
}

View File

@@ -7,7 +7,6 @@ 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;
@@ -19,5 +18,7 @@ interface FontInterface
public function hasFilename(): bool;
public function align(string $align): self;
public function getAlign(): string;
public function getBoxSize(): Polygon;
public function valign(string $align): self;
public function getValign(): string;
public function getBoxSize(string $text): Polygon;
}

16
src/Typography/Line.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
namespace Intervention\Image\Typography;
class Line
{
public function __construct(protected string $text)
{
//
}
public function __toString(): string
{
return $this->text;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Intervention\Image\Typography;
use Intervention\Image\Collection;
class TextBlock extends Collection
{
public function __construct(string $text)
{
foreach (explode("\n", $text) as $line) {
$this->push(new Line($line));
}
}
public function lines(): array
{
return $this->items;
}
}

View File

@@ -12,7 +12,7 @@ class AbstractFontTest extends TestCase
private function getAbstractFontMock()
{
// create mock
$mock = Mockery::mock(AbstractFont::class, ['test123'])
$mock = Mockery::mock(AbstractFont::class)
->shouldAllowMockingProtectedMethods()
->makePartial();
@@ -34,7 +34,6 @@ class AbstractFontTest extends TestCase
public function testConstructor(): void
{
$mock = $this->getAbstractFontMock();
$this->assertEquals('test123', $mock->getText());
$this->assertEquals(24.0, $mock->getSize());
$this->assertEquals(30, $mock->getAngle());
$this->assertEquals(__DIR__ . '/AbstractFontTest.php', $mock->getFilename());

View File

@@ -0,0 +1,21 @@
<?php
namespace Intervention\Image\Tests\Typography;
use Intervention\Image\Tests\TestCase;
use Intervention\Image\Typography\Line;
class LineTest extends TestCase
{
public function testConstructor(): void
{
$line = new Line('foo');
$this->assertInstanceOf(Line::class, $line);
}
public function testToString(): void
{
$line = new Line('foo');
$this->assertEquals('foo', (string) $line);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Intervention\Image\Tests\Typography;
use Intervention\Image\Tests\TestCase;
use Intervention\Image\Typography\TextBlock;
class TextBlockTest extends TestCase
{
protected function getTestBlock(): TextBlock
{
return new TextBlock(<<<EOF
foo
bar
baz
EOF);
}
public function testConstructor(): void
{
$block = $this->getTestBlock();
$this->assertInstanceOf(TextBlock::class, $block);
$this->assertEquals(3, $block->count());
}
public function testLines(): void
{
$block = $this->getTestBlock();
$this->assertCount(3, $block->lines());
}
}