Merge branch 'develop' into testing/views
@ -38,6 +38,8 @@ class ContentSecurityPolicy extends BaseConfig
|
||||
public $mediaSrc = null;
|
||||
|
||||
public $objectSrc = null;
|
||||
|
||||
public $manifestSrc = null;
|
||||
|
||||
public $pluginTypes = null;
|
||||
|
||||
|
@ -10,4 +10,14 @@ class FileException extends \RuntimeException implements ExceptionInterface
|
||||
return new static(lang('Files.cannotMove', [$from, $to, $error]));
|
||||
}
|
||||
|
||||
public static function forInvalidFilename(string $to = null)
|
||||
{
|
||||
return new self(lang('Files.invalidFilename', [$to]));
|
||||
}
|
||||
|
||||
public static function forCopyError(string $to = null)
|
||||
{
|
||||
return new self(lang('Files.cannotCopy', [$to]));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -140,6 +140,12 @@ class ContentSecurityPolicy
|
||||
* @var array
|
||||
*/
|
||||
protected $styleSrc = [];
|
||||
|
||||
/**
|
||||
* Used for security enforcement
|
||||
* @var array
|
||||
*/
|
||||
protected $manifestSrc = [];
|
||||
|
||||
/**
|
||||
* Used for security enforcement
|
||||
@ -432,6 +438,26 @@ class ContentSecurityPolicy
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Adds a new valid endpoint for manifest sources. Can be either
|
||||
* a URI class or simple string.
|
||||
*
|
||||
* @see https://www.w3.org/TR/CSP/#directive-manifest-src
|
||||
*
|
||||
* @param $uri
|
||||
* @param bool $reportOnly
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addManifestSrc($uri, bool $reportOnly = false)
|
||||
{
|
||||
$this->addOption($uri, 'manifestSrc', $reportOnly);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
@ -688,6 +714,7 @@ class ContentSecurityPolicy
|
||||
'plugin-types' => 'pluginTypes',
|
||||
'script-src' => 'scriptSrc',
|
||||
'style-src' => 'styleSrc',
|
||||
'manifest-src' => 'manifestSrc',
|
||||
'sandbox' => 'sandbox',
|
||||
'report-uri' => 'reportURI'
|
||||
];
|
||||
|
@ -52,9 +52,9 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
* d
|
||||
* @var \CodeIgniter\Images\Image
|
||||
*/
|
||||
protected $image;
|
||||
protected $width;
|
||||
protected $height;
|
||||
protected $image = null;
|
||||
protected $width = 0;
|
||||
protected $height = 0;
|
||||
protected $filePermissions = 0644;
|
||||
protected $xAxis = 0;
|
||||
protected $yAxis = 0;
|
||||
@ -112,13 +112,41 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
|
||||
$this->image = new Image($path, true);
|
||||
|
||||
$this->image->getProperties();
|
||||
$this->image->getProperties(false);
|
||||
$this->width = $this->image->origWidth;
|
||||
$this->height = $this->image->origHeight;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Make the image resource object if needed
|
||||
*/
|
||||
protected function ensureResource()
|
||||
{
|
||||
if ($this->resource == null)
|
||||
{
|
||||
$path = $this->image->getPathname();
|
||||
// if valid image type, make corresponding image resource
|
||||
switch ($this->image->imageType)
|
||||
{
|
||||
case IMAGETYPE_GIF:
|
||||
$this->resource = imagecreatefromgif($path);
|
||||
break;
|
||||
case IMAGETYPE_JPEG:
|
||||
$this->resource = imagecreatefromjpeg($path);
|
||||
break;
|
||||
case IMAGETYPE_PNG:
|
||||
$this->resource = imagecreatefrompng($path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the image instance.
|
||||
*
|
||||
@ -140,6 +168,7 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
$this->ensureResource();
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
@ -231,16 +260,15 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
throw ImageException::forMissingAngle();
|
||||
}
|
||||
|
||||
// cast angle as an int, for our use
|
||||
$angle = (int) $angle;
|
||||
|
||||
// Reassign the width and height
|
||||
if ($angle === 90 || $angle === 270)
|
||||
{
|
||||
$this->width = $this->image->origHeight;
|
||||
$this->height = $this->image->origWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->width = $this->image->origWidth;
|
||||
$this->height = $this->image->origHeight;
|
||||
$temp = $this->height;
|
||||
$this->width = $this->height;
|
||||
$this->height = $temp;
|
||||
}
|
||||
|
||||
// Call the Handler-specific version.
|
||||
@ -303,13 +331,13 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function flip(string $dir)
|
||||
public function flip(string $dir = 'vertical')
|
||||
{
|
||||
$dir = strtolower($dir);
|
||||
|
||||
if ($dir !== 'vertical' && $dir !== 'horizontal')
|
||||
{
|
||||
throw new ImageException(lang('images.invalidDirection'));
|
||||
throw ImageException::forInvalidDirection($dir);
|
||||
}
|
||||
|
||||
return $this->_flip($dir);
|
||||
@ -347,7 +375,7 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
* @param string $text
|
||||
* @param array $options
|
||||
*
|
||||
* @return BaseHandler
|
||||
* @return $this
|
||||
*/
|
||||
public function text(string $text, array $options = [])
|
||||
{
|
||||
@ -437,12 +465,9 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
throw ImageException::forEXIFUnsupported();
|
||||
}
|
||||
|
||||
$exif = exif_read_data($this->image->getPathname());
|
||||
|
||||
if ( ! is_null($key) && is_array($exif))
|
||||
{
|
||||
$exif = array_key_exists($key, $exif) ? $exif[$key] : false;
|
||||
@ -662,7 +687,12 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
*/
|
||||
protected function reproportion()
|
||||
{
|
||||
if (($this->width === 0 && $this->height === 0) || $this->image->origWidth === 0 || $this->image->origHeight === 0 || ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height)) || ! ctype_digit((string) $this->image->origWidth) || ! ctype_digit((string) $this->image->origHeight)
|
||||
if (($this->width === 0 && $this->height === 0) ||
|
||||
$this->image->origWidth === 0 ||
|
||||
$this->image->origHeight === 0 ||
|
||||
( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height)) ||
|
||||
! ctype_digit((string) $this->image->origWidth) ||
|
||||
! ctype_digit((string) $this->image->origHeight)
|
||||
)
|
||||
{
|
||||
return;
|
||||
@ -700,4 +730,16 @@ abstract class BaseHandler implements ImageHandlerInterface
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// accessor for testing; not part of interface
|
||||
public function getWidth()
|
||||
{
|
||||
return ($this->resource != null) ? $this->_getWidth() : $this->width;
|
||||
}
|
||||
|
||||
// accessor for testing; not part of interface
|
||||
public function getHeight()
|
||||
{
|
||||
return ($this->resource != null) ? $this->_getHeight() : $this->height;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,21 +42,17 @@ class GDHandler extends BaseHandler
|
||||
|
||||
public $version;
|
||||
|
||||
/**
|
||||
* Stores image resource in memory.
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
protected $resource;
|
||||
|
||||
public function __construct($config = null)
|
||||
{
|
||||
parent::__construct($config);
|
||||
|
||||
// We should never see this, so can't test it
|
||||
// @codeCoverageIgnoreStart
|
||||
if ( ! extension_loaded('gd'))
|
||||
{
|
||||
throw ImageException::forMissingExtension('GD');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -72,10 +68,7 @@ class GDHandler extends BaseHandler
|
||||
protected function _rotate(int $angle)
|
||||
{
|
||||
// Create the image handle
|
||||
if ( ! ($srcImg = $this->createImage()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$srcImg = $this->createImage();
|
||||
|
||||
// Set the background color
|
||||
// This won't work with transparent PNG files so we are
|
||||
@ -85,7 +78,7 @@ class GDHandler extends BaseHandler
|
||||
$white = imagecolorallocate($srcImg, 255, 255, 255);
|
||||
|
||||
// Rotate it!
|
||||
$destImg = imagerotate($this->resource, $angle, $white);
|
||||
$destImg = imagerotate($srcImg, $angle, $white);
|
||||
|
||||
// Kill the file handles
|
||||
imagedestroy($srcImg);
|
||||
@ -106,12 +99,9 @@ class GDHandler extends BaseHandler
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function _flatten(int $red = 255, int $green = 255, int $blue = 255) {
|
||||
|
||||
if ( ! ($src = $this->createImage()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public function _flatten(int $red = 255, int $green = 255, int $blue = 255)
|
||||
{
|
||||
$srcImg = $this->createImage();
|
||||
|
||||
if (function_exists('imagecreatetruecolor'))
|
||||
{
|
||||
@ -128,15 +118,14 @@ class GDHandler extends BaseHandler
|
||||
$matte = imagecolorallocate($dest, $red, $green, $blue);
|
||||
|
||||
imagefilledrectangle($dest, 0, 0, $this->width, $this->height, $matte);
|
||||
imagecopy($dest, $src, 0, 0, 0, 0, $this->width, $this->height);
|
||||
imagecopy($dest, $srcImg, 0, 0, 0, 0, $this->width, $this->height);
|
||||
|
||||
// Kill the file handles
|
||||
imagedestroy($src);
|
||||
imagedestroy($srcImg);
|
||||
|
||||
$this->resource = $dest;
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -157,7 +146,7 @@ class GDHandler extends BaseHandler
|
||||
|
||||
if ($direction === 'horizontal')
|
||||
{
|
||||
for ($i = 0; $i < $height; $i ++ )
|
||||
for ($i = 0; $i < $height; $i ++)
|
||||
{
|
||||
$left = 0;
|
||||
$right = $width - 1;
|
||||
@ -177,7 +166,7 @@ class GDHandler extends BaseHandler
|
||||
}
|
||||
else
|
||||
{
|
||||
for ($i = 0; $i < $width; $i ++ )
|
||||
for ($i = 0; $i < $width; $i ++)
|
||||
{
|
||||
$top = 0;
|
||||
$bottom = $height - 1;
|
||||
@ -422,7 +411,7 @@ class GDHandler extends BaseHandler
|
||||
|
||||
return imagecreatefrompng($path);
|
||||
default:
|
||||
throw ImageException::forInvalidImageCreate();
|
||||
throw ImageException::forInvalidImageCreate('Ima');
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,4 +549,15 @@ class GDHandler extends BaseHandler
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function _getWidth()
|
||||
{
|
||||
return imagesx($this->resource);
|
||||
}
|
||||
|
||||
public function _getHeight()
|
||||
{
|
||||
return imagesy($this->resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,6 +44,10 @@ use CodeIgniter\Images\Image;
|
||||
* To make this library as compatible as possible with the broadest
|
||||
* number of installations, we do not use the Imagick extension,
|
||||
* but simply use the command line version.
|
||||
*
|
||||
* hmm - the width & height accessors at the end use the imagick extension.
|
||||
*
|
||||
* FIXME - This needs conversion & unit testing, to use the imagick extension
|
||||
*
|
||||
* @package CodeIgniter\Images\Handlers
|
||||
*/
|
||||
@ -61,6 +65,13 @@ class ImageMagickHandler extends BaseHandler
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function __construct($config = null)
|
||||
{
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Handles the actual resizing of the image.
|
||||
*
|
||||
@ -76,7 +87,8 @@ class ImageMagickHandler extends BaseHandler
|
||||
//todo FIX THIS HANDLER PROPERLY
|
||||
|
||||
$escape = "\\";
|
||||
if (strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN') {
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
|
||||
{
|
||||
$escape = "";
|
||||
}
|
||||
|
||||
@ -141,9 +153,10 @@ class ImageMagickHandler extends BaseHandler
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function _flatten(int $red = 255, int $green = 255, int $blue = 255){
|
||||
public function _flatten(int $red = 255, int $green = 255, int $blue = 255)
|
||||
{
|
||||
|
||||
$flatten = "-background RGB({$red},{$green},{$blue}) -flatten";
|
||||
$flatten = "-background RGB({$red},{$green},{$blue}) -flatten";
|
||||
|
||||
$source = ! empty($this->resource) ? $this->resource : $this->image->getPathname();
|
||||
$destination = $this->getResourcePath();
|
||||
@ -408,4 +421,18 @@ class ImageMagickHandler extends BaseHandler
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function _getWidth()
|
||||
{
|
||||
return imagesx($this->resource);
|
||||
}
|
||||
|
||||
public function _getHeight()
|
||||
{
|
||||
return imagesy($this->resource);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ class Image extends File
|
||||
|
||||
if ( ! copy($this->getPathname(), "{$targetPath}{$targetName}"))
|
||||
{
|
||||
throw ImageException::forCopyError();
|
||||
throw ImageException::forCopyError($targetPath);
|
||||
}
|
||||
|
||||
chmod("{$targetPath}/{$targetName}", $perms);
|
||||
@ -132,6 +132,7 @@ class Image extends File
|
||||
|
||||
$vals = getimagesize($path);
|
||||
$types = [1 => 'gif', 2 => 'jpeg', 3 => 'png'];
|
||||
|
||||
$mime = 'image/' . ($types[$vals[2]] ?? 'jpg');
|
||||
|
||||
if ($return === true)
|
||||
|
@ -44,8 +44,9 @@ interface ImageHandlerInterface
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param bool $maintainRatio If true, will get the closest match possible while keeping aspect ratio true.
|
||||
* @param string $masterDim
|
||||
*/
|
||||
public function resize(int $width, int $height, bool $maintainRatio = false);
|
||||
public function resize(int $width, int $height, bool $maintainRatio = false, string $masterDim = 'auto');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
@ -58,10 +59,12 @@ interface ImageHandlerInterface
|
||||
* @param int|null $height
|
||||
* @param int|null $x X-axis coord to start cropping from the left of image
|
||||
* @param int|null $y Y-axis coord to start cropping from the top of image
|
||||
* @param bool $maintainRatio
|
||||
* @param string $masterDim
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function crop(int $width = null, int $height = null, int $x = null, int $y = null);
|
||||
public function crop(int $width = null, int $height = null, int $x = null, int $y = null, bool $maintainRatio = false, string $masterDim = 'auto');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
@ -110,6 +113,17 @@ interface ImageHandlerInterface
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Flip an image horizontally or vertically
|
||||
*
|
||||
* @param string $dir Direction to flip, either 'vertical' or 'horizontal'
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function flip(string $dir = 'vertical');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Combine cropping and resizing into a single command.
|
||||
*
|
||||
@ -133,4 +147,42 @@ interface ImageHandlerInterface
|
||||
public function fit(int $width, int $height, string $position);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Overlays a string of text over the image.
|
||||
*
|
||||
* Valid options:
|
||||
*
|
||||
* - color Text Color (hex number)
|
||||
* - shadowColor Color of the shadow (hex number)
|
||||
* - hAlign Horizontal alignment: left, center, right
|
||||
* - vAlign Vertical alignment: top, middle, bottom
|
||||
* - hOffset
|
||||
* - vOffset
|
||||
* - fontPath
|
||||
* - fontSize
|
||||
* - shadowOffset
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $options
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function text(string $text, array $options = []);
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Saves any changes that have been made to file.
|
||||
*
|
||||
* Example:
|
||||
* $image->resize(100, 200, true)
|
||||
* ->save($target);
|
||||
*
|
||||
* @param string $target
|
||||
* @param int $quality
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function save(string $target = null, int $quality = 90);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Files language strings.
|
||||
*
|
||||
@ -14,6 +13,8 @@
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
return [
|
||||
'fileNotFound' => 'File not found: {0}',
|
||||
'cannotMove' => 'Could not move file {0} to {1} ({2})',
|
||||
'fileNotFound' => 'File not found: {0}',
|
||||
'cannotMove' => 'Could not move file {0} to {1} ({2})',
|
||||
'invalidFilename' => 'Target filename missing or invalid: {0}',
|
||||
'cannotCopy' => 'Could not copy to {0} - make sure the folder is writeable',
|
||||
];
|
||||
|
BIN
tests/_support/Images/EXIFsamples/down-mirrored.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
tests/_support/Images/EXIFsamples/down.jpg
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
tests/_support/Images/EXIFsamples/left-mirrored.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/_support/Images/EXIFsamples/left.jpg
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
tests/_support/Images/EXIFsamples/right-mirrored.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
tests/_support/Images/EXIFsamples/right.jpg
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
tests/_support/Images/EXIFsamples/up-mirrored.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
tests/_support/Images/EXIFsamples/up.jpg
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
tests/_support/Images/Steveston_dusk.JPG
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
tests/_support/Images/ci-logo.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
tests/_support/Images/ci-logo.jpeg
Normal file
After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
96
tests/system/Images/BaseHandlerTest.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php namespace CodeIgniter\Images;
|
||||
|
||||
use CodeIgniter\Images\Exceptions\ImageException;
|
||||
use CodeIgniter\Files\Exceptions\FileException;
|
||||
use CodeIgniter\Config\Services;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStreamDirectory;
|
||||
|
||||
/**
|
||||
* Test the common image processing functionality.
|
||||
*
|
||||
* Note: some of the underlying PHP functions do not play nicely
|
||||
* with vfsStream, so the support files are used directly for
|
||||
* most work, and the virtual file system will be used for
|
||||
* testing saving only.
|
||||
*/
|
||||
class BaseHandlerTest extends \CIUnitTestCase
|
||||
{
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
if ( ! extension_loaded('gd'))
|
||||
{
|
||||
$this->markTestSkipped('The GD extension is not available.');
|
||||
return;
|
||||
}
|
||||
|
||||
// create virtual file system
|
||||
$this->root = vfsStream::setup();
|
||||
// copy our support files
|
||||
$this->origin = SUPPORTPATH . 'Images/';
|
||||
vfsStream::copyFromFileSystem($this->origin, $this->root);
|
||||
// make subfolders
|
||||
$structure = ['work' => [], 'wontwork' => []];
|
||||
vfsStream::create($structure);
|
||||
// with one of them read only
|
||||
$wont = $this->root->getChild('wontwork')->chmod(0400);
|
||||
|
||||
// for VFS tests
|
||||
$this->start = $this->root->url() . '/';
|
||||
$this->path = $this->start . 'ci-logo.png';
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testNew()
|
||||
{
|
||||
$handler = Services::image('gd', null, false);
|
||||
$this->assertTrue($handler instanceof Handlers\BaseHandler);
|
||||
}
|
||||
|
||||
public function testWithFile()
|
||||
{
|
||||
$path = $this->origin . 'ci-logo.png';
|
||||
$handler = Services::image('gd', null, false);
|
||||
$handler->withFile($path);
|
||||
|
||||
$image = $handler->getFile();
|
||||
$this->assertTrue($image instanceof Image);
|
||||
$this->assertEquals(155, $image->origWidth);
|
||||
$this->assertEquals($path, $image->getPathname());
|
||||
}
|
||||
|
||||
public function testMissingFile()
|
||||
{
|
||||
$this->expectException(\CodeIgniter\Files\Exceptions\FileNotFoundException::class);
|
||||
$handler = Services::image('gd', null, false);
|
||||
$handler->withFile($this->start . 'No_such_file.jpg');
|
||||
}
|
||||
|
||||
public function testFileTypes()
|
||||
{
|
||||
$handler = Services::image('gd', null, false);
|
||||
$handler->withFile($this->start . 'ci-logo.png');
|
||||
$image = $handler->getFile();
|
||||
$this->assertTrue($image instanceof Image);
|
||||
|
||||
$handler->withFile($this->start . 'ci-logo.jpeg');
|
||||
$image = $handler->getFile();
|
||||
$this->assertTrue($image instanceof Image);
|
||||
|
||||
$handler->withFile($this->start . 'ci-logo.gif');
|
||||
$image = $handler->getFile();
|
||||
$this->assertTrue($image instanceof Image);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Something handled by our Image
|
||||
public function testImageHandled()
|
||||
{
|
||||
$handler = Services::image('gd', null, false);
|
||||
$handler->withFile($this->path);
|
||||
$this->assertEquals($this->path, $handler->getPathname());
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,326 @@
|
||||
<?php namespace CodeIgniter\Images;
|
||||
|
||||
use CodeIgniter\Images\BaseHandler;
|
||||
use CodeIgniter\Images\Exceptions\ImageException;
|
||||
use CodeIgniter\Files\Exceptions\FileException;
|
||||
use CodeIgniter\Config\Services;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStreamDirectory;
|
||||
use DirectoryIterator;
|
||||
|
||||
/**
|
||||
* Unit testing for the GD image handler.
|
||||
* It is impractical to programmatically inspect the results of the
|
||||
* different transformations, so we have to rely on the underlying package.
|
||||
* We can make sure that we can call it without blowing up,
|
||||
* and we can make sure the code coverage is good.
|
||||
*
|
||||
* Was unable to test fontPath & related logic.
|
||||
*/
|
||||
class GDHandlerTest extends \CIUnitTestCase
|
||||
{
|
||||
protected $path = 'tests/_support/ci-logo.png';
|
||||
|
||||
public function testCanReachImageMethods()
|
||||
public function setUp()
|
||||
{
|
||||
$image = new Image(ROOTPATH.$this->path);
|
||||
if ( ! extension_loaded('gd'))
|
||||
{
|
||||
$this->markTestSkipped('The GD extension is not available.');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertInternalType('array', $image->getProperties(true));
|
||||
// create virtual file system
|
||||
$this->root = vfsStream::setup();
|
||||
// copy our support files
|
||||
$this->origin = SUPPORTPATH . 'Images/';
|
||||
// make subfolders
|
||||
$structure = ['work' => [], 'wontwork' => []];
|
||||
vfsStream::create($structure);
|
||||
// with one of them read only
|
||||
$wont = $this->root->getChild('wontwork')->chmod(0400);
|
||||
|
||||
$this->start = $this->root->url() . '/';
|
||||
|
||||
$this->path = $this->origin . 'ci-logo.png';
|
||||
$this->handler = Services::image('gd', null, false);
|
||||
}
|
||||
|
||||
public function testGetVersion()
|
||||
{
|
||||
$version = $this->handler->getVersion();
|
||||
// make sure that the call worked
|
||||
$this->assertNotFalse($version);
|
||||
// we should have a numeric version, with 3 digits
|
||||
$this->assertGreaterThan(100, $version);
|
||||
$this->assertLessThan(999, $version);
|
||||
}
|
||||
|
||||
public function testImageProperties()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$file = $this->handler->getFile();
|
||||
$props = $file->getProperties(true);
|
||||
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(155, $props['width']);
|
||||
$this->assertEquals(155, $file->origWidth);
|
||||
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
$this->assertEquals(200, $props['height']);
|
||||
$this->assertEquals(200, $file->origHeight);
|
||||
|
||||
$this->assertEquals('width="155" height="200"', $props['size_str']);
|
||||
}
|
||||
|
||||
public function testImageTypeProperties()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$file = $this->handler->getFile();
|
||||
$props = $file->getProperties(true);
|
||||
|
||||
$this->assertEquals(IMAGETYPE_PNG, $props['image_type']);
|
||||
$this->assertEquals('image/png', $props['mime_type']);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testResizeIgnored()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->resize(155, 200); // 155x200 result
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testResizeAbsolute()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->resize(123, 456, false); // 123x456 result
|
||||
$this->assertEquals(123, $this->handler->getWidth());
|
||||
$this->assertEquals(456, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testResizeAspect()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->resize(123, 456, true); // 123x159 result
|
||||
$this->assertEquals(123, $this->handler->getWidth());
|
||||
$this->assertEquals(159, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testResizeAspectWidth()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->resize(123, 0, true); // 123x159 result
|
||||
$this->assertEquals(123, $this->handler->getWidth());
|
||||
$this->assertEquals(159, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testResizeAspectHeight()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->resize(0, 456, true); // 354x456 result
|
||||
$this->assertEquals(354, $this->handler->getWidth());
|
||||
$this->assertEquals(456, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testCropTopLeft()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->crop(100, 100); // 100x100 result
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(100, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testCropMiddle()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->crop(100, 100, 50, 50, false); // 100x100 result
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(100, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testCropMiddlePreserved()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->crop(100, 100, 50, 50, true); // 78x100 result
|
||||
$this->assertEquals(78, $this->handler->getWidth());
|
||||
$this->assertEquals(100, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testCropTopLeftPreserveAspect()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->crop(100, 100); // 100x100 result
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(100, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testCropNothing()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->crop(155, 200); // 155x200 result
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testCropOutOfBounds()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->crop(100, 100, 100); // 55x100 result in 100x100
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(100, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testRotate()
|
||||
{
|
||||
$this->handler->withFile($this->path); // 155x200
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
|
||||
// first rotation
|
||||
$this->handler->rotate(90); // 200x155
|
||||
$this->assertEquals(200, $this->handler->getWidth());
|
||||
|
||||
// check image size again after another rotation
|
||||
$this->handler->rotate(180); // 200x155
|
||||
$this->assertEquals(200, $this->handler->getWidth());
|
||||
}
|
||||
|
||||
public function testRotateBadAngle()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->expectException(ImageException::class);
|
||||
$this->handler->rotate(77);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testFlatten()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->flatten();
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testFlip()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->flip();
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testHorizontal()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->flip('horizontal');
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testFlipVertical()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->flip('vertical');
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testFlipUnknown()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->expectException(ImageException::class);
|
||||
$this->handler->flip('bogus');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
public function testFit()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->fit(100, 100);
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(100, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testFitTaller()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->fit(100, 400);
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(400, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testFitAutoHeight()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->fit(100);
|
||||
$this->assertEquals(100, $this->handler->getWidth());
|
||||
$this->assertEquals(129, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
public function testFitPositions()
|
||||
{
|
||||
$choices = ['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'];
|
||||
$this->handler->withFile($this->path);
|
||||
foreach ($choices as $position)
|
||||
{
|
||||
$this->handler->fit(100, 100, $position);
|
||||
$this->assertEquals(100, $this->handler->getWidth(), 'Position ' . $position . ' failed');
|
||||
$this->assertEquals(100, $this->handler->getHeight(), 'Position ' . $position . ' failed');
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testText()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->text('vertical', ['hAlign' => 'right', 'vAlign' => 'bottom']);
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testMoreText()
|
||||
{
|
||||
$this->handler->withFile($this->path);
|
||||
$this->handler->text('vertical', ['vAlign' => 'middle', 'withShadow' => 'sure', 'shadowOffset' => 3]);
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testImageCreation()
|
||||
{
|
||||
foreach (['gif', 'jpeg', 'png'] as $type)
|
||||
{
|
||||
$this->handler->withFile($this->origin . 'ci-logo.' . $type);
|
||||
$this->handler->text('vertical');
|
||||
$this->assertEquals(155, $this->handler->getWidth());
|
||||
$this->assertEquals(200, $this->handler->getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
public function testImageSave()
|
||||
{
|
||||
foreach (['gif', 'jpeg', 'png'] as $type)
|
||||
{
|
||||
$this->handler->withFile($this->origin . 'ci-logo.' . $type);
|
||||
$this->handler->getResource(); // make sure resource is loaded
|
||||
$this->handler->save($this->start . 'work/ci-logo.' . $type);
|
||||
$this->assertTrue($this->root->hasChild('work/ci-logo.' . $type));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,56 +1,84 @@
|
||||
<?php namespace CodeIgniter\Images;
|
||||
|
||||
use CodeIgniter\Images\Exceptions\ImageException;
|
||||
use CodeIgniter\Files\Exceptions\FileException;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStreamDirectory;
|
||||
|
||||
class ImageTest extends \CIUnitTestCase
|
||||
{
|
||||
|
||||
protected $path = 'tests/_support/ci-logo.png';
|
||||
|
||||
public function setup()
|
||||
{
|
||||
// create virtual file system
|
||||
$this->root = vfsStream::setup();
|
||||
// copy our support files
|
||||
$this->origin = '_support/Images/';
|
||||
vfsStream::copyFromFileSystem(TESTPATH . $this->origin, $this->root);
|
||||
// make subfolders
|
||||
$structure = ['work' => [], 'wontwork' => []];
|
||||
vfsStream::create($structure);
|
||||
// with one of them read only
|
||||
$wont = $this->root->getChild('wontwork')->chmod(0400);
|
||||
|
||||
$this->start = $this->root->url() . '/';
|
||||
|
||||
$this->image = new Image($this->start . 'ci-logo.png');
|
||||
}
|
||||
|
||||
public function testBasicPropertiesInherited()
|
||||
{
|
||||
$image = new Image(ROOTPATH.$this->path);
|
||||
|
||||
$this->assertEquals('ci-logo.png', $image->getFilename());
|
||||
$this->assertEquals(ROOTPATH.$this->path, $image->getPathname());
|
||||
$this->assertEquals(ROOTPATH.'tests/_support', $image->getPath());
|
||||
$this->assertEquals('ci-logo.png', $image->getBasename());
|
||||
$this->assertEquals('ci-logo.png', $this->image->getFilename());
|
||||
$this->assertEquals($this->start . 'ci-logo.png', $this->image->getPathname());
|
||||
$this->assertEquals($this->root->url(), $this->image->getPath());
|
||||
$this->assertEquals('ci-logo.png', $this->image->getBasename());
|
||||
}
|
||||
|
||||
|
||||
public function testGetProperties()
|
||||
{
|
||||
$image = new Image(ROOTPATH.$this->path);
|
||||
|
||||
$expected = [
|
||||
'width' => 155,
|
||||
'height' => 200,
|
||||
'width' => 155,
|
||||
'height' => 200,
|
||||
'image_type' => IMAGETYPE_PNG,
|
||||
'size_str' => 'width="155" height="200"',
|
||||
'mime_type' => "image/png",
|
||||
'size_str' => 'width="155" height="200"',
|
||||
'mime_type' => "image/png",
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $image->getProperties(true));
|
||||
$this->assertEquals($expected, $this->image->getProperties(true));
|
||||
}
|
||||
|
||||
|
||||
public function testCanCopyDefaultName()
|
||||
public function testExtractProperties()
|
||||
{
|
||||
$image = new Image(ROOTPATH.$this->path);
|
||||
// extract properties from the image
|
||||
$this->assertTrue($this->image->getProperties(false));
|
||||
|
||||
$image->copy(WRITEPATH);
|
||||
|
||||
$this->assertFileExists(WRITEPATH.'ci-logo.png');
|
||||
|
||||
unlink(WRITEPATH.'ci-logo.png');
|
||||
$this->assertEquals(155, $this->image->origWidth);
|
||||
$this->assertEquals(200, $this->image->origHeight);
|
||||
$this->assertEquals(IMAGETYPE_PNG, $this->image->imageType);
|
||||
$this->assertEquals('width="155" height="200"', $this->image->sizeStr);
|
||||
$this->assertEquals("image/png", $this->image->mime);
|
||||
}
|
||||
|
||||
public function testCanCopyNewName()
|
||||
public function testCopyDefaultName()
|
||||
{
|
||||
$image = new Image(ROOTPATH.$this->path);
|
||||
$targetPath = $this->start . 'work';
|
||||
$this->image->copy($targetPath);
|
||||
$this->assertTrue($this->root->hasChild('work/ci-logo.png'));
|
||||
}
|
||||
|
||||
$image->copy(WRITEPATH, 'new-logo.png');
|
||||
public function testCopyNewName()
|
||||
{
|
||||
$this->image->copy($this->root->url(), 'new-logo.png');
|
||||
$this->assertTrue($this->root->hasChild('new-logo.png'));
|
||||
}
|
||||
|
||||
$this->assertFileExists(WRITEPATH.'new-logo.png');
|
||||
|
||||
unlink(WRITEPATH.'new-logo.png');
|
||||
public function testCopyNowhere()
|
||||
{
|
||||
$this->expectException(ImageException::class);
|
||||
$targetPath = $this->start . 'work';
|
||||
$this->image->copy($targetPath, '');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -179,6 +179,7 @@ class holds a number of methods that map pretty clearly to the appropriate heade
|
||||
$response->CSP->addFrameAncestor('none', $reportOnly);
|
||||
$response->CSP->addImageSrc('cdn.example.com', $reportOnly);
|
||||
$response->CSP->addMediaSrc('cdn.example.com', $reportOnly);
|
||||
$response->CSP->addManifestSrc('cdn.example.com', $reportOnly);
|
||||
$response->CSP->addObjectSrc('cdn.example.com', $reportOnly);
|
||||
$response->CSP->addPluginType('application/pdf', $reportOnly);
|
||||
$response->CSP->addScriptSrc('scripts.example.com', $reportOnly);
|
||||
|