<?php
/**
* XOOPS image access/edit
*
* You may not change or alter any portion of this comment or credits
* of supporting developers from this source code or any supporting source code
* which is considered copyrighted (c) material of the original comment or credit authors.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* @copyright (c) 2000-2016 XOOPS Project (www.xoops.org)
* @license GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
* @package core
* @since 2.5.7
* @author luciorota <lucio.rota@gmail.com>, Joe Lencioni <joe@shiftingpixel.com>
*
* Enhanced image access/edit
* This enhanced version is very useful in many cases, for example when you need a
* smallest version of an image. This script uses Xoops cache to minimize server load.
*
*
* Parameters need to be passed in through the URL's query string:
* @param int id Xoops image id;
* @param string url relative to XOOPS_ROOT_PATH, path of local image starting with "/"
* (e.g. /images/toast.jpg);
* @param string src relative to XOOPS_ROOT_PATH, path of local image starting with "/"
* @param int width (optional) maximum width of final image in pixels (e.g. 700);
* @param int height (optional) maximum height of final image in pixels (e.g. 700);
* @param string color (optional) background hex color for filling transparent PNGs (e.g. 900 or 16a942);
* @param string cropratio (optional) ratio of width to height to crop final image (e.g. 1:1 or 3:2);
* @param boolean nocache (optional) don't read image from the cache;
* @param boolean noservercache (optional) don't read image from the server cache;
* @param boolean nobrowsercache (optional) don't read image from the browser cache;
* @param int quality (optional, 0-100, default: 90) quality of output image;
* @param mixed filter (optional, imagefilter 2nd, 3rd, 4th, 5th arguments, more info on php.net
* manual) a filter or an array of filters;
* @param int radius (optional, 1, 2, 3 or 4 integer values, CW) round corner radius
* @param float angle (optional), rotation angle)
*
*/
/* @example image.php
* Resizing a JPEG:
* <img src="/image.php?url=image-name.jpg&width=100&height=100" alt="Don't forget your alt text" />
* Resizing and cropping a JPEG into a square:
* <img src="/image.php?url=image-name.jpg?width=100&height=100&cropratio=1:1" alt="Don't forget your alt text" />
* Matting a PNG with #990000:
* <img src="/image.php?url=image-name.png?color=900&image=/path/to/image.png" alt="Don't forget your alt text" />
* Apply a filter:
* <img src="/image.php?url=/path/to/image.png&filter=IMG_FILTER_COLORIZE,128,60,256" alt="Don't forget the alt text" />
* Apply more filters (array) :
* <img src="/image.php?url=/path/to/image.png&filter[]=IMG_FILTER_GRAYSCALE&filter[]=IMG_FILTER_COLORIZE,128,60,256" />
* Round the image corners:
* All corners with same radius:
* <img src="/image.php?url=/path/to/image.png&radius=20" alt="Don't forget your alt text" />
* Left and right corners with different radius (20 for left corners and 40 for right corners)
* <img src="/image.php?url=/path/to/image.png&radius=20,40" alt="Don't forget your alt text" />
* 4 corners, 4 radius, clockwise order
* <img src="/image.php?url=/path/to/image.png&radius=20,40,0,10" alt="Don't forget your alt text" />
*
*/
define('MEMORY_TO_ALLOCATE', '100M');
define('DEFAULT_IMAGE_QUALITY', 90);
define('DEFAULT_BACKGROUND_COLOR', '000000');
define('ONLY_LOCAL_IMAGES', true);
define('ENABLE_IMAGEFILTER', true); // Set to false to avoid excessive server load
define('ENABLE_ROUNDCORNER', true); // Set to false to avoid excessive server load
define('ENABLE_IMAGEROTATE', true); // Set to false to avoid excessive server load
if (get_magic_quotes_runtime()) {
set_magic_quotes_runtime(false); // will never get called on PHP 5.4+
}
if (function_exists('mb_http_output')) {
mb_http_output('pass');
}
$xoopsOption['nocommon'] = true;
require_once __DIR__ . '/mainfile.php';
include_once __DIR__ . '/include/defines.php';
include_once __DIR__ . '/include/functions.php';
include_once __DIR__ . '/include/version.php';
include_once __DIR__ . '/kernel/object.php';
include_once __DIR__ . '/class/xoopsload.php';
include_once __DIR__ . '/class/preload.php';
include_once __DIR__ . '/class/module.textsanitizer.php';
include_once __DIR__ . '/class/database/databasefactory.php';
require_once __DIR__ . '/class/criteria.php';
XoopsLoad::load('xoopslogger');
$xoopsLogger = XoopsLogger::getInstance();
$xoopsLogger->startTime();
error_reporting(0);
/**
* @param $etag
* @param $lastModified
* @return null
*/
function doConditionalGet($etag, $lastModified)
{
header("Last-Modified: $lastModified");
header("ETag: \"{$etag}\"");
$ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
$ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : false;
if (!$ifModifiedSince && !$ifNoneMatch) {
return null;
}
if ($ifNoneMatch && $ifNoneMatch != $etag && $ifNoneMatch != '"' . $etag . '"') {
return null;
} // etag is there but doesn't match
if ($ifModifiedSince && $ifModifiedSince != $lastModified) {
return null;
} // if-modified-since is there but doesn't match
// Nothing has changed since their last request - serve a 304 and exit
header('HTTP/1.1 304 Not Modified');
exit();
}
/**
* ref: http://www.tricksofit.com/2014/08/round-corners-on-image-using-php-and-gd-library
*
* @param resource $sourceImage GD Image resource
* @param int[] $radii array(top left, top right, bottom left, bottom right) of pixel radius
* for each corner. A 0 disables rounding on a corner.
*
* @return resource
*/
function imageCreateCorners($sourceImage, $radii)
{
$q = 2; // quality - improve alpha blending by using larger (*$q) image size
// find a unique color
$tryCounter = 0;
do {
if (++$tryCounter > 255) {
$r = 2;
$g = 254;
$b = 0;
break;
}
$r = rand(0, 255);
$g = rand(0, 255);
$b = rand(0, 255);
} while (imagecolorexact($sourceImage, $r, $g, $b) < 0);
$imageWidth = imagesx($sourceImage);
$imageHeight = imagesy($sourceImage);
$workingWidth = $imageWidth * $q;
$workingHeight = $imageHeight * $q;
$workingImage= imagecreatetruecolor($workingWidth, $workingHeight);
$alphaColor = imagecolorallocatealpha($workingImage, $r, $g, $b, 127);
imagealphablending($workingImage, false);
imagesavealpha($workingImage, true);
imagefilledrectangle($workingImage, 0, 0, $workingWidth, $workingHeight, $alphaColor);
imagefill($workingImage, 0, 0, $alphaColor);
imagecopyresampled($workingImage, $sourceImage, 0, 0, 0, 0, $workingWidth, $workingHeight, $imageWidth, $imageHeight);
if (0 < ($radius = $radii[0] * $q)) { // left top
imagearc($workingImage, $radius - 1, $radius - 1, $radius * 2, $radius * 2, 180, 270, $alphaColor);
imagefilltoborder($workingImage, 0, 0, $alphaColor, $alphaColor);
}
if (0 < ($radius = $radii[1] * $q)) { // right top
imagearc($workingImage, $workingWidth - $radius, $radius - 1, $radius * 2, $radius * 2, 270, 0, $alphaColor);
imagefilltoborder($workingImage, $workingWidth - 1, 0, $alphaColor, $alphaColor);
}
if (0 < ($radius = $radii[2] * $q)) { // left bottom
imagearc($workingImage, $radius - 1, $workingHeight - $radius, $radius * 2, $radius * 2, 90, 180, $alphaColor);
imagefilltoborder($workingImage, 0, $workingHeight - 1, $alphaColor, $alphaColor);
}
if (0 < ($radius = $radii[3] * $q)) { // right bottom
imagearc($workingImage, $workingWidth - $radius, $workingHeight - $radius, $radius * 2, $radius * 2, 0, 90, $alphaColor);
imagefilltoborder($workingImage, $workingWidth - 1, $workingHeight - 1, $alphaColor, $alphaColor);
}
imagealphablending($workingImage, true);
imagecolortransparent($workingImage, $alphaColor);
// scale back down to original size
$destinationImage = imagecreatetruecolor($imageWidth, $imageHeight);
imagealphablending($destinationImage, false);
imagesavealpha($destinationImage, true);
imagefilledrectangle($destinationImage, 0, 0, $imageWidth, $imageHeight, $alphaColor);
imagecopyresampled($destinationImage, $workingImage, 0, 0, 0, 0, $imageWidth, $imageHeight, $workingWidth, $workingHeight);
// imagedestroy($sourceImage);
imagedestroy($workingImage);
return $destinationImage;
}
/**
* @param $orig
* @param $final
*
* @return mixed
*/
function findSharp($orig, $final)
{
// Function from Ryan Rud (http://adryrun.com)
$final *= (750.0 / $orig);
$a = 52;
$b = -0.27810650887573124;
$c = .00047337278106508946;
$result = $a + $b * $final + $c * $final * $final;
return max(round($result), 0);
}
/**
* issue an error for bad request
*
* Many different issues end up here, so message is generic 404. This keeps us from leaking info by probing
*/
function exit404BadReq()
{
header('HTTP/1.1 404 Not Found');
exit();
}
/**
* check local image url for possible issues
*
* @param string $imageUrl url to local image starting at site root with a '/'
*
* @return bool true if name is acceptable, exit if not
*/
function imageFilenameCheck($imageUrl)
{
if ($imageUrl[0] !== '/') { // must start with slash
exit404BadReq();
}
if ($imageUrl === '/') { // can't be empty
exit404BadReq();
}
if (preg_match('/(\.\.|<|>|\:|[[:cntrl:]])/', $imageUrl)) { // no "..", "<", ">", ":" or controls
exit404BadReq();
}
$fullPath = XOOPS_ROOT_PATH . $imageUrl;
if (strpos($fullPath, XOOPS_VAR_PATH) === 0) { // no access to data (shouldn't be in root, but...)
exit404BadReq();
}
if (strpos($fullPath, XOOPS_PATH) === 0) { // no access to lib (shouldn't be in root, but...)
exit404BadReq();
}
return true;
}
/*
* Get image
*/
// Get id (Xoops image) or url or src (standard image)
$imageId = isset($_GET['id']) ? (int)$_GET['id'] : false;
$imageUrl = isset($_GET['url']) ? (string)$_GET['url'] : (isset($_GET['src']) ? (string)$_GET['src'] : false);
if (!empty($imageId)) {
// If image is a Xoops image
/* @var XoopsImageHandler $imageHandler */
$imageHandler = xoops_getHandler('image');
$criteria = new CriteriaCompo(new Criteria('i.image_display', true));
$criteria->add(new Criteria('i.image_id', $imageId));
$images = $imageHandler->getObjects($criteria, false, true);
if (count($images) != 1) {
// No Xoops images or to many Xoops images
header('Content-type: image/gif');
readfile(XOOPS_UPLOAD_PATH . '/blank.gif');
exit();
}
$image = $images[0];
// Get image category
$imgcatId = $image->getVar('imgcat_id');
$imgcatHandler = xoops_getHandler('imagecategory');
if (!$imgcat = $imgcatHandler->get($imgcatId)) {
// No Image category
header('Content-type: image/gif');
readfile(XOOPS_UPLOAD_PATH . '/blank.gif');
exit();
}
// Get image data
$imageFilename = $image->getVar('image_name'); // image filename
$imageMimetype = $image->getVar('image_mimetype');
$imageCreatedTime = $image->getVar('image_created'); // image creation date
if ($imgcat->getVar('imgcat_storetype') === 'db') {
$imagePath = null;
$imageData = $image->getVar('image_body');
} else {
$imagePath = XOOPS_UPLOAD_PATH . '/' . $image->getVar('image_name');
$imageData = file_get_contents($imagePath);
}
$sourceImage = imagecreatefromstring($imageData);
$imageWidth = imagesx($sourceImage);
$imageHeight = imagesy($sourceImage);
} elseif (!empty($imageUrl)) {
// If image is a standard image
if (ONLY_LOCAL_IMAGES) {
// Images must be local files, so for convenience we strip the domain if it's there
$imageUrl = str_replace(XOOPS_URL, '', $imageUrl);
// will exit on any unacceptable urls
imageFilenameCheck($imageUrl);
$imagePath = XOOPS_ROOT_PATH . $imageUrl;
if (!file_exists($imagePath)) {
exit404BadReq();
}
} else {
if ($imageUrl{0} === '/') {
$imageUrl = substr($imageUrl, 0, 1);
}
$imagePath = $imageUrl;
}
// Get the size and MIME type of the requested image
$imageFilename = basename($imagePath); // image filename
$imagesize = getimagesize($imagePath);
$imageWidth = $imagesize[0];
$imageHeight = $imagesize[1];
$imageMimetype = $imagesize['mime'];
$imageCreatedTime = filemtime($imagePath); // image creation date
$imageData = file_get_contents($imagePath);
switch ($imageMimetype) {
case 'image/gif':
$sourceImage = imagecreatefromgif($imagePath);
break;
case 'image/png':
$sourceImage = imagecreatefrompng($imagePath);
break;
case 'image/jpeg':
$sourceImage = imagecreatefromjpeg($imagePath);
break;
default:
exit404BadReq();
break;
}
} else {
// No id, no url, no src parameters
header('Content-type: image/gif');
readfile(XOOPS_ROOT_PATH . '/uploads/blank.gif');
exit();
}
/*
* Use Xoops cache
*/
// Get image_data from the Xoops cache only if the edited image has been cached after the latest modification
// of the original image
xoops_load('XoopsCache');
$edited_image_filename = 'editedimage_' . md5($_SERVER['REQUEST_URI']) . '_' . $imageFilename;
$cached_image = XoopsCache::read($edited_image_filename);
if (!isset($_GET['nocache']) && !isset($_GET['noservercache']) && !empty($cached_image)
&& ($cached_image['cached_time'] >= $imageCreatedTime)) {
header("Content-type: {$imageMimetype}");
header('Content-Length: ' . strlen($cached_image['image_data']));
echo $cached_image['image_data'];
exit();
}
/*
* Get/check editing parameters
*/
// width, height
$max_width = isset($_GET['width']) ? (int)$_GET['width'] : false;
$max_height = isset($_GET['height']) ? (int)$_GET['height'] : false;
// If either a max width or max height are not specified, we default to something large so the unspecified
// dimension isn't a constraint on our resized image.
// If neither are specified but the color is, we aren't going to be resizing at all, just coloring.
if (!$max_width && $max_height) {
$max_width = PHP_INT_MAX;
} elseif ($max_width && !$max_height) {
$max_height = PHP_INT_MAX;
} elseif (!$max_width && !$max_height) {
$max_width = $imageWidth;
$max_height = $imageHeight;
}
// color
$color = isset($_GET['color']) ? preg_replace('/[^0-9a-fA-F]/', '', (string)$_GET['color']) : false;
// filter, radius, angle
$filter = isset($_GET['filter']) ? $_GET['filter'] : false;
$radius = isset($_GET['radius']) ? (string)$_GET['radius'] : false;
$angle = isset($_GET['angle']) ? (float)$_GET['angle'] : false;
// If we don't have a width or height or color or filter or radius or rotate we simply output the original
// image and exit
if (empty($_GET['width']) && empty($_GET['height']) && empty($_GET['color']) && empty($_GET['filter'])
&& empty($_GET['radius']) && empty($_GET['angle'])) {
$last_modified_string = gmdate('D, d M Y H:i:s', $imageCreatedTime) . ' GMT';
$etag = md5($imageData);
doConditionalGet($etag, $last_modified_string);
header("Content-type: {$imageMimetype}");
header('Content-Length: ' . strlen($imageData));
echo $imageData;
exit();
}
// cropratio
$offset_x = 0;
$offset_y = 0;
if (isset($_GET['cropratio'])) {
$crop_ratio = explode(':', (string)$_GET['cropratio']);
if (count($crop_ratio) == 2) {
$ratio_computed = $imageWidth / $imageHeight;
$crop_radio_computed = (float)$crop_ratio[0] / (float)$crop_ratio[1];
if ($ratio_computed < $crop_radio_computed) {
// Image is too tall so we will crop the top and bottom
$orig_height = $imageHeight;
$imageHeight = $imageWidth / $crop_radio_computed;
$offset_y = ($orig_height - $imageHeight) / 2;
} elseif ($ratio_computed > $crop_radio_computed) {
// Image is too wide so we will crop off the left and right sides
$orig_width = $imageWidth;
$imageWidth = $imageHeight * $crop_radio_computed;
$offset_x = ($orig_width - $imageWidth) / 2;
}
}
}
// Setting up the ratios needed for resizing. We will compare these below to determine how to resize the image
// (based on height or based on width)
$xRatio = $max_width / $imageWidth;
$yRatio = $max_height / $imageHeight;
if ($xRatio * $imageHeight < $max_height) {
// Resize the image based on width
$tn_height = ceil($xRatio * $imageHeight);
$tn_width = $max_width;
} else {
// Resize the image based on height
$tn_width = ceil($yRatio * $imageWidth);
$tn_height = $max_height;
}
// quality
$quality = isset($_GET['quality']) ? (int)$_GET['quality'] : DEFAULT_IMAGE_QUALITY;
/*
* Start image editing
*/
// We don't want to run out of memory
ini_set('memory_limit', MEMORY_TO_ALLOCATE);
// Set up a blank canvas for our resized image (destination)
$destination_image = imagecreatetruecolor($tn_width, $tn_height);
imagealphablending($destination_image, false);
imagesavealpha($destination_image, true);
$transparent = imagecolorallocatealpha($destination_image, 255, 255, 255, 127);
imagefilledrectangle($destination_image, 0, 0, $tn_width, $tn_height, $transparent);
// Set up the appropriate image handling functions based on the original image's mime type
switch ($imageMimetype) {
case 'image/gif':
// We will be converting GIFs to PNGs to avoid transparency issues when resizing GIFs
// This is maybe not the ideal solution, but IE6 can suck it
$output_function = 'imagepng';
$imageMimetype = 'image/png'; // We need to convert GIFs to PNGs
$do_sharpen = false;
$quality = round(10 - ($quality / 10)); // We are converting the GIF to a PNG and PNG needs a compression
// level of 0 (no compression) through 9 (max)
break;
case 'image/png':
case 'image/x-png':
$output_function = 'imagepng';
$do_sharpen = false;
$quality = round(10 - ($quality / 10)); // PNG needs a compression level of 0 (no compression) through 9
break;
case 'image/jpeg':
case 'image/pjpeg':
$output_function = 'imagejpeg';
$do_sharpen = true;
break;
default:
exit404BadReq();
break;
}
// Resample the original image into the resized canvas we set up earlier
imagecopyresampled($destination_image, $sourceImage, 0, 0, $offset_x, $offset_y, $tn_width, $tn_height, $imageWidth, $imageHeight);
// Set background color
if (in_array($imageMimetype, array('image/gif', 'image/png'))) {
if (!$color) {
// If this is a GIF or a PNG, we need to set up transparency
imagealphablending($destination_image, false);
imagesavealpha($destination_image, true);
$png_transparency = imagecolorallocatealpha($destination_image, 0, 0, 0, 127);
imagefill($destination_image, 0, 0, $png_transparency);
} else {
// Fill the background with the specified color for matting purposes
if ($color[0] === '#') {
$color = substr($color, 1);
}
$background = false;
if (strlen($color) == 6) {
$background = imagecolorallocate(
$destination_image,
intval($color[0] . $color[1], 16),
intval($color[2] . $color[3], 16),
intval($color[4] . $color[5], 16)
);
} elseif (strlen($color) == 3) {
$background = imagecolorallocate(
$destination_image,
intval($color[0] . $color[0], 16),
intval($color[1] . $color[1], 16),
intval($color[2] . $color[2], 16)
);
}
if ($background) {
imagefill($destination_image, 0, 0, $background);
}
}
} else {
if (!$color) {
$color = DEFAULT_BACKGROUND_COLOR;
}
// Fill the background with the specified color for matting purposes
if ($color[0] === '#') {
$color = substr($color, 1);
}
$background = false;
if (strlen($color) == 6) {
$background = imagecolorallocate(
$destination_image,
intval($color[0] . $color[1], 16),
intval($color[2] . $color[3], 16),
intval($color[4] . $color[5], 16)
);
} elseif (strlen($color) == 3) {
$background = imagecolorallocate(
$destination_image,
intval($color[0] . $color[0], 16),
intval($color[1] . $color[1], 16),
intval($color[2] . $color[2], 16)
);
}
if ($background) {
imagefill($destination_image, 0, 0, $background);
}
}
// Imagefilter
if (ENABLE_IMAGEFILTER && !empty($filter)) {
$filterSet = (array) $filter;
foreach ($filterSet as $currentFilter) {
$rawFilterArgs = explode(',', $currentFilter);
$filterConst = constant(array_shift($rawFilterArgs));
if (null !== $filterConst) { // skip if unknown constant
$filterArgs = array();
$filterArgs[] = $destination_image;
$filterArgs[] = $filterConst;
foreach ($rawFilterArgs as $tempValue) {
$filterArgs[] = trim($tempValue);
}
call_user_func_array('imagefilter', $filterArgs);
}
}
}
// Round corners
if (ENABLE_ROUNDCORNER && !empty($radius)) {
$radii = explode(',', $radius);
switch (count($radii)) {
case 1:
$radii[3] = $radii[2] = $radii[1] = $radii[0];
break;
case 2:
$radii[3] = $radii[0];
$radii[2] = $radii[1];
break;
case 3:
$radii[3] = $radii[0];
break;
case 4:
// NOP
break;
}
$destination_image = imageCreateCorners($destination_image, $radii);
// we need png to support the alpha corners correctly
if ($imageMimetype === 'image/jpeg') {
$output_function = 'imagepng';
$imageMimetype = 'image/png';
$do_sharpen = false;
$quality = round(10 - ($quality / 10));
}
}
// Imagerotate
if (ENABLE_IMAGEROTATE && !empty($angle)) {
$destination_image = imagerotate($destination_image, $angle, $background, 0);
}
if ($do_sharpen) {
// Sharpen the image based on two things:
// (1) the difference between the original size and the final size
// (2) the final size
$sharpness = findSharp($imageWidth, $tn_width);
$sharpen_matrix = array(
array(-1, -2, -1),
array(-2, $sharpness + 12, -2),
array(-1, -2, -1));
$divisor = $sharpness;
$offset = 0;
imageconvolution($destination_image, $sharpen_matrix, $divisor, $offset);
}
// Put the data of the resized image into a variable
ob_start();
$output_function($destination_image, null, $quality);
$imageData = ob_get_contents();
ob_end_clean();
// Update $image_created_time
$imageCreatedTime = time();
// Clean up the memory
imagedestroy($sourceImage);
imagedestroy($destination_image);
/*
* Write the just edited image into the Xoops cache
*/
$cached_image['edited_image_filename'] = $edited_image_filename;
$cached_image['image_data'] = $imageData;
$cached_image['cached_time'] = $imageCreatedTime;
XoopsCache::write($edited_image_filename, $cached_image);
/*
* Send the edited image to the browser
*/
// See if the browser already has the image
$last_modified_string = gmdate('D, d M Y H:i:s', $imageCreatedTime) . ' GMT';
$etag = md5($imageData);
doConditionalGet($etag, $last_modified_string);
header('HTTP/1.1 200 OK');
// if image is cacheable
if (!isset($_GET['nocache']) && !isset($_GET['nobrowsercache'])) {
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $imageCreatedTime) . 'GMT');
header('Cache-control: max-age=31536000');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . 'GMT');
} else {
// "Kill" the browser cache
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // past date
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache'); // HTTP/1.0
}
header("Content-type: {$imageMimetype}");
header("Content-disposition: filename={$imageFilename}");
header('Content-Length: ' . strlen($imageData));
echo $imageData;
|