diff --git a/.github/workflows/build.yml b/.github/workflows/gmagick.yml
similarity index 92%
rename from .github/workflows/build.yml
rename to .github/workflows/gmagick.yml
index 719d5a9..d87b5dd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/gmagick.yml
@@ -19,12 +19,13 @@ on:
- 'infection.json.dist'
- 'psalm.xml'
-name: build
+name: gmagick
jobs:
phpunit:
uses: yiisoft/actions/.github/workflows/phpunit.yml@master
with:
+ extensions: intl, gmagick
os: >-
['ubuntu-latest', 'windows-latest']
php: >-
diff --git a/.github/workflows/imagick.yml b/.github/workflows/imagick.yml
new file mode 100644
index 0000000..244c016
--- /dev/null
+++ b/.github/workflows/imagick.yml
@@ -0,0 +1,32 @@
+on:
+ pull_request:
+ paths-ignore:
+ - 'docs/**'
+ - 'README.md'
+ - 'CHANGELOG.md'
+ - '.gitignore'
+ - '.gitattributes'
+ - 'infection.json.dist'
+ - 'psalm.xml'
+
+ push:
+ paths-ignore:
+ - 'docs/**'
+ - 'README.md'
+ - 'CHANGELOG.md'
+ - '.gitignore'
+ - '.gitattributes'
+ - 'infection.json.dist'
+ - 'psalm.xml'
+
+name: imagick
+
+jobs:
+ phpunit:
+ uses: yiisoft/actions/.github/workflows/phpunit.yml@master
+ with:
+ extensions: intl, imagick
+ os: >-
+ ['ubuntu-latest', 'windows-latest']
+ php: >-
+ ['8.1', '8.2', '8.3']
diff --git a/composer.json b/composer.json
index cd5155d..1599aa9 100644
--- a/composer.json
+++ b/composer.json
@@ -1,15 +1,19 @@
{
- "name": "yii2/template",
- "type": "library",
- "description": "_____",
+ "name": "yii2-extensions/imagine",
+ "type": "yii2-extension",
+ "description": "The Imagine integration for the Yii framework.",
"keywords": [
- "_____"
+ "yii2",
+ "imagine",
+ "image",
+ "helper"
],
"license": "mit",
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.1",
+ "imagine/imagine": "^1.3",
"yiisoft/yii2": "^2.2"
},
"require-dev": {
@@ -19,12 +23,12 @@
},
"autoload": {
"psr-4": {
- "yii\\template\\": "src"
+ "yii\\imagine\\": "src"
}
},
"autoload-dev": {
"psr-4": {
- "yii\\template\\tests\\": "tests"
+ "yiiunit\\imagine\\": "tests"
}
},
"extra": {
diff --git a/phpstan.neon b/phpstan.neon
index 0b239cb..7c7b893 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -8,7 +8,7 @@ parameters:
- YII_ENV_PROD
- YII_ENV_TEST
- level: 2
+ level: 3
paths:
- src
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index f29a28d..1031c61 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,24 +1,24 @@
-
-
- tests
-
-
+
+
+ tests
+
+
-
+
diff --git a/src/BaseImage.php b/src/BaseImage.php
new file mode 100644
index 0000000..b86a3d4
--- /dev/null
+++ b/src/BaseImage.php
@@ -0,0 +1,465 @@
+
+ * @author Qiang Xue
+ * @since 2.0
+ */
+class BaseImage
+{
+ /**
+ * GD2 driver definition for Imagine implementation using the GD library.
+ */
+ const DRIVER_GD2 = 'gd2';
+ /**
+ * imagick driver definition.
+ */
+ const DRIVER_IMAGICK = 'imagick';
+ /**
+ * gmagick driver definition.
+ */
+ const DRIVER_GMAGICK = 'gmagick';
+
+ /**
+ * @var ImagineInterface instance.
+ */
+ private static $_imagine;
+
+
+ /**
+ * @var array|string the driver to use. This can be either a single driver name or an array of driver names.
+ * If the latter, the first available driver will be used.
+ */
+ public static $driver = [self::DRIVER_GMAGICK, self::DRIVER_IMAGICK, self::DRIVER_GD2];
+ /**
+ * @var string background color to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET` mode with
+ * both width and height specified. Default is white.
+ *
+ * @since 2.0.4
+ */
+ public static $thumbnailBackgroundColor = 'FFF';
+ /**
+ * @var string|int background alpha (transparency) to use when creating thumbnails in
+ * `ImageInterface::THUMBNAIL_INSET` mode with both width and height specified. Default is solid.
+ *
+ * @since 2.0.4
+ */
+ public static $thumbnailBackgroundAlpha = 100;
+
+ /**
+ * Returns the `Imagine` object that supports various image manipulations.
+ * @return ImagineInterface the `Imagine` object
+ */
+ public static function getImagine()
+ {
+ if (self::$_imagine === null) {
+ self::$_imagine = static::createImagine();
+ }
+
+ return self::$_imagine;
+ }
+
+ /**
+ * @param ImagineInterface $imagine the `Imagine` object.
+ */
+ public static function setImagine($imagine)
+ {
+ self::$_imagine = $imagine;
+ }
+
+ /**
+ * Creates an `Imagine` object based on the specified [[driver]].
+ * @return ImagineInterface the new `Imagine` object
+ * @throws InvalidConfigException if [[driver]] is unknown or the system doesn't support any [[driver]].
+ */
+ protected static function createImagine()
+ {
+ foreach ((array) static::$driver as $driver) {
+ switch ($driver) {
+ case self::DRIVER_GMAGICK:
+ if (class_exists('Gmagick', false)) {
+ return new \Imagine\Gmagick\Imagine();
+ }
+ break;
+ case self::DRIVER_IMAGICK:
+ if (class_exists('Imagick', false)) {
+ return new \Imagine\Imagick\Imagine();
+ }
+ break;
+ case self::DRIVER_GD2:
+ if (function_exists('gd_info')) {
+ return new \Imagine\Gd\Imagine();
+ }
+ break;
+ default:
+ throw new InvalidConfigException("Unknown driver: $driver");
+ }
+ }
+ throw new InvalidConfigException('Your system does not support any of these drivers: ' . implode(',', (array) static::$driver));
+ }
+
+ /**
+ * Takes either file path or ImageInterface. In case of file path, creates an instance of ImageInterface from it.
+ *
+ * @param string|resource|ImageInterface $image
+ * @return ImageInterface
+ * @throws InvalidArgumentException if the image argument is invalid.
+ * @since 2.1.0
+ */
+ protected static function ensureImageInterfaceInstance($image)
+ {
+ if ($image instanceof ImageInterface) {
+ return $image;
+ }
+
+ if (is_resource($image)) {
+ return static::getImagine()->read($image);
+ }
+
+ if (is_string($image)) {
+ return static::getImagine()->open(Yii::getAlias($image));
+ }
+
+ throw new InvalidArgumentException(
+ 'File should be either ImageInterface, resource or a string containing file path.'
+ );
+ }
+
+ /**
+ * Crops an image.
+ *
+ * For example:
+ *
+ * ```php
+ * $obj->crop('path\to\image.jpg', 200, 200, [5, 5]);
+ *
+ * $point = new \Imagine\Image\Point(5, 5);
+ * $obj->crop('path\to\image.jpg', 200, 200, $point);
+ * ```
+ *
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param int $width the crop width
+ * @param int $height the crop height
+ * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
+ * @return ImageInterface
+ * @throws InvalidArgumentException if the `$start` parameter is invalid.
+ */
+ public static function crop($image, $width, $height, array $start = [0, 0])
+ {
+ if (!isset($start[0], $start[1])) {
+ throw new InvalidArgumentException('$start must be an array of two elements.');
+ }
+
+ return static::ensureImageInterfaceInstance($image)
+ ->copy()
+ ->crop(new Point($start[0], $start[1]), new Box($width, $height));
+ }
+
+ /**
+ * Rotates an image automatically based on EXIF information.
+ *
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param string $color
+ * @return \Imagine\Image\ImageInterface
+ * @since 2.1.0
+ */
+ public static function autorotate($image, $color = '000000')
+ {
+ return (new Autorotate($color))->apply(static::ensureImageInterfaceInstance($image));
+ }
+
+ /**
+ * Creates a thumbnail image.
+ *
+ * If one of thumbnail dimensions is set to `null`, another one is calculated automatically based on aspect ratio of
+ * original image. Note that calculated thumbnail dimension may vary depending on the source image in this case.
+ *
+ * If both dimensions are specified, resulting thumbnail would be exactly the width and height specified. How it's
+ * achieved depends on the mode defined via settings parameter.
+ *
+ * If `ImageInterface::THUMBNAIL_OUTBOUND` mode is used, which is default, then the thumbnail is scaled so that
+ * its smallest side equals the length of the corresponding side in the original image. Any excess outside of
+ * the scaled thumbnail’s area will be cropped, and the returned thumbnail will have the exact width and height
+ * specified.
+ *
+ * If thumbnail mode is `ImageInterface::THUMBNAIL_INSET`, the original image is scaled down so it is fully
+ * contained within the thumbnail dimensions. The rest is filled with background that could be configured via
+ * [[Image::$thumbnailBackgroundColor]] and [[Image::$thumbnailBackgroundAlpha]].
+ *
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param int $width the width in pixels to create the thumbnail
+ * @param int $height the height in pixels to create the thumbnail
+ * @param int $settings settings for resizing original image, one or more of the ManipulatorInterface::THUMBNAIL_ flags (joined with |)
+ * @return ImageInterface
+ */
+ public static function thumbnail($image, $width, $height, $settings = ManipulatorInterface::THUMBNAIL_OUTBOUND)
+ {
+ $img = self::ensureImageInterfaceInstance($image);
+
+ /** @var BoxInterface $sourceBox */
+ $sourceBox = $img->getSize();
+ $thumbnailBox = static::getThumbnailBox($sourceBox, $width, $height);
+
+ $allowUpscale = (bool) ($settings & ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+ if (self::isUpscaling($sourceBox, $thumbnailBox) && !$allowUpscale) {
+ return $img->copy();
+ }
+
+ $img = $img->thumbnail($thumbnailBox, $settings);
+
+ if ($settings & ImageInterface::THUMBNAIL_OUTBOUND) {
+ return $img;
+ }
+
+ $size = $img->getSize();
+
+ if ($size->getWidth() == $width && $size->getHeight() == $height) {
+ return $img;
+ }
+
+ $palette = new RGB();
+ $color = $palette->color(static::$thumbnailBackgroundColor, static::$thumbnailBackgroundAlpha);
+
+ // create empty image to preserve aspect ratio of thumbnail
+ $thumb = static::getImagine()->create($thumbnailBox, $color);
+
+ // calculate points
+ $startX = 0;
+ $startY = 0;
+ if ($size->getWidth() < $width) {
+ $startX = ceil(($width - $size->getWidth()) / 2);
+ }
+ if ($size->getHeight() < $height) {
+ $startY = ceil(($height - $size->getHeight()) / 2);
+ }
+
+ $thumb->paste($img, new Point($startX, $startY));
+
+ return $thumb;
+ }
+
+ /**
+ * Resizes an image.
+ *
+ * If one of the dimensions is set to `null`, another one is calculated automatically based on aspect ratio of
+ * original image.
+ *
+ * If both of the dimensions are set then new dimensions are calculated so that image keeps aspect ratio.
+ *
+ * You can set $keepAspectRatio to false if you want to force fixed width and height.
+ *
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param int $width the width in pixels
+ * @param int $height the height in pixels
+ * @param bool $keepAspectRatio should the image keep aspect ratio
+ * @param bool $allowUpscaling should the image be upscaled if needed
+ * @return ImageInterface
+ *
+ * @since 2.1.1
+ */
+ public static function resize($image, $width, $height, $keepAspectRatio = true, $allowUpscaling = false)
+ {
+ $img = self::ensureImageInterfaceInstance($image)->copy();
+
+ /** @var BoxInterface $sourceBox */
+ $sourceBox = $img->getSize();
+ $destinationBox = static::getBox($sourceBox, $width, $height, $keepAspectRatio);
+
+ if ($allowUpscaling === false && self::isUpscaling($sourceBox, $destinationBox)) {
+ return $img;
+ }
+
+ return $img->resize($destinationBox);
+ }
+
+ /**
+ * Adds a watermark to an existing image.
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param string|resource|ImageInterface $watermarkImage either ImageInterface, resource or a string containing watermark file path
+ * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
+ * @return ImageInterface
+ * @throws InvalidArgumentException if `$start` is invalid.
+ */
+ public static function watermark($image, $watermarkImage, array $start = [0, 0])
+ {
+ if (!isset($start[0], $start[1])) {
+ throw new InvalidArgumentException('$start must be an array of two elements.');
+ }
+
+ $img = self::ensureImageInterfaceInstance($image);
+ $watermark = self::ensureImageInterfaceInstance($watermarkImage);
+ $img->paste($watermark, new Point($start[0], $start[1]));
+
+ return $img;
+ }
+
+ /**
+ * Draws a text string on an existing image.
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param string $text the text to write to the image
+ * @param string $fontFile the file path or path alias
+ * @param array $start the starting position of the text. This must be an array with two elements representing `x` and `y` coordinates.
+ * @param array $fontOptions the font options. The following options may be specified:
+ *
+ * - color: The font color. Defaults to "fff".
+ * - size: The font size. Defaults to 12.
+ * - angle: The angle to use to write the text. Defaults to 0.
+ *
+ * @return ImageInterface
+ * @throws InvalidArgumentException if `$fontOptions` is invalid.
+ */
+ public static function text($image, $text, $fontFile, array $start = [0, 0], array $fontOptions = [])
+ {
+ if (!isset($start[0], $start[1])) {
+ throw new InvalidArgumentException('$start must be an array of two elements.');
+ }
+
+ $fontSize = ArrayHelper::getValue($fontOptions, 'size', 12);
+ $fontColor = ArrayHelper::getValue($fontOptions, 'color', 'fff');
+ $fontAngle = ArrayHelper::getValue($fontOptions, 'angle', 0);
+
+ $palette = new RGB();
+ $color = $palette->color($fontColor);
+
+ $img = self::ensureImageInterfaceInstance($image);
+ $font = static::getImagine()->font(Yii::getAlias($fontFile), $fontSize, $color);
+
+ $img->draw()->text($text, $font, new Point($start[0], $start[1]), $fontAngle);
+
+ return $img;
+ }
+
+ /**
+ * Adds a frame around of the image. Please note that the image size will increase by `$margin` x 2.
+ * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
+ * @param int $margin the frame size to add around the image
+ * @param string $color the frame color
+ * @param int $alpha the alpha value of the frame.
+ * @return ImageInterface
+ */
+ public static function frame($image, $margin = 20, $color = '666', $alpha = 100)
+ {
+ $img = self::ensureImageInterfaceInstance($image);
+
+ $size = $img->getSize();
+
+ $pasteTo = new Point($margin, $margin);
+
+ $palette = new RGB();
+ $color = $palette->color($color, $alpha);
+
+ $box = new Box($size->getWidth() + ceil($margin * 2), $size->getHeight() + ceil($margin * 2));
+
+ $finalImage = static::getImagine()->create($box, $color);
+
+ $finalImage->paste($img, $pasteTo);
+
+ return $finalImage;
+ }
+
+ /**
+ * Returns box for a thumbnail to be created. If one of the dimensions is set to `null`, another one is calculated
+ * automatically based on width to height ratio of original image box.
+ *
+ * @param BoxInterface $sourceBox original image box
+ * @param int $width thumbnail width
+ * @param int $height thumbnail height
+ * @return BoxInterface thumbnail box
+ *
+ * @since 2.0.4
+ */
+ public static function getThumbnailBox(BoxInterface $sourceBox, $width, $height)
+ {
+ if ($width !== null && $height !== null) {
+ return new Box($width, $height);
+ }
+
+ return self::getBox($sourceBox, $width, $height, false);
+ }
+
+ /**
+ * Returns box for an image to be created.
+ *
+ * If one of the dimensions is set to `null`, another one is calculated automatically based on width to height ratio
+ * of original image box.
+ *
+ * If both of the dimensions are set then new dimensions are calculated so that image keeps aspect ratio.
+ *
+ * You can set $keepAspectRatio to false if you want to force fixed width and height.
+ *
+ * @param BoxInterface $sourceBox original image box
+ * @param int $width new image width
+ * @param int $height new image height
+ * @param bool $keepAspectRatio should we keep aspect ratio even if both with and height are set
+ * @return BoxInterface new image box
+ * @throws InvalidArgumentException if both width and height are null.
+ *
+ * @since 2.1.1
+ */
+ public static function getBox(BoxInterface $sourceBox, $width, $height, $keepAspectRatio = true)
+ {
+ if ($width === null && $height === null) {
+ throw new InvalidArgumentException('Width and height cannot be null at same time.');
+ }
+
+ $ratio = $sourceBox->getWidth() / $sourceBox->getHeight();
+ if ($keepAspectRatio === false) {
+ if ($height === null) {
+ $height = ceil($width / $ratio);
+ } elseif ($width === null) {
+ $width = ceil($height * $ratio);
+ }
+ } else {
+ if ($height === null) {
+ $height = ceil($width / $ratio);
+ } elseif ($width === null) {
+ $width = ceil($height * $ratio);
+ } elseif ($width / $height > $ratio) {
+ $width = $height * $ratio;
+ } else {
+ $height = $width / $ratio;
+ }
+ }
+
+ return new Box($width, $height);
+ }
+
+ /**
+ * Checks if upscaling is going to happen
+ *
+ * @param BoxInterface $sourceBox
+ * @param BoxInterface $destinationBox
+ * @return bool
+ */
+ public static function isUpscaling(BoxInterface $sourceBox, BoxInterface $destinationBox)
+ {
+ return ($sourceBox->getWidth() <= $destinationBox->getWidth() && $sourceBox->getHeight() <= $destinationBox->getHeight()) || (!$destinationBox->getWidth() && !$destinationBox->getHeight());
+ }
+}
diff --git a/src/Example.php b/src/Example.php
deleted file mode 100644
index 067eeb8..0000000
--- a/src/Example.php
+++ /dev/null
@@ -1,13 +0,0 @@
-save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);
+ * ```
+ *
+ * @author Antonio Ramirez
+ * @author Qiang Xue
+ * @since 2.0
+ */
+final class Image extends BaseImage
+{
+}
diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php
deleted file mode 100644
index 2825a4e..0000000
--- a/tests/ExampleTest.php
+++ /dev/null
@@ -1,18 +0,0 @@
-assertTrue($example->getExample());
- }
-}
diff --git a/tests/ImageGdTest.php b/tests/ImageGdTest.php
new file mode 100644
index 0000000..f535da6
--- /dev/null
+++ b/tests/ImageGdTest.php
@@ -0,0 +1,33 @@
+markTestSkipped('Skipping ImageGdTest, Gd not installed');
+ } else {
+ Image::setImagine(null);
+ Image::$driver = Image::DRIVER_GD2;
+ parent::setUp();
+ }
+ }
+
+ protected function isFontTestSupported()
+ {
+ $infos = gd_info();
+
+ return isset($infos['FreeType Support']) ? $infos['FreeType Support'] : false;
+ }
+}
diff --git a/tests/ImageGmagickTest.php b/tests/ImageGmagickTest.php
new file mode 100644
index 0000000..a5fcf62
--- /dev/null
+++ b/tests/ImageGmagickTest.php
@@ -0,0 +1,31 @@
+markTestSkipped('Skipping ImageGmagickTest, Gmagick is not installed');
+ } else {
+ Image::setImagine(null);
+ Image::$driver = Image::DRIVER_GMAGICK;
+ parent::setUp();
+ }
+ }
+
+ protected function isFontTestSupported()
+ {
+ return true;
+ }
+}
diff --git a/tests/ImageImagickTest.php b/tests/ImageImagickTest.php
new file mode 100644
index 0000000..e17027c
--- /dev/null
+++ b/tests/ImageImagickTest.php
@@ -0,0 +1,33 @@
+markTestSkipped('Skipping ImageImagickTest, Imagick is not installed');
+ } elseif (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('Imagine does not seem to support HHVM right now.');
+ } else {
+ Image::setImagine(null);
+ Image::$driver = Image::DRIVER_IMAGICK;
+ parent::setUp();
+ }
+ }
+
+ protected function isFontTestSupported()
+ {
+ return true;
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..2b61969
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,63 @@
+destroyApplication();
+ }
+
+ /**
+ * Populates Yii::$app with a new application
+ * The application will be destroyed on tearDown() automatically.
+ * @param array $config The application configuration, if needed
+ * @param string $appClass name of the application class to create
+ */
+ protected function mockApplication($config = [], $appClass = '\yii\console\Application')
+ {
+ new $appClass(ArrayHelper::merge([
+ 'id' => 'testapp',
+ 'basePath' => __DIR__,
+ 'vendorPath' => dirname(__DIR__) . '/vendor',
+ ], $config));
+ }
+
+ protected function mockWebApplication($config = [], $appClass = '\yii\web\Application')
+ {
+ new $appClass(ArrayHelper::merge([
+ 'id' => 'testapp',
+ 'basePath' => __DIR__,
+ 'vendorPath' => dirname(__DIR__) . '/vendor',
+ 'components' => [
+ 'request' => [
+ 'cookieValidationKey' => 'wefJDF8sfdsfSDefwqdxj9oq',
+ 'scriptFile' => __DIR__ .'/index.php',
+ 'scriptUrl' => '/index.php',
+ ],
+ ]
+ ], $config));
+ }
+
+ /**
+ * Destroys application in Yii::$app by setting it to null.
+ */
+ protected function destroyApplication()
+ {
+ Yii::$app = null;
+ Yii::$container = new Container();
+ }
+}
diff --git a/tests/base/AbstractImage.php b/tests/base/AbstractImage.php
new file mode 100644
index 0000000..b9fc8e6
--- /dev/null
+++ b/tests/base/AbstractImage.php
@@ -0,0 +1,192 @@
+imageFile = Yii::getAlias('@yiiunit/imagine/data/large.jpg');
+ $this->watermarkFile = Yii::getAlias('@yiiunit/imagine/data/xparent.gif');
+ $this->runtimeTextFile = Yii::getAlias('@yiiunit/imagine/runtime/image-text-test.png');
+ $this->runtimeWatermarkFile = Yii::getAlias('@yiiunit/imagine/runtime/image-watermark-test.png');
+ parent::setUp();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function tearDown(): void
+ {
+ @unlink($this->runtimeTextFile);
+ @unlink($this->runtimeWatermarkFile);
+ }
+
+ public function testText()
+ {
+ if (!$this->isFontTestSupported()) {
+ $this->markTestSkipped('Skipping ImageGdTest Gd not installed');
+ }
+
+ $fontFile = Yii::getAlias('@yiiunit/imagine/data/GothamRnd-Light.otf');
+
+ $img = Image::text($this->imageFile, 'Yii-2 Image', $fontFile, [0, 0], [
+ 'size' => 12,
+ 'color' => '000'
+ ]);
+
+ $img->save($this->runtimeTextFile);
+ $this->assertTrue(file_exists($this->runtimeTextFile));
+
+ }
+
+ public function testCrop()
+ {
+ $point = [20, 20];
+ $img = Image::crop($this->imageFile, 100, 100, $point);
+
+ $this->assertEquals(100, $img->getSize()->getWidth());
+ $this->assertEquals(100, $img->getSize()->getHeight());
+
+ }
+
+ public function testWatermark()
+ {
+ $img = Image::watermark($this->imageFile, $this->watermarkFile);
+ $img->save($this->runtimeWatermarkFile);
+ $this->assertTrue(file_exists($this->runtimeWatermarkFile));
+ }
+
+ public function testFrame()
+ {
+ $frameSize = 5;
+ $original = Image::getImagine()->open($this->imageFile);
+ $originalSize = $original->getSize();
+ $img = Image::frame($this->imageFile, $frameSize, '666', 0);
+ $size = $img->getSize();
+
+ $this->assertEquals($size->getWidth(), $originalSize->getWidth() + ($frameSize * 2));
+ }
+
+ public function testThumbnail()
+ {
+ // THUMBNAIL_OUTBOUND mode.
+ $img = Image::thumbnail($this->imageFile, 120, 120);
+
+ $this->assertEquals(120, $img->getSize()->getWidth());
+ $this->assertEquals(120, $img->getSize()->getHeight());
+
+ // THUMBNAIL_INSET mode. Missing thumbnail part is filled with background so dimensions are exactly
+ // the ones specified.
+ $img = Image::thumbnail($this->imageFile, 120, 120, ImageInterface::THUMBNAIL_INSET);
+
+ $this->assertEquals(120, $img->getSize()->getWidth());
+ $this->assertEquals(120, $img->getSize()->getHeight());
+
+ // Height omitted and is calculated based on original image aspect ratio regardless of the mode.
+ $img = Image::thumbnail($this->imageFile, 120, null);
+
+ $this->assertEquals(120, $img->getSize()->getWidth());
+ $this->assertEquals(62, $img->getSize()->getHeight());
+
+ $img = Image::thumbnail($this->imageFile, 120, null, ImageInterface::THUMBNAIL_INSET);
+
+ $this->assertEquals(120, $img->getSize()->getWidth());
+ $this->assertEquals(62, $img->getSize()->getHeight());
+
+ // Width omitted and is calculated based on original image aspect ratio regardless of the mode.
+ $img = Image::thumbnail($this->imageFile, null, 120);
+
+ $this->assertEquals(234, $img->getSize()->getWidth());
+ $this->assertEquals(120, $img->getSize()->getHeight());
+
+ $img = Image::thumbnail($this->imageFile, null, 120, ImageInterface::THUMBNAIL_INSET);
+
+ $this->assertEquals(234, $img->getSize()->getWidth());
+ $this->assertEquals(120, $img->getSize()->getHeight());
+ }
+
+ public function testThumbnailWithUpscaleFlag()
+ {
+ // THUMBNAIL_OUTBOUND mode.
+ $img = Image::thumbnail($this->imageFile, 700, 700, ImageInterface::THUMBNAIL_OUTBOUND | ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+
+ $this->assertEquals(700, $img->getSize()->getWidth());
+ $this->assertEquals(700, $img->getSize()->getHeight());
+
+ // THUMBNAIL_INSET mode. Missing thumbnail part is filled with background so dimensions are exactly
+ // the ones specified.
+ $img = Image::thumbnail($this->imageFile, 700, 700, ImageInterface::THUMBNAIL_INSET | ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+
+ $this->assertEquals(700, $img->getSize()->getWidth());
+ $this->assertEquals(700, $img->getSize()->getHeight());
+
+ // Height omitted and is calculated based on original image aspect ratio regardless of the mode.
+ $img = Image::thumbnail($this->imageFile, 840, null, ImageInterface::THUMBNAIL_OUTBOUND | ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+
+ $this->assertEquals(840, $img->getSize()->getWidth());
+ $this->assertEquals(432, $img->getSize()->getHeight());
+
+ $img = Image::thumbnail($this->imageFile, 840, null, ImageInterface::THUMBNAIL_INSET | ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+
+ $this->assertEquals(840, $img->getSize()->getWidth());
+ $this->assertEquals(432, $img->getSize()->getHeight());
+
+ // Width omitted and is calculated based on original image aspect ratio regardless of the mode.
+ $img = Image::thumbnail($this->imageFile, null, 540, ImageInterface::THUMBNAIL_OUTBOUND | ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+
+ $this->assertEquals(1050, $img->getSize()->getWidth());
+ $this->assertEquals(540, $img->getSize()->getHeight());
+
+ $img = Image::thumbnail($this->imageFile, null, 540, ImageInterface::THUMBNAIL_INSET | ImageInterface::THUMBNAIL_FLAG_UPSCALE);
+
+ $this->assertEquals(1050, $img->getSize()->getWidth());
+ $this->assertEquals(540, $img->getSize()->getHeight());
+ }
+
+ /**
+ * @dataProvider \yiiunit\imagine\providers\Data::resize
+ */
+ public function testResize($width, $height, $keepAspectRatio, $allowUpscaling, $newWidth, $newHeight)
+ {
+ $img = Image::resize($this->imageFile, $width, $height, $keepAspectRatio, $allowUpscaling);
+
+ $this->assertEquals($newWidth, $img->getSize()->getWidth());
+ $this->assertEquals($newHeight, $img->getSize()->getHeight());
+ }
+
+ public function testShouldThrowExceptionOnDriverInvalidArgument()
+ {
+ Image::setImagine(null);
+ Image::$driver = 'fake-driver';
+
+ $this->expectException(InvalidConfigException::class);
+ $this->expectExceptionMessage('Unknown driver: fake-driver');
+ Image::getImagine();
+ }
+
+ public function testIfAutoRotateThrowsException()
+ {
+ $img = Image::thumbnail($this->imageFile, 120, 120);
+ $this->assertInstanceOf('\Imagine\Image\ImageInterface', Image::autorotate($img));
+ }
+
+ abstract protected function isFontTestSupported();
+}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..2541676
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,15 @@
+
+ [350, 350, true, false, 350, 180],
+ 'Height and width set. Image should be resized to exact dimensions.' =>
+ [350, 350, false, false, 350, 350],
+ 'Height omitted and is calculated based on original image aspect ratio.' =>
+ [350, null, true, false, 350, 180],
+ 'Width omitted and is calculated based on original image aspect ratio.' =>
+ [null, 180, true, false, 350, 180],
+ 'Upscaling' =>
+ [800, 800, true, true, 800, 411],
+ ];
+ }
+}