mirror of
https://github.com/Intervention/image.git
synced 2025-09-03 02:42:45 +02:00
Merge remote-tracking branch 'upstream/master' into fix/minetype
This commit is contained in:
16
.travis.yml
16
.travis.yml
@@ -1,5 +1,9 @@
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
dist: trusty
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
@@ -14,15 +18,9 @@ matrix:
|
||||
- php: nightly
|
||||
- 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:
|
||||
- composer self-update
|
||||
- composer install --prefer-source --no-interaction --dev
|
||||
- printf "\n" | pecl install imagick
|
||||
- composer self-update || true
|
||||
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader
|
||||
|
||||
script: vendor/bin/phpunit
|
||||
|
@@ -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.
|
||||
|
||||
[](https://packagist.org/packages/intervention/image)
|
||||
[](https://travis-ci.org/Intervention/image)
|
||||
[](https://packagist.org/packages/intervention/image/stats)
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -18,7 +20,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin
|
||||
|
||||
- [Installation](http://image.intervention.io/getting_started/installation)
|
||||
- [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
|
||||
|
||||
@@ -36,7 +38,7 @@ $img->insert('public/watermark.png');
|
||||
$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
|
||||
|
||||
@@ -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).
|
||||
|
||||
Copyright 2014 [Oliver Vogel](http://olivervogel.net/)
|
||||
Copyright 2017 [Oliver Vogel](http://olivervogel.com/)
|
||||
|
@@ -7,8 +7,8 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Oliver Vogel",
|
||||
"email": "oliver@olivervogel.net",
|
||||
"homepage": "http://olivervogel.net/"
|
||||
"email": "oliver@olivervogel.com",
|
||||
"homepage": "http://olivervogel.com/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"guzzlehttp/psr7": "~1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "3.*",
|
||||
"phpunit/phpunit": "^4.8 || ^5.7",
|
||||
"mockery/mockery": "~0.9.2"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -33,6 +33,14 @@
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Intervention\\Image\\ImageServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Image": "Intervention\\Image\\Facades\\Image"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable"
|
||||
|
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace Intervention\Image;
|
||||
|
||||
use GuzzleHttp\Psr7\Stream;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
abstract class AbstractDecoder
|
||||
{
|
||||
/**
|
||||
@@ -85,22 +88,35 @@ abstract class AbstractDecoder
|
||||
/**
|
||||
* Init from given stream
|
||||
*
|
||||
* @param $stream
|
||||
* @param StreamInterface|resource $stream
|
||||
* @return \Intervention\Image\Image
|
||||
*/
|
||||
public function initFromStream($stream)
|
||||
{
|
||||
$offset = ftell($stream);
|
||||
$shouldAndCanSeek = $offset !== 0 && $this->isStreamSeekable($stream);
|
||||
|
||||
if ($shouldAndCanSeek) {
|
||||
rewind($stream);
|
||||
if (!$stream instanceof StreamInterface) {
|
||||
$stream = new Stream($stream);
|
||||
}
|
||||
|
||||
$data = @stream_get_contents($stream);
|
||||
try {
|
||||
$offset = $stream->tell();
|
||||
} catch (\RuntimeException $e) {
|
||||
$offset = 0;
|
||||
}
|
||||
|
||||
$shouldAndCanSeek = $offset !== 0 && $stream->isSeekable();
|
||||
|
||||
if ($shouldAndCanSeek) {
|
||||
fseek($stream, $offset);
|
||||
$stream->rewind();
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $stream->getContents();
|
||||
} catch (\RuntimeException $e) {
|
||||
$data = null;
|
||||
}
|
||||
|
||||
if ($shouldAndCanSeek) {
|
||||
$stream->seek($offset);
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
@@ -213,6 +217,7 @@ abstract class AbstractDecoder
|
||||
*/
|
||||
public function isStream()
|
||||
{
|
||||
if ($this->data instanceof StreamInterface) return true;
|
||||
if (!is_resource($this->data)) return false;
|
||||
if (get_resource_type($this->data) !== 'stream') return false;
|
||||
|
||||
@@ -332,6 +337,7 @@ abstract class AbstractDecoder
|
||||
case $this->isFilePath():
|
||||
return $this->initFromPath($this->data);
|
||||
|
||||
// isBase64 has to be after isFilePath to prevent false positives
|
||||
case $this->isBase64():
|
||||
return $this->initFromBinary(base64_decode($this->data));
|
||||
|
||||
|
@@ -74,6 +74,13 @@ abstract class AbstractEncoder
|
||||
*/
|
||||
abstract protected function processIco();
|
||||
|
||||
/**
|
||||
* Processes and returns image as WebP encoded string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function processWebp();
|
||||
|
||||
/**
|
||||
* Process a given image
|
||||
*
|
||||
@@ -145,6 +152,12 @@ abstract class AbstractEncoder
|
||||
case 'image/vnd.adobe.photoshop':
|
||||
$this->result = $this->processPsd();
|
||||
break;
|
||||
|
||||
case 'webp':
|
||||
case 'image/webp':
|
||||
case 'image/x-webp':
|
||||
$this->result = $this->processWebp();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \Intervention\Image\Exception\NotSupportedException(
|
||||
|
@@ -50,6 +50,7 @@ class Constraint
|
||||
|
||||
/**
|
||||
* Fix the given argument in current constraint
|
||||
*
|
||||
* @param integer $type
|
||||
* @return void
|
||||
*/
|
||||
|
@@ -14,37 +14,54 @@ class Decoder extends \Intervention\Image\AbstractDecoder
|
||||
*/
|
||||
public function initFromPath($path)
|
||||
{
|
||||
$info = @getimagesize($path);
|
||||
|
||||
if ($info === false) {
|
||||
if ( ! file_exists($path)) {
|
||||
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
|
||||
switch ($info[2]) {
|
||||
case IMAGETYPE_PNG:
|
||||
switch (strtolower($mime)) {
|
||||
case 'image/png':
|
||||
case 'image/x-png':
|
||||
$core = @imagecreatefrompng($path);
|
||||
break;
|
||||
|
||||
case IMAGETYPE_JPEG:
|
||||
$core = @imagecreatefromjpeg($path);
|
||||
case 'image/jpg':
|
||||
case 'image/jpeg':
|
||||
case 'image/pjpeg':
|
||||
$core = @imagecreatefromjpeg($path);
|
||||
if (!$core) {
|
||||
$core= @imagecreatefromstring(file_get_contents($path));
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGETYPE_GIF:
|
||||
case 'image/gif':
|
||||
$core = @imagecreatefromgif($path);
|
||||
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:
|
||||
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)) {
|
||||
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
|
||||
$image = $this->initFromGdResource($core);
|
||||
$image->mime = $info['mime'];
|
||||
$image->mime = $mime;
|
||||
$image->setFileInfoFromPath($path);
|
||||
|
||||
return $image;
|
||||
|
@@ -55,6 +55,23 @@ class Encoder extends \Intervention\Image\AbstractEncoder
|
||||
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
|
||||
*
|
||||
|
@@ -40,7 +40,9 @@ class ImageServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
return $this->provider->boot();
|
||||
if (method_exists($this->provider, 'boot')) {
|
||||
return $this->provider->boot();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -77,7 +77,7 @@ class ImageServiceProviderLaravel5 extends ServiceProvider
|
||||
// imagecache route
|
||||
if (is_string(config('imagecache.route'))) {
|
||||
|
||||
$filename_pattern = '[ \w\\.\\/\\-\\@]+';
|
||||
$filename_pattern = '[ \w\\.\\/\\-\\@\(\)]+';
|
||||
|
||||
// route to access template applied image file
|
||||
$app['router']->get(config('imagecache.route').'/{template}/{filename}', array(
|
||||
|
@@ -70,6 +70,28 @@ class Encoder extends \Intervention\Image\AbstractEncoder
|
||||
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
|
||||
*
|
||||
|
@@ -61,6 +61,9 @@ class AbstractDecoderTest extends PHPUnit_Framework_TestCase
|
||||
$source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r'));
|
||||
$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);
|
||||
$this->assertFalse($source->isStream());
|
||||
}
|
||||
|
@@ -46,6 +46,20 @@ class EncoderTest extends PHPUnit_Framework_TestCase
|
||||
$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
|
||||
*/
|
||||
@@ -155,6 +169,16 @@ class EncoderTest extends PHPUnit_Framework_TestCase
|
||||
$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()
|
||||
{
|
||||
$core = $this->getImagickMock('tiff');
|
||||
|
@@ -28,6 +28,14 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
|
||||
$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()
|
||||
{
|
||||
$str = file_get_contents('tests/images/circle.png');
|
||||
@@ -75,6 +83,22 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
|
||||
$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()
|
||||
{
|
||||
$img = $this->manager()->canvas(30, 20);
|
||||
@@ -1474,6 +1498,15 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
|
||||
$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()
|
||||
{
|
||||
$img = $this->manager()->make('tests/images/trim.png');
|
||||
@@ -1643,4 +1676,10 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
|
||||
'driver' => 'gd'
|
||||
));
|
||||
}
|
||||
|
||||
private function getMime($data)
|
||||
{
|
||||
$finfo = new finfo(FILEINFO_MIME);
|
||||
return $finfo->buffer($data);
|
||||
}
|
||||
}
|
||||
|
BIN
tests/images/test.webp
Executable file
BIN
tests/images/test.webp
Executable file
Binary file not shown.
After Width: | Height: | Size: 82 B |
Reference in New Issue
Block a user