diff --git a/composer.json b/composer.json index 7b1c938..2241d18 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ ], "require": { - "bolt/filesystem": "^2.0", + "bolt/filesystem": "^2.1", + "contao/imagine-svg": "^0.1.2", "doctrine/cache": "^1.4", "ext-gd": "*", "php": "^5.5.9 || ^7.0" diff --git a/src/Creator.php b/src/Creator.php index 5d9d213..f24014f 100644 --- a/src/Creator.php +++ b/src/Creator.php @@ -2,7 +2,10 @@ namespace Bolt\Thumbs; use Bolt\Filesystem\Exception\IOException; +use Contao\ImagineSvg\Imagine as SvgImagine; use Exception; +use Imagine\Image\Box; +use Imagine\Exception\RuntimeException as ImagineRuntimeException; use RuntimeException; /** @@ -13,6 +16,8 @@ */ class Creator implements CreatorInterface { + /** @var SvgImagine */ + protected $svgImagine; /** @var bool */ protected $limitUpscaling; /** @var Color */ @@ -21,11 +26,13 @@ class Creator implements CreatorInterface /** * Creator constructor. * - * @param bool $limitUpscaling - * @param Color $background + * @param bool $limitUpscaling + * @param SvgImagine $svgImagine + * @param Color $background */ - public function __construct($limitUpscaling = true, Color $background = null) + public function __construct($limitUpscaling = true, SvgImagine $svgImagine = null, Color $background = null) { + $this->svgImagine = $svgImagine ?: new SvgImagine(); $this->limitUpscaling = (bool) $limitUpscaling; $this->background = $background ?: Color::white(); } @@ -131,6 +138,25 @@ protected function resize(Transaction $transaction) $fit = $transaction->getAction() === Action::FIT; $border = $transaction->getAction() === Action::BORDER; + if ($transaction->getSrcImage()->getMimeType() === 'image/svg+xml') { + try { + return $this->resizeSvg($transaction); + } catch (ImagineRuntimeException $e) { + // If the image is the fallback image throw exception + if ($transaction->getSrcImage()->getFullPath() === $transaction->getErrorImage()->getFullPath()) { + throw new RuntimeException( + 'There was an error with the thumbnail image requested and additionally the fallback image could not be displayed.', + 0, + $e + ); + } + + // Fallback to error image + $transaction->setErrorImage($transaction->getSrcImage()); + return $this->create($transaction); + } + } + try { $img = ImageResource::createFromString($transaction->getSrcImage()->read()); } catch (Exception $e) { @@ -186,4 +212,22 @@ protected function resize(Transaction $transaction) return $img->toString(); } + + /** + * Resize SVG image. + * + * @param Transaction $transaction + * + * @return string + */ + protected function resizeSvg(Transaction $transaction) + { + $image = $this->svgImagine->load($transaction->getSrcImage()->read()); + + $target = $transaction->getTarget(); + + $image->resize(new Box($target->getWidth(), $target->getHeight())); + + return $image->get('svg'); + } } diff --git a/src/ImageResource.php b/src/ImageResource.php index bd0db75..e73469b 100644 --- a/src/ImageResource.php +++ b/src/ImageResource.php @@ -18,7 +18,7 @@ class ImageResource { /** @var resource */ protected $resource; - /** @var Image\Type */ + /** @var Image\TypeInterface */ protected $type; /** @var Image\Info */ protected $info; @@ -29,15 +29,15 @@ class ImageResource protected static $normalizeJpegOrientation = true; /** - * ImageResource constructor. + * Constructor. * * Either type or info need to be provided. * - * @param resource $resource A GD resource - * @param Image\Type $type Image type - * @param Image\Info $info Image info + * @param resource $resource A GD resource + * @param Image\TypeInterface $type Image type + * @param Image\Info $info Image info */ - public function __construct($resource, Image\Type $type = null, Image\Info $info = null) + public function __construct($resource, Image\TypeInterface $type = null, Image\Info $info = null) { if (!is_resource($resource) || !get_resource_type($resource) === 'gd') { throw new InvalidArgumentException('Given resource must be a GD resource'); @@ -106,12 +106,12 @@ public static function createFromString($data) /** * Creates a new image given the width and height. * - * @param Dimensions $dimensions Image dimensions - * @param Image\Type $type Type of image + * @param Dimensions $dimensions Image dimensions + * @param Image\TypeInterface $type Type of image * * @return ImageResource */ - public static function createNew(Dimensions $dimensions, Image\Type $type) + public static function createNew(Dimensions $dimensions, Image\TypeInterface $type) { $resource = imagecreatetruecolor($dimensions->getWidth(), $dimensions->getHeight()); if ($resource === false) { @@ -148,7 +148,7 @@ public function getDimensions() /** * Returns the image type. * - * @return Image\Type + * @return Image\TypeInterface */ public function getType() { diff --git a/src/Responder.php b/src/Responder.php index d0bd445..d477dc5 100644 --- a/src/Responder.php +++ b/src/Responder.php @@ -73,7 +73,7 @@ public function respond(Transaction $transaction) $this->saveStaticThumbnail($transaction->getRequestPath(), $thumbnail); // Return thumbnail - return new Thumbnail($image, $thumbnail); + return new Thumbnail($transaction->getSrcImage(), $thumbnail); } /** diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9aad39a..604041c 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -2,6 +2,7 @@ namespace Bolt\Thumbs; +use Contao\ImagineSvg\Imagine as SvgImagine; use Silex\Application; use Silex\ServiceProviderInterface; @@ -33,7 +34,11 @@ public function register(Application $app) }); $app['thumbnails.creator'] = $app->share(function ($app) { - return new Creator($app['thumbnails.limit_upscaling']); + return new Creator($app['thumbnails.limit_upscaling'], $app['imagine.svg']); + }); + + $app['imagine.svg'] = $app->share(function () { + return new SvgImagine(); }); $app['thumbnails.finder'] = $app->share(function ($app) { diff --git a/tests/CreatorTest.php b/tests/CreatorTest.php index 9a8ae72..7707a48 100644 --- a/tests/CreatorTest.php +++ b/tests/CreatorTest.php @@ -20,6 +20,8 @@ class CreatorTest extends \PHPUnit_Framework_TestCase protected $landscapeImage; /** @var Image 427x640 */ protected $portraitImage; + /** @var Image */ + protected $svgImage; public function setup() { @@ -27,6 +29,7 @@ public function setup() $this->logoJpg = $this->fs->getImage('generic-logo.jpg'); $this->landscapeImage = $this->fs->getImage('samples/sample1.jpg'); $this->portraitImage = $this->fs->getImage('samples/sample2.jpg'); + $this->svgImage = $this->fs->getImage('samples/nut.svg'); } /** @@ -171,6 +174,15 @@ public function testPortraitBorder() $this->assertDimensions($expected, $result); } + public function testSvg() + { + $transaction = new Transaction($this->portraitImage, Action::RESIZE, new Dimensions(200, 500)); + + $result = (new Creator())->create($transaction); + + $this->assertDimensions(new Dimensions(200, 299), $result); + } + /** * @param Dimensions $expected * @param Dimensions|string $actual diff --git a/tests/images/samples/nut.svg b/tests/images/samples/nut.svg new file mode 100644 index 0000000..89704c1 --- /dev/null +++ b/tests/images/samples/nut.svg @@ -0,0 +1,6 @@ + + + + + +