1
0
mirror of https://github.com/mosbth/cimage.git synced 2025-08-26 17:14:27 +02:00

Compare commits

..

28 Commits
0715 ... v0.8.1

Author SHA1 Message Date
Mikael Roos
0249056761 Updated version number in define.php. 2020-06-08 12:38:44 +02:00
Mikael Roos
4ea72be49a Enable to set JPEG image as interlaced, implement feature #177. 2020-06-08 12:26:40 +02:00
Mikael Roos
2ce1f18fe5 Set PHP 7.0 as precondition (to prepare to update the codebase). 2020-06-08 12:26:01 +02:00
Mikael Roos
0f1f537b62 Add function getValue() to read from querystring. 2020-06-08 12:25:47 +02:00
Mikael Roos
1411adc828 Fix error in composer.json 2020-05-06 09:51:12 +02:00
Mikael Roos
86737af69e Update composer.json and move ext-gd from required to suggested to ease installation where cli does not have all extensions installed. 2020-05-06 09:06:46 +02:00
Mikael Roos
c563275ed5 Support PHP 7.4, some minor fixes with notices. 2020-01-15 16:51:39 +01:00
Mikael Roos
8fec09b195 Merge pull request #176 from SpaceLenore/patch-1
fixed markdown syntax
2018-10-28 19:08:45 +01:00
Lenore
ac16343cd7 fixed markdown syntax
added spaces so the markdown gets parsed correctly
2018-10-28 17:49:35 +01:00
Mikael Roos
cd142c5880 prepare to tag 2017-11-06 10:24:47 +01:00
Mikael Roos
91dd92d483 Remove webroot/img/{round8.PNG,wider.JPEG,wider.JPG} to avoid unzip warning message when installing with composer. 2017-11-06 10:24:16 +01:00
Mikael Roos
4b64d921d1 Adding docker-compose.yml #169. 2017-06-26 10:30:21 +02:00
Mikael Roos
4211d7e0e6 Adding docker-compose.yml #169. 2017-06-26 10:30:08 +02:00
Mikael Roos
dd8878c8bd add issue number 2017-03-31 00:55:38 +02:00
Mikael Roos
f9604518e4 prepare to tag v0.7.19 2017-03-31 00:47:55 +02:00
Mikael Roos
61aa52854e Move exception handler from functions.php to img.php. 2017-03-31 00:46:43 +02:00
Mikael Roos
401478c839 Correct XSS injection in check_system.php. 2016-08-31 15:26:14 +02:00
Mikael Roos
f0ab9479d6 read lena.tif, will need it in the future 2016-08-13 18:33:09 +02:00
Mikael Roos
9ff7a61ca9 remove tif version of lena 2016-08-13 17:49:36 +02:00
Mikael Roos
3170beb832 Composer suggests ext-imagick and ext-curl. 2016-08-11 17:39:45 +02:00
Mikael Roos
8001f72a1a prepare to tag v0.7.18 2016-08-09 13:27:46 +02:00
Mikael Roos
0f9e0220f1 Made &lossless a requirement to not use the original image. 2016-08-09 13:27:30 +02:00
Mikael Roos
e59ef91991 prepare to tag v0.7.17 2016-08-09 13:23:04 +02:00
Mikael Roos
2337dbe94c Made &lossless part of the generated cache filename. 2016-08-09 13:22:33 +02:00
Mikael Roos
c5de59a754 prepare to tag v0.7.16 2016-08-09 13:02:25 +02:00
Mikael Roos
7ab19d39d6 adding support for pngquant 2016-08-09 13:01:38 +02:00
Mikael Roos
9f6cba9292 changed date of release 2016-08-09 10:21:41 +02:00
Mikael Roos
21e53887b8 prepare to tag v0.7.15 2016-08-09 10:19:49 +02:00
17 changed files with 855 additions and 184 deletions

View File

@@ -155,6 +155,13 @@ class CImage
/**
* Do lossy output using external postprocessing tools.
*/
private $lossy = null;
/**
* Verbose mode to print out a trace and display the created image
*/
@@ -190,7 +197,15 @@ class CImage
/**
* Path to command for filter optimize, for example optipng or null.
* Path to command for lossy optimize, for example pngquant.
*/
private $pngLossy;
private $pngLossyCmd;
/**
* Path to command for filter optimize, for example optipng.
*/
private $pngFilter;
private $pngFilterCmd;
@@ -198,7 +213,7 @@ class CImage
/**
* Path to command for deflate optimize, for example pngout or null.
* Path to command for deflate optimize, for example pngout.
*/
private $pngDeflate;
private $pngDeflateCmd;
@@ -408,6 +423,13 @@ class CImage
/*
* Use interlaced progressive mode for JPEG images.
*/
private $interlace = false;
/*
* Image copy strategy, defaults to RESAMPLE.
*/
@@ -823,10 +845,14 @@ class CImage
'blur' => null,
'convolve' => null,
'rotateAfter' => null,
'interlace' => null,
// Output format
'outputFormat' => null,
'dpr' => 1,
// Postprocessing using external tools
'lossy' => null,
);
// Convert crop settings from string to array
@@ -1005,13 +1031,15 @@ class CImage
$this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
// width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
if ($this->newWidth
&& $this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
$this->log("Setting new width based on % to {$this->newWidth}");
}
// height as %
if ($this->newHeight[strlen($this->newHeight)-1] == '%') {
if ($this->newHeight
&& $this->newHeight[strlen($this->newHeight)-1] == '%') {
$this->newHeight = $this->height * substr($this->newHeight, 0, -1) / 100;
$this->log("Setting new height based on % to {$this->newHeight}");
}
@@ -1163,7 +1191,7 @@ class CImage
$this->newWidth = $width;
$this->newHeight = $height;
}
// Get image dimensions for pre-resize image.
if ($this->cropToFit || $this->fillToFit) {
@@ -1347,6 +1375,7 @@ class CImage
&& !$this->autoRotate
&& !$this->bgColor
&& ($this->upscale === self::UPSCALE_DEFAULT)
&& !$this->lossy
) {
$this->log("Using original image.");
$this->output($this->pathToImage);
@@ -1380,6 +1409,8 @@ class CImage
$compress = $this->compress ? "_co{$this->compress}" : null;
$rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
$rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null;
$lossy = $this->lossy ? "_l" : null;
$interlace = $this->interlace ? "_i" : null;
$saveAs = $this->normalizeFileExtension();
$saveAs = $saveAs ? "_$saveAs" : null;
@@ -1446,7 +1477,7 @@ class CImage
. $quality . $filters . $sharpen . $emboss . $blur . $palette
. $optimize . $compress
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor
. $convolve . $copyStrat . $saveAs;
. $convolve . $copyStrat . $lossy . $interlace . $saveAs;
return $this->setTarget($file, $base);
}
@@ -1766,7 +1797,7 @@ class CImage
// Resize by crop to fit
$this->log("Resizing using strategy - Crop to fit");
if (!$this->upscale
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
$this->log("Resizing - smaller image, do not upscale.");
@@ -2345,6 +2376,14 @@ class CImage
$this->jpegOptimizeCmd = null;
}
if (array_key_exists("png_lossy", $options)
&& $options['png_lossy'] !== false) {
$this->pngLossy = $options['png_lossy'];
$this->pngLossyCmd = $options['png_lossy_cmd'];
} else {
$this->pngLossyCmd = null;
}
if (isset($options['png_filter']) && $options['png_filter']) {
$this->pngFilterCmd = $options['png_filter_cmd'];
} else {
@@ -2410,6 +2449,12 @@ class CImage
case 'jpeg':
case 'jpg':
// Set as interlaced progressive JPEG
if ($this->interlace) {
$this->Log("Set JPEG image to be interlaced.");
$res = imageinterlace($this->image, true);
}
$this->Log("Saving image as JPEG to cache using quality = {$this->quality}.");
imagejpeg($this->image, $this->cacheFileName, $this->quality);
@@ -2446,6 +2491,24 @@ class CImage
imagesavealpha($this->image, true);
imagepng($this->image, $this->cacheFileName, $this->compress);
// Use external program to process lossy PNG, if defined
$lossyEnabled = $this->pngLossy === true;
$lossySoftEnabled = $this->pngLossy === null;
$lossyActiveEnabled = $this->lossy === true;
if ($lossyEnabled || ($lossySoftEnabled && $lossyActiveEnabled)) {
if ($this->verbose) {
clearstatcache();
$this->log("Lossy enabled: $lossyEnabled");
$this->log("Lossy soft enabled: $lossySoftEnabled");
$this->Log("Filesize before lossy optimize: " . filesize($this->cacheFileName) . " bytes.");
}
$res = array();
$cmd = $this->pngLossyCmd . " $this->cacheFileName $this->cacheFileName";
exec($cmd, $res);
$this->Log($cmd);
$this->Log($res);
}
// Use external program to filter PNG, if defined
if ($this->pngFilterCmd) {
if ($this->verbose) {

View File

@@ -45,18 +45,18 @@ There are several ways of installing. You either install the whole project which
###Install source from GitHub
### Install source from GitHub
The [sourcode is available on GitHub](https://github.com/mosbth/cimage). Clone, fork or [download as zip](https://github.com/mosbth/cimage/archive/master.zip).
**Latest stable version is v0.7.14 released 2016-08-08.**
**Latest stable version is v0.7.18 released 2016-08-09.**
I prefer cloning like this. Do switch to the latest stable version.
```bash
git clone git://github.com/mosbth/cimage.git
cd cimage
git checkout v0.7.14
git checkout v0.7.18
```
Make the cache-directory writable by the webserver.
@@ -66,7 +66,7 @@ chmod 777 cache
```
###Install all-included bundle
### Install all-included bundle
There are some all-included bundles of `img.php` that can be downloaded and used without dependency to the rest of the sourcecode.
@@ -79,14 +79,14 @@ There are some all-included bundles of `img.php` that can be downloaded and used
Dowload the version of your choice like this.
```bash
wget https://raw.githubusercontent.com/mosbth/cimage/v0.7.14/webroot/imgp.php
wget https://raw.githubusercontent.com/mosbth/cimage/v0.7.18/webroot/imgp.php
```
Open up the file in your editor and edit the array `$config`. Ensure that the paths to the image directory and the cache directory matches your environment, or create an own config-file for the script.
###Install from Packagist
### Install from Packagist
You can install the package [`mos/cimage` from Packagist](https://packagist.org/packages/mos/cimage) using composer.
@@ -139,13 +139,13 @@ Get going quickly
###Check out the test page
### Check out the test page
Try it out by pointing your browser to the test file `webroot/test/test.php`. It will show some example images and you can review how they are created.
###Process your first image
### Process your first image
<img src="https://cimage.se/cimage/imgd.php?src=example/kodim04.png&amp;w=w2&amp;a=40,0,50,0" alt=''>
@@ -159,20 +159,20 @@ and try to resize it to a thumbnail by adding the options
###What does "processing the image" involves?
### What does "processing the image" involves?
Add `&verbose` to the link to get a verbose output of what is happens during image processing. This is useful for developers and those who seek a deeper understanding on how it works behind the scene.
###Check your system
### Check your system
Open up `webroot/check_system.php` if you are uncertain that your system has the right extensions loaded.
###How does it work?
### How does it work?
Review the settings in `webroot/img_config.php` and check out `webroot/img.php` on how it uses `CImage`.
@@ -193,7 +193,7 @@ Basic usage
###Select the source
### Select the source
Open an image through `img.php` by using its `src` attribute.
@@ -209,7 +209,7 @@ All images are stored in a directory structure and you access them as:
###Resize using constraints on width and height
### Resize using constraints on width and height
Create a thumbnail of the image by applying constraints on width and height, or one of them.
@@ -223,7 +223,7 @@ Think of the constraints as a imaginary box where the image should fit. With `wi
###Resize to fit a certain dimension
### Resize to fit a certain dimension
Creating a thumbnail with a certain dimension of width and height, usually involves stretching or cropping the image to fit in the selected dimensions. Here is how you create a image that has the exact dimensions of 300x150 pixels, by either *stretching*, *cropping* or *fill to fit*.
@@ -243,7 +243,7 @@ Fill to fit is useful when you have some image that must fit in a certain dimens
###List of parameters
### List of parameters
`img.php` supports a lot of parameters. Combine the parameters to get the desired behavior and resulting image. For example, take the original image, resize it using width, aspect-ratio and crop-to-fit, apply a sharpen effect, save the image as JPEG using quality 30.
@@ -255,7 +255,7 @@ Fill to fit is useful when you have some image that must fit in a certain dimens
Here is a list of all parameters that you can use together with `img.php`, grouped by its basic intent of usage.
####Mandatory options and debugging
#### Mandatory options and debugging
Option `src` is the only mandatory option. The options in this section is useful for debugging or deciding what version of the target image is used.
@@ -270,7 +270,7 @@ Option `src` is the only mandatory option. The options in this section is useful
####Options for deciding width and height of target image
#### Options for deciding width and height of target image
These options are all affecting the final dimensions, width and height, of the resulting image.
@@ -283,7 +283,7 @@ These options are all affecting the final dimensions, width and height, of the r
####Options for resize strategy
#### Options for resize strategy
These options affect strategy to use when resizing an image into a target image that has both width and height set.
@@ -296,7 +296,7 @@ These options affect strategy to use when resizing an image into a target image
####Options for cropping part of image
#### Options for cropping part of image
These options enable to decide what part of image to crop out.
@@ -307,7 +307,7 @@ These options enable to decide what part of image to crop out.
####General processing options
#### General processing options
These options are general options affecting processing.
@@ -317,7 +317,7 @@ These options are general options affecting processing.
####Processing of image before resizing
#### Processing of image before resizing
This option are executed *before* the image is resized.
@@ -329,7 +329,7 @@ This option are executed *before* the image is resized.
####Processing of image after resizing
#### Processing of image after resizing
These options are executed *after* the image is resized.
@@ -347,7 +347,7 @@ These options are executed *after* the image is resized.
####Saving image, affecting quality and file size
#### Saving image, affecting quality and file size
Options for saving the target image.
@@ -403,7 +403,7 @@ Consult the file `webroot/img-config.php` for a complete list together with the
###Create and name the config file
### Create and name the config file
The file `img.php` looks for the config-file `img_config.php`, and uses it if its found. The three files where everything is included -- `imgd.php`, `imgp.php` and `imgs.php` -- includes an empty `$config`-array which can be overridden by saving a config-file in the same directory. If the script is `imgp.php` then name the config-file `imgp_config.php` and it will find it and use those settings.
@@ -468,19 +468,19 @@ Here are some thoughts when applying `img.php` on a live system.
###Select the proper mode
### Select the proper mode
Select the proper mode for `img.php`. Set it to "strict" or "production" to prevent outsiders to get information about your system. Use only "development" for internal use since its quite verbose in its nature of error reporting.
###Put the installation directory outside web root
### Put the installation directory outside web root
Edit the config file to put the installation directory -- and the cache directory -- outside of the web root. Best practice would be to store the installation directory and cache, outside of the web root. The only thing needed in the web root is `img.php` and `img_config.php` (if used) which can be placed, for example, in `/img/img.php` or just as `/img.php`.
###Friendly urls through `.htaccess`
### Friendly urls through `.htaccess`
Use `.htaccess`and rewrite rules (Apache) to get friendly image urls. Put `img.php` in the `/img` directory. Put the file `.htaccess` in the web root.
@@ -518,7 +518,7 @@ The result is good readable urls to your images. Its easy for the search engine
###Monitor cache size
### Monitor cache size
There is a utility `cache.bash` included for monitoring the size of the cache-directory. It generates an output like this.
@@ -556,13 +556,13 @@ Use it as a base if you feel the need to monitor the size och the cache-director
###Read-only cache
### Read-only cache
The cache directory need to be writable for `img.php` to create new files. But its possible to first create all cache-files and then set the directory to be read-only. This will give you a way of shutting of `img.php` from creating new cache files. `img.php` will then continue to work for all images having a cached version but will fail if someone tries to create a new, not previously cached, version of the image.
###Post-processing with external tools
### Post-processing with external tools
You can use external tools to post-process the images to optimize the file size. This option is available for JPEG and for PNG images. Post-processing is disabled by default, edit `img_config.php` to enable it.
@@ -572,7 +572,7 @@ These tools for post processing is not a part of `CImage` and `img.php`, you nee
###Allowing remote download of images
### Allowing remote download of images
You can allow `img.php` to download remote images. That can be enabled in the config-file. However, before doing so, consider the implications on allowing anyone to download a file, hopefully an image, to your server and then the possibility to access it through the webserver.

View File

@@ -5,11 +5,82 @@ Revision history
[![Build Status](https://scrutinizer-ci.com/g/mosbth/cimage/badges/build.png?b=master)](https://scrutinizer-ci.com/g/mosbth/cimage/build-status/master)
v0.7.15 (2016-08-08)
v0.8.1 (2020-06-08)
-------------------------------------
* Added the [Lenna/Lena sample image](http://www.cs.cmu.edu/~chuck/lennapg/) as tif and created a png, jpeg and webp version using Imagick convert `convert lena.tif lena.{png,jpg,webp}`.
* Support saving to imgp format through `sa=webp`, #132.
* Updated version number in define.php.
v0.8.0 (2020-06-08)
-------------------------------------
* Enable to set JPEG image as interlaced, implement feature #177.
* Add function getValue() to read from querystring.
* Set PHP 7.0 as precondition (to prepare to update the codebase).
v0.7.23 (2020-05-06)
-------------------------------------
* Fix error in composer.json
v0.7.22 (2020-05-06)
-------------------------------------
* Update composer.json and move ext-gd from required to suggested to ease installation where cli does not have all extensions installed.
v0.7.21 (2020-01-15)
-------------------------------------
* Support PHP 7.4, some minor fixes with notices.
v0.7.20 (2017-11-06)
-------------------------------------
* Remove webroot/img/{round8.PNG,wider.JPEG,wider.JPG} to avoid unzip warning message when installing with composer.
* Adding docker-compose.yml #169.
v0.7.19 (2017-03-31)
-------------------------------------
* Move exception handler from functions.php to img.php #166.
* Correct XSS injection in `check_system.php`.
* Composer suggests ext-imagick and ext-curl.
v0.7.18 (2016-08-09)
-------------------------------------
* Made `&lossless` a requirement to not use the original image.
v0.7.17 (2016-08-09)
-------------------------------------
* Made `&lossless` part of the generated cache filename.
v0.7.16 (2016-08-09)
-------------------------------------
* Fix default mode to be production.
* Added pngquant as extra postprocessing utility for PNG-images, #154.
* Bug `&status` wrong variable name for fast track cache.
v0.7.15 (2016-08-09)
-------------------------------------
* Added the [Lenna/Lena sample image](http://www.cs.cmu.edu/~chuck/lennapg/) as tif and created a png, jpeg and webp version using Imagick convert `convert lena.tif lena.{png,jpg,webp}`, #152.
* Limited and basic support for WEBP format, se #132.
v0.7.14 (2016-08-08)
@@ -66,7 +137,7 @@ v0.7.8 (2015-12-06)
* HTTP error messages now 403, 404 and 500 as in #128 and #127.
* More examples on dealing with cache through bash `bin/cache.bash`, #129.
* Added conversion to sRGB using option `?srgb`. #120.
* Added conversion to sRGB using option `?srgb`. #120.
* Added Gitter badge to README, #126.
* Fix proper download url in README, #125.
* Change path in `webroot/htaccess` to make it work in current environment.
@@ -77,11 +148,11 @@ v0.7.7 (2015-10-21)
* One can now add a HTTP header for Cache-Control in the config file, #109.
* Added hook in img,php before CImage is called, #123.
* Added configuration for default jpeg quality and png compression in the config file, #107.
* Added configuration for default jpeg quality and png compression in the config file, #107.
* Strip comments and whitespace in imgs.php, #115.
* Bundle imgs.php did not have the correct mode.
* Adding option &status to get an overview of the installed on configured utilities, #116.
* Bug, all files saved as png-files, when not saving as specific file.
* Bug, all files saved as png-files, when not saving as specific file.
* Removed saving filename extension for alias images.
* Added option to decide if resample or resize when copying images internally. `&no-resample` makes resize, instead of resample as is default.
* Verbose now correctly states if transparent color is detected.
@@ -136,14 +207,14 @@ v0.7.1 (2015-07-25)
* Using `CWhitelist` for checking hotlinking to images, fix #88.
* Added mode for `test` which enables logging verbose mode to file, fix #97.
* Improved codestyle and added `phpcs.xml` to start using phpcs to check code style, fix #95.
* Adding `composer.json` for publishing on packagist.
* Adding `composer.json` for publishing on packagist.
* Add permalink to setup for comparing images with `webroot/compare/compare.php`, fix #92.
* Allow space in filename by using `urlencode()` and allow space as valid filenam character. fix #91.
* Support redirections for remote images, fix #87, fix #90.
* Support redirections for remote images, fix #87, fix #90.
* Improving usage of Travis and Scrutinizer.
* Naming cache-file using md5 for remote images, fix #86.
* Loading images without depending on filename extension, fix #85.
* Adding unittest with phpunit #84, fix #13
* Adding unittest with phpunit #84, fix #13
* Adding support for whitelist of remote hostnames, #84
* Adding phpdoc, fix #48.
* Adding travis, fix #15.

View File

@@ -1,5 +1,5 @@
{
"name": "mos/cimage",
"name": "mos/cimage",
"type": "library",
"description": "Process, scale, resize, crop and filter images.",
"keywords": ["image", "imageprocessing", "gd"],
@@ -18,11 +18,13 @@
"docs": "http://dbwebb.se/opensource/cimage"
},
"require": {
"php": ">=5.3",
"ext-gd": "*"
"php": ">=7.0"
},
"suggest": {
"ext-exif": "*"
"ext-curl": "*",
"ext-exif": "*",
"ext-gd": "*",
"ext-imagick": "*"
},
"autoload": {
"files": [

View File

@@ -1,6 +1,6 @@
<?php
// Version of cimage and img.php
define("CIMAGE_VERSION", "v0.7.15 (2016-08-09)");
define("CIMAGE_VERSION", "v0.8.1 (2020-06-08)");
// For CRemoteImage
define("CIMAGE_USER_AGENT", "CImage/" . CIMAGE_VERSION);

30
docker-compose.yml Normal file
View File

@@ -0,0 +1,30 @@
version: "3"
services:
php74:
image: anax/dev:php74-apache
ports:
- "8074:80"
volumes:
- .:/home/anax/repo
php71:
image: cimage/php71-apache:latest
#build: .
ports:
- "8071:80"
volumes:
- ./:/var/www/html/
php70:
image: cimage/php70-apache:latest
#build: .
ports:
- "8070:80"
volumes:
- .:/var/www/html
php56:
image: cimage/php56-apache:latest
#build: .
ports:
- "8056:80"
volumes:
- .:/var/www/html

View File

@@ -67,22 +67,6 @@ function errorPage($msg, $type = 500)
/**
* Custom exception handler.
*/
set_exception_handler(function ($exception) {
errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>",
500
);
});
/**
* Get input from query string or return default value if not set.
*
@@ -123,6 +107,25 @@ function getDefined($key, $defined, $undefined)
/**
* Get value of input from query string or else $undefined.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $undefined value to return when $key has no, or empty value in $_GET.
*
* @return mixed value as or $undefined.
*/
function getValue($key, $undefined)
{
$val = get($key);
if (is_null($val) || $val === "") {
return $undefined;
}
return $val;
}
/**
* Get value from config array or default if key is not set in config array.
*
@@ -148,7 +151,7 @@ function getConfig($key, $default)
*
* @return void or array.
*/
function verbose($msg = null)
function verbose($msg = null, $arg = "")
{
global $verbose, $verboseFile;
static $log = array();
@@ -161,5 +164,34 @@ function verbose($msg = null)
return $log;
}
$log[] = $msg;
if (is_null($arg)) {
$arg = "null";
} elseif ($arg === false) {
$arg = "false";
} elseif ($arg === true) {
$arg = "true";
}
$log[] = $msg . $arg;
}
/**
* Log when verbose mode, when used without argument it returns the result.
*
* @param string $msg to log.
*
* @return void or array.
*/
function checkExternalCommand($what, $enabled, $commandString)
{
$no = $enabled ? null : 'NOT';
$text = "Post processing $what is $no enabled.<br>";
list($command) = explode(" ", $commandString);
$no = is_executable($command) ? null : 'NOT';
$text .= "The command for $what is $no an executable.<br>";
return $text;
}

View File

@@ -2,7 +2,7 @@
echo 'Current PHP version: ' . phpversion() . '<br><br>';
echo 'Running on: ' . $_SERVER['SERVER_SOFTWARE'] . '<br><br>';
echo 'Running on: ' . htmlentities($_SERVER['SERVER_SOFTWARE']) . '<br><br>';
$no = extension_loaded('exif') ? null : 'NOT';
echo "Extension exif is $no loaded.<br>";
@@ -21,6 +21,9 @@ if (!$no) {
echo "<strong>Checking path for postprocessing tools</strong>";
echo "<br>pngquant: ";
system("which pngquant");
echo "<br>optipng: ";
system("which optipng");

View File

@@ -8,6 +8,22 @@
*
*/
/**
* Custom exception handler.
*/
set_exception_handler(function ($exception) {
errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>",
500
);
});
/**
* Get configuration options from file, if the file exists, else use $config
* if its defined or create an empty $config.
@@ -269,7 +285,7 @@ $allowRemote = getConfig('remote_allow', false);
if ($allowRemote && $passwordMatch !== false) {
$cacheRemote = $cache->getPathToSubdir("remote");
$pattern = getConfig('remote_pattern', null);
$img->setRemoteDownload($allowRemote, $cacheRemote, $pattern);
@@ -365,7 +381,7 @@ if ($dummyEnabled && $srcImage === $dummyFilename) {
matching file exists on the filesystem.',
404
);
}
}
}
if ($imagePathConstraint && !$dummyImage && !$remoteSource) {
@@ -424,7 +440,7 @@ if (isset($sizes[$newWidth])) {
}
// Support width as % of original width
if ($newWidth[strlen($newWidth)-1] == '%') {
if ($newWidth && $newWidth[strlen($newWidth)-1] == '%') {
is_numeric(substr($newWidth, 0, -1))
or errorPage('Width % not numeric.', 404);
} else {
@@ -449,7 +465,7 @@ if (isset($sizes[$newHeight])) {
}
// height
if ($newHeight[strlen($newHeight)-1] == '%') {
if ($newHeight && $newHeight[strlen($newHeight)-1] == '%') {
is_numeric(substr($newHeight, 0, -1))
or errorPage('Height % out of range.', 404);
} else {
@@ -480,7 +496,7 @@ $aspectRatioConstant = getConfig('aspect_ratio_constant', function () {
// Check to replace predefined aspect ratio
$aspectRatios = call_user_func($aspectRatioConstant);
$negateAspectRatio = ($aspectRatio[0] == '!') ? true : false;
$negateAspectRatio = ($aspectRatio && $aspectRatio[0] == '!') ? true : false;
$aspectRatio = $negateAspectRatio ? substr($aspectRatio, 1) : $aspectRatio;
if (isset($aspectRatios[$aspectRatio])) {
@@ -840,6 +856,9 @@ verbose("upscale = $upscale");
* Get details for post processing
*/
$postProcessing = getConfig('postprocessing', array(
'png_lossy' => false,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output',
'png_filter' => false,
'png_filter_cmd' => '/usr/local/bin/optipng -q',
@@ -852,6 +871,15 @@ $postProcessing = getConfig('postprocessing', array(
/**
* lossy - Do lossy postprocessing, if available.
*/
$lossy = getDefined(array('lossy'), true, null);
verbose("lossy = $lossy");
/**
* alias - Save resulting image to another alias name.
* Password always apply, must be defined.
@@ -892,6 +920,18 @@ if ($cacheControl) {
/**
* interlace - Enable configuration for interlaced progressive JPEG images.
*/
$interlaceConfig = getConfig('interlace', null);
$interlaceValue = getValue('interlace', null);
$interlaceDefined = getDefined('interlace', true, null);
$interlace = $interlaceValue ?? $interlaceDefined ?? $interlaceConfig;
verbose("interlace (configfile) = ", $interlaceConfig);
verbose("interlace = ", $interlace);
/**
* Prepare a dummy image and use it as source image.
*/
@@ -970,7 +1010,7 @@ if ($status) {
$res = $cache->getStatusOfSubdir("srgb");
$text .= "Cache srgb $res\n";
$res = $cache->getStatusOfSubdir($fasttrackCache);
$res = $cache->getStatusOfSubdir($fastTrackCache);
$text .= "Cache fasttrack $res\n";
$text .= "Alias path writable = " . is_writable($aliasPath) . "\n";
@@ -987,6 +1027,11 @@ if ($status) {
$no = extension_loaded('gd') ? null : 'NOT';
$text .= "Extension gd is $no loaded.<br>";
$text .= checkExternalCommand("PNG LOSSY", $postProcessing["png_lossy"], $postProcessing["png_lossy_cmd"]);
$text .= checkExternalCommand("PNG FILTER", $postProcessing["png_filter"], $postProcessing["png_filter_cmd"]);
$text .= checkExternalCommand("PNG DEFLATE", $postProcessing["png_deflate"], $postProcessing["png_deflate_cmd"]);
$text .= checkExternalCommand("JPEG OPTIMIZE", $postProcessing["jpeg_optimize"], $postProcessing["jpeg_optimize_cmd"]);
if (!$no) {
$text .= print_r(gd_info(), 1);
}
@@ -1048,6 +1093,7 @@ if (is_callable($hookBeforeCImage)) {
'blur' => $blur,
'convolve' => $convolve,
'rotateAfter' => $rotateAfter,
'interlace' => $interlace,
// Output format
'outputFormat' => $outputFormat,
@@ -1055,6 +1101,7 @@ if (is_callable($hookBeforeCImage)) {
// Other
'postProcessing' => $postProcessing,
'lossy' => $lossy,
));
verbose(print_r($allConfig, 1));
extract($allConfig);
@@ -1135,10 +1182,14 @@ $img->log("Incoming arguments: " . print_r(verbose(), 1))
'blur' => $blur,
'convolve' => $convolve,
'rotateAfter' => $rotateAfter,
'interlace' => $interlace,
// Output format
'outputFormat' => $outputFormat,
'dpr' => $dpr,
// Postprocessing using external tools
'lossy' => $lossy,
)
)
->loadImageDetails()

BIN
webroot/img/duke.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -67,7 +67,7 @@ return array(
*/
'image_path' => __DIR__ . '/img/',
'cache_path' => __DIR__ . '/../cache/',
//'alias_path' => __DIR__ . '/img/alias/',
'alias_path' => __DIR__ . '/img/alias/',
@@ -116,7 +116,7 @@ return array(
* password_type: 'text' // use plain password, not encoded,
*/
//'password_always' => false, // always require password,
//'password' => false, // "secret-password",
'password' => "moped", // "secret-password",
//'password_type' => 'text', // supports 'text', 'md5', 'hash',
@@ -321,8 +321,16 @@ return array(
* Post processing of images using external tools, set to true or false
* and set command to be executed.
*
* The png_lossy can alos have a value of null which means that its
* enabled but not used as default. Each image having the option
* &lossy will be processed. This means one can individually choose
* when to use the lossy processing.
*
* Default values.
*
* png_lossy: false
* png_lossy_cmd: '/usr/local/bin/pngquant --force --output'
*
* png_filter: false
* png_filter_cmd: '/usr/local/bin/optipng -q'
*
@@ -334,6 +342,9 @@ return array(
*/
/*
'postprocessing' => array(
'png_lossy' => null,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output',
'png_filter' => false,
'png_filter_cmd' => '/usr/local/bin/optipng -q',
@@ -474,6 +485,18 @@ return array(
"scale" => 14,
"luminanceStrategy" => 3,
"customCharacterSet" => null,
);
},*/
), */
/**
* Default options for creating interlaced progressive JPEG images. Set
* to true to always render jpeg images as interlaced. This setting can
* be overridden by using `?interlace`, `?interlace=true` or
* `?interlace=false`.
*
* Default values are:
* interlace: false
*/
/*'interlace' => false,*/
);

View File

@@ -38,11 +38,16 @@ $config = array(
// Version of cimage and img.php
define("CIMAGE_VERSION", "v0.7.13 (2016-08-08)");
define("CIMAGE_VERSION", "v0.8.1 (2020-06-08)");
// For CRemoteImage
define("CIMAGE_USER_AGENT", "CImage/" . CIMAGE_VERSION);
// Image type IMG_WEBP is only defined from 5.6.25
if (!defined("IMG_WEBP")) {
define("IMG_WEBP", -1);
}
/**
@@ -113,22 +118,6 @@ function errorPage($msg, $type = 500)
/**
* Custom exception handler.
*/
set_exception_handler(function ($exception) {
errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>",
500
);
});
/**
* Get input from query string or return default value if not set.
*
@@ -169,6 +158,25 @@ function getDefined($key, $defined, $undefined)
/**
* Get value of input from query string or else $undefined.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $undefined value to return when $key has no, or empty value in $_GET.
*
* @return mixed value as or $undefined.
*/
function getValue($key, $undefined)
{
$val = get($key);
if (is_null($val) || $val === "") {
return $undefined;
}
return $val;
}
/**
* Get value from config array or default if key is not set in config array.
*
@@ -194,7 +202,7 @@ function getConfig($key, $default)
*
* @return void or array.
*/
function verbose($msg = null)
function verbose($msg = null, $arg = "")
{
global $verbose, $verboseFile;
static $log = array();
@@ -207,7 +215,36 @@ function verbose($msg = null)
return $log;
}
$log[] = $msg;
if (is_null($arg)) {
$arg = "null";
} elseif ($arg === false) {
$arg = "false";
} elseif ($arg === true) {
$arg = "true";
}
$log[] = $msg . $arg;
}
/**
* Log when verbose mode, when used without argument it returns the result.
*
* @param string $msg to log.
*
* @return void or array.
*/
function checkExternalCommand($what, $enabled, $commandString)
{
$no = $enabled ? null : 'NOT';
$text = "Post processing $what is $no enabled.<br>";
list($command) = explode(" ", $commandString);
$no = is_executable($command) ? null : 'NOT';
$text .= "The command for $what is $no an executable.<br>";
return $text;
}
@@ -1255,6 +1292,13 @@ class CImage
/**
* Do lossy output using external postprocessing tools.
*/
private $lossy = null;
/**
* Verbose mode to print out a trace and display the created image
*/
@@ -1290,7 +1334,15 @@ class CImage
/**
* Path to command for filter optimize, for example optipng or null.
* Path to command for lossy optimize, for example pngquant.
*/
private $pngLossy;
private $pngLossyCmd;
/**
* Path to command for filter optimize, for example optipng.
*/
private $pngFilter;
private $pngFilterCmd;
@@ -1298,7 +1350,7 @@ class CImage
/**
* Path to command for deflate optimize, for example pngout or null.
* Path to command for deflate optimize, for example pngout.
*/
private $pngDeflate;
private $pngDeflateCmd;
@@ -1508,6 +1560,13 @@ class CImage
/*
* Use interlaced progressive mode for JPEG images.
*/
private $interlace = false;
/*
* Image copy strategy, defaults to RESAMPLE.
*/
@@ -1740,7 +1799,7 @@ class CImage
*/
private function checkFileExtension($extension)
{
$valid = array('jpg', 'jpeg', 'png', 'gif');
$valid = array('jpg', 'jpeg', 'png', 'gif', 'webp');
in_array(strtolower($extension), $valid)
or $this->raiseError('Not a valid file extension.');
@@ -1763,7 +1822,7 @@ class CImage
if ($extension == 'jpeg') {
$extension = 'jpg';
}
}
return $extension;
}
@@ -1923,10 +1982,14 @@ class CImage
'blur' => null,
'convolve' => null,
'rotateAfter' => null,
'interlace' => null,
// Output format
'outputFormat' => null,
'dpr' => 1,
// Postprocessing using external tools
'lossy' => null,
);
// Convert crop settings from string to array
@@ -2041,17 +2104,34 @@ class CImage
is_readable($file)
or $this->raiseError('Image file does not exist.');
// Get details on image
$info = list($this->width, $this->height, $this->fileType, $this->attr) = getimagesize($file);
$info = list($this->width, $this->height, $this->fileType) = getimagesize($file);
if (empty($info)) {
throw new Exception("The file doesn't seem to be a valid image.");
// To support webp
$this->fileType = false;
if (function_exists("exif_imagetype")) {
$this->fileType = exif_imagetype($file);
if ($this->fileType === false) {
if (function_exists("imagecreatefromwebp")) {
$webp = imagecreatefromwebp($file);
if ($webp !== false) {
$this->width = imagesx($webp);
$this->height = imagesy($webp);
$this->fileType = IMG_WEBP;
}
}
}
}
}
if (!$this->fileType) {
throw new Exception("Loading image details, the file doesn't seem to be a valid image.");
}
if ($this->verbose) {
$this->log("Loading image details for: {$file}");
$this->log(" Image width x height (type): {$this->width} x {$this->height} ({$this->fileType}).");
$this->log(" Image filesize: " . filesize($file) . " bytes.");
$this->log(" Image mimetype: " . image_type_to_mime_type($this->fileType));
$this->log(" Image mimetype: " . $this->getMimeType());
}
return $this;
@@ -2059,6 +2139,23 @@ class CImage
/**
* Get mime type for image type.
*
* @return $this
* @throws Exception
*/
protected function getMimeType()
{
if ($this->fileType === IMG_WEBP) {
return "image/webp";
}
return image_type_to_mime_type($this->fileType);
}
/**
* Init new width and height and do some sanity checks on constraints, before any
* processing can be done.
@@ -2071,13 +2168,15 @@ class CImage
$this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
// width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
if ($this->newWidth
&& $this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
$this->log("Setting new width based on % to {$this->newWidth}");
}
// height as %
if ($this->newHeight[strlen($this->newHeight)-1] == '%') {
if ($this->newHeight
&& $this->newHeight[strlen($this->newHeight)-1] == '%') {
$this->newHeight = $this->height * substr($this->newHeight, 0, -1) / 100;
$this->log("Setting new height based on % to {$this->newHeight}");
}
@@ -2229,7 +2328,7 @@ class CImage
$this->newWidth = $width;
$this->newHeight = $height;
}
// Get image dimensions for pre-resize image.
if ($this->cropToFit || $this->fillToFit) {
@@ -2413,6 +2512,7 @@ class CImage
&& !$this->autoRotate
&& !$this->bgColor
&& ($this->upscale === self::UPSCALE_DEFAULT)
&& !$this->lossy
) {
$this->log("Using original image.");
$this->output($this->pathToImage);
@@ -2446,6 +2546,8 @@ class CImage
$compress = $this->compress ? "_co{$this->compress}" : null;
$rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
$rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null;
$lossy = $this->lossy ? "_l" : null;
$interlace = $this->interlace ? "_i" : null;
$saveAs = $this->normalizeFileExtension();
$saveAs = $saveAs ? "_$saveAs" : null;
@@ -2512,7 +2614,7 @@ class CImage
. $quality . $filters . $sharpen . $emboss . $blur . $palette
. $optimize . $compress
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor
. $convolve . $copyStrat . $saveAs;
. $convolve . $copyStrat . $lossy . $interlace . $saveAs;
return $this->setTarget($file, $base);
}
@@ -2570,9 +2672,14 @@ class CImage
$this->setSource($src, $dir);
}
$this->loadImageDetails($this->pathToImage);
$this->loadImageDetails();
$this->image = imagecreatefromstring(file_get_contents($this->pathToImage));
if ($this->fileType === IMG_WEBP) {
$this->image = imagecreatefromwebp($this->pathToImage);
} else {
$imageAsString = file_get_contents($this->pathToImage);
$this->image = imagecreatefromstring($imageAsString);
}
if ($this->image === false) {
throw new Exception("Could not load image.");
}
@@ -2827,7 +2934,7 @@ class CImage
// Resize by crop to fit
$this->log("Resizing using strategy - Crop to fit");
if (!$this->upscale
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
$this->log("Resizing - smaller image, do not upscale.");
@@ -3406,6 +3513,14 @@ class CImage
$this->jpegOptimizeCmd = null;
}
if (array_key_exists("png_lossy", $options)
&& $options['png_lossy'] !== false) {
$this->pngLossy = $options['png_lossy'];
$this->pngLossyCmd = $options['png_lossy_cmd'];
} else {
$this->pngLossyCmd = null;
}
if (isset($options['png_filter']) && $options['png_filter']) {
$this->pngFilterCmd = $options['png_filter_cmd'];
} else {
@@ -3433,9 +3548,11 @@ class CImage
// switch on mimetype
if (isset($this->extension)) {
return strtolower($this->extension);
} else {
return substr(image_type_to_extension($this->fileType), 1);
} elseif ($this->fileType === IMG_WEBP) {
return "webp";
}
return substr(image_type_to_extension($this->fileType), 1);
}
@@ -3469,6 +3586,12 @@ class CImage
case 'jpeg':
case 'jpg':
// Set as interlaced progressive JPEG
if ($this->interlace) {
$this->Log("Set JPEG image to be interlaced.");
$res = imageinterlace($this->image, true);
}
$this->Log("Saving image as JPEG to cache using quality = {$this->quality}.");
imagejpeg($this->image, $this->cacheFileName, $this->quality);
@@ -3491,6 +3614,11 @@ class CImage
imagegif($this->image, $this->cacheFileName);
break;
case 'webp':
$this->Log("Saving image as WEBP to cache using quality = {$this->quality}.");
imagewebp($this->image, $this->cacheFileName, $this->quality);
break;
case 'png':
default:
$this->Log("Saving image as PNG to cache using compression = {$this->compress}.");
@@ -3500,6 +3628,24 @@ class CImage
imagesavealpha($this->image, true);
imagepng($this->image, $this->cacheFileName, $this->compress);
// Use external program to process lossy PNG, if defined
$lossyEnabled = $this->pngLossy === true;
$lossySoftEnabled = $this->pngLossy === null;
$lossyActiveEnabled = $this->lossy === true;
if ($lossyEnabled || ($lossySoftEnabled && $lossyActiveEnabled)) {
if ($this->verbose) {
clearstatcache();
$this->log("Lossy enabled: $lossyEnabled");
$this->log("Lossy soft enabled: $lossySoftEnabled");
$this->Log("Filesize before lossy optimize: " . filesize($this->cacheFileName) . " bytes.");
}
$res = array();
$cmd = $this->pngLossyCmd . " $this->cacheFileName $this->cacheFileName";
exec($cmd, $res);
$this->Log($cmd);
$this->Log($res);
}
// Use external program to filter PNG, if defined
if ($this->pngFilterCmd) {
if ($this->verbose) {
@@ -3679,6 +3825,7 @@ class CImage
$format = $this->outputFormat;
}
$this->log("### Output");
$this->log("Output format is: $format");
if (!$this->verbose && $format == 'json') {
@@ -3712,7 +3859,8 @@ class CImage
$this->fastTrackCache->addHeader($header);
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
&& strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
if ($this->verbose) {
$this->log("304 not modified");
@@ -3727,10 +3875,8 @@ class CImage
} else {
// Get details on image
$info = getimagesize($file);
!empty($info) or $this->raiseError("The file doesn't seem to be an image.");
$mime = $info['mime'];
$this->loadImageDetails($file);
$mime = $this->getMimeType();
$size = filesize($file);
if ($this->verbose) {
@@ -3791,7 +3937,7 @@ class CImage
$this->load($file);
$details['filename'] = basename($file);
$details['mimeType'] = image_type_to_mime_type($this->fileType);
$details['mimeType'] = $this->getMimeType($this->fileType);
$details['width'] = $this->width;
$details['height'] = $this->height;
$details['aspectRatio'] = round($this->width / $this->height, 3);
@@ -3893,6 +4039,7 @@ class CImage
private function verboseOutput()
{
$log = null;
$this->log("### Summary of verbose log");
$this->log("As JSON: \n" . $this->json());
$this->log("Memory peak: " . round(memory_get_peak_usage() /1024/1024) . "M");
$this->log("Memory limit: " . ini_get('memory_limit'));
@@ -4299,6 +4446,22 @@ class CFastTrackCache
*
*/
/**
* Custom exception handler.
*/
set_exception_handler(function ($exception) {
errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>",
500
);
});
/**
* Get configuration options from file, if the file exists, else use $config
* if its defined or create an empty $config.
@@ -4560,7 +4723,7 @@ $allowRemote = getConfig('remote_allow', false);
if ($allowRemote && $passwordMatch !== false) {
$cacheRemote = $cache->getPathToSubdir("remote");
$pattern = getConfig('remote_pattern', null);
$img->setRemoteDownload($allowRemote, $cacheRemote, $pattern);
@@ -4656,7 +4819,7 @@ if ($dummyEnabled && $srcImage === $dummyFilename) {
matching file exists on the filesystem.',
404
);
}
}
}
if ($imagePathConstraint && !$dummyImage && !$remoteSource) {
@@ -4715,7 +4878,7 @@ if (isset($sizes[$newWidth])) {
}
// Support width as % of original width
if ($newWidth[strlen($newWidth)-1] == '%') {
if ($newWidth && $newWidth[strlen($newWidth)-1] == '%') {
is_numeric(substr($newWidth, 0, -1))
or errorPage('Width % not numeric.', 404);
} else {
@@ -4740,7 +4903,7 @@ if (isset($sizes[$newHeight])) {
}
// height
if ($newHeight[strlen($newHeight)-1] == '%') {
if ($newHeight && $newHeight[strlen($newHeight)-1] == '%') {
is_numeric(substr($newHeight, 0, -1))
or errorPage('Height % out of range.', 404);
} else {
@@ -4771,7 +4934,7 @@ $aspectRatioConstant = getConfig('aspect_ratio_constant', function () {
// Check to replace predefined aspect ratio
$aspectRatios = call_user_func($aspectRatioConstant);
$negateAspectRatio = ($aspectRatio[0] == '!') ? true : false;
$negateAspectRatio = ($aspectRatio && $aspectRatio[0] == '!') ? true : false;
$aspectRatio = $negateAspectRatio ? substr($aspectRatio, 1) : $aspectRatio;
if (isset($aspectRatios[$aspectRatio])) {
@@ -5131,6 +5294,9 @@ verbose("upscale = $upscale");
* Get details for post processing
*/
$postProcessing = getConfig('postprocessing', array(
'png_lossy' => false,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output',
'png_filter' => false,
'png_filter_cmd' => '/usr/local/bin/optipng -q',
@@ -5143,6 +5309,15 @@ $postProcessing = getConfig('postprocessing', array(
/**
* lossy - Do lossy postprocessing, if available.
*/
$lossy = getDefined(array('lossy'), true, null);
verbose("lossy = $lossy");
/**
* alias - Save resulting image to another alias name.
* Password always apply, must be defined.
@@ -5183,6 +5358,18 @@ if ($cacheControl) {
/**
* interlace - Enable configuration for interlaced progressive JPEG images.
*/
$interlaceConfig = getConfig('interlace', null);
$interlaceValue = getValue('interlace', null);
$interlaceDefined = getDefined('interlace', true, null);
$interlace = $interlaceValue ?? $interlaceDefined ?? $interlaceConfig;
verbose("interlace (configfile) = ", $interlaceConfig);
verbose("interlace = ", $interlace);
/**
* Prepare a dummy image and use it as source image.
*/
@@ -5261,7 +5448,7 @@ if ($status) {
$res = $cache->getStatusOfSubdir("srgb");
$text .= "Cache srgb $res\n";
$res = $cache->getStatusOfSubdir($fasttrackCache);
$res = $cache->getStatusOfSubdir($fastTrackCache);
$text .= "Cache fasttrack $res\n";
$text .= "Alias path writable = " . is_writable($aliasPath) . "\n";
@@ -5278,6 +5465,11 @@ if ($status) {
$no = extension_loaded('gd') ? null : 'NOT';
$text .= "Extension gd is $no loaded.<br>";
$text .= checkExternalCommand("PNG LOSSY", $postProcessing["png_lossy"], $postProcessing["png_lossy_cmd"]);
$text .= checkExternalCommand("PNG FILTER", $postProcessing["png_filter"], $postProcessing["png_filter_cmd"]);
$text .= checkExternalCommand("PNG DEFLATE", $postProcessing["png_deflate"], $postProcessing["png_deflate_cmd"]);
$text .= checkExternalCommand("JPEG OPTIMIZE", $postProcessing["jpeg_optimize"], $postProcessing["jpeg_optimize_cmd"]);
if (!$no) {
$text .= print_r(gd_info(), 1);
}
@@ -5339,6 +5531,7 @@ if (is_callable($hookBeforeCImage)) {
'blur' => $blur,
'convolve' => $convolve,
'rotateAfter' => $rotateAfter,
'interlace' => $interlace,
// Output format
'outputFormat' => $outputFormat,
@@ -5346,6 +5539,7 @@ if (is_callable($hookBeforeCImage)) {
// Other
'postProcessing' => $postProcessing,
'lossy' => $lossy,
));
verbose(print_r($allConfig, 1));
extract($allConfig);
@@ -5426,10 +5620,14 @@ $img->log("Incoming arguments: " . print_r(verbose(), 1))
'blur' => $blur,
'convolve' => $convolve,
'rotateAfter' => $rotateAfter,
'interlace' => $interlace,
// Output format
'outputFormat' => $outputFormat,
'dpr' => $dpr,
// Postprocessing using external tools
'lossy' => $lossy,
)
)
->loadImageDetails()

View File

@@ -38,11 +38,16 @@ $config = array(
// Version of cimage and img.php
define("CIMAGE_VERSION", "v0.7.13 (2016-08-08)");
define("CIMAGE_VERSION", "v0.8.1 (2020-06-08)");
// For CRemoteImage
define("CIMAGE_USER_AGENT", "CImage/" . CIMAGE_VERSION);
// Image type IMG_WEBP is only defined from 5.6.25
if (!defined("IMG_WEBP")) {
define("IMG_WEBP", -1);
}
/**
@@ -113,22 +118,6 @@ function errorPage($msg, $type = 500)
/**
* Custom exception handler.
*/
set_exception_handler(function ($exception) {
errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>",
500
);
});
/**
* Get input from query string or return default value if not set.
*
@@ -169,6 +158,25 @@ function getDefined($key, $defined, $undefined)
/**
* Get value of input from query string or else $undefined.
*
* @param mixed $key as string or array of string values to look for in $_GET.
* @param mixed $undefined value to return when $key has no, or empty value in $_GET.
*
* @return mixed value as or $undefined.
*/
function getValue($key, $undefined)
{
$val = get($key);
if (is_null($val) || $val === "") {
return $undefined;
}
return $val;
}
/**
* Get value from config array or default if key is not set in config array.
*
@@ -194,7 +202,7 @@ function getConfig($key, $default)
*
* @return void or array.
*/
function verbose($msg = null)
function verbose($msg = null, $arg = "")
{
global $verbose, $verboseFile;
static $log = array();
@@ -207,7 +215,36 @@ function verbose($msg = null)
return $log;
}
$log[] = $msg;
if (is_null($arg)) {
$arg = "null";
} elseif ($arg === false) {
$arg = "false";
} elseif ($arg === true) {
$arg = "true";
}
$log[] = $msg . $arg;
}
/**
* Log when verbose mode, when used without argument it returns the result.
*
* @param string $msg to log.
*
* @return void or array.
*/
function checkExternalCommand($what, $enabled, $commandString)
{
$no = $enabled ? null : 'NOT';
$text = "Post processing $what is $no enabled.<br>";
list($command) = explode(" ", $commandString);
$no = is_executable($command) ? null : 'NOT';
$text .= "The command for $what is $no an executable.<br>";
return $text;
}
@@ -1255,6 +1292,13 @@ class CImage
/**
* Do lossy output using external postprocessing tools.
*/
private $lossy = null;
/**
* Verbose mode to print out a trace and display the created image
*/
@@ -1290,7 +1334,15 @@ class CImage
/**
* Path to command for filter optimize, for example optipng or null.
* Path to command for lossy optimize, for example pngquant.
*/
private $pngLossy;
private $pngLossyCmd;
/**
* Path to command for filter optimize, for example optipng.
*/
private $pngFilter;
private $pngFilterCmd;
@@ -1298,7 +1350,7 @@ class CImage
/**
* Path to command for deflate optimize, for example pngout or null.
* Path to command for deflate optimize, for example pngout.
*/
private $pngDeflate;
private $pngDeflateCmd;
@@ -1508,6 +1560,13 @@ class CImage
/*
* Use interlaced progressive mode for JPEG images.
*/
private $interlace = false;
/*
* Image copy strategy, defaults to RESAMPLE.
*/
@@ -1740,7 +1799,7 @@ class CImage
*/
private function checkFileExtension($extension)
{
$valid = array('jpg', 'jpeg', 'png', 'gif');
$valid = array('jpg', 'jpeg', 'png', 'gif', 'webp');
in_array(strtolower($extension), $valid)
or $this->raiseError('Not a valid file extension.');
@@ -1763,7 +1822,7 @@ class CImage
if ($extension == 'jpeg') {
$extension = 'jpg';
}
}
return $extension;
}
@@ -1923,10 +1982,14 @@ class CImage
'blur' => null,
'convolve' => null,
'rotateAfter' => null,
'interlace' => null,
// Output format
'outputFormat' => null,
'dpr' => 1,
// Postprocessing using external tools
'lossy' => null,
);
// Convert crop settings from string to array
@@ -2041,17 +2104,34 @@ class CImage
is_readable($file)
or $this->raiseError('Image file does not exist.');
// Get details on image
$info = list($this->width, $this->height, $this->fileType, $this->attr) = getimagesize($file);
$info = list($this->width, $this->height, $this->fileType) = getimagesize($file);
if (empty($info)) {
throw new Exception("The file doesn't seem to be a valid image.");
// To support webp
$this->fileType = false;
if (function_exists("exif_imagetype")) {
$this->fileType = exif_imagetype($file);
if ($this->fileType === false) {
if (function_exists("imagecreatefromwebp")) {
$webp = imagecreatefromwebp($file);
if ($webp !== false) {
$this->width = imagesx($webp);
$this->height = imagesy($webp);
$this->fileType = IMG_WEBP;
}
}
}
}
}
if (!$this->fileType) {
throw new Exception("Loading image details, the file doesn't seem to be a valid image.");
}
if ($this->verbose) {
$this->log("Loading image details for: {$file}");
$this->log(" Image width x height (type): {$this->width} x {$this->height} ({$this->fileType}).");
$this->log(" Image filesize: " . filesize($file) . " bytes.");
$this->log(" Image mimetype: " . image_type_to_mime_type($this->fileType));
$this->log(" Image mimetype: " . $this->getMimeType());
}
return $this;
@@ -2059,6 +2139,23 @@ class CImage
/**
* Get mime type for image type.
*
* @return $this
* @throws Exception
*/
protected function getMimeType()
{
if ($this->fileType === IMG_WEBP) {
return "image/webp";
}
return image_type_to_mime_type($this->fileType);
}
/**
* Init new width and height and do some sanity checks on constraints, before any
* processing can be done.
@@ -2071,13 +2168,15 @@ class CImage
$this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");
// width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
if ($this->newWidth
&& $this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
$this->log("Setting new width based on % to {$this->newWidth}");
}
// height as %
if ($this->newHeight[strlen($this->newHeight)-1] == '%') {
if ($this->newHeight
&& $this->newHeight[strlen($this->newHeight)-1] == '%') {
$this->newHeight = $this->height * substr($this->newHeight, 0, -1) / 100;
$this->log("Setting new height based on % to {$this->newHeight}");
}
@@ -2229,7 +2328,7 @@ class CImage
$this->newWidth = $width;
$this->newHeight = $height;
}
// Get image dimensions for pre-resize image.
if ($this->cropToFit || $this->fillToFit) {
@@ -2413,6 +2512,7 @@ class CImage
&& !$this->autoRotate
&& !$this->bgColor
&& ($this->upscale === self::UPSCALE_DEFAULT)
&& !$this->lossy
) {
$this->log("Using original image.");
$this->output($this->pathToImage);
@@ -2446,6 +2546,8 @@ class CImage
$compress = $this->compress ? "_co{$this->compress}" : null;
$rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
$rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null;
$lossy = $this->lossy ? "_l" : null;
$interlace = $this->interlace ? "_i" : null;
$saveAs = $this->normalizeFileExtension();
$saveAs = $saveAs ? "_$saveAs" : null;
@@ -2512,7 +2614,7 @@ class CImage
. $quality . $filters . $sharpen . $emboss . $blur . $palette
. $optimize . $compress
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor
. $convolve . $copyStrat . $saveAs;
. $convolve . $copyStrat . $lossy . $interlace . $saveAs;
return $this->setTarget($file, $base);
}
@@ -2570,9 +2672,14 @@ class CImage
$this->setSource($src, $dir);
}
$this->loadImageDetails($this->pathToImage);
$this->loadImageDetails();
$this->image = imagecreatefromstring(file_get_contents($this->pathToImage));
if ($this->fileType === IMG_WEBP) {
$this->image = imagecreatefromwebp($this->pathToImage);
} else {
$imageAsString = file_get_contents($this->pathToImage);
$this->image = imagecreatefromstring($imageAsString);
}
if ($this->image === false) {
throw new Exception("Could not load image.");
}
@@ -2827,7 +2934,7 @@ class CImage
// Resize by crop to fit
$this->log("Resizing using strategy - Crop to fit");
if (!$this->upscale
if (!$this->upscale
&& ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
$this->log("Resizing - smaller image, do not upscale.");
@@ -3406,6 +3513,14 @@ class CImage
$this->jpegOptimizeCmd = null;
}
if (array_key_exists("png_lossy", $options)
&& $options['png_lossy'] !== false) {
$this->pngLossy = $options['png_lossy'];
$this->pngLossyCmd = $options['png_lossy_cmd'];
} else {
$this->pngLossyCmd = null;
}
if (isset($options['png_filter']) && $options['png_filter']) {
$this->pngFilterCmd = $options['png_filter_cmd'];
} else {
@@ -3433,9 +3548,11 @@ class CImage
// switch on mimetype
if (isset($this->extension)) {
return strtolower($this->extension);
} else {
return substr(image_type_to_extension($this->fileType), 1);
} elseif ($this->fileType === IMG_WEBP) {
return "webp";
}
return substr(image_type_to_extension($this->fileType), 1);
}
@@ -3469,6 +3586,12 @@ class CImage
case 'jpeg':
case 'jpg':
// Set as interlaced progressive JPEG
if ($this->interlace) {
$this->Log("Set JPEG image to be interlaced.");
$res = imageinterlace($this->image, true);
}
$this->Log("Saving image as JPEG to cache using quality = {$this->quality}.");
imagejpeg($this->image, $this->cacheFileName, $this->quality);
@@ -3491,6 +3614,11 @@ class CImage
imagegif($this->image, $this->cacheFileName);
break;
case 'webp':
$this->Log("Saving image as WEBP to cache using quality = {$this->quality}.");
imagewebp($this->image, $this->cacheFileName, $this->quality);
break;
case 'png':
default:
$this->Log("Saving image as PNG to cache using compression = {$this->compress}.");
@@ -3500,6 +3628,24 @@ class CImage
imagesavealpha($this->image, true);
imagepng($this->image, $this->cacheFileName, $this->compress);
// Use external program to process lossy PNG, if defined
$lossyEnabled = $this->pngLossy === true;
$lossySoftEnabled = $this->pngLossy === null;
$lossyActiveEnabled = $this->lossy === true;
if ($lossyEnabled || ($lossySoftEnabled && $lossyActiveEnabled)) {
if ($this->verbose) {
clearstatcache();
$this->log("Lossy enabled: $lossyEnabled");
$this->log("Lossy soft enabled: $lossySoftEnabled");
$this->Log("Filesize before lossy optimize: " . filesize($this->cacheFileName) . " bytes.");
}
$res = array();
$cmd = $this->pngLossyCmd . " $this->cacheFileName $this->cacheFileName";
exec($cmd, $res);
$this->Log($cmd);
$this->Log($res);
}
// Use external program to filter PNG, if defined
if ($this->pngFilterCmd) {
if ($this->verbose) {
@@ -3679,6 +3825,7 @@ class CImage
$format = $this->outputFormat;
}
$this->log("### Output");
$this->log("Output format is: $format");
if (!$this->verbose && $format == 'json') {
@@ -3712,7 +3859,8 @@ class CImage
$this->fastTrackCache->addHeader($header);
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
&& strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
if ($this->verbose) {
$this->log("304 not modified");
@@ -3727,10 +3875,8 @@ class CImage
} else {
// Get details on image
$info = getimagesize($file);
!empty($info) or $this->raiseError("The file doesn't seem to be an image.");
$mime = $info['mime'];
$this->loadImageDetails($file);
$mime = $this->getMimeType();
$size = filesize($file);
if ($this->verbose) {
@@ -3791,7 +3937,7 @@ class CImage
$this->load($file);
$details['filename'] = basename($file);
$details['mimeType'] = image_type_to_mime_type($this->fileType);
$details['mimeType'] = $this->getMimeType($this->fileType);
$details['width'] = $this->width;
$details['height'] = $this->height;
$details['aspectRatio'] = round($this->width / $this->height, 3);
@@ -3893,6 +4039,7 @@ class CImage
private function verboseOutput()
{
$log = null;
$this->log("### Summary of verbose log");
$this->log("As JSON: \n" . $this->json());
$this->log("Memory peak: " . round(memory_get_peak_usage() /1024/1024) . "M");
$this->log("Memory limit: " . ini_get('memory_limit'));
@@ -4299,6 +4446,22 @@ class CFastTrackCache
*
*/
/**
* Custom exception handler.
*/
set_exception_handler(function ($exception) {
errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>",
500
);
});
/**
* Get configuration options from file, if the file exists, else use $config
* if its defined or create an empty $config.
@@ -4560,7 +4723,7 @@ $allowRemote = getConfig('remote_allow', false);
if ($allowRemote && $passwordMatch !== false) {
$cacheRemote = $cache->getPathToSubdir("remote");
$pattern = getConfig('remote_pattern', null);
$img->setRemoteDownload($allowRemote, $cacheRemote, $pattern);
@@ -4656,7 +4819,7 @@ if ($dummyEnabled && $srcImage === $dummyFilename) {
matching file exists on the filesystem.',
404
);
}
}
}
if ($imagePathConstraint && !$dummyImage && !$remoteSource) {
@@ -4715,7 +4878,7 @@ if (isset($sizes[$newWidth])) {
}
// Support width as % of original width
if ($newWidth[strlen($newWidth)-1] == '%') {
if ($newWidth && $newWidth[strlen($newWidth)-1] == '%') {
is_numeric(substr($newWidth, 0, -1))
or errorPage('Width % not numeric.', 404);
} else {
@@ -4740,7 +4903,7 @@ if (isset($sizes[$newHeight])) {
}
// height
if ($newHeight[strlen($newHeight)-1] == '%') {
if ($newHeight && $newHeight[strlen($newHeight)-1] == '%') {
is_numeric(substr($newHeight, 0, -1))
or errorPage('Height % out of range.', 404);
} else {
@@ -4771,7 +4934,7 @@ $aspectRatioConstant = getConfig('aspect_ratio_constant', function () {
// Check to replace predefined aspect ratio
$aspectRatios = call_user_func($aspectRatioConstant);
$negateAspectRatio = ($aspectRatio[0] == '!') ? true : false;
$negateAspectRatio = ($aspectRatio && $aspectRatio[0] == '!') ? true : false;
$aspectRatio = $negateAspectRatio ? substr($aspectRatio, 1) : $aspectRatio;
if (isset($aspectRatios[$aspectRatio])) {
@@ -5131,6 +5294,9 @@ verbose("upscale = $upscale");
* Get details for post processing
*/
$postProcessing = getConfig('postprocessing', array(
'png_lossy' => false,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output',
'png_filter' => false,
'png_filter_cmd' => '/usr/local/bin/optipng -q',
@@ -5143,6 +5309,15 @@ $postProcessing = getConfig('postprocessing', array(
/**
* lossy - Do lossy postprocessing, if available.
*/
$lossy = getDefined(array('lossy'), true, null);
verbose("lossy = $lossy");
/**
* alias - Save resulting image to another alias name.
* Password always apply, must be defined.
@@ -5183,6 +5358,18 @@ if ($cacheControl) {
/**
* interlace - Enable configuration for interlaced progressive JPEG images.
*/
$interlaceConfig = getConfig('interlace', null);
$interlaceValue = getValue('interlace', null);
$interlaceDefined = getDefined('interlace', true, null);
$interlace = $interlaceValue ?? $interlaceDefined ?? $interlaceConfig;
verbose("interlace (configfile) = ", $interlaceConfig);
verbose("interlace = ", $interlace);
/**
* Prepare a dummy image and use it as source image.
*/
@@ -5261,7 +5448,7 @@ if ($status) {
$res = $cache->getStatusOfSubdir("srgb");
$text .= "Cache srgb $res\n";
$res = $cache->getStatusOfSubdir($fasttrackCache);
$res = $cache->getStatusOfSubdir($fastTrackCache);
$text .= "Cache fasttrack $res\n";
$text .= "Alias path writable = " . is_writable($aliasPath) . "\n";
@@ -5278,6 +5465,11 @@ if ($status) {
$no = extension_loaded('gd') ? null : 'NOT';
$text .= "Extension gd is $no loaded.<br>";
$text .= checkExternalCommand("PNG LOSSY", $postProcessing["png_lossy"], $postProcessing["png_lossy_cmd"]);
$text .= checkExternalCommand("PNG FILTER", $postProcessing["png_filter"], $postProcessing["png_filter_cmd"]);
$text .= checkExternalCommand("PNG DEFLATE", $postProcessing["png_deflate"], $postProcessing["png_deflate_cmd"]);
$text .= checkExternalCommand("JPEG OPTIMIZE", $postProcessing["jpeg_optimize"], $postProcessing["jpeg_optimize_cmd"]);
if (!$no) {
$text .= print_r(gd_info(), 1);
}
@@ -5339,6 +5531,7 @@ if (is_callable($hookBeforeCImage)) {
'blur' => $blur,
'convolve' => $convolve,
'rotateAfter' => $rotateAfter,
'interlace' => $interlace,
// Output format
'outputFormat' => $outputFormat,
@@ -5346,6 +5539,7 @@ if (is_callable($hookBeforeCImage)) {
// Other
'postProcessing' => $postProcessing,
'lossy' => $lossy,
));
verbose(print_r($allConfig, 1));
extract($allConfig);
@@ -5426,10 +5620,14 @@ $img->log("Incoming arguments: " . print_r(verbose(), 1))
'blur' => $blur,
'convolve' => $convolve,
'rotateAfter' => $rotateAfter,
'interlace' => $interlace,
// Output format
'outputFormat' => $outputFormat,
'dpr' => $dpr,
// Postprocessing using external tools
'lossy' => $lossy,
)
)
->loadImageDetails()

File diff suppressed because one or more lines are too long