mirror of
https://github.com/mosbth/cimage.git
synced 2025-08-05 07:37:37 +02:00
implemented filters and quality, chenged how arguments was handled.
This commit is contained in:
136
CImage.php
136
CImage.php
@@ -19,7 +19,12 @@ class CImage {
|
||||
private $cropWidth;
|
||||
private $cropHeight;
|
||||
public $keepRatio;
|
||||
public $cropToFit;
|
||||
public $crop;
|
||||
public $crop_x;
|
||||
public $crop_y;
|
||||
private $quality;
|
||||
public $filters;
|
||||
public $saveFolder;
|
||||
public $newName;
|
||||
private $newFileName;
|
||||
@@ -35,20 +40,12 @@ class CImage {
|
||||
* Constructor, can take arguments to init the object.
|
||||
*
|
||||
* @param $pathToImage string the filepath to the image.
|
||||
* @param $newWidth integer the new width or null.
|
||||
* @param $newHeight integer the new width or null.
|
||||
* @param $keepRatio boolean true to keep aspect ratio else false.
|
||||
* @param $saveFolder string path to folder where to save the new file or null to skip saving.
|
||||
* @param $newName string new filename or leave to null to autogenerate filename.
|
||||
*/
|
||||
public function __construct($pathToImage=null, $newWidth=null, $newHeight=null,
|
||||
$keepRatio=true, $crop=false, $saveFolder=null, $newName=null) {
|
||||
public function __construct($pathToImage=null, $saveFolder=null, $newName=null) {
|
||||
$this->pathToImage = $pathToImage;
|
||||
$this->fileExtension = pathinfo($this->pathToImage, PATHINFO_EXTENSION);
|
||||
$this->newWidth = $newWidth;
|
||||
$this->newHeight = $newHeight;
|
||||
$this->keepRatio = $keepRatio;
|
||||
$this->crop = $crop;
|
||||
$this->saveFolder = $saveFolder;
|
||||
$this->newName = $newName;
|
||||
}
|
||||
@@ -69,8 +66,20 @@ class CImage {
|
||||
*/
|
||||
public function CreateFilename() {
|
||||
$parts = pathinfo($this->pathToImage);
|
||||
$crop = $this->crop ? '_c_' : null;
|
||||
return $this->saveFolder . '/' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $crop . '.' . $parts['extension'];
|
||||
$crop = $this->cropToFit ? '_cf' : null;
|
||||
$crop_x = $this->crop_x ? "_x{$this->crop_x}" : null;
|
||||
$crop_y = $this->crop_y ? "_y{$this->crop_y}" : null;
|
||||
$quality = $this->quality == 100 ? null : "_q{$this->quality}";
|
||||
$filters = null;
|
||||
foreach($this->filters as $filter) {
|
||||
if(is_array($filter)) {
|
||||
$filters .= "_f{$filter['id']}";
|
||||
for($i=1;$i<=$filter['argc'];$i++) {
|
||||
$filters .= ":".$filter["arg{$i}"];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->saveFolder . '/' . $parts['filename'] . '_' . round($this->newWidth) . '_' . round($this->newHeight) . $crop . $crop_x . $crop_y . $quality . $filters . '.' . $parts['extension'];
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +89,9 @@ class CImage {
|
||||
public function Init() {
|
||||
is_null($this->newWidth) or is_numeric($this->newWidth) or $this->RaiseError('Width not numeric');
|
||||
is_null($this->newHeight) or is_numeric($this->newHeight) or $this->RaiseError('Height not numeric');
|
||||
is_numeric($this->quality) and $this->quality >= 0 and $this->quality <= 100 or $this->RaiseError('Quality not in range.');
|
||||
//is_numeric($this->crop_x) && is_numeric($this->crop_y) or $this->RaiseError('Quality not in range.');
|
||||
//filter
|
||||
is_readable($this->pathToImage) or $this->RaiseError('File does not exist.');
|
||||
in_array($this->fileExtension, $this->validExtensions) or $this->RaiseError('Not a valid file extension.');
|
||||
is_null($this->saveFolder) or is_writable($this->saveFolder) or $this->RaiseError('Save directory does not exist or is not writable.');
|
||||
@@ -126,6 +138,34 @@ class CImage {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map filter name to PHP filter and id.
|
||||
*
|
||||
* @param string $name the name of the filter.
|
||||
*/
|
||||
private function MapFilter($name) {
|
||||
$map = array(
|
||||
'negate' => array('id'=>0, 'argc'=>0, 'type'=>IMG_FILTER_NEGATE),
|
||||
'grayscale' => array('id'=>1, 'argc'=>0, 'type'=>IMG_FILTER_GRAYSCALE),
|
||||
'brightness' => array('id'=>2, 'argc'=>1, 'type'=>IMG_FILTER_BRIGHTNESS),
|
||||
'contrast' => array('id'=>3, 'argc'=>1, 'type'=>IMG_FILTER_CONTRAST),
|
||||
'colorize' => array('id'=>4, 'argc'=>4, 'type'=>IMG_FILTER_COLORIZE),
|
||||
'edgedetect' => array('id'=>5, 'argc'=>0, 'type'=>IMG_FILTER_EDGEDETECT),
|
||||
'emboss' => array('id'=>6, 'argc'=>0, 'type'=>IMG_FILTER_EMBOSS),
|
||||
'gaussian_blur' => array('id'=>7, 'argc'=>0, 'type'=>IMG_FILTER_GAUSSIAN_BLUR),
|
||||
'selective_blur' => array('id'=>8, 'argc'=>0, 'type'=>IMG_FILTER_SELECTIVE_BLUR),
|
||||
'mean_removal' => array('id'=>9, 'argc'=>0, 'type'=>IMG_FILTER_MEAN_REMOVAL),
|
||||
'smooth' => array('id'=>10, 'argc'=>1, 'type'=>IMG_FILTER_SMOOTH),
|
||||
'pixelate' => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE),
|
||||
);
|
||||
if(isset($map[$name]))
|
||||
return $map[$name];
|
||||
else {
|
||||
$this->RaiseError('No such filter.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate new width and height of image.
|
||||
*/
|
||||
@@ -137,7 +177,7 @@ class CImage {
|
||||
if(isset($this->newWidth) && isset($this->newHeight)) {
|
||||
|
||||
// Use newWidth and newHeigh as min width/height, image should fit the area.
|
||||
if($this->crop) {
|
||||
if($this->cropToFit) {
|
||||
$ratioWidth = $this->width/$this->newWidth;
|
||||
$ratioHeight = $this->height/$this->newHeight;
|
||||
$ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
|
||||
@@ -179,8 +219,55 @@ class CImage {
|
||||
|
||||
/**
|
||||
* Resize the image and optionally store/cache the new imagefile. Output the image.
|
||||
*
|
||||
* @param integer $newWidth the new width or null. Default is null.
|
||||
* @param integer $newHeight the new width or null. Default is null.
|
||||
* @param boolean $keepRatio true to keep aspect ratio else false. Default is true.
|
||||
* @param boolean $cropToFit true to crop image to fit in box specified by $newWidth and $newHeight. Default is false.
|
||||
* @param integer $quality the quality to use when saving the file, range 0-100, default is full quality which is 100.
|
||||
* @param array $crop.
|
||||
* @param array $filter.
|
||||
*/
|
||||
public function ResizeAndOutput() {
|
||||
public function ResizeAndOutput($args) {
|
||||
$defaults = array(
|
||||
'newWidth'=>null,
|
||||
'newHeight'=>null,
|
||||
'keepRatio'=>true,
|
||||
'cropToFit'=>false,
|
||||
'quality'=>100,
|
||||
'crop'=>array('w'=>null, 'h'=>null, 'x'=>0, 'y'=>0),
|
||||
'filters'=>null,
|
||||
);
|
||||
// Convert crop settins from string to array
|
||||
if(isset($args['crop']) && !is_array($args['crop'])) {
|
||||
$args['crop'] = array();
|
||||
}
|
||||
|
||||
// Convert filter settins 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++) {
|
||||
if(isset($parts[$i])) {
|
||||
$filter["arg{$i}"] = $parts[$i];
|
||||
} else {
|
||||
$this->RaiseError('Missing arg to filter, review how many arguments are needed at http://php.net/manual/en/function.imagefilter.php');
|
||||
}
|
||||
}
|
||||
$args['filters'][$key] = $filter;
|
||||
}
|
||||
}
|
||||
//echo "<pre>" . print_r($args['filters'], true) . "</pre>";
|
||||
|
||||
// Merge default arguments with incoming and set properties.
|
||||
$args = array_merge($defaults, $args);
|
||||
foreach($defaults as $key=>$val) {
|
||||
$this->{$key} = $args[$key];
|
||||
}
|
||||
|
||||
// Init the object and do sanity checks on arguments
|
||||
$this->Init()->CalculateNewWidthAndHeight();
|
||||
|
||||
// Use original image?
|
||||
@@ -188,8 +275,6 @@ class CImage {
|
||||
$this->Output($this->pathToImage);
|
||||
}
|
||||
|
||||
//echo "{$this->newWidth}:{$this->newHeight}";
|
||||
|
||||
// Check cache before resizing.
|
||||
$this->newFileName = $this->CreateFilename();
|
||||
if(is_readable($this->newFileName)) {
|
||||
@@ -208,10 +293,9 @@ class CImage {
|
||||
/**
|
||||
* Resize, crop and output the image.
|
||||
*
|
||||
* @param $imageQuality number the quality to use when saving the file, default is full quality.
|
||||
*/
|
||||
public function ResizeAndSave($imageQuality="100") {
|
||||
if($this->crop) {
|
||||
public function ResizeAndSave() {
|
||||
if($this->cropToFit) {
|
||||
$cropX = ($this->cropWidth/2) - ($this->newWidth/2);
|
||||
$cropY = ($this->cropHeight/2) - ($this->newHeight/2);
|
||||
$imgPreCrop = imagecreatetruecolor($this->cropWidth, $this->cropHeight);
|
||||
@@ -223,13 +307,25 @@ class CImage {
|
||||
imagecopyresampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
|
||||
}
|
||||
|
||||
if(isset($this->filters) && is_array($this->filters)) {
|
||||
foreach($this->filters as $filter) {
|
||||
switch($filter['argc']) {
|
||||
case 0: imagefilter($imageResized, $filter['type']); break;
|
||||
case 1: imagefilter($imageResized, $filter['type'], $filter['arg1']); break;
|
||||
case 2: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2']); break;
|
||||
case 3: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']); break;
|
||||
case 4: imagefilter($imageResized, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch($this->fileExtension)
|
||||
{
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
if(imagetypes() & IMG_JPG) {
|
||||
if($this->saveFolder) {
|
||||
imagejpeg($imageResized, $this->newFileName, $imageQuality);
|
||||
imagejpeg($imageResized, $this->newFileName, $this->quality);
|
||||
}
|
||||
$imgFunction = 'imagejpeg';
|
||||
}
|
||||
@@ -249,7 +345,7 @@ class CImage {
|
||||
$quality = 9 - round(($imageQuality/100) * 9);
|
||||
if (imagetypes() & IMG_PNG) {
|
||||
if($this->saveFolder) {
|
||||
imagepng($imageResized, $this->newFileName, $quality);
|
||||
imagepng($imageResized, $this->newFileName, $this->quality);
|
||||
}
|
||||
$imgFunction = 'imagepng';
|
||||
}
|
||||
|
14
README.md
14
README.md
@@ -21,6 +21,20 @@ Mikael Roos (mos@dbwebb.se)
|
||||
Revision history
|
||||
----------------
|
||||
|
||||
ToDo.
|
||||
|
||||
* crop
|
||||
* Pre-defined sizes.
|
||||
|
||||
|
||||
v0.2 (2012-05-09)
|
||||
|
||||
* Implemented filters as in http://php.net/manual/en/function.imagefilter.php
|
||||
* Changed `crop` to `crop_to_fit`, woks the same way.
|
||||
* Changed arguments to method and sends them in array.
|
||||
* Added quality-setting.
|
||||
* Added testcases for above.
|
||||
|
||||
v0.1.1 (2012-04-27)
|
||||
|
||||
* Corrected calculation where both width and height were set.
|
||||
|
33
img.php
33
img.php
@@ -14,21 +14,30 @@ $pathToCache = __DIR__.'/cache/';
|
||||
$srcImage = isset($_GET['src']) ? $pathToImages . basename($_GET['src']) : null;
|
||||
$newWidth = isset($_GET['width']) ? $_GET['width'] : (isset($_GET['w']) ? $_GET['w'] : null);
|
||||
$newHeight = isset($_GET['height']) ? $_GET['height'] : (isset($_GET['h']) ? $_GET['h'] : null);
|
||||
$keepRatio = isset($_GET['no-ratio']) ? false : true; // Keep Aspect Ratio?
|
||||
$crop = isset($_GET['crop']) ? true : false; // Crop image?
|
||||
$keepRatio = isset($_GET['no-ratio']) ? false : true;
|
||||
$cropToFit = isset($_GET['crop-to-fit']) ? true : false;
|
||||
$crop = isset($_GET['crop']) ? $_GET['crop'] : (isset($_GET['c']) ? $_GET['c'] : null);
|
||||
$quality = isset($_GET['quality']) ? $_GET['quality'] : (isset($_GET['q']) ? $_GET['q'] : 100);
|
||||
|
||||
// Add all filters to an array
|
||||
$filters = array();
|
||||
$filter = isset($_GET['filter']) ? $_GET['filter'] : (isset($_GET['f']) ? $_GET['f'] : null);
|
||||
if($filter) { $filters[] = $filter; }
|
||||
for($i=0; $i<10;$i++) {
|
||||
$filter = isset($_GET["filter{$i}"]) ? $_GET["filter{$i}"] : (isset($_GET["f{$i}"]) ? $_GET["f{$i}"] : null);
|
||||
if($filter) { $filters[] = $filter; }
|
||||
}
|
||||
|
||||
// Do some sanity checks
|
||||
!preg_match('/^[\w-\.]+$/', $srcImage) or die('Filename contains invalid characters.');
|
||||
if(isset($newWidth)) {
|
||||
$newWidth < 1000 or die('To large width.');
|
||||
$newWidth > 10 or die('To small width.');
|
||||
}
|
||||
if(isset($newHeight)) {
|
||||
$newHeight < 1000 or die('To large height.');
|
||||
$newHeight > 10 or die('To small height.');
|
||||
}
|
||||
is_null($newWidth) or ($newWidth > 10 && $newWidth < 1000) or die('Width out of range.');
|
||||
is_null($newHeight) or ($newHeight > 10 && $newHeight < 1000) or die('Hight out of range.');
|
||||
$quality >= 0 and $quality <= 100 or die('Quality out of range');
|
||||
|
||||
// Create the image object
|
||||
require(__DIR__.'/CImage.php');
|
||||
$img = new CImage($srcImage, $newWidth, $newHeight, $keepRatio, $crop, $pathToCache);
|
||||
$img->ResizeAndOutput();
|
||||
$img = new CImage($srcImage, $pathToCache);
|
||||
$img->ResizeAndOutput(array('newWidth'=>$newWidth, 'newHeight'=>$newHeight, 'keepRatio'=>$keepRatio,
|
||||
'cropToFit'=>$cropToFit, 'quality'=>$quality,
|
||||
'crop'=>$crop, 'filters'=>$filters,
|
||||
));
|
77
test.php
77
test.php
@@ -7,36 +7,81 @@
|
||||
<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&w=200&h=200'>img.php?src=wider.jpg&w=200&h=200</a> or <a href='img.php?src=higher.jpg&w=200&h=200'>img.php?src=higher.jpg&w=200&h=200</a></p>
|
||||
<p>Supported arguments throught the querystring are:</p>
|
||||
|
||||
<ul>
|
||||
<li>h, height: h=200 sets the height to 200px.
|
||||
<li>w, width: w=200 sets the width to 200px.
|
||||
<li>crop: together with both h & w makes the image fit in the box.
|
||||
<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>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>Mikael Roos (mos@dbwebb.se)</p>
|
||||
|
||||
|
||||
<h2>Testcases</h2>
|
||||
|
||||
<?php
|
||||
$testcase = array(
|
||||
array('text'=>'Original image', 'query'=>''),
|
||||
array('text'=>'Max width 200.', 'query'=>'&w=200'),
|
||||
array('text'=>'Max height 200.', 'query'=>'&h=200'),
|
||||
array('text'=>'Max width 200 and max height 200.', 'query'=>'&w=200&h=200'),
|
||||
array('text'=>'No-ratio makes image fit in area of width 200 and height 200.', 'query'=>'&w=200&h=200&no-ratio'),
|
||||
array('text'=>'Crop to fit in width 200 and height 200.', 'query'=>'&w=200&h=200&crop-to-fit'),
|
||||
array('text'=>'Crop to fit in width 200 and height 100.', 'query'=>'&w=200&h=100&crop-to-fit'),
|
||||
array('text'=>'Crop to fit in width 100 and height 200.', 'query'=>'&w=100&h=200&crop-to-fit'),
|
||||
array('text'=>'Quality 70', 'query'=>'&w=200&h=200&quality=70'),
|
||||
array('text'=>'Quality 40', 'query'=>'&w=200&h=200&quality=40'),
|
||||
array('text'=>'Quality 10', 'query'=>'&w=200&h=200&quality=10'),
|
||||
array('text'=>'Filter: Negate', 'query'=>'&w=200&h=200&f=negate'),
|
||||
array('text'=>'Filter: Grayscale', 'query'=>'&w=200&h=200&f=grayscale'),
|
||||
array('text'=>'Filter: Brightness 90', 'query'=>'&w=200&h=200&f=brightness,90'),
|
||||
array('text'=>'Filter: Contrast 50', 'query'=>'&w=200&h=200&f=contrast,50'),
|
||||
array('text'=>'Filter: Colorize 0,255,0,0', 'query'=>'&w=200&h=200&f=colorize,0,255,0,0'),
|
||||
array('text'=>'Filter: Edge detect', 'query'=>'&w=200&h=200&f=edgedetect'),
|
||||
array('text'=>'Filter: Emboss', 'query'=>'&w=200&h=200&f=emboss'),
|
||||
array('text'=>'Filter: Gaussian blur', 'query'=>'&w=200&h=200&f=gaussian_blur'),
|
||||
array('text'=>'Filter: Selective blur', 'query'=>'&w=200&h=200&f=selective_blur'),
|
||||
array('text'=>'Filter: Mean removal', 'query'=>'&w=200&h=200&f=mean_removal'),
|
||||
array('text'=>'Filter: Smooth 2', 'query'=>'&w=200&h=200&f=smooth,2'),
|
||||
array('text'=>'Filter: Pixelate 10,10', 'query'=>'&w=200&h=200&f=pixelate,10,10'),
|
||||
array('text'=>'Multiple filter: Negate, Grayscale and Pixelate 10,10', 'query'=>'&w=200&h=200&&f=negate&f0=grayscale&f1=pixelate,10,10'),
|
||||
);
|
||||
?>
|
||||
|
||||
<h3>Test case with image <code>wider.jpg</code></h3>
|
||||
<table>
|
||||
<caption>Test cases</caption>
|
||||
<caption>Test case with image <code>wider.jpg</code></caption>
|
||||
<thead><tr><th>Testcase:</th><th>Result:</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Original image of wider.jpg.</td><td><img src='img.php?src=wider.jpg' /></td></tr>
|
||||
<tr><td>wider.jpg max width 200.</td><td><img src='img.php?src=wider.jpg&w=200' /></td></tr>
|
||||
<tr><td>wider.jpg max height 200.</td><td><img src='img.php?src=wider.jpg&h=200' /></td></tr>
|
||||
<tr><td>wider.jpg max width 200 and max height 200.</td><td><img src='img.php?src=wider.jpg&w=200&h=200' /></td></tr>
|
||||
<tr><td>wider.jpg max width 200 and max height 200 and no-ratio.</td><td><img src='img.php?src=wider.jpg&w=200&h=200&no-ratio' /></td></tr>
|
||||
<tr><td>wider.jpg max width 200 and max height 200 and cropped.</td><td><img src='img.php?src=wider.jpg&w=200&h=200&crop' /></td></tr>
|
||||
<tr><td>wider.jpg max width 200 and max height 100 and cropped.</td><td><img src='img.php?src=wider.jpg&w=200&h=100&crop' /></td></tr>
|
||||
<tr><td>Original image of higher.jpg.</td><td><img src='img.php?src=higher.jpg' /></td></tr>
|
||||
<tr><td>higher.jpg max width 200.</td><td><img src='img.php?src=higher.jpg&w=200' /></td></tr>
|
||||
<tr><td>higher.jpg max height 200.</td><td><img src='img.php?src=higher.jpg&h=200' /></td></tr>
|
||||
<tr><td>higher.jpg max width 200 and max height 200.</td><td><img src='img.php?src=higher.jpg&w=200&h=200' /></td></tr>
|
||||
<tr><td>higher.jpg max width 200 and max height 200 and no-ratio.</td><td><img src='img.php?src=higher.jpg&w=200&h=200&no-ratio' /></td></tr>
|
||||
<tr><td>higher.jpg max width 200 and max height 200 and cropped.</td><td><img src='img.php?src=higher.jpg&w=200&h=200&crop' /></td></tr>
|
||||
<tr><td>higher.jpg max width 200 and max height 100 and cropped.</td><td><img src='img.php?src=higher.jpg&w=200&h=100&crop' /></td></tr>
|
||||
<?php
|
||||
foreach($testcase as $key => $val) {
|
||||
$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>";
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Test case with image <code>higher.jpg</code></h3>
|
||||
<table>
|
||||
<caption>Test case with image <code>higher.jpg</code></caption>
|
||||
<thead><tr><th>Testcase:</th><th>Result:</th></tr></thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach($testcase as $key => $val) {
|
||||
$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>";
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user