1
0
mirror of https://github.com/Intervention/image.git synced 2025-09-03 10:53:01 +02:00

Merge remote-tracking branch 'upstream/master' into fix/minetype

This commit is contained in:
tchiotludo
2017-09-04 12:36:06 +02:00
15 changed files with 201 additions and 49 deletions

View File

@@ -1,5 +1,9 @@
language: php language: php
sudo: false
dist: trusty
php: php:
- 5.4 - 5.4
- 5.5 - 5.5
@@ -14,15 +18,9 @@ matrix:
- php: nightly - php: nightly
- php: hhvm - php: hhvm
before_install:
- sudo add-apt-repository -y ppa:moti-p/cc
- sudo apt-get update
- sudo apt-get -y --reinstall install imagemagick
- yes | pecl install imagick-beta
- if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.4" ]]; then echo "extension = imagick.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
before_script: before_script:
- composer self-update - printf "\n" | pecl install imagick
- composer install --prefer-source --no-interaction --dev - composer self-update || true
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader
script: vendor/bin/phpunit script: vendor/bin/phpunit

View File

@@ -2,7 +2,9 @@
Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy **Laravel** integration. Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy **Laravel** integration.
[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image)
[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image) [![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image)
[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats)
## Requirements ## Requirements
@@ -18,7 +20,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin
- [Installation](http://image.intervention.io/getting_started/installation) - [Installation](http://image.intervention.io/getting_started/installation)
- [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel) - [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel)
- [Official Documentation](http://image.intervention.io/) - [Basic Usage](http://image.intervention.io/use/basics)
## Code Examples ## Code Examples
@@ -36,7 +38,7 @@ $img->insert('public/watermark.png');
$img->save('public/bar.jpg'); $img->save('public/bar.jpg');
``` ```
Refer to the [documentation](http://image.intervention.io/) to learn more about Intervention Image. Refer to the [official documentation](http://image.intervention.io/) to learn more about Intervention Image.
## Contributing ## Contributing
@@ -50,4 +52,4 @@ Contributions to the Intervention Image library are welcome. Please note the fol
Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT). Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT).
Copyright 2014 [Oliver Vogel](http://olivervogel.net/) Copyright 2017 [Oliver Vogel](http://olivervogel.com/)

View File

@@ -7,8 +7,8 @@
"authors": [ "authors": [
{ {
"name": "Oliver Vogel", "name": "Oliver Vogel",
"email": "oliver@olivervogel.net", "email": "oliver@olivervogel.com",
"homepage": "http://olivervogel.net/" "homepage": "http://olivervogel.com/"
} }
], ],
"require": { "require": {
@@ -17,7 +17,7 @@
"guzzlehttp/psr7": "~1.1" "guzzlehttp/psr7": "~1.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "3.*", "phpunit/phpunit": "^4.8 || ^5.7",
"mockery/mockery": "~0.9.2" "mockery/mockery": "~0.9.2"
}, },
"suggest": { "suggest": {
@@ -33,6 +33,14 @@
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.3-dev" "dev-master": "2.3-dev"
},
"laravel": {
"providers": [
"Intervention\\Image\\ImageServiceProvider"
],
"aliases": {
"Image": "Intervention\\Image\\Facades\\Image"
}
} }
}, },
"minimum-stability": "stable" "minimum-stability": "stable"

View File

@@ -2,6 +2,9 @@
namespace Intervention\Image; namespace Intervention\Image;
use GuzzleHttp\Psr7\Stream;
use Psr\Http\Message\StreamInterface;
abstract class AbstractDecoder abstract class AbstractDecoder
{ {
/** /**
@@ -85,22 +88,35 @@ abstract class AbstractDecoder
/** /**
* Init from given stream * Init from given stream
* *
* @param $stream * @param StreamInterface|resource $stream
* @return \Intervention\Image\Image * @return \Intervention\Image\Image
*/ */
public function initFromStream($stream) public function initFromStream($stream)
{ {
$offset = ftell($stream); if (!$stream instanceof StreamInterface) {
$shouldAndCanSeek = $offset !== 0 && $this->isStreamSeekable($stream); $stream = new Stream($stream);
if ($shouldAndCanSeek) {
rewind($stream);
} }
$data = @stream_get_contents($stream); try {
$offset = $stream->tell();
} catch (\RuntimeException $e) {
$offset = 0;
}
$shouldAndCanSeek = $offset !== 0 && $stream->isSeekable();
if ($shouldAndCanSeek) { if ($shouldAndCanSeek) {
fseek($stream, $offset); $stream->rewind();
}
try {
$data = $stream->getContents();
} catch (\RuntimeException $e) {
$data = null;
}
if ($shouldAndCanSeek) {
$stream->seek($offset);
} }
if ($data) { if ($data) {
@@ -112,18 +128,6 @@ abstract class AbstractDecoder
); );
} }
/**
* Checks if we can move the pointer for this stream
*
* @param resource $stream
* @return bool
*/
private function isStreamSeekable($stream)
{
$metadata = stream_get_meta_data($stream);
return $metadata['seekable'];
}
/** /**
* Determines if current source data is GD resource * Determines if current source data is GD resource
* *
@@ -213,6 +217,7 @@ abstract class AbstractDecoder
*/ */
public function isStream() public function isStream()
{ {
if ($this->data instanceof StreamInterface) return true;
if (!is_resource($this->data)) return false; if (!is_resource($this->data)) return false;
if (get_resource_type($this->data) !== 'stream') return false; if (get_resource_type($this->data) !== 'stream') return false;
@@ -332,6 +337,7 @@ abstract class AbstractDecoder
case $this->isFilePath(): case $this->isFilePath():
return $this->initFromPath($this->data); return $this->initFromPath($this->data);
// isBase64 has to be after isFilePath to prevent false positives
case $this->isBase64(): case $this->isBase64():
return $this->initFromBinary(base64_decode($this->data)); return $this->initFromBinary(base64_decode($this->data));

View File

@@ -74,6 +74,13 @@ abstract class AbstractEncoder
*/ */
abstract protected function processIco(); abstract protected function processIco();
/**
* Processes and returns image as WebP encoded string
*
* @return string
*/
abstract protected function processWebp();
/** /**
* Process a given image * Process a given image
* *
@@ -145,6 +152,12 @@ abstract class AbstractEncoder
case 'image/vnd.adobe.photoshop': case 'image/vnd.adobe.photoshop':
$this->result = $this->processPsd(); $this->result = $this->processPsd();
break; break;
case 'webp':
case 'image/webp':
case 'image/x-webp':
$this->result = $this->processWebp();
break;
default: default:
throw new \Intervention\Image\Exception\NotSupportedException( throw new \Intervention\Image\Exception\NotSupportedException(

View File

@@ -50,6 +50,7 @@ class Constraint
/** /**
* Fix the given argument in current constraint * Fix the given argument in current constraint
*
* @param integer $type * @param integer $type
* @return void * @return void
*/ */

View File

@@ -14,37 +14,54 @@ class Decoder extends \Intervention\Image\AbstractDecoder
*/ */
public function initFromPath($path) public function initFromPath($path)
{ {
$info = @getimagesize($path); if ( ! file_exists($path)) {
if ($info === false) {
throw new \Intervention\Image\Exception\NotReadableException( throw new \Intervention\Image\Exception\NotReadableException(
"Unable to read image from file ({$path})." "Unable to find file ({$path})."
); );
} }
// get mime type of file
$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
// define core // define core
switch ($info[2]) { switch (strtolower($mime)) {
case IMAGETYPE_PNG: case 'image/png':
case 'image/x-png':
$core = @imagecreatefrompng($path); $core = @imagecreatefrompng($path);
break; break;
case IMAGETYPE_JPEG: case 'image/jpg':
$core = @imagecreatefromjpeg($path); case 'image/jpeg':
case 'image/pjpeg':
$core = @imagecreatefromjpeg($path);
if (!$core) {
$core= @imagecreatefromstring(file_get_contents($path));
}
break; break;
case IMAGETYPE_GIF: case 'image/gif':
$core = @imagecreatefromgif($path); $core = @imagecreatefromgif($path);
break; break;
case 'image/webp':
case 'image/x-webp':
if ( ! function_exists('imagecreatefromwebp')) {
throw new \Intervention\Image\Exception\NotReadableException(
"Unsupported image type. GD/PHP installation does not support WebP format."
);
}
$core = @imagecreatefromwebp($path);
break;
default: default:
throw new \Intervention\Image\Exception\NotReadableException( throw new \Intervention\Image\Exception\NotReadableException(
"Unable to read image type. GD driver is only able to decode JPG, PNG or GIF files." "Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files."
); );
} }
if (empty($core)) { if (empty($core)) {
throw new \Intervention\Image\Exception\NotReadableException( throw new \Intervention\Image\Exception\NotReadableException(
"Unable to read image from file ({$path})." "Unable to decode image from file ({$path})."
); );
} }
@@ -52,7 +69,7 @@ class Decoder extends \Intervention\Image\AbstractDecoder
// build image // build image
$image = $this->initFromGdResource($core); $image = $this->initFromGdResource($core);
$image->mime = $info['mime']; $image->mime = $mime;
$image->setFileInfoFromPath($path); $image->setFileInfoFromPath($path);
return $image; return $image;

View File

@@ -55,6 +55,23 @@ class Encoder extends \Intervention\Image\AbstractEncoder
return $buffer; return $buffer;
} }
protected function processWebp()
{
if ( ! function_exists('imagewebp')) {
throw new \Intervention\Image\Exception\NotSupportedException(
"Webp format is not supported by PHP installation."
);
}
ob_start();
imagewebp($this->image->getCore(), null, $this->quality);
$this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp';
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
/** /**
* Processes and returns encoded image as TIFF string * Processes and returns encoded image as TIFF string
* *

View File

@@ -40,7 +40,9 @@ class ImageServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
return $this->provider->boot(); if (method_exists($this->provider, 'boot')) {
return $this->provider->boot();
}
} }
/** /**

View File

@@ -77,7 +77,7 @@ class ImageServiceProviderLaravel5 extends ServiceProvider
// imagecache route // imagecache route
if (is_string(config('imagecache.route'))) { if (is_string(config('imagecache.route'))) {
$filename_pattern = '[ \w\\.\\/\\-\\@]+'; $filename_pattern = '[ \w\\.\\/\\-\\@\(\)]+';
// route to access template applied image file // route to access template applied image file
$app['router']->get(config('imagecache.route').'/{template}/{filename}', array( $app['router']->get(config('imagecache.route').'/{template}/{filename}', array(

View File

@@ -70,6 +70,28 @@ class Encoder extends \Intervention\Image\AbstractEncoder
return $imagick->getImagesBlob(); return $imagick->getImagesBlob();
} }
protected function processWebp()
{
if ( ! \Imagick::queryFormats('WEBP')) {
throw new \Intervention\Image\Exception\NotSupportedException(
"Webp format is not supported by Imagick installation."
);
}
$format = 'webp';
$compression = \Imagick::COMPRESSION_JPEG;
$imagick = $this->image->getCore();
$imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE);
$imagick->setFormat($format);
$imagick->setImageFormat($format);
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);
$imagick->setImageCompressionQuality($this->quality);
return $imagick->getImagesBlob();
}
/** /**
* Processes and returns encoded image as TIFF string * Processes and returns encoded image as TIFF string
* *

View File

@@ -61,6 +61,9 @@ class AbstractDecoderTest extends PHPUnit_Framework_TestCase
$source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r')); $source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r'));
$this->assertTrue($source->isStream()); $this->assertTrue($source->isStream());
$source = $this->getTestDecoder(new \GuzzleHttp\Psr7\Stream(fopen(__DIR__ . '/images/test.jpg', 'r')));
$this->assertTrue($source->isStream());
$source = $this->getTestDecoder(null); $source = $this->getTestDecoder(null);
$this->assertFalse($source->isStream()); $this->assertFalse($source->isStream());
} }

View File

@@ -46,6 +46,20 @@ class EncoderTest extends PHPUnit_Framework_TestCase
$this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result)); $this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result));
} }
public function testProcessWebpGd()
{
if (function_exists('imagewebp')) {
$core = imagecreatefromjpeg(__DIR__.'/images/test.jpg');
$encoder = new GdEncoder;
$image = Mockery::mock('\Intervention\Image\Image');
$image->shouldReceive('getCore')->once()->andReturn($core);
$image->shouldReceive('setEncoded')->once()->andReturn($image);
$img = $encoder->process($image, 'webp', 90);
$this->assertInstanceOf('Intervention\Image\Image', $img);
$this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result));
}
}
/** /**
* @expectedException \Intervention\Image\Exception\NotSupportedException * @expectedException \Intervention\Image\Exception\NotSupportedException
*/ */
@@ -155,6 +169,16 @@ class EncoderTest extends PHPUnit_Framework_TestCase
$this->assertEquals('mock-gif', $encoder->result); $this->assertEquals('mock-gif', $encoder->result);
} }
/**
* @expectedException \Intervention\Image\Exception\NotSupportedException
*/
public function testProcessWebpImagick()
{
$encoder = new ImagickEncoder;
$image = Mockery::mock('\Intervention\Image\Image');
$img = $encoder->process($image, 'webp', 90);
}
public function testProcessTiffImagick() public function testProcessTiffImagick()
{ {
$core = $this->getImagickMock('tiff'); $core = $this->getImagickMock('tiff');

View File

@@ -28,6 +28,14 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
$this->manager()->make('tests/images/broken.png'); $this->manager()->make('tests/images/broken.png');
} }
/**
* @expectedException \Intervention\Image\Exception\NotReadableException
*/
public function testMakeFromNotExisting()
{
$this->manager()->make('tests/images/not_existing.png');
}
public function testMakeFromString() public function testMakeFromString()
{ {
$str = file_get_contents('tests/images/circle.png'); $str = file_get_contents('tests/images/circle.png');
@@ -75,6 +83,22 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
$this->assertEquals(10, $img->getHeight()); $this->assertEquals(10, $img->getHeight());
} }
public function testMakeFromWebp()
{
if (function_exists('imagecreatefromwebp')) {
$img = $this->manager()->make('tests/images/test.webp');
$this->assertInstanceOf('Intervention\Image\Image', $img);
$this->assertInternalType('resource', $img->getCore());
$this->assertEquals(16, $img->getWidth());
$this->assertEquals(16, $img->getHeight());
$this->assertEquals('image/webp', $img->mime);
$this->assertEquals('tests/images', $img->dirname);
$this->assertEquals('test.webp', $img->basename);
$this->assertEquals('webp', $img->extension);
$this->assertEquals('test', $img->filename);
}
}
public function testCanvas() public function testCanvas()
{ {
$img = $this->manager()->canvas(30, 20); $img = $this->manager()->canvas(30, 20);
@@ -1474,6 +1498,15 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
$this->assertInternalType('resource', imagecreatefromstring($img->encoded)); $this->assertInternalType('resource', imagecreatefromstring($img->encoded));
} }
public function testEncodeWebp()
{
if (function_exists('imagewebp')) {
$img = $this->manager()->make('tests/images/trim.png');
$data = (string) $img->encode('webp');
$this->assertEquals('image/webp; charset=binary', $this->getMime($data));
}
}
public function testEncodeDataUrl() public function testEncodeDataUrl()
{ {
$img = $this->manager()->make('tests/images/trim.png'); $img = $this->manager()->make('tests/images/trim.png');
@@ -1643,4 +1676,10 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
'driver' => 'gd' 'driver' => 'gd'
)); ));
} }
private function getMime($data)
{
$finfo = new finfo(FILEINFO_MIME);
return $finfo->buffer($data);
}
} }

BIN
tests/images/test.webp Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B