diff --git a/CHANGELOG.md b/CHANGELOG.md index 1301c93..9c38bc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.34 + +- Deprecate calling `Crop::getCroppedImage()` and `Crop::getCroppedThumbnail()` without explicitly passing `true` for the $applyRotation parameter. It will always be `true` in 3.0 + ## 2.30 - Ensure compatibility with PHP 8.5 diff --git a/composer.json b/composer.json index fbf2290..52626a8 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "intervention/image": "^2.5", "symfony/config": "^5.4|^6.0|^7.0|^8.0", "symfony/dependency-injection": "^5.4|^6.0|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/form": "^5.4|^6.0|^7.0|^8.0", "symfony/http-kernel": "^5.4|^6.0|^7.0|^8.0", "symfony/options-resolver": "^5.4|^6.0|^7.0|^8.0", diff --git a/src/Model/Crop.php b/src/Model/Crop.php index 67ff36b..4052658 100644 --- a/src/Model/Crop.php +++ b/src/Model/Crop.php @@ -46,6 +46,7 @@ class Crop 'y' => 0, 'width' => null, 'height' => null, + 'rotate' => 0, ]; public function __construct(ImageManager $imageManager, string $filename) @@ -54,8 +55,11 @@ class Crop $this->filename = $filename; } - public function getCroppedThumbnail(int $maxWidth, int $maxHeight, string $format = 'jpg', int $quality = 80): string + public function getCroppedThumbnail(int $maxWidth, int $maxHeight, string $format = 'jpg', int $quality = 80, bool $applyRotation = false): string { + if (\func_num_args() < 5 || false === $applyRotation) { + trigger_deprecation('symfony/ux-cropperjs', '2.34', 'Not passing "true" to the "$applyRotation" argument of "%s()" is deprecated. Rotation will be applied by default in Symfony UX 3.0.', __METHOD__); + } $image = $this->createCroppedImage(); $image->resize($maxWidth, $maxHeight, static function ($constraint) { @@ -63,13 +67,20 @@ class Crop $constraint->upsize(); }); + if ($applyRotation && !empty($this->options['rotate'])) { + $image->rotate(-1 * $this->options['rotate']); + } + $image->encode($format, $quality); return $image->getEncoded(); } - public function getCroppedImage(string $format = 'jpg', int $quality = 80): string + public function getCroppedImage(string $format = 'jpg', int $quality = 80, bool $applyRotation = false): string { + if (\func_num_args() < 3 || false === $applyRotation) { + trigger_deprecation('symfony/ux-cropperjs', '2.34', 'Not passing "true" to the "$applyRotation" argument of "%s()" is deprecated. Rotation will be applied by default in Symfony UX 3.0.', __METHOD__); + } $image = $this->createCroppedImage(); // Max size @@ -80,6 +91,10 @@ class Crop }); } + if ($applyRotation && !empty($this->options['rotate'])) { + $image->rotate(-1 * $this->options['rotate']); + } + $image->encode($format, $quality); return $image->getEncoded(); diff --git a/tests/Model/CropTest.php b/tests/Model/CropTest.php new file mode 100644 index 0000000..ae7a116 --- /dev/null +++ b/tests/Model/CropTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\Cropperjs\Tests\Model; + +use Intervention\Image\ImageManager; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; +use Symfony\UX\Cropperjs\Model\Crop; + +/** + * @internal + */ +class CropTest extends TestCase +{ + use ExpectDeprecationTrait; + private string $testImagePath; + + protected function setUp(): void + { + $this->testImagePath = tempnam(sys_get_temp_dir(), 'crop_test_').'.jpg'; + ob_start(); + imagejpeg(imagecreatetruecolor(200, 100), $this->testImagePath); + ob_end_clean(); + } + + protected function tearDown(): void + { + if (file_exists($this->testImagePath)) { + unlink($this->testImagePath); + } + } + + private function createCrop(int $rotate = 0): Crop + { + $imageManager = new ImageManager(); + $crop = new Crop($imageManager, $this->testImagePath); + + $crop->setOptions(json_encode([ + 'width' => null, + 'height' => null, + 'rotate' => $rotate, + ])); + + return $crop; + } + + public function testGetCroppedImageWithRotation() + { + $crop = $this->createCrop(rotate: 90); + + $result = $crop->getCroppedImage(applyRotation: true); + + $image = imagecreatefromstring($result); + $this->assertSame(100, imagesx($image)); + $this->assertSame(200, imagesy($image)); + } + + public function testGetCroppedImageWithoutRotation() + { + $crop = $this->createCrop(rotate: 0); + + $result = $crop->getCroppedImage(applyRotation: true); + + $image = imagecreatefromstring($result); + $this->assertSame(200, imagesx($image)); + $this->assertSame(100, imagesy($image)); + } + + /** + * @group legacy + */ + public function testGetCroppedImageWithApplyRotationFalseTriggersDeprecation() + { + $crop = $this->createCrop(); + + $this->expectDeprecation('Since symfony/ux-cropperjs 2.34: Not passing "true" to the "$applyRotation" argument of "Symfony\UX\Cropperjs\Model\Crop::getCroppedImage()" is deprecated. Rotation will be applied by default in Symfony UX 3.0.'); + + $crop->getCroppedImage(applyRotation: false); + } + + public function testGetCroppedThumbnailWithRotation() + { + $crop = $this->createCrop(rotate: 90); + + $result = $crop->getCroppedThumbnail(200, 200, applyRotation: true); + + $image = imagecreatefromstring($result); + $this->assertSame(100, imagesx($image)); + $this->assertSame(200, imagesy($image)); + } + + public function testGetCroppedThumbnailWithoutRotation() + { + $crop = $this->createCrop(rotate: 0); + + $result = $crop->getCroppedThumbnail(200, 200, applyRotation: true); + + $image = imagecreatefromstring($result); + $this->assertSame(200, imagesx($image)); + $this->assertSame(100, imagesy($image)); + } + + /** + * @group legacy + */ + public function testGetCroppedThumbnailWithApplyRotationFalseTriggersDeprecation() + { + $crop = $this->createCrop(); + + $this->expectDeprecation('Since symfony/ux-cropperjs 2.34: Not passing "true" to the "$applyRotation" argument of "Symfony\UX\Cropperjs\Model\Crop::getCroppedThumbnail()" is deprecated. Rotation will be applied by default in Symfony UX 3.0.'); + + $crop->getCroppedThumbnail(200, 200, applyRotation: false); + } +}