From f3c3e7df563a6f1961279a6db9056c4c95a58d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Wed, 28 Sep 2016 22:00:23 +0200 Subject: [PATCH 1/6] Add two more badges to the readme file --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ac574e5..5c45d78 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Contao image library [![](https://img.shields.io/travis/contao/image/master.svg?style=flat-square)](https://travis-ci.org/contao/image/) [![](https://img.shields.io/scrutinizer/g/contao/image/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/contao/image/) [![](https://img.shields.io/coveralls/contao/image/master.svg?style=flat-square)](https://coveralls.io/github/contao/image) +[![](https://img.shields.io/packagist/v/contao/image.svg?style=flat-square)](https://packagist.org/packages/contao/image) +[![](https://img.shields.io/packagist/dt/contao/image.svg?style=flat-square)](https://packagist.org/packages/contao/image) This library provides methods to resize images based on resize configurations and generates responsive images to be used with `` and `srcset`. It is From 9a3f206c8aa35492951d86b5b0309431b2b00d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Thu, 29 Sep 2016 15:55:06 +0200 Subject: [PATCH 2/6] Fix the AppVeyor setup --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index fd5ca9c..5fdae26 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,13 +4,14 @@ platform: x86 clone_folder: c:\projects\contao-image init: + - SET PATH=c:\php;%PATH% - SET SYMFONY_DEPRECATIONS_HELPER=weak install: - - cinst -y OpenSSL.Light - - SET PATH=C:\Program Files\OpenSSL;%PATH% - - cinst -y php - - cd c:\tools\php + - mkdir c:\php && cd c:\php + - appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.6.25-Win32-VC11-x86.zip + - 7z x php-5.6.25-Win32-VC11-x86.zip -y >nul + - del *.zip - copy php.ini-production php.ini - echo date.timezone="UTC" >> php.ini - echo extension_dir=ext >> php.ini @@ -28,7 +29,6 @@ install: - echo extension=php_sockets.dll >> php.ini - echo extension=php_xmlrpc.dll >> php.ini - echo extension=php_xsl.dll >> php.ini - - SET PATH=c:\tools\php;%PATH% - cd c:\projects\contao-image - php -r "readfile('http://getcomposer.org/installer');" | php - php composer.phar update --no-progress --no-interaction --ansi From 7e3713b0d30e8b12ec63eb9ba9667686c0bb72eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Wed, 16 Nov 2016 21:37:50 +0100 Subject: [PATCH 3/6] Better performance of getDimensions for SVG images --- src/Image.php | 95 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/src/Image.php b/src/Image.php index 268cf8b..481f749 100644 --- a/src/Image.php +++ b/src/Image.php @@ -10,11 +10,17 @@ namespace Contao\Image; +use Contao\ImagineSvg\Image as SvgImage; +use Contao\ImagineSvg\Imagine as SvgImagine; +use DOMDocument; use Imagine\Image\Box; +use Imagine\Image\BoxInterface; use Imagine\Image\ImagineInterface; +use Imagine\Image\Metadata\MetadataBag; use Imagine\Image\Point; use Symfony\Component\Filesystem\Filesystem; use Webmozart\PathUtil\Path; +use XMLReader; /** * Image class. @@ -112,13 +118,27 @@ public function getUrl($rootDir, $prefix = '') public function getDimensions() { if (null === $this->dimensions) { - $size = @getimagesize($this->getPath()); // try native getimagesize() for better performance - if (!empty($size[0]) && !empty($size[1])) { - $this->dimensions = new ImageDimensions(new Box($size[0], $size[1])); + // Try getSvgSize() or native getimagesize() for better performance + if ($this->imagine instanceof SvgImagine) { + $size = $this->getSvgSize(); + + if (null !== $size) { + $this->dimensions = new ImageDimensions($size); + } } else { - $this->dimensions = new ImageDimensions($this->imagine->open($this->getPath())->getSize()); + $size = @getimagesize($this->path); + + if (!empty($size[0]) && !empty($size[1])) { + $this->dimensions = new ImageDimensions(new Box($size[0], $size[1])); + } } + + // Fall back to Imagine + if (null === $this->dimensions) { + $this->dimensions = new ImageDimensions($this->imagine->open($this->path)->getSize()); + } + } return $this->dimensions; @@ -145,4 +165,71 @@ public function setImportantPart(ImportantPartInterface $importantPart = null) return $this; } + + /** + * Reads the SVG image file partially and returns the size of it. + * + * This is faster than reading and parsing the whole SVG file just to get + * the size of it, especially for large files. + * + * @return BoxInterface|null + */ + private function getSvgSize() + { + $size = null; + $reader = new XMLReader(); + + // Enable the entity loader at first to make XMLReader::open() work + // see https://bugs.php.net/bug.php?id=73328 + $disableEntities = libxml_disable_entity_loader(false); + $internalErrors = libxml_use_internal_errors(true); + + if ($reader->open('compress.zlib://'.$this->path, LIBXML_NONET)) { + + // After opening the file disable the entity loader for security reasons + libxml_disable_entity_loader(true); + + $size = $this->getSvgSizeFromReader($reader); + + $reader->close(); + + } + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + libxml_clear_errors(); + + return $size; + } + + /** + * Extracts the SVG image size from the given XMLReader object + * + * @param XMLReader $reader + * + * @return BoxInterface|null + */ + private function getSvgSizeFromReader(XMLReader $reader) + { + // Move the pointer to the first element in the document + while ($reader->read() && $reader->nodeType !== XMLReader::ELEMENT); + + if ($reader->nodeType !== XMLReader::ELEMENT || $reader->name !== 'svg') { + return null; + } + + $document = new DOMDocument(); + $svg = $document->createElement('svg'); + $document->appendChild($svg); + + foreach (['width', 'height', 'viewBox'] as $key) { + if ($value = $reader->getAttribute($key)) { + $svg->setAttribute($key, $value); + } + } + + $image = new SvgImage($document, new MetadataBag()); + + return $image->getSize(); + } } From 7afe9ad4ac9a885a850cbb43b2e1147b6af7289d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Fri, 18 Nov 2016 15:55:31 +0100 Subject: [PATCH 4/6] Check if the compress.zlib wrapper is available before using it --- src/Image.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Image.php b/src/Image.php index 481f749..85d6602 100644 --- a/src/Image.php +++ b/src/Image.php @@ -176,15 +176,27 @@ public function setImportantPart(ImportantPartInterface $importantPart = null) */ private function getSvgSize() { + static $zlibSupport; + + if (null === $zlibSupport) { + $zlibSupport = in_array('compress.zlib', stream_get_wrappers()); + } + $size = null; $reader = new XMLReader(); + $path = $this->path; + + if ($zlibSupport) { + $path = 'compress.zlib://'.$path; + } + // Enable the entity loader at first to make XMLReader::open() work // see https://bugs.php.net/bug.php?id=73328 $disableEntities = libxml_disable_entity_loader(false); $internalErrors = libxml_use_internal_errors(true); - if ($reader->open('compress.zlib://'.$this->path, LIBXML_NONET)) { + if ($reader->open($path, LIBXML_NONET)) { // After opening the file disable the entity loader for security reasons libxml_disable_entity_loader(true); From 10557d9fdcb1f1bf375d3cdfb0b36f75980359f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Fri, 18 Nov 2016 16:39:22 +0100 Subject: [PATCH 5/6] Test getDimensions for partial image files --- tests/ImageTest.php | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/ImageTest.php b/tests/ImageTest.php index 17e019c..23b549a 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -13,6 +13,7 @@ use Contao\Image\Image; use Contao\Image\ImageDimensions; use Contao\Image\ImportantPart; +use Imagine\Gd\Imagine as GdImagine; use Imagine\Image\Box; use Imagine\Image\ImagineInterface; use Imagine\Image\Point; @@ -25,6 +26,29 @@ */ class ImageTest extends \PHPUnit_Framework_TestCase { + /** + * @var string + */ + private $rootDir; + + /** + * {@inheritdoc} + */ + public function setUp() + { + $this->rootDir = __DIR__.'/tmp'; + } + + /** + * {@inheritdoc} + */ + public function tearDown() + { + if (file_exists($this->rootDir)) { + (new Filesystem())->remove($this->rootDir); + } + } + /** * Tests the object instantiation. */ @@ -183,6 +207,49 @@ public function testGetDimensions() $this->assertEquals(new ImageDimensions(new Box(100, 100)), $image->getDimensions()); } + /** + * Tests the getDimensions() method determines the dimensions without + * Imagine and by only reading the file partially. + */ + public function testGetDimensionsPartialFile() + { + if (!is_dir($this->rootDir)) { + mkdir($this->rootDir, 0777, true); + } + + $image = (new GdImagine()) + ->create(new Box(1000, 1000)) + ->get('jpg') + ; + + // Only store the first 500 bytes of the image + file_put_contents($this->rootDir.'/dummy.jpg', substr($image, 0, 500)); + + $image = $this->createImage($this->rootDir.'/dummy.jpg'); + + $this->assertEquals(new ImageDimensions(new Box(1000, 1000)), $image->getDimensions()); + } + + /** + * Tests the getDimensions() method determines the SVG dimensions without + * Imagine and by only reading the file partially. + */ + public function testGetDimensionsPartialFileSvg() + { + $imagine = $this->getMock('Contao\ImagineSvg\Imagine'); + + if (!is_dir($this->rootDir)) { + mkdir($this->rootDir, 0777, true); + } + + // Only store a partial SVG file without an end tag + file_put_contents($this->rootDir.'/dummy.svg', ''); + + $image = $this->createImage($this->rootDir.'/dummy.svg', $imagine); + + $this->assertEquals(new ImageDimensions(new Box(1000, 1000)), $image->getDimensions()); + } + /** * Tests the getImportantPart() method. */ From 6e9e7dfa16702dfd21685fa3757c5640bd2be4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Auswo=CC=88ger?= Date: Sun, 20 Nov 2016 14:15:01 +0100 Subject: [PATCH 6/6] Test getDimensions with invalid SVG file --- tests/ImageTest.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/ImageTest.php b/tests/ImageTest.php index 23b549a..b7fe7cd 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -13,6 +13,7 @@ use Contao\Image\Image; use Contao\Image\ImageDimensions; use Contao\Image\ImportantPart; +use Exception; use Imagine\Gd\Imagine as GdImagine; use Imagine\Image\Box; use Imagine\Image\ImagineInterface; @@ -250,6 +251,31 @@ public function testGetDimensionsPartialFileSvg() $this->assertEquals(new ImageDimensions(new Box(1000, 1000)), $image->getDimensions()); } + /** + * Tests the getDimensions() method handles invalid SVG images. + */ + public function testGetDimensionsInvalidSvg() + { + if (!is_dir($this->rootDir)) { + mkdir($this->rootDir, 0777, true); + } + + file_put_contents($this->rootDir.'/dummy.svg', ''); + + $imagine = $this->getMock('Contao\ImagineSvg\Imagine'); + + $imagine + ->method('open') + ->willThrowException(new Exception) + ; + + $image = $this->createImage($this->rootDir.'/dummy.svg', $imagine); + + $this->setExpectedException('Exception'); + + $image->getDimensions(); + } + /** * Tests the getImportantPart() method. */