diff --git a/.github/workflows/phpstan.yaml b/.github/workflows/phpstan.yaml
new file mode 100644
index 0000000..e362fbd
--- /dev/null
+++ b/.github/workflows/phpstan.yaml
@@ -0,0 +1,14 @@
+name: PHPStan
+on: push
+jobs:
+ phpstan:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: php-actions/composer@v6
+ with:
+ dev: yes
+ - uses: php-actions/phpstan@v3
+ with:
+ path: src
+ level: max
diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml
new file mode 100644
index 0000000..a93faa6
--- /dev/null
+++ b/.github/workflows/phpunit.yaml
@@ -0,0 +1,13 @@
+name: PHPUnit
+on: push
+jobs:
+ phpunit:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: php-actions/composer@v6
+ with:
+ dev: yes
+ - uses: php-actions/phpunit@v3
+ with:
+ version: 10
diff --git a/.gitignore b/.gitignore
index b9c83f1..b81d74f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ composer.phar
composer.lock
debug.log
/examples/out/*
-!.gitkeep
\ No newline at end of file
+!.gitkeep
+.phpunit.result.cache
\ No newline at end of file
diff --git a/README.md b/README.md
index bae7426..e3e563f 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,11 @@ This library is under active development, and until a 1.0 release is made you sh
### Current progress
-| Driver | Rotate | Mirror | Resize | Crop |
-| :--------- | :----: | :----: | :----: | :--: |
-| GD | X | X | X | X |
-| Imagick | | | | |
-| Gmagick | | | | |
-| ImagickCLI | X | X | X | X |
+| Driver | Rotate | Mirror | Resize | Crop |
+| :-------------- | :----: | :----: | :----: | :---: |
+| GDDriver | X | X | X | X |
+| MagickDriver | | | | |
+| MagickCliDriver | X | X | X | X |
## Roadmap
@@ -22,9 +21,8 @@ This library is under active development, and until a 1.0 release is made you sh
A 1.0 release will not be made until the following drivers are available and solidly tested:
* GD
-* Imagick
-* Gmagick
-* GmagickCLI
+* Magick (unified driver that will use either ImageMagick or Gmagick automatically depending on what is available)
+* MagickCliDriver (unified driver that can be configured to use either ImageMagick or Gmagick CLI tools)
### Transforms
diff --git a/composer.json b/composer.json
index 2d40691..aad378d 100644
--- a/composer.json
+++ b/composer.json
@@ -24,14 +24,13 @@
},
"require-dev": {
"ext-gmagick": "*",
- "ext-gd": "*"
+ "ext-gd": "*",
+ "phpunit/phpunit": "^10.3",
+ "phpstan/phpstan": "^1.10"
},
"autoload-dev": {
"psr-4": {
"ByJoby\\ImageTransform\\tests\\": "tests/"
}
- },
- "scripts": {
- "test": "./vendor/bin/atoum -d tests"
}
}
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..478362b
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,4 @@
+parameters:
+ level: max
+ paths:
+ - src
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..bc2f7c1
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+ tests/
+
+
+
+
+ src
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/DefaultDriver.php b/src/DefaultDriver.php
index d7c2d06..7718952 100644
--- a/src/DefaultDriver.php
+++ b/src/DefaultDriver.php
@@ -10,7 +10,8 @@ use ByJoby\ImageTransform\Drivers\GDDriver;
*/
class DefaultDriver
{
- protected $driver;
+ /** @var DriverInterface|null */
+ protected static $driver;
public static function get(): DriverInterface
{
diff --git a/src/Drivers/AbstractCliDriver.php b/src/Drivers/AbstractCliDriver.php
index 8a9021f..21f9642 100644
--- a/src/Drivers/AbstractCliDriver.php
+++ b/src/Drivers/AbstractCliDriver.php
@@ -4,10 +4,4 @@ namespace ByJoby\ImageTransform\Drivers;
abstract class AbstractCliDriver extends AbstractDriver
{
- public function __construct()
- {
- if (!function_exists('exec')) {
- throw new \Exception("CLI drivers can't be used with the current configuration because exec is disabled");
- }
- }
}
diff --git a/src/Drivers/AbstractDriver.php b/src/Drivers/AbstractDriver.php
index aaa8d41..211b827 100644
--- a/src/Drivers/AbstractDriver.php
+++ b/src/Drivers/AbstractDriver.php
@@ -5,20 +5,25 @@ namespace ByJoby\ImageTransform\Drivers;
use ByJoby\ImageTransform\DriverInterface;
use ByJoby\ImageTransform\Image;
use ByJoby\ImageTransform\Sizers\AbstractSizer;
+use Exception;
abstract class AbstractDriver implements DriverInterface
{
+ /** @var string|null */
protected $tempDir = null;
+ /** @var int */
protected $chmod_dir = 0775;
+ /** @var int */
protected $chmod_file = 0665;
- abstract protected function doSave(Image $image, string $filename);
+ abstract protected function doSave(Image $image, string $filename): void;
public function tempDir(): string
{
if (!$this->tempDir) {
$this->setTempDir(sys_get_temp_dir() . '/byjoby_image-transform/' . uniqid("", true));
}
+ // @phpstan-ignore-next-line this is actually checked
return $this->tempDir;
}
@@ -31,7 +36,7 @@ abstract class AbstractDriver implements DriverInterface
return $this;
}
- protected function mkdir(string $dir)
+ protected function mkdir(string $dir): bool
{
// return true if dir exists and is writeable
if (is_dir($dir) && is_writeable($dir)) {
@@ -74,12 +79,15 @@ abstract class AbstractDriver implements DriverInterface
}
touch($filename);
}
- $this->doSave($image, realpath($filename));
+ $filename = realpath($filename);
+ if (!$filename) throw new Exception("Invalid filename or path");
+ $this->doSave($image, $filename);
chmod($filename, $this->chmod_file);
return null;
} else {
$filename = $this->tempDir() . '/' . uniqid() . '.jpg';
$this->doSave($image, $filename);
+ /** @var string we can count on this being a string because we just wrote it */
$output = file_get_contents($filename);
unlink($filename);
return $output;
diff --git a/src/Drivers/AbstractExtensionDriver.php b/src/Drivers/AbstractExtensionDriver.php
index b454259..00e0765 100644
--- a/src/Drivers/AbstractExtensionDriver.php
+++ b/src/Drivers/AbstractExtensionDriver.php
@@ -6,14 +6,20 @@ use ByJoby\ImageTransform\Image;
abstract class AbstractExtensionDriver extends AbstractDriver
{
+ // @phpstan-ignore-next-line we specify types in subclasses
abstract protected function getImageObject(Image $image);
+ // @phpstan-ignore-next-line we specify types in subclasses
abstract protected function doResize($object, Image $image);
+ // @phpstan-ignore-next-line we specify types in subclasses
abstract protected function doCrop($object, Image $image);
+ // @phpstan-ignore-next-line we specify types in subclasses
abstract protected function doFlip($object, Image $image);
+ // @phpstan-ignore-next-line we specify types in subclasses
abstract protected function doRotation($object, Image $image);
- abstract protected function saveImageObject($object, string $filename);
+ // @phpstan-ignore-next-line we specify types in subclasses
+ abstract protected function saveImageObject($object, string $filename): void;
- public function doSave(Image $image, string $filename)
+ public function doSave(Image $image, string $filename): void
{
$object = $this->getImageObject($image);
$object = $this->doRotation($object, $image);
diff --git a/src/Drivers/GDDriver.php b/src/Drivers/GDDriver.php
index 36bf4b3..07bb48b 100644
--- a/src/Drivers/GDDriver.php
+++ b/src/Drivers/GDDriver.php
@@ -3,6 +3,7 @@
namespace ByJoby\ImageTransform\Drivers;
use ByJoby\ImageTransform\Image;
+use GdImage;
/**
* This driver uses PHP's built-in GD libary. This is by far the slowest driver,
@@ -18,42 +19,62 @@ class GDDriver extends AbstractExtensionDriver
}
}
- protected function getImageObject(Image $image)
+ protected function getImageObject(Image $image): GdImage
{
$source = $image->source();
- $extension = strtolower(preg_replace('/^.+\./', '', $source));
+ /** @var string */
+ $extension = preg_replace('/^.+\./', '', $source);
+ $extension = strtolower($extension);
switch ($extension) {
case 'bmp':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefrombmp($source);
case 'gif':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefromgif($source);
case 'jpg':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefromjpeg($source);
case 'jpeg':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefromjpeg($source);
case 'png':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefrompng($source);
case 'wbmp':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefromwbmp($source);
case 'webp':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefromwebp($source);
case 'xbm':
+ // @phpstan-ignore-next-line this will throw an exception, which is good
return imagecreatefromxbm($source);
default:
throw new \Exception("Unsupported input type: " . htmlentities($source));
}
}
- protected function doResize($object, Image $image)
+ /**
+ * @param GdImage $object
+ * @param Image $image
+ * @return GdImage
+ */
+ protected function doResize($object, Image $image): GdImage
{
$sizer = $image->sizer();
if ($sizer->resizeToHeight() && $sizer->resizeToWidth()) {
// sizer is calling for a resize
+ /** @var GdImage */
$new = imagecreatetruecolor($sizer->resizeToWidth(), $sizer->resizeToHeight());
imagecopyresampled(
- $new, $object,
- 0, 0,//dst x/y
- 0, 0,
+ $new,
+ $object,
+ 0,
+ 0,
+ //dst x/y
+ 0,
+ 0,
$sizer->resizeToWidth(), $sizer->resizeToHeight(),
$sizer->originalWidth(), $sizer->originalHeight()
);
@@ -64,17 +85,26 @@ class GDDriver extends AbstractExtensionDriver
}
}
- protected function doCrop($object, Image $image)
+ /**
+ * @param GdImage $object
+ * @param Image $image
+ * @return GdImage
+ */
+ protected function doCrop($object, Image $image): GdImage
{
$sizer = $image->sizer();
if ($sizer->cropToHeight() && $sizer->cropToWidth()) {
// sizer is calling for a crop
+ /** @var GdImage */
$new = imagecreatetruecolor($sizer->cropToWidth(), $sizer->cropToHeight());
imagecopyresampled(
- $new, $object,
- ($sizer->cropToWidth()-$sizer->resizeToWidth())/2,($sizer->cropToHeight()-$sizer->resizeToHeight())/2,
- 0,0,
- $sizer->resizetoWidth(),$sizer->resizeToHeight(),$sizer->resizeToWidth(),$sizer->resizeToHeight()
+ $new,
+ $object,
+ ($sizer->cropToWidth() - $sizer->resizeToWidth()) / 2, ($sizer->cropToHeight() - $sizer->resizeToHeight()) / 2,
+ 0,
+ 0,
+ // @phpstan-ignore-next-line these are definitely set
+ $sizer->resizetoWidth(), $sizer->resizeToHeight(), $sizer->resizeToWidth(), $sizer->resizeToHeight()
);
return $new;
} else {
@@ -83,47 +113,65 @@ class GDDriver extends AbstractExtensionDriver
}
}
- protected function doFlip($object, Image $image)
+ /**
+ * @param GdImage $object
+ * @param Image $image
+ * @return GdImage
+ */
+ protected function doFlip($object, Image $image): GdImage
{
if ($image->getFlipH()) {
- imageflip($object,IMG_FLIP_HORIZONTAL);
+ imageflip($object, IMG_FLIP_HORIZONTAL);
}
if ($image->getFlipV()) {
- imageflip($object,IMG_FLIP_VERTICAL);
+ imageflip($object, IMG_FLIP_VERTICAL);
}
return $object;
}
- protected function doRotation($object, Image $image)
+ /**
+ * @param GdImage $object
+ * @param Image $image
+ * @return GdImage
+ */
+ protected function doRotation($object, Image $image): GdImage
{
if ($rotationAmount = 360 - $image->rotation() * 90) {
+ // @phpstan-ignore-next-line
return imagerotate($object, $rotationAmount, 0);
}
return $object;
}
- protected function saveImageObject($object, string $filename)
+ /**
+ * @param GdImage $object
+ * @param string $filename
+ * @return void
+ */
+ protected function saveImageObject($object, string $filename): void
{
- $extension = strtolower(preg_replace('/^.+\./', '', $filename));
+ /** @var string */
+ $extension = preg_replace('/^.+\./', '', $filename);
+ $extension = strtolower($extension);
switch ($extension) {
case 'bmp':
- return imagebmp($object, $filename);
+ imagebmp($object, $filename);
case 'gif':
- return imagegif($object, $filename);
+ imagegif($object, $filename);
case 'jpg':
- return imagejpeg($object, $filename);
+ imagejpeg($object, $filename);
case 'jpeg':
- return imagejpeg($object, $filename);
+ imagejpeg($object, $filename);
case 'png':
- return imagepng($object, $filename);
+ imagepng($object, $filename);
case 'wbmp':
- return imagewbmp($object, $filename);
+ imagewbmp($object, $filename);
case 'webp':
- return imagewebp($object, $filename);
+ imagewebp($object, $filename);
case 'xbm':
- return imagexbm($object, $filename);
+ imagexbm($object, $filename);
default:
throw new \Exception("Unsupported output type: " . htmlentities($filename));
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Drivers/MagickCliDriver.php b/src/Drivers/MagickCliDriver.php
index d9af730..92fc32b 100644
--- a/src/Drivers/MagickCliDriver.php
+++ b/src/Drivers/MagickCliDriver.php
@@ -13,12 +13,8 @@ use ByJoby\ImageTransform\Image;
*/
class MagickCliDriver extends AbstractCliDriver
{
- protected $mogrify_executable;
-
- public function __construct($mogrify_executable = 'magick mogrify')
+ public function __construct(protected string $mogrify_executable = 'magick mogrify')
{
- parent::__construct();
- $this->mogrify_executable = $mogrify_executable;
}
protected function mogrifyExecutable(): string
@@ -26,7 +22,7 @@ class MagickCliDriver extends AbstractCliDriver
return $this->mogrify_executable;
}
- protected function doSave(Image $image, string $filename)
+ protected function doSave(Image $image, string $filename): void
{
// basics of command
$command = [
diff --git a/src/Drivers/MagickDriver.php b/src/Drivers/MagickDriver.php
new file mode 100644
index 0000000..d979883
--- /dev/null
+++ b/src/Drivers/MagickDriver.php
@@ -0,0 +1,144 @@
+use_gmagick) return new Gmagick($image->source());
+ else return new Imagick($image->source());
+ }
+
+ /**
+ * @param Gmagick|Imagick $object
+ * @param Image $image
+ * @return Gmagick|Imagick
+ */
+ protected function doResize($object, Image $image): Gmagick|Imagick
+ {
+ $sizer = $image->sizer();
+ if ($sizer->resizeToHeight() && $sizer->resizeToWidth()) {
+ if ($object instanceof Gmagick) {
+ $object->resizeimage(
+ $sizer->resizeToWidth(),
+ $sizer->resizeToHeight(),
+ Gmagick::FILTER_LANCZOS,
+ 0
+ );
+ } else {
+ $object->resizeImage(
+ $sizer->resizeToWidth(),
+ $sizer->resizeToHeight(),
+ Imagick::FILTER_LANCZOS,
+ 0
+ );
+ }
+ }
+ return $object;
+ }
+
+ /**
+ * @param Gmagick|Imagick $object
+ * @param Image $image
+ * @return Gmagick|Imagick
+ */
+ protected function doCrop($object, Image $image): Gmagick|Imagick
+ {
+ $sizer = $image->sizer();
+ $height = $sizer->cropToHeight();
+ $width = $sizer->cropToWidth();
+ if ($height && $width) {
+ $x = intval(($sizer->resizetoWidth() - $width) / 2);
+ $y = intval(($sizer->resizetoHeight() - $height) / 2);
+ if ($object instanceof Gmagick) {
+ $object->cropimage(
+ $width,
+ $height,
+ $x,
+ $y
+ );
+ } else {
+ $object->cropImage(
+ $width,
+ $height,
+ $x,
+ $y
+ );
+ }
+ }
+ return $object;
+ }
+
+ /**
+ * @param Gmagick|Imagick $object
+ * @param Image $image
+ * @return Gmagick|Imagick
+ */
+ protected function doFlip($object, Image $image): Gmagick|Imagick
+ {
+ if ($image->getFlipH()) {
+ if ($object instanceof Gmagick) $object->flipimage();
+ else $object->flipImage();
+ }
+ if ($image->getFlipV()) {
+ if ($object instanceof Gmagick) $object->flopimage();
+ else $object->flopImage();
+ }
+ return $object;
+ }
+
+ /**
+ * @param Gmagick|Imagick $object
+ * @param Image $image
+ * @return Gmagick|Imagick
+ */
+ protected function doRotation($object, Image $image): Gmagick|Imagick
+ {
+ if ($image->rotation()) {
+ if ($object instanceof Gmagick) $object->rotateimage(new GmagickPixel("#000"), $image->rotation() * 90);
+ else $object->rotateImage(new ImagickPixel("#000"), $image->rotation() * 90);
+ }
+ return $object;
+ }
+
+ /**
+ * @param Gmagick|Imagick $object
+ * @param string $filename
+ * @return void
+ */
+ protected function saveImageObject($object, string $filename): void
+ {
+ /** @var string */
+ $format = preg_replace('/^.+\./', '', $filename);
+ $format = strtoupper($format);
+ if ($format == 'JPG') $format = 'JPEG';
+ if ($object instanceof Gmagick) {
+ $object->setimageformat($format);
+ $object->writeimage($filename, true);
+ } else {
+ $object->setImageFormat($format);
+ $object->writeImage($filename);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Image.php b/src/Image.php
index 6564b4f..9dc3306 100644
--- a/src/Image.php
+++ b/src/Image.php
@@ -7,13 +7,21 @@ use ByJoby\ImageTransform\Sizers\Original;
class Image
{
+ /** @var string */
protected $source;
+ /** @var DriverInterface|null */
protected $driver;
+ /** @var int */
protected $originalWidth;
+ /** @var int */
protected $originalHeight;
+ /** @var int */
protected $rotation = 0;
+ /** @var boolean */
protected $flipH = false;
+ /** @var boolean */
protected $flipV = false;
+ /** @var AbstractSizer */
protected $sizer = null;
public function __construct(string $source, AbstractSizer|null $sizer = null)
@@ -30,10 +38,11 @@ class Image
public function setSource(string $source): static
{
// set source
- $this->source = realpath($source);
- if (!$this->source) {
- throw new \Exception("Source image not found: " . htmlentities($source));
+ $source = realpath($source);
+ if (!$source) {
+ throw new \Exception("Source image not found");
}
+ $this->source = $source;
// validate file
if (!is_file($this->source)) {
throw new \Exception("Image file doesn't exist: " . htmlentities($this->source));
@@ -42,7 +51,11 @@ class Image
throw new \Exception("Invalid image file: " . htmlentities($this->source));
}
// get height/width
- list($this->originalWidth, $this->originalHeight) = getimagesize($this->source);
+ $size = getimagesize($this->source);
+ if (!$size) {
+ throw new \Exception("Couldn't get image size: " . htmlentities($this->source));
+ }
+ list($this->originalWidth, $this->originalHeight) = $size;
// return self
return $this;
}
@@ -55,7 +68,7 @@ class Image
public function setSizer(AbstractSizer $sizer): static
{
$this->sizer = clone $sizer;
- $this->sizer->image($this);
+ $this->sizer->setImage($this);
return $this;
}
diff --git a/src/Sizers/AbstractSizer.php b/src/Sizers/AbstractSizer.php
index 08f8834..4ca31e2 100644
--- a/src/Sizers/AbstractSizer.php
+++ b/src/Sizers/AbstractSizer.php
@@ -6,6 +6,7 @@ use ByJoby\ImageTransform\Image;
abstract class AbstractSizer
{
+ /** @var Image */
protected $image;
abstract public function width(): int;
@@ -46,8 +47,9 @@ abstract class AbstractSizer
return $this->originalWidth()/$this->originalHeight();
}
- public function image(Image $image)
+ public function setImage(Image $image): static
{
$this->image = $image;
+ return $this;
}
}
diff --git a/src/Sizers/Cover.php b/src/Sizers/Cover.php
index 8cdb921..a2823cd 100644
--- a/src/Sizers/Cover.php
+++ b/src/Sizers/Cover.php
@@ -4,6 +4,7 @@ namespace ByJoby\ImageTransform\Sizers;
class Cover extends AbstractSizer
{
+ /** @var int */
protected $width, $height;
public function __construct(int $width, int $height)
@@ -17,14 +18,17 @@ class Cover extends AbstractSizer
return $this->width / $this->height;
}
+ /**
+ * @return array{height:int,width:int}
+ */
protected function calculateSize(): array
{
if ($this->targetRatio() < $this->originalRatio()) {
$height = $this->height;
- $width = round($height * $this->originalRatio());
+ $width = intval(round($height * $this->originalRatio()));
} else {
$width = $this->width;
- $height = round($width / $this->originalRatio());
+ $height = intval(round($width / $this->originalRatio()));
}
return [
'height' => $height,
@@ -61,4 +65,4 @@ class Cover extends AbstractSizer
{
return $this->height;
}
-}
+}
\ No newline at end of file
diff --git a/src/Sizers/Fit.php b/src/Sizers/Fit.php
index 8c46839..5fe74dd 100644
--- a/src/Sizers/Fit.php
+++ b/src/Sizers/Fit.php
@@ -4,6 +4,7 @@ namespace ByJoby\ImageTransform\Sizers;
class Fit extends AbstractSizer
{
+ /** @var int */
protected $width, $height;
public function __construct(int $width, int $height)
@@ -27,14 +28,17 @@ class Fit extends AbstractSizer
return $this->width / $this->height;
}
+ /**
+ * @return array{height:int,width:int}
+ */
protected function calculateSize(): array
{
if ($this->targetRatio() > $this->originalRatio()) {
$height = $this->height;
- $width = round($height * $this->originalRatio());
+ $width = intval(round($height * $this->originalRatio()));
} else {
$width = $this->width;
- $height = round($width / $this->originalRatio());
+ $height = intval(round($width / $this->originalRatio()));
}
return [
'height' => $height,
diff --git a/tests/100x200.jpg b/tests/100x200.jpg
deleted file mode 100644
index aa3bcc5..0000000
Binary files a/tests/100x200.jpg and /dev/null differ
diff --git a/tests/200x100.jpg b/tests/200x100.jpg
deleted file mode 100644
index cb9088b..0000000
Binary files a/tests/200x100.jpg and /dev/null differ
diff --git a/tests/200x200.jpg b/tests/200x200.jpg
deleted file mode 100644
index 6a4cf3e..0000000
Binary files a/tests/200x200.jpg and /dev/null differ
diff --git a/tests/Sizers/CoverTest.php b/tests/Sizers/CoverTest.php
new file mode 100644
index 0000000..276a47e
--- /dev/null
+++ b/tests/Sizers/CoverTest.php
@@ -0,0 +1,109 @@
+createMock(Image::class);
+ $image->method("originalWidth")->willReturn(200);
+ $image->method("originalHeight")->willReturn(200);
+ // spin up a 50x50 sizer
+ $sizer = new Cover(50, 50);
+ $sizer->setImage($image);
+ // resized to cover
+ $this->assertEquals(50, $sizer->resizeToWidth());
+ $this->assertEquals(50, $sizer->resizeToHeight());
+ // cropped to size
+ $this->assertEquals(50, $sizer->cropToWidth());
+ $this->assertEquals(50, $sizer->cropToHeight());
+ }
+
+ public function testSquareImageSizedLandscape(): void
+ {
+ // mock 200x200 image
+ $image = $this->createMock(Image::class);
+ $image->method("originalWidth")->willReturn(200);
+ $image->method("originalHeight")->willReturn(200);
+ // spin up a 100x50 sizer
+ $sizer = new Cover(100, 50);
+ $sizer->setImage($image);
+ // resized to cover
+ $this->assertEquals(100, $sizer->resizeToWidth());
+ $this->assertEquals(100, $sizer->resizeToHeight());
+ // cropped to size
+ $this->assertEquals(100, $sizer->cropToWidth());
+ $this->assertEquals(50, $sizer->cropToHeight());
+ }
+ public function testSquareImageSizedPortrait(): void
+ {
+ // mock 200x200 image
+ $image = $this->createMock(Image::class);
+ $image->method("originalWidth")->willReturn(200);
+ $image->method("originalHeight")->willReturn(200);
+ // spin up a 50x100 sizer
+ $sizer = new Cover(50, 100);
+ $sizer->setImage($image);
+ // resized to cover
+ $this->assertEquals(100, $sizer->resizeToWidth());
+ $this->assertEquals(100, $sizer->resizeToHeight());
+ // cropped to size
+ $this->assertEquals(50, $sizer->cropToWidth());
+ $this->assertEquals(100, $sizer->cropToHeight());
+ }
+
+ public function testLandscapeImageSizedSquare(): void
+ {
+ // mock 200x100 image
+ $image = $this->createMock(Image::class);
+ $image->method("originalWidth")->willReturn(200);
+ $image->method("originalHeight")->willReturn(100);
+ // spin up a 50x50 sizer
+ $sizer = new Cover(50, 50);
+ $sizer->setImage($image);
+ // resized to cover
+ $this->assertEquals(100, $sizer->resizeToWidth());
+ $this->assertEquals(50, $sizer->resizeToHeight());
+ // cropped to size
+ $this->assertEquals(50, $sizer->cropToWidth());
+ $this->assertEquals(50, $sizer->cropToHeight());
+ }
+
+ public function testLandscapeImageSizedLandscape(): void
+ {
+ // mock 200x100 image
+ $image = $this->createMock(Image::class);
+ $image->method("originalWidth")->willReturn(200);
+ $image->method("originalHeight")->willReturn(100);
+ // spin up a 100x50 sizer
+ $sizer = new Cover(100, 50);
+ $sizer->setImage($image);
+ // resized to cover
+ $this->assertEquals(100, $sizer->resizeToWidth());
+ $this->assertEquals(50, $sizer->resizeToHeight());
+ // cropped to size
+ $this->assertEquals(100, $sizer->cropToWidth());
+ $this->assertEquals(50, $sizer->cropToHeight());
+ }
+ public function testLandscapeImageSizedPortrait(): void
+ {
+ // mock 200x100 image
+ $image = $this->createMock(Image::class);
+ $image->method("originalWidth")->willReturn(200);
+ $image->method("originalHeight")->willReturn(100);
+ // spin up a 50x100 sizer
+ $sizer = new Cover(50, 100);
+ $sizer->setImage($image);
+ // resized to cover
+ $this->assertEquals(200, $sizer->resizeToWidth());
+ $this->assertEquals(100, $sizer->resizeToHeight());
+ // cropped to size
+ $this->assertEquals(50, $sizer->cropToWidth());
+ $this->assertEquals(100, $sizer->cropToHeight());
+ }
+}
\ No newline at end of file
diff --git a/tests/input-100x200.png b/tests/input-100x200.png
new file mode 100644
index 0000000..8f08d38
Binary files /dev/null and b/tests/input-100x200.png differ
diff --git a/tests/input-200x100.png b/tests/input-200x100.png
new file mode 100644
index 0000000..d4518f7
Binary files /dev/null and b/tests/input-200x100.png differ
diff --git a/tests/input-200x200.png b/tests/input-200x200.png
new file mode 100644
index 0000000..cbe4d4b
Binary files /dev/null and b/tests/input-200x200.png differ
diff --git a/tests/test.svg b/tests/test.svg
new file mode 100644
index 0000000..1d0ad19
--- /dev/null
+++ b/tests/test.svg
@@ -0,0 +1,196 @@
+
+
+
+