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

Compare commits

..

45 Commits

Author SHA1 Message Date
Mikael Roos
ccc59fbb3a Prepare to tag v0.5.3 2014-11-21 21:14:19 +01:00
Mikael Roos
e85bc6cee5 Adding testfiles with capital extension 2014-11-21 21:12:22 +01:00
Mikael Roos
3b7485b3b9 Support filenames of uppercase JPEG, JPG, PNG and GIF, as proposed in #37. 2014-11-21 21:01:22 +01:00
Mikael Roos
c83eac8c3c Changing CImage::output() as proposed in #37. 2014-11-21 20:20:35 +01:00
Mikael Roos
78ac29752e Adding security check that image filename is always below the path
`image_path` as specified in `img_config.php` #37.
2014-11-21 19:57:11 +01:00
Mikael Roos
4b64237a4c Changeing comment 2014-11-21 19:37:33 +01:00
Mikael Roos
85f0e3e7d3 moving testilfes to one dir 2014-11-21 19:35:04 +01:00
Mikael Roos
da227d49dd Use the new config item for checking valid filename characters. 2014-11-21 19:34:01 +01:00
Mikael Roos
26449a3236 Adding configuration item to set what valid characters are for the image
filename.
2014-11-21 19:32:05 +01:00
Mikael Roos
20a4f848b7 Moved all test-files into own directory. 2014-11-21 19:24:36 +01:00
Mikael Roos
ec3a6f3491 check if extension exif is loaded #36 2014-09-01 01:27:45 +02:00
Mikael Roos
8523ef5e31 adding testcases for #36 2014-09-01 00:38:26 +02:00
Mikael Roos
63112f58b1 Cleaning up code #23, added rotate and autoRotate #36 2014-08-31 23:58:02 +02:00
Mikael Roos
c96ed10f80 Cleaning up code #23, added rotate and autoRotate #36 2014-08-31 23:57:34 +02:00
Mikael Roos
67524bd090 Adding MIT license text as should be. 2014-08-21 02:46:00 +02:00
Mikael Roos
1f9a11747b Readding cache dir 2014-08-21 02:37:58 +02:00
Mikael Roos
4ed55d59e3 finally setting jpeg_ignore_warning to true #34 2014-08-21 02:30:32 +02:00
Mikael Roos
8dde82993b improving error handling when opening (non)-recoverable images 2014-08-21 02:23:15 +02:00
Mikael Roos
fb261b1b05 improving error handling when opening (non)-recoverable images 2014-08-21 02:22:20 +02:00
Mikael Roos
1a6cef6580 improving error handling when opening (non)-recoverable images 2014-08-21 02:21:32 +02:00
Mikael Roos
2643d4954f must have @ before open images, warning contra notice, recoverable and non recoverable 2014-08-21 02:13:01 +02:00
Mikael Roos
ef1d56085a Verbose error when failing to open image 2014-08-21 02:08:49 +02:00
Mikael Roos
28cda0ea28 changing error handling to dir when failing to open a corrupt image 2014-08-21 02:02:50 +02:00
Mikael Roos
ed9949b717 adding corrupt jpegs for #34 2014-08-21 01:37:05 +02:00
Mikael Roos
c249a472d6 adding pre 2014-08-21 01:25:19 +02:00
Mikael Roos
c8c2e5f95f updated with change #34 2014-08-21 01:20:50 +02:00
Mikael Roos
8db8501eb0 Setting gd.jpeg_ignore_warning to true #34 as default 2014-08-21 01:19:56 +02:00
Mikael Roos
2b0b3859a7 Extended info printed #35 2014-08-21 00:31:05 +02:00
Mikael Roos
1e70883771 Now sends 404 header as it should #32 when providing a error page 2014-08-20 23:55:23 +02:00
Mikael Roos
b2002d0087 Now sends 404 header as it should #32 when providing a error page 2014-08-20 23:54:13 +02:00
Mikael Roos
9aac0cde8e Removed old description, its in the README now, focus on testcases. 2014-08-20 23:49:25 +02:00
Mikael Roos
3dec1d0a6d Merge pull request #33 from garagesocial/patch-2
Update CImage.php
2014-08-15 08:54:59 +02:00
Garagesocial
577e5fa5d4 Update CImage.php 2014-08-15 02:07:07 -04:00
Mikael Roos
38a6c86317 Merge pull request #31 from garagesocial/patch-1
Comment Typo CImage.php
2014-08-07 08:18:17 +02:00
Garagesocial
dcb390a17b Comment Typo CImage.php 2014-08-06 19:02:55 -04:00
Mikael Roos
9e461787c7 comment reflected wrongly to area variabel, issue #29 2014-05-20 01:39:51 +02:00
Mikael Roos
a96cd7a4cc changed colors 2014-05-20 01:15:53 +02:00
Mikael Roos
98d971204f correcting links to image 2014-05-20 01:04:26 +02:00
Mikael Roos
9707065b9e trying and failing to verify issue 29 2014-05-20 00:56:30 +02:00
Mikael Roos
5cd953b9b3 issue 29 and code formatting 2014-05-20 00:56:08 +02:00
Mikael Roos
b4f51f3438 code formatting and making log() public 2014-05-20 00:55:43 +02:00
Mikael Roos
dedd01bc71 Helper to check if img.php is supported on this system 2014-05-20 00:12:46 +02:00
Mikael Roos
67d0cf27ee code formatting and slightly reformatted verbose output 2014-05-20 00:12:09 +02:00
Mikael Roos
161fdfe769 code formatting 2014-05-20 00:00:27 +02:00
Mikael Roos
6cff9ac105 better error reporting for development environment 2014-05-19 23:59:50 +02:00
32 changed files with 1078 additions and 262 deletions

4
.gitignore vendored
View File

@@ -1,4 +1,4 @@
# Cache files #
######################
cache/_*

cache/*

View File

@@ -91,7 +91,7 @@ class CImage
/**
* File extension to use when saving image
* File extension to use when saving image.
*/
private $extension;
@@ -151,33 +151,46 @@ class CImage
private $jpegOptimize;
/**
* Image dimensions, calculated from loaded image.
*/
private $width; // Calculated from source image
private $height; // Calculated from source image
private $width; // Calculated from source image
private $height; // Calculated from source image
private $offset;
public $keepRatio;
public $cropToFit;
public $crop;
/**
* New image dimensions, incoming as argument or calculated.
*/
private $newWidth;
private $newWidthOrig; // Save original value
private $newHeight;
private $newHeightOrig; // Save original value
/**
* Array with details on how to crop, incoming as argument and calculated.
*/
public $crop;
public $cropOrig; // Save original value
/**
* Properties
*/
public $newWidth;
public $newHeight;
private $cropWidth;
private $cropHeight;
public $crop_x;
public $crop_y;
public $filters;
private $type; // Calculated from source image
private $attr; // Calculated from source image
private $useCache; // Use the cache if true, set to false to ignore the cached file.
private $useOriginal; // Use original image if possible
/**
* Properties (clean up these)
*/
private $offset;
public $keepRatio;
public $cropToFit;
private $cropWidth;
private $cropHeight;
public $crop_x;
public $crop_y;
public $filters;
private $type; // Calculated from source image
private $attr; // Calculated from source image
private $useCache; // Use the cache if true, set to false to ignore the cached file.
private $useOriginal; // Use original image if possible
@@ -224,7 +237,7 @@ class CImage
{
$valid = array('jpg', 'jpeg', 'png', 'gif');
in_array($extension, $valid)
in_array(strtolower($extension), $valid)
or $this->raiseError('Not a valid file extension.');
return $this;
@@ -249,7 +262,7 @@ class CImage
$this->imageSrc = ltrim($src, '/');
$this->imageFolder = rtrim($dir, '/');
$this->pathToImage = $this->imageFolder . '/' . $this->imageSrc;
$this->fileExtension = pathinfo($this->pathToImage, PATHINFO_EXTENSION);
$this->fileExtension = strtolower(pathinfo($this->pathToImage, PATHINFO_EXTENSION));
$this->extension = $this->fileExtension;
$this->checkFileExtension($this->fileExtension);
@@ -314,7 +327,11 @@ class CImage
'useOriginal' => true,
// Pre-processing, before resizing is done
'scale' => null,
'scale' => null,
'rotateBefore' => null,
// General options
'bgColor' => 0,
// Post-processing, after resizing is done
'palette' => null,
@@ -322,6 +339,8 @@ class CImage
'sharpen' => null,
'emboss' => null,
'blur' => null,
'rotateAfter' => null,
'autoRotate' => false,
// Options for saving
//'quality' => null,
@@ -329,7 +348,7 @@ class CImage
//'saveAs' => null,
);
// Convert crop settins from string to array
// Convert crop settings from string to array
if (isset($args['crop']) && !is_array($args['crop'])) {
$pices = explode(',', $args['crop']);
$args['crop'] = array(
@@ -340,7 +359,7 @@ class CImage
);
}
// Convert area settins from string to array
// Convert area settings from string to array
if (isset($args['area']) && !is_array($args['area'])) {
$pices = explode(',', $args['area']);
$args['area'] = array(
@@ -351,13 +370,13 @@ class CImage
);
}
// Convert filter settins from array of string to array of array
// Convert filter settings from array of string to array of array
if (isset($args['filters']) && is_array($args['filters'])) {
foreach ($args['filters'] as $key => $filterStr) {
$parts = explode(',', $filterStr);
$filter = $this->mapFilter($parts[0]);
$filter['str'] = $filterStr;
for ($i=1;$i<=$filter['argc'];$i++) {
for ($i=1; $i<=$filter['argc']; $i++) {
if (isset($parts[$i])) {
$filter["arg{$i}"] = $parts[$i];
} else {
@@ -371,10 +390,15 @@ class CImage
// Merge default arguments with incoming and set properties.
//$args = array_merge_recursive($defaults, $args);
$args = array_merge($defaults, $args);
foreach ($defaults as $key=>$val) {
foreach ($defaults as $key => $val) {
$this->{$key} = $args[$key];
}
// Save original values to enable re-calculating
$this->newWidthOrig = $this->newWidth;
$this->newHeightOrig = $this->newHeight;
$this->cropOrig = $this->crop;
return $this;
}
@@ -405,9 +429,9 @@ class CImage
'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE),
);
if (isset($map[$name]))
if (isset($map[$name])) {
return $map[$name];
else {
} else {
throw new Exception('No such filter.');
}
}
@@ -415,13 +439,12 @@ class CImage
/**
* Init new width and height and do some sanity checks on constraints, before any
* processing can be done.
* Load image details from original image file.
*
* @return $this
* @throws Exception
*/
public function initDimensions()
public function loadImageDetails()
{
is_readable($this->pathToImage)
or $this->raiseError('Image file does not exist.');
@@ -436,6 +459,20 @@ class CImage
$this->log("Image filesize: " . filesize($this->pathToImage) . " bytes.");
}
return $this;
}
/**
* Init new width and height and do some sanity checks on constraints, before any
* processing can be done.
*
* @return $this
* @throws Exception
*/
public function initDimensions()
{
// width as %
if ($this->newWidth[strlen($this->newWidth)-1] == '%') {
$this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
@@ -539,11 +576,16 @@ class CImage
// Calculate new width and height if keeping aspect-ratio.
if ($this->keepRatio) {
$this->log("Keep aspect ratio.");
// Crop-to-fit and both new width and height are set.
if ($this->cropToFit && isset($this->newWidth) && isset($this->newHeight)) {
// Use newWidth and newHeigh as width/height, image should fit in box.
;
$this->log("Use newWidth and newHeigh as width/height, image should fit in box.");
} elseif (isset($this->newWidth) && isset($this->newHeight)) {
// Both new width and height are set.
// Use newWidth and newHeigh as max width/height, image should not be larger.
$ratioWidth = $width / $this->newWidth;
@@ -551,18 +593,27 @@ class CImage
$ratio = ($ratioWidth > $ratioHeight) ? $ratioWidth : $ratioHeight;
$this->newWidth = round($width / $ratio);
$this->newHeight = round($height / $ratio);
$this->log("New width and height was set.");
} elseif (isset($this->newWidth)) {
// Use new width as max-width
$factor = (float)$this->newWidth / (float)$width;
$this->newHeight = round($factor * $height);
$this->log("New width was set.");
} elseif (isset($this->newHeight)) {
// Use new height as max-hight
$factor = (float)$this->newHeight / (float)$height;
$this->newWidth = round($factor * $width);
$this->log("New height was set.");
}
// Use newWidth and newHeigh as defined width/height, image should fit the area.
if ($this->cropToFit) {
$this->log("Crop to fit.");
$ratioWidth = $width / $this->newWidth;
$ratioHeight = $height / $this->newHeight;
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
@@ -573,6 +624,7 @@ class CImage
// Crop, ensure to set new width and height
if ($this->crop) {
$this->log("Crop.");
$this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
$this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
}
@@ -587,6 +639,27 @@ class CImage
/**
* Re-calculate image dimensions when original image dimension has changed.
*
* @return $this
*/
public function reCalculateDimensions()
{
$this->log("Re-calculate image dimensions, newWidth x newHeigh was: " . $this->newWidth . " x " . $this->newHeight);
$this->newWidth = $this->newWidthOrig;
$this->newHeight = $this->newHeightOrig;
$this->crop = $this->cropOrig;
$this->initDimensions()
->calculateNewWidthAndHeight();
return $this;
}
/**
* Set extension for filename to save as.
*
@@ -597,6 +670,7 @@ class CImage
public function setSaveAsExtension($saveAs = null)
{
if (isset($saveAs)) {
$saveAs = strtolower($saveAs);
$this->checkFileExtension($saveAs);
$this->saveAs = $saveAs;
$this->extension = $saveAs;
@@ -656,7 +730,7 @@ class CImage
/**
* Use original image if possible, check options wchich affects image processing.
* Use original image if possible, check options which affects image processing.
*
* @param boolean $useOrig default is to use original if possible, else set to false.
*
@@ -677,6 +751,10 @@ class CImage
&& !$this->quality
&& !$this->compress
&& !$this->saveAs
&& !$this->rotateBefore
&& !$this->rotateAfter
&& !$this->autoRotate
&& !$this->bgColor
) {
$this->log("Using original image.");
$this->output($this->pathToImage);
@@ -695,13 +773,16 @@ class CImage
*/
public function generateFilename($base)
{
$parts = pathinfo($this->pathToImage);
$cropToFit = $this->cropToFit ? '_cf' : null;
$crop_x = $this->crop_x ? "_x{$this->crop_x}" : null;
$crop_y = $this->crop_y ? "_y{$this->crop_y}" : null;
$scale = $this->scale ? "_s{$this->scale}" : null;
$quality = $this->quality ? "_q{$this->quality}" : null;
$compress = $this->compress ? "_co{$this->compress}" : null;
$parts = pathinfo($this->pathToImage);
$cropToFit = $this->cropToFit ? '_cf' : null;
$crop_x = $this->crop_x ? "_x{$this->crop_x}" : null;
$crop_y = $this->crop_y ? "_y{$this->crop_y}" : null;
$scale = $this->scale ? "_s{$this->scale}" : null;
$bgColor = $this->bgColor ? "_bgc{$this->bgColor}" : null;
$quality = $this->quality ? "_q{$this->quality}" : null;
$compress = $this->compress ? "_co{$this->compress}" : null;
$rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
$rotateAfter = $this->rotateAfter ? "_ra{$this->rotateAfter}" : null;
$offset = isset($this->offset)
? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left']
@@ -716,7 +797,7 @@ class CImage
foreach ($this->filters as $filter) {
if (is_array($filter)) {
$filters .= "_f{$filter['id']}";
for ($i=1;$i<=$filter['argc'];$i++) {
for ($i=1; $i<=$filter['argc']; $i++) {
$filters .= ":".$filter["arg{$i}"];
}
}
@@ -728,9 +809,13 @@ class CImage
$blur = $this->blur ? 'b' : null;
$palette = $this->palette ? 'p' : null;
$autoRotate = $this->autoRotate ? 'ar' : null;
/*
$this->extension = isset($this->extension)
? $this->extension
: $parts['extension'];
*/
// Check optimizing options
$optimize = null;
@@ -746,7 +831,7 @@ class CImage
$file = $subdir . '_' . $parts['filename'] . '_' . round($this->newWidth) . '_'
. round($this->newHeight) . $offset . $crop . $cropToFit . $crop_x . $crop_y
. $quality . $filters . $sharpen . $emboss . $blur . $palette . $optimize
. $scale . '.' . $this->extension;
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor . '.' . $this->extension;
return $this->setTarget($file, $base);
}
@@ -788,6 +873,37 @@ class CImage
/**
* Error message when failing to load somehow corrupt image.
*
* @return void
*
*/
public function failedToLoad()
{
header("HTTP/1.0 404 Not Found");
echo("CImage.php says 404: Fatal error when opening image.<br>");
switch ($this->fileExtension) {
case 'jpg':
case 'jpeg':
$this->image = imagecreatefromjpeg($this->pathToImage);
break;
case 'gif':
$this->image = imagecreatefromgif($this->pathToImage);
break;
case 'png':
$this->image = imagecreatefrompng($this->pathToImage);
break;
}
exit();
}
/**
* Load image from disk.
*
@@ -809,14 +925,18 @@ class CImage
case 'jpg':
case 'jpeg':
$this->image = @imagecreatefromjpeg($this->pathToImage);
$this->image or $this->failedToLoad();
break;
case 'gif':
$this->image = @imagecreatefromgif($this->pathToImage);
$this->image or $this->failedToLoad();
break;
case 'png':
$this->image = @imagecreatefrompng($this->pathToImage);
$this->image or $this->failedToLoad();
$type = $this->getPngType();
$hasFewColors = imagecolorstotal($this->image);
@@ -852,7 +972,7 @@ class CImage
*/
private function getPngType()
{
$pngType = ord (file_get_contents ($this->pathToImage, false, null, 25, 1));
$pngType = ord(file_get_contents($this->pathToImage, false, null, 25, 1));
switch ($pngType) {
@@ -920,6 +1040,20 @@ class CImage
{
$this->log("Pre-process before resizing");
// Rotate image
if ($this->rotateBefore) {
$this->log("Rotating image.");
$this->rotate($this->rotateBefore, $this->bgColor)
->reCalculateDimensions();
}
// Auto-rotate image
if ($this->autoRotate) {
$this->log("Auto rotating image.");
$this->rotateExif()
->reCalculateDimensions();
}
// Scale the original image before starting
if (isset($this->scale)) {
$this->log("Scale by {$this->scale}%");
@@ -1020,6 +1154,12 @@ class CImage
{
$this->log("Post-process after resizing");
// Rotate image
if ($this->rotateAfter) {
$this->log("Rotating image.");
$this->rotate($this->rotateAfter, $this->bgColor);
}
// Apply filters
if (isset($this->filters) && is_array($this->filters)) {
@@ -1052,25 +1192,25 @@ class CImage
}
// Convert to palette image
if($this->palette) {
if ($this->palette) {
$this->log("Converting to palette image.");
$this->trueColorToPalette();
}
// Blur the image
if($this->blur) {
if ($this->blur) {
$this->log("Blur.");
$this->blurImage();
}
// Emboss the image
if($this->emboss) {
if ($this->emboss) {
$this->log("Emboss.");
$this->embossImage();
}
// Sharpen the image
if($this->sharpen) {
if ($this->sharpen) {
$this->log("Sharpen.");
$this->sharpenImage();
}
@@ -1080,6 +1220,73 @@ class CImage
/**
* Rotate image using angle.
*
* @param float $angle to rotate image.
* @param int $anglebgColor to fill image with if needed.
*
* @return $this
*/
public function rotate($angle, $bgColor)
{
$this->log("Rotate image " . $angle . " degrees with filler color " . $bgColor);
$this->image = imagerotate($this->image, $angle, $bgColor);
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
$this->log("New image dimension width x height: " . $this->width . " x " . $this->height);
return $this;
}
/**
* Rotate image using information in EXIF.
*
* @return $this
*/
public function rotateExif()
{
if (!in_array($this->fileExtension, array('jpg', 'jpeg'))) {
$this->log("Autorotate ignored, EXIF not supported by this filetype.");
return $this;
}
$exif = exif_read_data($this->pathToImage);
if (!empty($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 3:
$this->log("Autorotate 180.");
$this->rotate(180, $this->bgColor);
break;
case 6:
$this->log("Autorotate -90.");
$this->rotate(-90, $this->bgColor);
break;
case 8:
$this->log("Autorotate 90.");
$this->rotate(90, $this->bgColor);
break;
default:
$this->log("Autorotate ignored, unknown value as orientation.");
}
} else {
$this->log("Autorotate ignored, no orientation in EXIF.");
}
return $this;
}
/**
* Convert true color image to palette image, keeping alpha.
* http://stackoverflow.com/questions/5752514/how-to-convert-png-to-8-bit-png-using-php-gd-library
@@ -1183,7 +1390,7 @@ class CImage
* @param int $height of the new image.
* @return image resource.
*/
private function CreateImageKeepTransparency($width, $height)
private function createImageKeepTransparency($width, $height)
{
$this->log("Creating a new working image width={$width}px, height={$height}px.");
$img = imagecreatetruecolor($width, $height);
@@ -1343,10 +1550,8 @@ class CImage
$this->log("Outputting image: $file");
// Get details on image
$info = list($width, $height, $type, $attr) = getimagesize($file);
!empty($info) or $this->raiseError("The file doesn't seem to be an image.");
$mime = $info['mime'];
// Get image modification time
clearstatcache();
$lastModified = filemtime($file);
$gmdate = gmdate("D, d M Y H:i:s", $lastModified);
@@ -1372,6 +1577,11 @@ class CImage
exit;
}
// Get details on image
$info = getimagesize($file);
!empty($info) or $this->raiseError("The file doesn't seem to be an image.");
$mime = $info['mime'];
header('Content-type: ' . $mime);
readfile($file);
}
@@ -1386,13 +1596,15 @@ class CImage
*
* @param string $message to log.
*
* @return void
* @return this
*/
private function log($message)
public function log($message)
{
if ($this->verbose) {
$this->log[] = $message;
}
return $this;
}

View File

@@ -1,2 +1,21 @@
This is a software solution to a general known problem.
Free software. Use at own risk.
The MIT License (MIT)
Copyright (c) 2012 - 2014 Mikael Roos, me@mikaelroos.se
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -131,6 +131,30 @@ Revision history
-------------------------------------
v0.5.3 (2014-11-21)
* Support filenames of uppercase JPEG, JPG, PNG and GIF, as proposed in #37.
* Changing `CImage::output()` as proposed in #37.
* Adding security check that image filename is always below the path `image_path` as specified in `img_config.php` #37.
* Adding configuration item in `img_config.php` for setting valid characters in image filename.
* Moving `webroot/test*` into directory `webroot/test`.
* `webroot/check_system.php` now outputs if extension for exif is loaded.
* Broke API when `initDimensions()` split into two methods, new `initDimensions()` and `loadImageDetails()`.
* Added `autoRotate,`aro` to auto rotate image based on EXIF information.
* Added `bgColor,`bgc` to use as backgroundcolor when needing a filler color, for example rotate 45.
* Added `rotateBefore,`rb` to rotate image a certain angle before processing.
* Added `rotateAfter,`ra` to rotate image a certain angle after processing.
* Cleaned up code formatting, removed trailing spaces.
* Removed @ from opening images, better to display correct warning when failing #34, but put it back again.
* Setting gd.jpeg_ignore_warning to true as default #34.
* `webroot/check_system.php` now outputs version of PHP and GD.
* #32 correctly send 404 header when serving an error message.
* Trying to verify issue #29, but can not.
* Adding structure for testprograms together with, use `webroot/test_issue29.php` as sample.
* Improving code formatting.
* Moving parts of verbose output from img.php to CImage.php.
v0.5.2 (2014-04-01)
* Correcting issue #26 providing error message when not using postprocessing.

15
webroot/check_system.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
echo 'Current PHP version: ' . phpversion() . '<br><br>';
echo 'Running on: ' . $_SERVER['SERVER_SOFTWARE'] . '<br><br>';
$no = extension_loaded('gd') ? null : 'NOT';
echo "Extension gd is $no loaded.<br>";
$no = extension_loaded('exif') ? null : 'NOT';
echo "Extension exif is $no loaded.<br>";
if (!$no) {
echo "<pre>", var_dump(gd_info()), "</pre>";
}

View File

@@ -14,7 +14,7 @@
*/
function errorPage($msg)
{
header("Status: 404 Not Found");
header("HTTP/1.0 404 Not Found");
die('img.php say 404: ' . $msg);
}
@@ -23,7 +23,7 @@ function errorPage($msg)
/**
* Custom exception handler.
*/
set_exception_handler(function($exception) {
set_exception_handler(function ($exception) {
errorPage("<p><b>img.php: Uncaught exception:</b> <p>" . $exception->getMessage() . "</p><pre>" . $exception->getTraceAsString(), "</pre>");
});
@@ -128,9 +128,32 @@ $verbose = getDefined(array('verbose', 'v'), true, false);
$srcImage = get('src')
or errorPage('Must set src-attribute.');
preg_match('#^[a-z0-9A-Z-/_\.]+$#', $srcImage)
// Check for valid/invalid characters
preg_match($config['valid_filename'], $srcImage)
or errorPage('Filename contains invalid characters.');
// Check that the image is a file below the directory 'image_path'.
if ($config['image_path_constraint']) {
$pathToImage = realpath($config['image_path'] . $srcImage);
$imageDir = realpath($config['image_path']);
is_file($pathToImage)
or errorPage(
'Source image is not a valid file, check the filename and that a
matching file exists on the filesystem.'
);
substr_compare($imageDir, $pathToImage, 0, strlen($imageDir)) == 0
or errorPage(
'Security constraint: Source image is not below the directory "image_path"
as specified in the config file img_config.php.'
);
}
verbose("src = $srcImage");
@@ -306,7 +329,7 @@ verbose("save as = $saveAs");
$scale = get(array('scale', 's'));
is_null($scale)
or ($scale >= 0 and $quality <= 400)
or ($scale >= 0 and $scale <= 400)
or errorPage('Scale out of range');
verbose("scale = $scale");
@@ -349,6 +372,68 @@ verbose("blur = $blur");
/**
* rotate - Rotate the image with an angle, before processing
*/
/*
$rotate = get(array('rotate', 'r'));
is_null($rotate)
or ($rotate >= -360 and $rotate <= 360)
or errorPage('Rotate out of range');
verbose("rotate = $rotate");
*/
/**
* rotateBefore - Rotate the image with an angle, before processing
*/
$rotateBefore = get(array('rotateBefore', 'rb'));
is_null($rotateBefore)
or ($rotateBefore >= -360 and $rotateBefore <= 360)
or errorPage('RotateBefore out of range');
verbose("rotateBefore = $rotateBefore");
/**
* rotateAfter - Rotate the image with an angle, before processing
*/
$rotateAfter = get(array('rotateAfter', 'ra', 'rotate', 'r'));
is_null($rotateAfter)
or ($rotateAfter >= -360 and $rotateAfter <= 360)
or errorPage('RotateBefore out of range');
verbose("rotateAfter = $rotateAfter");
/**
* bgColor - Default background color to use
*/
$bgColor = hexdec(get(array('bgColor', 'bgc')));
is_null($bgColor)
or ($bgColor >= 0 and $bgColor <= hexdec("FFFFFF"))
or errorPage('Background color needs a hex value');
verbose("bgColor = $bgColor");
/**
* autoRotate - Auto rotate based on EXIF information
*/
$autoRotate = getDefined(array('autoRotate', 'aro'), true, false);
verbose("autoRotate = $autoRotate");
/**
* filter, f, f0-f9 - Processing option, post filter for various effects using imagefilter()
*/
@@ -380,11 +465,9 @@ if ($verbose) {
unset($query['nocache']);
unset($query['nc']);
$url1 = '?' . http_build_query($query);
$log = htmlentities(print_r(verbose(), 1));
echo <<<EOD
<a href=$url1><code>$url1</code></a><br>
<img src='{$url1}' />
<pre>$log</pre>
EOD;
}
@@ -398,29 +481,37 @@ require $config['cimage_class'];
$img = new CImage();
$img->setVerbose($verbose)
->log("Incoming arguments: " . print_r(verbose(), 1))
->setSource($srcImage, $config['image_path'])
->setOptions(
array(
// Options for calculate dimensions
'newWidth' => $newWidth,
'newHeight' => $newHeight,
'aspectRatio' => $aspectRatio,
'keepRatio' => $keepRatio,
'cropToFit' => $cropToFit,
'crop' => $crop,
'area' => $area,
// Options for calculate dimensions
'newWidth' => $newWidth,
'newHeight' => $newHeight,
'aspectRatio' => $aspectRatio,
'keepRatio' => $keepRatio,
'cropToFit' => $cropToFit,
'crop' => $crop,
'area' => $area,
// Pre-processing, before resizing is done
'scale' => $scale,
// Pre-processing, before resizing is done
'scale' => $scale,
'rotateBefore' => $rotateBefore,
// Post-processing, after resizing is done
'palette' => $palette,
'filters' => $filters,
'sharpen' => $sharpen,
'emboss' => $emboss,
'blur' => $blur,
// General processing options
'bgColor' => $bgColor,
// Post-processing, after resizing is done
'palette' => $palette,
'filters' => $filters,
'sharpen' => $sharpen,
'emboss' => $emboss,
'blur' => $blur,
'rotateAfter' => $rotateAfter,
'autoRotate' => $autoRotate,
)
)
->loadImageDetails()
->initDimensions()
->calculateNewWidthAndHeight()
->setSaveAsExtension($saveAs)

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
webroot/img/issue34/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
webroot/img/issue34/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
webroot/img/round8.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
webroot/img/wider.JPEG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
webroot/img/wider.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -17,6 +17,20 @@ return array(
/**
* Check that the imagefile is a file below 'image_path' using realpath().
*/
'image_path_constraint' => true,
/**
* A regexp for validating characters in the image filename.
*/
'valid_filename' => '#^[a-z0-9A-Z-/_\.]+$#',
/**
* Set default timezone, it defaults to UTC if not specified otherwise.
*
@@ -55,7 +69,7 @@ return array(
* Predefined size constants.
*
*/
'size_constant' => function() {
'size_constant' => function () {
// Set sizes to map constant to value, easier to use with width or height
$sizes = array(
@@ -63,7 +77,7 @@ return array(
'w2' => 630,
);
// Add column width to $area, useful for use as predefined size for width (or height).
// Add grid column width, useful for use as predefined size for width (or height).
$gridColumnWidth = 30;
$gridGutterWidth = 10;
$gridColumns = 24;
@@ -81,7 +95,7 @@ return array(
* Predefined aspect ratios.
*
*/
'aspect_ratio_constant' => function() {
'aspect_ratio_constant' => function () {
return array(
'3:1' => 3/1,
'3:2' => 3/2,
@@ -98,9 +112,11 @@ return array(
/**
* Set error reporting to match development or production environment
*/
'error_reporting' => function() {
error_reporting(-1);
'error_reporting' => function () {
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
set_time_limit(20);
ini_set('gd.jpeg_ignore_warning', 1); // Ignore warning of corrupt jpegs
},
);

View File

@@ -5,25 +5,6 @@
</head>
<body>
<h1>Testing <code>CImage.php</code> through <code>img.php</code></h1>
<p>You can test any variation of resizing the images through <a href='img.php?src=wider.jpg&amp;w=200&amp;h=200'>img.php?src=wider.jpg&amp;w=200&amp;h=200</a> or <a href='img.php?src=higher.jpg&amp;w=200&amp;h=200'>img.php?src=higher.jpg&amp;w=200&amp;h=200</a></p>
<p>Supported arguments throught the querystring are:</p>
<ul>
<li>h, height: h=200 sets the new height to max 200px.
<li>w, width: w=200 sets the new width to max 200px.
<li>crop-to-fit: set together with both h & w to make the image fit in the box and crop out the rest.
<li>no-ratio: do not keep aspect ratio.
<li>crop: crops an area from the original image, set width, height, start_x and start_y to define the area to crop, for example crop=100,100,10,10.
<li>q, quality: q=70 or quality=70 sets the image quality as value between 0 and 100, default is full quality/100.
<li>f, filter: apply filter to image, f=colorize,0,255,0,0 makes image greener. Supports all filters as defined in <a href='http://php.net/manual/en/function.imagefilter.php'><code>imagefilter()</code></a>
<li>f0-f9: same as f/filter, just add more filters. Applied in order f, f0-f9.
</ul>
<p>Supports <code>.jpg</code>, <code>.png</code> and <code>.gif</code>.</p>
<p>Sourcecode and issues on github: <a href='https://github.com/mosbth/cimage'>https://github.com/mosbth/cimage</a></p>
<p>Copyright 2012, Mikael Roos (mos@dbwebb.se)</p>
<h2>Testcases</h2>
@@ -66,7 +47,7 @@ $testcase = array(
<tbody>
<?php
foreach($testcase as $key => $val) {
$url = "img.php?src=wider.jpg{$val['query']}";
$url = "../img.php?src=wider.jpg{$val['query']}";
echo "<tr><td id=w$key><a href=#w$key>$key</a></br>{$val['text']}</br><code><a href='$url'>".htmlentities($url)."</a></code></td><td><img src='$url' /></td></tr>";
}
?>
@@ -80,7 +61,7 @@ foreach($testcase as $key => $val) {
<tbody>
<?php
foreach($testcase as $key => $val) {
$url = "img.php?src=higher.jpg{$val['query']}";
$url = "../img.php?src=higher.jpg{$val['query']}";
echo "<tr><td id=h$key><a href=#h$key>$key</a></br>{$val['text']}</br><code><a href='$url'>".htmlentities($url)."</a></code></td><td><img src='$url' /></td></tr>";
}
?>

View File

@@ -0,0 +1,75 @@
<!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 29</title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 29</h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
echo "<p>Version of PHP is: " . phpversion();
$imgphp = "../img.php?src=";
$images = array(
'issue29/400x265.jpg',
'issue29/400x268.jpg',
'issue29/400x300.jpg',
'issue29/465x304.jpg',
'issue29/640x273.jpg',
);
$testcase = array(
'&w=300&cf&q=80&nc',
'&w=75&h=75&cf&q=80&nc',
'&w=75&h=75&q=80',
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>

View File

@@ -0,0 +1,77 @@
<!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 36 - autoRotate</title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 36 - autoRotate</h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
$imgphp = "../img.php?src=";
$images = array(
'issue36/me-0.jpg',
'issue36/me-90.jpg',
'issue36/me-180.jpg',
'issue36/me-270.jpg',
'issue36/flower-0.jpg',
'issue36/flower-90.jpg',
'issue36/flower-180.jpg',
'issue36/flower-270.jpg',
);
$testcase = array(
'&aro&nc',
'&aro&nc&w=200',
'&aro&nc&h=200',
'&aro&nc&w=200&h=200&cf',
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>

View File

@@ -0,0 +1,76 @@
<?php
$angle = 180;
?><!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 36 - rotateBefore, rotateAfter <?=$angle?></title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 36 - rotateBefore, rotateAfter <?=$angle?></h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
$imgphp = "../img.php?src=";
$images = array(
'kodim08.png',
'kodim04.png',
);
$testcase = array(
"&rb=$angle&nc",
"&rb=$angle&nc&w=200",
"&rb=$angle&nc&h=200",
"&rb=$angle&nc&w=200&h=200&cf",
"&ra=$angle&nc",
"&ra=$angle&nc&w=200",
"&ra=$angle&nc&h=200",
"&ra=$angle&nc&w=200&h=200&cf",
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>

View File

@@ -0,0 +1,76 @@
<?php
$angle = 270;
?><!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 36 - rotateBefore, rotateAfter <?=$angle?></title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 36 - rotateBefore, rotateAfter <?=$angle?></h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
$imgphp = "../img.php?src=";
$images = array(
'kodim08.png',
'kodim04.png',
);
$testcase = array(
"&rb=$angle&nc",
"&rb=$angle&nc&w=200",
"&rb=$angle&nc&h=200",
"&rb=$angle&nc&w=200&h=200&cf",
"&ra=$angle&nc",
"&ra=$angle&nc&w=200",
"&ra=$angle&nc&h=200",
"&ra=$angle&nc&w=200&h=200&cf",
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>

View File

@@ -0,0 +1,78 @@
<?php
$angle = 45;
?><!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 36 - rotateBefore, rotateAfter <?=$angle?></title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 36 - rotateBefore, rotateAfter <?=$angle?></h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
$imgphp = "../img.php?src=";
$images = array(
'kodim08.png',
'kodim04.png',
);
$testcase = array(
"&rb=$angle&bgc=ffffff&nc",
"&rb=$angle&bgc=ffffff&nc&w=200",
"&rb=$angle&bgc=ffffff&nc&h=200",
"&rb=$angle&bgc=ffffff&nc&w=200&h=200&cf",
"&rb=$angle&bgc=ffffff&nc&w=200&h=200&cf&crop=200,200,center,center",
"&ra=$angle&bgc=ffffff&nc",
"&ra=$angle&bgc=ffffff&nc&w=200",
"&ra=$angle&bgc=ffffff&nc&h=200",
"&ra=$angle&bgc=ffffff&nc&w=200&h=200&cf",
"&ra=$angle&bgc=ffffff&nc&w=200&h=200&cf&crop=200,200,center,center",
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>

View File

@@ -0,0 +1,76 @@
<?php
$angle = 90;
?><!doctype html>
<head>
<meta charset='utf-8'/>
<title>Testing img for issue 36 - rotateBefore, rotateAfter <?=$angle?></title>
<style>
body {background-color: #ccc;}
</style>
</head>
<body>
<h1>Testing issue 36 - rotateBefore, rotateAfter <?=$angle?></h1>
<?php
error_reporting(-1); // Report all type of errors
ini_set('display_errors', 1); // Display all errors
ini_set('output_buffering', 0); // Do not buffer outputs, write directly
$imgphp = "../img.php?src=";
$images = array(
'kodim08.png',
'kodim04.png',
);
$testcase = array(
"&rb=$angle&nc",
"&rb=$angle&nc&w=200",
"&rb=$angle&nc&h=200",
"&rb=$angle&nc&w=200&h=200&cf",
"&ra=$angle&nc",
"&ra=$angle&nc&w=200",
"&ra=$angle&nc&h=200",
"&ra=$angle&nc&w=200&h=200&cf",
);
?>
<h2>Images used in test</h2>
<p>The following images are used for this test.</p>
<?php foreach($images as $image) : ?>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php endforeach; ?>
<h2>Testcases used for each image</h2>
<p>The following testcases are used for each image.</p>
<?php foreach($testcase as $tc) : ?>
<code><?=$tc?></code><br>
<?php endforeach; ?>
<h2>Applying testcase for each image</h2>
<?php foreach($images as $image) : ?>
<h3><?=$image?></h3>
<p><code><a href="img/<?=$image?>"><?=$image?></a></code><br>
<img src="<?=$imgphp . $image?>"></p>
<?php foreach($testcase as $tc) : ?>
<h4><?=$tc?></h4>
<p><code><a href="<?=$imgphp . $image . $tc?>"><?=$image . $tc?></a></code><br>
<img src="<?=$imgphp . $image . $tc?>"></p>
<?php endforeach; ?>
<?php endforeach; ?>