File: //home/bk/__backup/api/Image.php
<?php
require_once('Mgc.php');
class Image extends Mgc {
private $allowed_extentions = array('png', 'gif', 'jpg', 'jpeg', 'ico');
private $gregwar_image;
public function __construct() {
parent::__construct();
$this->gregwar_image = new Gregwar\Image\Image();
}
/**
* Создание превью изображения
* @param $filename файл с изображением (без пути к файлу)
* @param max_w максимальная ширина
* @param max_h максимальная высота
* @return $string имя файла превью
*/
public function resize($filename, $original_images_dir = null, $resized_images_dir = null) {
list($source_file, $width , $height, $set_watermark, $crop_params) = $this->get_resize_params($filename);
$size = $width.'x'.$height;
$image_sizes = array();
if ($this->settings->image_sizes) {
$image_sizes = explode('|', $this->settings->image_sizes);
}
if (!in_array($size, $image_sizes)){
header("http/1.1 404 not found");
exit();
}
// Если вайл удаленный (http://), зальем его себе
if (preg_match("~^https?://~", $source_file)) {
// Имя оригинального файла
if(!$original_file = $this->download_image($source_file)) {
return false;
}
} else {
$original_file = $source_file;
}
$resized_file = $this->add_resize_params($original_file, $width, $height, $set_watermark, $crop_params);
// Пути к папкам с картинками
if ($original_images_dir && !$resized_images_dir) {
$resized_images_dir = $original_images_dir;
} else {
if (!$original_images_dir) {
$original_images_dir = $this->config->original_images_dir;
}
if (!$resized_images_dir) {
$resized_images_dir = $this->config->resized_images_dir;
}
}
$originals_dir = $this->config->root_dir.$original_images_dir;
$preview_dir = $this->config->root_dir.$resized_images_dir;
if (!file_exists($originals_dir.$original_file)) {
return false;
}
$watermark_offet_x = $this->settings->watermark_offset_x;
$watermark_offet_y = $this->settings->watermark_offset_y;
$watermark_transparency = 1-min(100, $this->settings->watermark_transparency)/100;
if($set_watermark && is_file($this->config->root_dir.$this->config->watermark_file)) {
$watermark = $this->config->root_dir.$this->config->watermark_file;
} else {
$watermark = null;
}
if ($this->config->resize_library == 'gregwar_image') {
$this->image_gregwar_image($originals_dir.$original_file, $preview_dir.$resized_file, $width, $height, $watermark, $watermark_offet_x, $watermark_offet_y, $watermark_transparency, $crop_params);
} elseif ($this->config->resize_library == 'imagick' && class_exists('Imagick')) {
$this->image_constrain_imagick($originals_dir.$original_file, $preview_dir.$resized_file, $width, $height, $watermark, $watermark_offet_x, $watermark_offet_y, $watermark_transparency);
} else {
$this->image_constrain_gd($originals_dir.$original_file, $preview_dir.$resized_file, $width, $height, $watermark, $watermark_offet_x, $watermark_offet_y, $watermark_transparency);
}
return $preview_dir.$resized_file;
}
/*Добавление параметров ресайза для изображения*/
public function add_resize_params($filename, $width=0, $height=0, $set_watermark=false, $crop_params = array()) {
if('.' != ($dirname = pathinfo($filename, PATHINFO_DIRNAME))) {
$file = $dirname.'/'.pathinfo($filename, PATHINFO_FILENAME);
} else {
$file = pathinfo($filename, PATHINFO_FILENAME);
}
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if($width>0 || $height>0) {
$resized_filename = $file.'.'.($width>0?$width:'').'x'.($height>0?$height:'').($set_watermark?'w':'');
} else {
$resized_filename = $file.($set_watermark?'.w':'').$ext;
}
if ($crop_params['x_pos'] && $crop_params['y_pos']) {
$resized_filename .= '.'.$crop_params['x_pos'].'.'.$crop_params['y_pos'];
}
return $resized_filename.'.'.$ext;
}
/*Выборка параметров изображения для ресайза*/
public function get_resize_params($filename) {
// Определаяем параметры ресайза
if(!preg_match('/(.+)\.([0-9]*)x([0-9]*)(w)?(\.(left|center|right)\.(top|center|bottom))?\.([^\.]+)$/', $filename, $matches)) {
return false;
}
$file = $matches[1]; // имя запрашиваемого файла
$width = $matches[2]; // ширина будущего изображения
$height = $matches[3]; // высота будущего изображения
$set_watermark = $matches[4] == 'w'; // ставить ли водяной знак
$ext = $matches[8]; // расширение файла
// crop params
if (!empty($matches[5])) {
$crop_params['x_pos'] = $matches[6];
$crop_params['y_pos'] = $matches[7];
}
return array($file.'.'.$ext, $width, $height, $set_watermark, $crop_params);
}
/*Загрузка изображения*/
public function download_image($filename) {
// Заливаем только есть такой файл есть в базе
$this->db->query('SELECT 1 FROM __images WHERE filename=? LIMIT 1', $filename);
if(!$this->db->result()) {
return false;
}
// Имя оригинального файла
$basename = preg_replace('~(.+)\.([0-9]*)x([0-9]*)(w)?\.([^\.\?]+)(\?.*)?$~', '${1}.${5}', $filename);
$basename = explode('&', pathinfo($basename, PATHINFO_BASENAME));
$uploaded_file = array_shift($basename);
$base = urldecode(pathinfo($uploaded_file, PATHINFO_FILENAME));
$ext = pathinfo($uploaded_file, PATHINFO_EXTENSION);
// Если такой файл существует, нужно придумать другое название
$new_name = urldecode($uploaded_file);
while(file_exists($this->config->root_dir.$this->config->original_images_dir.$new_name)) {
$new_base = pathinfo($new_name, PATHINFO_FILENAME);
if(preg_match('/_([0-9]+)$/', $new_base, $parts)) {
$new_name = $base.'_'.($parts[1]+1).'.'.$ext;
} else {
$new_name = $base.'_1.'.$ext;
}
}
// Перед долгим копированием займем это имя
fclose(fopen($this->config->root_dir.$this->config->original_images_dir.$new_name, 'w'));
if (copy($filename, $this->config->root_dir.$this->config->original_images_dir.$new_name)) {
$this->db->query('UPDATE __images SET filename=? WHERE filename=?', $new_name, $filename);
return $new_name;
} else {
@unlink($this->config->root_dir.$this->config->original_images_dir.$new_name);
return false;
}
}
/*Загрузка изображения*/
public function upload_image($filename, $name, $original_dir = null) {
// Имя оригинального файла
$name = preg_replace('~(.+)\.([0-9]*)x([0-9]*)(w)?\.([^\.\?]+)$~', '${1}.${5}', $name);
$name = $this->correct_filename($name);
$uploaded_file = $new_name = pathinfo($name, PATHINFO_BASENAME);
$base = pathinfo($uploaded_file, PATHINFO_FILENAME);
$ext = pathinfo($uploaded_file, PATHINFO_EXTENSION);
if (!$original_dir) {
$original_dir = $this->config->original_images_dir;
}
if(in_array(strtolower($ext), $this->allowed_extentions)) {
while(file_exists($this->config->root_dir.$original_dir.$new_name)) {
$new_base = pathinfo($new_name, PATHINFO_FILENAME);
if(preg_match('/_([0-9]+)$/', $new_base, $parts)) {
$new_name = $base.'_'.($parts[1]+1).'.'.$ext;
} else {
$new_name = $base.'_1.'.$ext;
}
}
if(move_uploaded_file($filename, $this->config->root_dir.$original_dir.$new_name)) {
return $new_name;
}
}
return false;
}
private function image_gregwar_image($src_file, $dst_file, $max_w, $max_h, $watermark=null, $watermark_offet_x=0, $watermark_offet_y=0, $watermark_opacity=1, $crop_params) {
$image = $this->gregwar_image->open($src_file);
// размеры исходного изображения
$src_w = $image->width();
$src_h = $image->height();
list($dst_w, $dst_h) = $this->calc_contrain_size($src_w, $src_h, $max_w, $max_h);
if (!empty($crop_params)) {
$x_pos = $crop_params['x_pos'];
$y_pos = $crop_params['y_pos'];
$dst_w = min($src_w, $max_w);
$dst_h = min($src_h, $max_h);
$image->zoomCrop($dst_w, $dst_h, 'transparent', $x_pos, $y_pos);
} else {
$image->cropResize($dst_w, $dst_h);
}
if ($watermark && is_readable($watermark)) {
$watermark_image = $this->gregwar_image->open($watermark);
// размеры водяного знака
$watermark_width = $watermark_image->width();
$watermark_height = $watermark_image->height();
$watermark_x = min(($dst_w-$watermark_width)*$watermark_offet_x/100, $dst_w);
$watermark_y = min(($dst_h-$watermark_height)*$watermark_offet_y/100, $dst_h);
$image->merge($watermark_image, $watermark_x, $watermark_y, $watermark_width, $watermark_height);
}
$src_ext = $image->guessType();
if ($src_ext == 'gif') {
$src_ext = 'png';
}
$image->save($dst_file, $src_ext, $this->settings->image_quality ? $this->settings->image_quality : 80);
}
/**
* Создание превью средствами gd
* @param $src_file исходный файл
* @param $dst_file файл с результатом
* @param max_w максимальная ширина
* @param max_h максимальная высота
* @return bool
*/
private function image_constrain_gd($src_file, $dst_file, $max_w, $max_h, $watermark=null, $watermark_offet_x=0, $watermark_offet_y=0, $watermark_opacity=1) {
$quality = 100;
// Параметры исходного изображения
@list($src_w, $src_h, $src_type) = array_values(getimagesize($src_file));
$src_type = image_type_to_mime_type($src_type);
if(empty($src_w) || empty($src_h) || empty($src_type)) {
return false;
}
// Нужно ли обрезать?
if (!$watermark && ($src_w <= $max_w) && ($src_h <= $max_h)) {
// Нет - просто скопируем файл
if (!copy($src_file, $dst_file)) {
return false;
}
return true;
}
// Размеры превью при пропорциональном уменьшении
@list($dst_w, $dst_h) = $this->calc_contrain_size($src_w, $src_h, $max_w, $max_h);
// Читаем изображение
switch ($src_type) {
case 'image/jpeg':
$src_img = imageCreateFromJpeg($src_file);
break;
case 'image/gif':
$src_img = imageCreateFromGif($src_file);
break;
case 'image/png':
$src_img = imageCreateFromPng($src_file);
imagealphablending($src_img, true);
break;
default:
return false;
}
if(empty($src_img)) {
return false;
}
$src_colors = imagecolorstotal($src_img);
// create destination image (indexed, if possible)
if ($src_colors > 0 && $src_colors <= 256) {
$dst_img = imagecreate($dst_w, $dst_h);
} else {
$dst_img = imagecreatetruecolor($dst_w, $dst_h);
}
if (empty($dst_img)) {
return false;
}
$transparent_index = imagecolortransparent($src_img);
if ($transparent_index >= 0 && $transparent_index <= 128) {
$t_c = imagecolorsforindex($src_img, $transparent_index);
$transparent_index = imagecolorallocate($dst_img, $t_c['red'], $t_c['green'], $t_c['blue']);
if ($transparent_index === false) {
return false;
}
if (!imagefill($dst_img, 0, 0, $transparent_index)) {
return false;
}
imagecolortransparent($dst_img, $transparent_index);
}
// or preserve alpha transparency for png
elseif ($src_type === 'image/png') {
if (!imagealphablending($dst_img, false)) {
return false;
}
$transparency = imagecolorallocatealpha($dst_img, 0, 0, 0, 127);
if (false === $transparency) {
return false;
}
if (!imagefill($dst_img, 0, 0, $transparency)) {
return false;
}
if (!imagesavealpha($dst_img, true)) {
return false;
}
}
// resample the image with new sizes
if (!imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_w, $dst_h, $src_w, $src_h)) {
return false;
}
// Watermark
if(!empty($watermark) && is_readable($watermark)) {
$overlay = imagecreatefrompng($watermark);
// Get the size of overlay
$owidth = imagesx($overlay);
$oheight = imagesy($overlay);
$watermark_x = min(($dst_w-$owidth)*$watermark_offet_x/100, $dst_w);
$watermark_y = min(($dst_h-$oheight)*$watermark_offet_y/100, $dst_h);
imagecopy($dst_img, $overlay, $watermark_x, $watermark_y, 0, 0, $owidth, $oheight);
}
// recalculate quality value for png image
if ('image/png' === $src_type) {
$quality = round(($quality / 100) * 10);
if ($quality < 1) {
$quality = 1;
} elseif ($quality > 10) {
$quality = 10;
}
$quality = 10 - $quality;
}
// Сохраняем изображение
switch ($src_type) {
case 'image/jpeg':
return imageJpeg($dst_img, $dst_file, $quality);
case 'image/gif':
return imageGif($dst_img, $dst_file, $quality);
case 'image/png':
imagesavealpha($dst_img, true);
return imagePng($dst_img, $dst_file, $quality);
default:
return false;
}
}
/**
* Создание превью средствами imagick
* @param $src_file исходный файл
* @param $dst_file файл с результатом
* @param max_w максимальная ширина
* @param max_h максимальная высота
* @return bool
*/
private function image_constrain_imagick($src_file, $dst_file, $max_w, $max_h, $watermark=null, $watermark_offet_x=0, $watermark_offet_y=0, $watermark_opacity=1) {
$thumb = new Imagick();
$sharpen = 0.2;
// Читаем изображение
if(!$thumb->readImage($src_file)) {
return false;
}
// Размеры исходного изображения
$src_w = $thumb->getImageWidth();
$src_h = $thumb->getImageHeight();
// Нужно ли обрезать?
if (!$watermark && ($src_w <= $max_w) && ($src_h <= $max_h)) {
// Нет - просто скопируем файл
if (!copy($src_file, $dst_file)) {
return false;
}
return true;
}
// Размеры превью при пропорциональном уменьшении
list($dst_w, $dst_h) = $this->calc_contrain_size($src_w, $src_h, $max_w, $max_h);
// Уменьшаем
$thumb->thumbnailImage($dst_w, $dst_h);
// Устанавливаем водяной знак
if($watermark && is_readable($watermark)) {
$overlay = new Imagick($watermark);
$overlay->evaluateImage(Imagick::EVALUATE_MULTIPLY, $watermark_opacity, Imagick::CHANNEL_ALPHA);
// Get the size of overlay
$owidth = $overlay->getImageWidth();
$oheight = $overlay->getImageHeight();
$watermark_x = min(($dst_w-$owidth)*$watermark_offet_x/100, $dst_w);
$watermark_y = min(($dst_h-$oheight)*$watermark_offet_y/100, $dst_h);
}
// Анимированные gif требуют прохода по фреймам
foreach($thumb as $frame) {
// Уменьшаем
$frame->thumbnailImage($dst_w, $dst_h);
/* Set the virtual canvas to correct size */
$frame->setImagePage($dst_w, $dst_h, 0, 0);
// Наводим резкость
$thumb->adaptiveSharpenImage($sharpen, $sharpen);
if(isset($overlay) && is_object($overlay)) {
$frame->compositeImage($overlay, imagick::COMPOSITE_OVER, $watermark_x, $watermark_y, imagick::COLOR_ALPHA);
}
}
// Убираем комменты и т.п. из картинки
$thumb->stripImage();
$thumb->setImageCompressionQuality($this->settings->image_quality ? $this->settings->image_quality : 80 );
$thumb->setImageCompression($this->settings->image_quality ? $this->settings->image_quality : 80);
// Записываем картинку
if(!$thumb->writeImages($dst_file, true)) {
return false;
}
// Уборка
$thumb->destroy();
if(isset($overlay) && is_object($overlay)) {
$overlay->destroy();
}
return true;
}
/**
* Вычисляет размеры изображения, до которых нужно его пропорционально уменьшить, чтобы вписать в квадрат $max_w x $max_h
* @param src_w ширина исходного изображения
* @param src_h высота исходного изображения
* @param max_w максимальная ширина
* @param max_h максимальная высота
* @return array(w, h)
*/
public function calc_contrain_size($src_w, $src_h, $max_w = 0, $max_h = 0) {
if($src_w == 0 || $src_h == 0) {
return false;
}
$dst_w = $src_w;
$dst_h = $src_h;
if($src_w > $max_w && $max_w>0) {
$dst_h = $src_h * ($max_w/$src_w);
$dst_w = $max_w;
}
if($dst_h > $max_h && $max_h>0) {
$dst_w = $dst_w * ($max_h/$dst_h);
$dst_h = $max_h;
}
return array($dst_w, $dst_h);
}
private function files_identical($fn1, $fn2) {
$buffer_len = 1024;
if(!$fp1 = fopen(dirname(dirname(__FILE__)).'/'.$fn1, 'rb')) {
return FALSE;
}
if(!$fp2 = fopen($fn2, 'rb')) {
fclose($fp1);
return FALSE;
}
$same = TRUE;
while (!feof($fp1) and !feof($fp2)) {
if(fread($fp1, $buffer_len) !== fread($fp2, $buffer_len)) {
$same = FALSE;
break;
}
}
if(feof($fp1) !== feof($fp2)) {
$same = FALSE;
}
fclose($fp1);
fclose($fp2);
return $same;
}
/*Транслит названия изображения*/
public function correct_filename($filename) {
$ru = explode('-', "А-а-Б-б-В-в-Ґ-ґ-Г-г-Д-д-Е-е-Ё-ё-Є-є-Ж-ж-З-з-И-и-І-і-Ї-ї-Й-й-К-к-Л-л-М-м-Н-н-О-о-П-п-Р-р-С-с-Т-т-У-у-Ф-ф-Х-х-Ц-ц-Ч-ч-Ш-ш-Щ-щ-Ъ-ъ-Ы-ы-Ь-ь-Э-э-Ю-ю-Я-я");
$en = explode('-', "A-a-B-b-V-v-G-g-G-g-D-d-E-e-E-e-E-e-ZH-zh-Z-z-I-i-I-i-I-i-J-j-K-k-L-l-M-m-N-n-O-o-P-p-R-r-S-s-T-t-U-u-F-f-H-h-TS-ts-CH-ch-SH-sh-SCH-sch---Y-y---E-e-YU-yu-YA-ya");
$res = str_replace($ru, $en, $filename);
$res = preg_replace("/[\s]+/ui", '-', $res);
$res = preg_replace("/[^a-zA-Z0-9\.\-\_]+/ui", '', $res);
$res = strtolower($res);
return $res;
}
/**
* Удаления изображения и его ресайзов
* id - id сущности
* field - поле в таблице
* table - имя таблицы без префиксов like "blog"(not "__blog" or "s_blog")
*/
public function delete_image($id, $field = null, $table = null, $original_dir = null, $resized_dir = null, $lang_id = 0, $lang_field = '') {
if (!$field || !$table || !$original_dir) {
return false;
}
if (!$lang_id) {
$query = $this->db->placehold("SELECT $field FROM __$table WHERE id=?", $id);
$this->db->query($query);
$filename = $this->db->result($field);
if (!empty($filename)) {
$query = $this->db->placehold("UPDATE __$table SET $field='' WHERE id=?", $id);
$this->db->query($query);
$query = $this->db->placehold("SELECT count(*) as count FROM __$table WHERE $field=? LIMIT 1", $filename);
$this->db->query($query);
$count = $this->db->result('count');
if($count == 0) {
$file = pathinfo($filename, PATHINFO_FILENAME);
$ext = pathinfo($filename, PATHINFO_EXTENSION);
// Удалить все ресайзы
if (!empty($resized_dir)) {
$rezised_images = glob($this->config->root_dir.$resized_dir.$file.".*x*.".$ext);
if(is_array($rezised_images)) {
foreach ($rezised_images as $f) {
@unlink($f);
}
}
}
@unlink($this->config->root_dir.$original_dir.$filename);
}
}
} else {
$query = $this->db->placehold("SELECT $field FROM __lang_$table WHERE $lang_field=? and lang_id=?", $id, $lang_id);
$this->db->query($query);
$filename = $this->db->result($field);
if (!empty($filename)) {
$query = $this->db->placehold("UPDATE __lang_$table SET $field='' WHERE $lang_field=? and lang_id=?", $id, $lang_id);
$this->db->query($query);
$query = $this->db->placehold("SELECT count(*) as count FROM __lang_$table WHERE $field=? LIMIT 1", $filename);
$this->db->query($query);
$count = $this->db->result('count');
if($count == 0) {
$file = pathinfo($filename, PATHINFO_FILENAME);
$ext = pathinfo($filename, PATHINFO_EXTENSION);
// Удалить все ресайзы
if (!empty($resized_dir)) {
$rezised_images = glob($this->config->root_dir.$resized_dir.$file.".*x*.".$ext);
if(is_array($rezised_images)) {
foreach ($rezised_images as $f) {
@unlink($f);
}
}
}
@unlink($this->config->root_dir.$original_dir.$filename);
}
}
}
}
}