Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better performance for reading dimensions of SVG images #23

Merged
merged 6 commits into from
Nov 20, 2016
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<picture>` and `srcset`. It is
Expand Down
10 changes: 5 additions & 5 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
107 changes: 103 additions & 4 deletions src/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -145,4 +165,83 @@ 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()
{
static $zlibSupport;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a static class property such as self::$zlibSupport?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think so, because it is only used in this method and nowhere else.
The exception would be if our styleguide doesn’t allow static function variables at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, our style guide does not forbid to use them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I keep it as is then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if you prefer. 😄


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($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();
}
}