示例#1
0
 public static function &process_image($params)
 {
     $mod = cms_utils::get_module(MOD_CGSMARTIMAGE);
     $config = cmsms()->GetConfig();
     $want_transform = 0;
     $do_transform = 0;
     $have_transform = 0;
     $dest_fname = '';
     $dest_url = '';
     $img = '';
     $srcfile = '';
     $rel = 0;
     $outp = array();
     // output params
     $outp['id'] = '';
     $outp['name'] = '';
     $outp['class'] = '';
     $outp['style'] = '';
     $outp['src'] = '';
     $outp['width'] = '';
     $outp['height'] = '';
     $outp['error'] = '';
     $outp['output'] = null;
     $opp = array();
     // operation params
     $opp['overwrite'] = 0;
     $opp['nobcache'] = 0;
     $opp['noremote'] = 0;
     $opp['noembed'] = 0;
     $opp['noauto'] = 0;
     $opp['noppradjust'] = 0;
     $opp['norotate'] = 0;
     $opp['notimecheck'] = 0;
     $opp['noautoscale'] = 0;
     $opp['notag'] = 0;
     $opp['noresponsive'] = 0;
     $opp['nodpradjust'] = 0;
     $opp['nobreakpoints'] = 0;
     $opp['max_width'] = 0;
     $opp['max_height'] = 0;
     $opp['d_max_width'] = 0;
     // device calculated max width (including breakpoints)
     $opp['d_max_height'] = 0;
     // device calculated max width (including breakpoints)
     $opp['src'] = '';
     $opp['quality'] = 75;
     $opp['filters'] = array();
     $opp['force_type'] = '';
     $opp['force_ext'] = $mod->GetPreference('force_extension', 0);
     $opp['progressive'] = (int) $mod->GetPreference('progressive', 0);
     $opp['autoscale_op'] = $mod->GetPreference('autoscale_op', 'croptofit');
     $cge = cms_utils::get_module(MOD_CGEXTENSIONS);
     $srcimgsize = '';
     // src image size
     $lastfilter = null;
     try {
         $tmp = $mod->GetPreference('aliases');
         if ($tmp) {
             $aliases = unserialize($tmp);
         }
         // first pass... expand aliases and build src
         $new_params = array();
         foreach ($params as $key => $value) {
             if (startswith($key, 'alias')) {
                 // expand alias
                 $options = self::_get_alias_options($value);
                 if ($options) {
                     // parse a string into an array of arguments.
                     $data = self::_expand_quoted_string($options);
                     if (is_array($data)) {
                         foreach ($data as $key => $value) {
                             $new_params[$key] = $value;
                         }
                     }
                     continue;
                 }
             } elseif (startswith($key, 'src') && $key != 'src') {
                 // handle src1 through src99 arguments.
                 if (!isset($new_params['src'])) {
                     $new_params['src'] = '';
                 }
                 if (!empty($new_params['src']) && !endswith($new_params['src'], '/')) {
                     $new_params['src'] .= '/';
                 }
                 $new_params['src'] .= $value;
                 continue;
             }
             // everything else just gets added.
             $new_params[$key] = $value;
         }
         $params = $new_params;
         // second pass, build our arrays
         $parse_params = function ($parms, $depth = 0) use(&$parse_params, &$opp, &$outp, &$mod) {
             foreach ($parms as $key => $value) {
                 $matches = array();
                 if (preg_match('/^filter_[0-9]._/', $key, $matches)) {
                     $key = substr($key, strlen($matches[0]));
                     $filter = ucwords($key);
                     $args = explode(',', $value);
                     $classname = 'CGImage_' . $filter . '_Filter';
                     if (!class_exists($classname)) {
                         throw new Exception($mod->Lang('error_unknownfilter', $filter));
                     }
                     // add it to the ops.
                     $opp['filters'][] = array($classname, $args);
                     // done.
                     continue;
                 }
                 if (startswith($key, 'filter_')) {
                     // handle filter argument.
                     $filter = ucwords(substr($key, strlen('filter_')));
                     $args = explode(',', $value);
                     $classname = 'CGImage_' . $filter . '_Filter';
                     if (!class_exists($classname)) {
                         throw new Exception($mod->Lang('error_unknownfilter', $filter));
                     }
                     // add it to the ops.
                     $opp['filters'][] = array($classname, $args);
                     // done.
                     continue;
                 }
                 switch ($key) {
                     case 'data':
                         if ($depth == 0) {
                             $parse_params($value, $depth + 1);
                         }
                         break;
                     case 'class':
                     case 'id':
                     case 'style':
                     case 'name':
                     case 'alt':
                     case 'rel':
                     case 'title':
                     case 'autoscale_op':
                         $outp[$key] = trim($value);
                         break;
                     case 'width':
                     case 'height':
                         $outp[$key] = (int) $value;
                         break;
                     case 'max_width':
                     case 'max_height':
                         $opp[$key] = (int) $value;
                         break;
                     case 'src':
                     case 'force_type':
                         $opp[$key] = trim($value);
                         break;
                     case 'quality':
                         $opp['quality'] = (int) $value;
                         $opp['quality'] = min(100, max(0, $opp['quality']));
                         break;
                     case 'overwrite':
                     case 'notag':
                     case 'noremote':
                     case 'noresponsive':
                     case 'nodpradjust':
                     case 'nobreakpoints':
                     case 'nobcache':
                     case 'noembed':
                     case 'noauto':
                     case 'norotate':
                     case 'notimecheck':
                     case 'force_ext':
                     case 'progressive':
                         $opp[$key] = cge_utils::to_bool($value);
                         break;
                 }
             }
         };
         $parse_params($params);
         if (!$opp['src']) {
             throw new Exception($mod->Lang('error_missingparam', 'src'));
         }
         //
         // find the source image ... the actual filename
         // use some automagic intelligence to find it.
         //
         $relative_to = null;
         $src_decoded = urldecode($opp['src']);
         if (!$srcfile && startswith($src_decoded, $config['uploads_url'])) {
             $tmp = str_replace($config['uploads_url'], $config['uploads_path'], $src_decoded);
             if (is_file($tmp)) {
                 $relative_to = 'uploads';
                 $srcfile = $tmp;
             }
         }
         if (!$srcfile && startswith($src_decoded, $config['root_url'])) {
             $tmp = str_replace($config['root_url'], $config['root_path'], $src_decoded);
             if (is_file($tmp)) {
                 $relative_to = 'root';
                 $srcfile = $tmp;
             }
         }
         if (!$srcfile && isset($config['ssl_url']) && startswith($src_decoded, $config['ssl_url'])) {
             $tmp = str_replace($config['ssl_url'], $config['root_path'], $src_decoded);
             if (is_file($tmp)) {
                 $relative_to = 'root';
                 $srcfile = $tmp;
             }
         }
         if (!$srcfile && startswith($opp['src'], '/')) {
             // treat as absolute filename
             $rp1 = realpath($config['root_path']);
             $rp2 = realpath($opp['src']);
             if (startswith($rp2, $rp1) && is_file($opp['src'])) {
                 $relative_to = 'root';
                 $srcfile = $opp['src'];
             }
         }
         if (!$srcfile) {
             // check relative path wrt the uploads dir.
             $tmp = cms_join_path($config['uploads_path'], $opp['src']);
             $rp1 = realpath($config['uploads_path']);
             $rp2 = realpath($tmp);
             if (startswith($rp2, $rp1) && is_file($tmp)) {
                 $relative_to = 'uploads';
                 $srcfile = $tmp;
             }
         }
         if (!$srcfile) {
             // check relative path wrt the root dir.
             $tmp = cms_join_path($config['root_path'], $opp['src']);
             $rp1 = realpath($config['root_path']);
             $rp2 = realpath($tmp);
             if (startswith($rp2, $rp1) && is_file($tmp)) {
                 $relative_to = 'root';
                 $srcfile = $tmp;
             }
         }
         if (!$srcfile && $opp['noremote'] == 0 && (startswith($opp['src'], 'http:') || startswith($opp['src'], 'https:') || startswith($opp['src'], 'ftp:'))) {
             // okay, gotta assume that ths is a remote file
             // get it, and cache it.
             $cachefile = TMP_CACHE_LOCATION . '/cgsi_' . md5($opp['src']) . '.img';
             if (!is_file($cachefile)) {
                 $data = @file_get_contents($opp['src']);
                 if ($data) {
                     file_put_contents($cachefile, $data);
                     $srcfile = $cachefile;
                 }
             } else {
                 $srcfile = $cachefile;
             }
         }
         if (!$srcfile) {
             throw new Exception($mod->Lang('error_srcnotfound', $opp['src']));
             return $outp;
         }
         // get the source image size
         $srcinfo = getimagesize($srcfile);
         if (!is_array($srcinfo) || count($srcinfo) < 2) {
             throw new Exception($mod->Lang('error_srcnotfound', $opp['src']));
         } else {
             $srcimgsize = array('width' => $srcinfo[0], 'height' => $srcinfo[1]);
             $memory_needed = round($srcinfo[0] * $srcinfo[1] * (isset($srcinfo['bits']) ? $srcinfo['bits'] : 8) * (isset($srcinfo['channels']) ? $srcinfo['channels'] : 3) / 8 + 65535);
             if ($mod->GetPreference('checkmemory', 1) && !cge_utils::have_enough_memory($memory_needed)) {
                 throw new Exception($mod->Lang('error_insufficientmemory') . ': ' . (int) ($memory_needed / 1024) . 'k');
             }
         }
         // are we automagically rotating?
         if (!$opp['norotate'] && function_exists('exif_read_data')) {
             // if there is already a rotate filter in the list, we won't override that.
             $fn = 0;
             for ($f = 0; $f < count($opp['filters']); $f++) {
                 if ($opp['filters'][$f][0] == 'CGImage_Rotate_Filter') {
                     $fn = 1;
                     break;
                 }
             }
             if ($fn == 0) {
                 // we can try to read the exif information to find an orientation.
                 $exif = @exif_read_data($srcfile, 0, TRUE);
                 if (is_array($exif) && isset($exif['IFD0']) && isset($exif['IFD0']['Orientation']) && is_int($exif['IFD0']['Orientation'])) {
                     // found an orientation, now we gotta figure out what filters to add.
                     $orientation = (int) $exif['IFD0']['Orientation'];
                     $new_filters = array();
                     switch ($orientation) {
                         case 1:
                             // nothing.
                             break;
                         case 2:
                             // horizontal flip.
                             $new_filters[] = array('CGImage_Flip_Filter', 0);
                             break;
                         case 3:
                             // rotate 180
                             $new_filters[] = array('CGImage_Rotate_Filter', array(180));
                             break;
                         case 4:
                             $new_filters[] = array('CGImage_Flip_Filter', 1);
                             break;
                         case 5:
                             $new_filters[] = array('CGImage_Flip_Filter', 1);
                             $new_filters[] = array('CGImage_Rotate_Filter', array(90));
                             break;
                         case 6:
                             $new_filters[] = array('CGImage_Rotate_Filter', array(90));
                             break;
                         case 7:
                             $new_filters[] = array('CGImage_Flip_Filter', 0);
                             $new_filters[] = array('CGImage_Rotate_Filter', array(90));
                             break;
                         case 8:
                             $new_filters[] = array('CGImage_Rotate_Filter', array(-90));
                             break;
                     }
                     $opp['filters'] = array_merge($new_filters, $opp['filters']);
                 }
             }
         }
         // doing responsive images... get device width and height.
         // set it into max_width and max_height
         $device_caps = null;
         if (!$opp['noresponsive']) {
             $device_caps = self::get_device_capabilities();
             if (!$device_caps && $mod->GetPreference('assume_responsive')) {
                 // if assume responsive is enabled.  we are assuming that a cookie will be present providing device capabilities...
                 // if we could not find the cookie then output nothing so that we do not do redundant image processing.
                 // the next request will have the device capabilities (the javascript that generates the cookie forces a reload if it has to set the cookie)
                 $outp['output'] = '<!-- CGSmartImage processing stopped because of assume_responsive preference and no device capabilities found -->';
                 // this is way better than a goto.
                 throw new \CGSIStopHereException('force_responsive enabled');
             }
             if (is_array($device_caps) && isset($device_caps['width']) && isset($device_caps['height'])) {
                 // we found device capabilities.
                 $dpr = max(1, \cge_param::get_int($device_caps, 'dpr', 1));
                 // we have to do auto-scaling now, responsive stuff trumps the setting.
                 $opp['noautoscale'] = 0;
                 // merge that data with any max_width and max_height that have already been supplied.
                 $opp['d_max_width'] = (int) $device_caps['width'];
                 $opp['d_max_height'] = (int) $device_caps['height'];
                 // experimental... check for breakpoints
                 // and if a breakpoint can be found, further adjust the max_width and max_height params
                 if (($tmp = $mod->GetPreference('responsive_breakpoints')) && !$opp['nobreakpoints']) {
                     $tmp = explode(',', $tmp);
                     $bp = array();
                     for ($i = 0; $i < count($tmp); $i++) {
                         $t1 = (int) trim($tmp[$i]);
                         if ($t1 > 0) {
                             $bp[] = $t1;
                         }
                     }
                     if (count($bp)) {
                         // we have valid breakpoints.
                         // find the most suitable one given our max resolution.
                         asort($bp);
                         $lval = max($opp['d_max_width'], $opp['d_max_height']);
                         for ($i = count($bp) - 1; $i > 0; $i--) {
                             if ($bp[$i] > $lval) {
                                 continue;
                             }
                             break;
                         }
                         list($max_w, $max_h) = self::_adjust_sizes_to_aspect_ratio($srcimgsize['width'], $srcimgsize['height'], $bp[$i]);
                         $opp['d_max_width'] = (int) $max_w;
                         $opp['d_max_height'] = (int) $max_h;
                     }
                 }
             }
         }
         if (!$opp['noautoscale']) {
             $destimgsize['width'] = self::_min_not0($srcimgsize['width'], $opp['max_width'], $opp['d_max_width'], $outp['width']);
             $destimgsize['height'] = self::_min_not0($srcimgsize['height'], $opp['max_height'], $opp['d_max_height'], $outp['height']);
             $dominant = null;
             if ($opp['max_width'] || $outp['width']) {
                 $dominant = $dominant ? 'b' : 'x';
             }
             if ($opp['max_height'] || $outp['height']) {
                 $dominant = $dominant ? 'b' : 'y';
             }
             if ($dominant == 'b') {
                 $dominant = $destimgsize['width'] < $destimgsize['height'] ? 'x' : 'y';
             }
             $aspect_ratio = $srcimgsize['width'] / $srcimgsize['height'];
             switch ($dominant) {
                 case 'x':
                     /* x is dominant */
                     // adjust to x dimension, retain aspect ratio
                     $destimgsize['height'] = (int) ($destimgsize['width'] / $aspect_ratio);
                     break;
                 case 'y':
                     /* y is dominant */
                     // adjust to y dimension, retain aspect ratio
                     $destimgsize['width'] = (int) ($destimgsize['height'] * $aspect_ratio);
                     break;
             }
             if ($device_caps && !$opp['nodpradjust'] && isset($device_caps['dpr']) && $device_caps['dpr'] > 1) {
                 // this will adjust the destination width and height based on dpr
                 $tmp = $destimgsize;
                 $tmp['width'] *= $dpr;
                 $tmp['height'] *= $dpr;
                 if ($tmp['width'] <= $srcimgsize['width'] && $tmp['height'] <= $srcimgsize['height']) {
                     // retain the original size of the image in the width and height attributes of the img tag
                     // but for higher dpr devices generate a bigger image.
                     $outp['width'] = $destimgsize['width'];
                     $outp['height'] = $destimgsize['height'];
                     $opp['noauto'] = 1;
                     $destimgsize = $tmp;
                 }
             }
             // make sure we're actually doing something
             if ($destimgsize != $srcimgsize) {
                 switch (strtolower($opp['autoscale_op'])) {
                     case 'resize':
                         $filter = 'CGImage_Resize_Filter';
                         $tmp = $destimgsize;
                         $tmp['resample'] = 1;
                         $lastfilter = array($filter, $tmp);
                         break;
                     case 'croptofit':
                     default:
                         $filter = 'CGImage_Croptofit_Filter';
                         $tmp = $destimgsize;
                         $tmp['loc'] = 'c';
                         $tmp['upscale'] = 0;
                         $lastfilter = array($filter, $tmp);
                         break;
                 }
                 if (!$opp['noauto']) {
                     // we are allowing auto tags... so we adjust the outp stuff to our finished resolution
                     $outp['width'] = $destimgsize['width'];
                     $outp['height'] = $destimgsize['height'];
                 }
             }
         }
         //
         // check if we are actually doing anything
         //
         // if we are forcing a type and that type is not the same as our current type.
         if ($opp['force_type']) {
             $tmp_a = self::parse_type($opp['force_type']);
             if ($tmp_a != $srcinfo['mime']) {
                 $want_transform = 1;
             }
         }
         // if we got the image from a remote location, but there are no other filters, we will do a simple transform
         if (!$relative_to) {
             $want_transform = 1;
             if (!count($opp['filters'])) {
                 $filter = 'CGImage_NOOP_Filter';
                 $opp['filters'][] = array($filter, array());
             }
         }
         // if we have a last filter... add it.
         if ($lastfilter) {
             $opp['filters'][] = $lastfilter;
         }
         // if there are any filters... then we have to transform this image
         if (count(array_keys($opp['filters']))) {
             $want_transform = 1;
         }
         //
         // end of smartness stuff... now begin the work
         //
         if ($want_transform) {
             // calculate our destination name and url.
             $tmp = basename($srcfile);
             if (!isset($outp['alt'])) {
                 $outp['alt'] = $tmp;
             }
             $ext = strrchr($tmp, '.');
             $t2 = md5(serialize($opp));
             $destname = 'img-' . $t2;
             $t3 = self::parse_type($opp['force_type'] ? $opp['force_type'] : $srcinfo['mime']);
             if ($opp['force_ext']) {
                 $destname .= self::get_extension($t3);
             }
             $destdir = $mod->get_cache_path();
             if (!is_dir($destdir)) {
                 @mkdir($destdir, 0777, true);
                 touch($destdir . '/index.html');
             }
             if (!is_dir($destdir)) {
                 throw new Exception($mod->Lang('error_mkdir', $destdir));
             }
             // see if it exists
             $dest_fname = $destdir . '/' . $destname;
             $dest_url = $mod->get_cached_image_url($destname);
             $t1 = filemtime($srcfile);
             $t2 = is_file($dest_fname) ? filemtime($dest_fname) : 0;
             if (!is_file($dest_fname) || $t2 < $t1 && !$opp['notimecheck'] || $opp['overwrite']) {
                 $do_transform = 1;
             }
         } else {
             // no transofmration... just use the src image
             // but make it into an absolute URL
             $dest_fname = $srcfile;
             switch ($relative_to) {
                 case 'root':
                     $_src = $srcfile;
                     if (startswith($_src, $config['root_path'])) {
                         $_src = substr($_src, strlen($config['root_path']));
                     }
                     $dest_url = $config['root_url'] . $_src;
                     break;
                 case 'uploads':
                     $_src = $srcfile;
                     if (startswith($srcfile, $config['uploads_path'])) {
                         $_src = substr($srcfile, strlen($config['uploads_path']));
                     }
                     $dest_url = $config['uploads_url'] . $_src;
                     break;
             }
         }
         if ($do_transform) {
             try {
                 // load the image.
                 $img = new CGImageBase($srcfile);
                 // process filters
                 $i = 0;
                 while ($i < count($opp['filters'])) {
                     $filter = $opp['filters'][$i][0];
                     $filter_obj = new $filter($opp['filters'][$i][1]);
                     $img = $filter_obj->transform($img);
                     $img['dirty'] = 1;
                     // force the image dirty, just so that we can save it.
                     $i++;
                 }
                 // check some stuff
                 if ($opp['noauto'] == 0 && ($outp['width'] && $img['width'] < $outp['width'] || $outp['height'] && $img['height'] < $outp['height'])) {
                     // user specified a width, and/or height...but they are smaller than the output of the filtering.
                     // this will ensure that the tag will match the image.
                     $outp['width'] = $img['width'];
                     $outp['height'] = $img['height'];
                 }
                 // and write the thing.
                 if (is_object($img)) {
                     if ($opp['force_type']) {
                         $img['type'] = self::parse_type($opp['force_type']);
                     }
                     if ($img['dirty']) {
                         $img->save($dest_fname, $opp['quality'], $opp['progressive']);
                     }
                 }
             } catch (Exception $e) {
                 audit('', 'CGSmartImage', 'Error encountered on ' . $opp['src'] . ': ' . $e->GetMessage());
                 throw $e;
             }
         }
         // if
         // now, we have a cached filename ... need to get its dimensions.
         // and make sure that we're never outputting anything bigger than the cached dimensions
         // unless noauto is set
         if ($opp['noauto'] == 0) {
             if ($outp['width'] && $srcimgsize['width'] < $outp['width'] || $outp['height'] && $srcimgsize['height'] < $outp['height']) {
                 $outp['width'] = $t_width;
                 $outp['height'] = $t_height;
             }
         }
         // at this point, we're ready to handle building the tag.
         if ($opp['nobcache']) {
             $dest_url .= '?x=' . time();
         }
         if (!isset($outp['alt'])) {
             if ($dest_fname) {
                 $outp['alt'] = basename($dest_fname);
             } else {
                 if ($srcfile) {
                     $outp['alt'] = basename($srcfile);
                 } else {
                     $outp['alt'] = basename($opp['src']);
                 }
             }
         }
         // build the output.
         if ($opp['notag'] || self::_is_stylesheet()) {
             $outp['src'] = $dest_url;
             if (!$opp['noembed'] && $mod->can_embed($dest_fname)) {
                 $type = cge_utils::get_mime_type($dest_fname);
                 if ($type && $type != 'unknown') {
                     $tmp = base64_encode(file_get_contents($dest_fname));
                     $outp['src'] = 'data:' . $type . ';base64,' . $tmp;
                 }
             } else {
                 $outp['src'] = $dest_url;
             }
             if (!isset($outp['output'])) {
                 $outp['output'] = $outp['src'];
             }
         } else {
             //
             // gotta build a tag.
             //
             // get the src first.
             if (!$opp['noembed'] && $mod->can_embed($dest_fname)) {
                 $type = cge_utils::get_mime_type($dest_fname);
                 if ($type && $type != 'unknown') {
                     $tmp = base64_encode(file_get_contents($dest_fname));
                     $outp['src'] = 'data:' . $type . ';base64,' . $tmp;
                 }
             }
             if (!isset($outp['src']) || !$outp['src']) {
                 // fallback to the destination url.
                 $outp['src'] = $dest_url;
             }
             if ($dest_fname && !$opp['noauto']) {
                 $details = getimagesize($dest_fname);
                 if (is_array($details)) {
                     $outp['width'] = (int) $details[0];
                     $outp['height'] = (int) $details[1];
                 }
             }
             // now we can build the tag.
             $output = '<img';
             foreach ($outp as $key => $value) {
                 if (!$value && $key != 'alt') {
                     continue;
                 }
                 // empty alt is valid... stupid, but valid.
                 $output .= ' ' . $key . '="' . $value . '"';
             }
             $output .= '/>';
             $outp['output'] = $output;
         }
     } catch (\CGSIStopHereException $e) {
         // we were forced to stop
         if (!$outp['output']) {
             $outp['error'] = 'No output from CGSIStopHereException';
         }
     } catch (Exception $e) {
         $outp['error'] = $e->GetMessage();
     }
     // here we're gonna return something
     return $outp;
 }