/**
  * Rotate an image.  Valid options are degrees
  *
  * @param string     $input_file
  * @param string     $output_file
  * @param array      $options
  */
 static function rotate($input_file, $output_file, $options)
 {
     graphics::init_toolkit();
     module::event("graphics_rotate", $input_file, $output_file, $options);
     // BEGIN mod to original function
     $image_info = getimagesize($input_file);
     // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG)
     if (module::get_var("image_optimizer", "rotate_jpg") || $image_info[2] == 2) {
         // rotate_jpg enabled, the file is a jpg.  get args
         $path = module::get_var("image_optimizer", "path_jpg");
         $exec_args = " -rotate ";
         $exec_args .= $options["degrees"] > 0 ? $options["degrees"] : $options["degrees"] + 360;
         $exec_args .= " -copy all -optimize -outfile ";
         // run it - from input_file to tmp_file
         $tmp_file = image_optimizer::make_temp_name($output_file);
         exec(escapeshellcmd($path) . $exec_args . escapeshellarg($tmp_file) . " " . escapeshellarg($input_file), $exec_output, $exec_status);
         if ($exec_status || !filesize($tmp_file)) {
             // either a blank/nonexistant file or an error - log an error and pass to normal function
             Kohana_Log::add("error", "image_optimizer rotation failed on " . $output_file);
             unlink($tmp_file);
         } else {
             // worked - move temp to output
             rename($tmp_file, $output_file);
             $status = true;
         }
     }
     if (!$status) {
         // we got here if we weren't supposed to use jpegtran or if jpegtran failed
         // END mod to original function
         Image::factory($input_file)->quality(module::get_var("gallery", "image_quality"))->rotate($options["degrees"])->save($output_file);
         // BEGIN mod to original function
     }
     // END mod to original function
     module::event("graphics_rotate_completed", $input_file, $output_file, $options);
 }
 static function deactivate()
 {
     // ensure that update modes are disabled
     image_optimizer::deactivate_update_mode("thumb");
     image_optimizer::deactivate_update_mode("resize");
     // remove graphics rules
     image_optimizer::remove_image_optimizer_rule("thumb");
     image_optimizer::remove_image_optimizer_rule("resize");
 }
 private function _get_admin_form()
 {
     $form = new Forge("admin/image_optimizer/save", "", "post", array("id" => "g-image-optimizer-admin-form"));
     $group_paths = $form->group("paths")->label(t("Toolkit paths"))->set_attr("id", "g-image-optimizer-admin-form-paths");
     foreach (array('jpg', 'png', 'gif') as $type) {
         $path = strval(module::get_var("image_optimizer", "path_" . $type, null));
         $group_paths->input("path_" . $type)->label(t("Path for") . " " . image_optimizer::tool_name($type) . " (" . t("no symlinks, default") . " " . MODPATH . "image_optimizer/lib/" . image_optimizer::tool_name($type) . ")")->value($path);
     }
     $group_rotate = $form->group("rotate")->label(t("Full-size image rotation"))->set_attr("id", "g-image-optimizer-admin-form-rotate");
     $group_rotate->checkbox("rotate_jpg")->label(t("Override default toolkit and use") . " " . image_optimizer::tool_name('jpg') . " " . t("for rotation"))->checked(module::get_var("image_optimizer", "rotate_jpg", null));
     foreach (array('thumb', 'resize') as $target) {
         ${'group_' . $target} = $form->group($target)->label(ucfirst($target) . " " . t("images optimization"))->set_attr("id", "g-image-optimizer-admin-form-" . $target);
         ${'group_' . $target}->checkbox("enable_" . $target)->label(t("Enable optimization"))->checked(module::get_var("image_optimizer", "enable_" . $target, null));
         ${'group_' . $target}->checkbox("update_mode_" . $target)->label(t("Enable update mode - deactivates all other graphics rules to allow fast optimization on existing images; MUST deactivate this after initial rebuild!"))->checked(module::get_var("image_optimizer", "update_mode_" . $target, null));
         ${'group_' . $target}->checkbox("rebuild_" . $target)->label(t("Mark all existing images for rebuild - afterward, go to Maintenace | Rebuild Images"))->checked(false);
         // always set as false
         ${'group_' . $target}->dropdown("convert_" . $target . "_png")->label(t("PNG conversion"))->options(array(0 => t("none"), "jpg" => "JPG " . t("(not lossless)")))->selected(module::get_var("image_optimizer", "convert_" . $target . "_png", null));
         ${'group_' . $target}->dropdown("convert_" . $target . "_gif")->label(t("GIF conversion"))->options(array(0 => t("none"), "jpg" => "JPG " . t("(not lossless)"), "png" => "PNG " . t("(lossless)")))->selected(module::get_var("image_optimizer", "convert_" . $target . "_gif", null));
         ${'group_' . $target}->dropdown("optlevel_" . $target . "_jpg")->label(t("JPG compression optimization (default: enabled)"))->options(array(0 => t("disabled"), 1 => t("enabled")))->selected(module::get_var("image_optimizer", "optlevel_" . $target . "_jpg", null));
         ${'group_' . $target}->dropdown("optlevel_" . $target . "_png")->label(t("PNG compression optimization (default: level 2)"))->options(array(0 => t("disabled"), 1 => t("level 1: 1 trial"), 2 => t("level 2: 8 trials"), 3 => t("level 3: 16 trials"), 4 => t("level 4: 24 trials"), 5 => t("level 5: 48 trials"), 6 => t("level 6: 120 trials"), 7 => t("level 7: 240 trials")))->selected(module::get_var("image_optimizer", "optlevel_" . $target . "_png", null));
         ${'group_' . $target}->dropdown("optlevel_" . $target . "_gif")->label(t("GIF compression optimization (default: enabled)"))->options(array(0 => t("disabled"), 1 => t("enabled")))->selected(module::get_var("image_optimizer", "optlevel_" . $target . "_gif", null));
         ${'group_' . $target}->checkbox("metastrip_" . $target)->label(t("Remove all meta data"))->checked(module::get_var("image_optimizer", "metastrip_" . $target, null));
         ${'group_' . $target}->checkbox("progressive_" . $target)->label(t("Make images progressive/interlaced"))->checked(module::get_var("image_optimizer", "progressive_" . $target, null));
     }
     $form->submit("")->value(t("Save"));
     return $form;
 }
 /**
  * the main optimize function
  *
  * the function arguments are the same format as other graphics rules.  the only "option" is $target, hence why it's renamed in the function def.
  *
  * NOTE: unlike other graphics transformations, this only uses the output file!  if it isn't already there, we don't do anything.
  * among other things, this means that the original, full-size images are never touched.
  */
 static function optimize($input_file, $output_file, $target, $item = null)
 {
     // see if output file exists and is writable
     if (is_writable($output_file)) {
         // see if input is a supported file type.  if not, return without doing anything.
         $image_info = getimagesize($input_file);
         // [0]=w, [1]=h, [2]=type (1=GIF, 2=JPG, 3=PNG)
         switch ($image_info[2]) {
             case 1:
                 $type_old = "gif";
                 $convert = module::get_var("image_optimizer", "convert_" . $target . "_gif");
                 break;
             case 2:
                 $type_old = "jpg";
                 $convert = 0;
                 // no conversion possible here...
                 break;
             case 3:
                 $type_old = "png";
                 $convert = module::get_var("image_optimizer", "convert_" . $target . "_png");
                 break;
             default:
                 // not a supported file type
                 return;
         }
     } else {
         // file doesn't exist or isn't writable
         return;
     }
     // set new file type
     $type = $convert ? $convert : $type_old;
     // convert image type (if applicable).  this isn't necessarily lossless.
     if ($convert) {
         $output_file_new = legal_file::change_extension($output_file, $type);
         // perform conversion using standard Gallery toolkit (GD/ImageMagick/GraphicsMagick)
         // note: if input was a GIF, this will kill animation
         $image = Image::factory($output_file)->quality(module::get_var("gallery", "image_quality"))->save($output_file_new);
         // if filenames are different, move the new on top of the old
         if ($output_file != $output_file_new) {
             /**
              * HACK ALERT!  Gallery3 is still broken with regard to treating thumb/resizes with proper extensions.  This doesn't try to fix that.
              *   Normal Gallery setup:
              *     photo thumb -> keep photo type, keep photo extension
              *     album thumb -> keep source photo thumb type, change extension to jpg (i.e. ".album.jpg" even for png/gif)
              * Also, missing_photo.png is similarly altered...
              *
              * Anyway, to avoid many rewrites of core functions (and not-easily-reversible database changes), this module also forces the extension to stay the same.
              *   With image optimizer conversion:
              *     photo thumb -> change type, keep photo extension (i.e. "photo.png" photo becomes "photo.png" thumb even if type has changed)
              *     album thumb -> keep source photo thumb type, change extension to jpg (i.e. ".album.jpg" even for png/gif)
              */
             rename($output_file_new, $output_file);
         }
     }
     // get module variables
     $configstatus = module::get_var("image_optimizer", "configstatus_" . $type);
     $path = module::get_var("image_optimizer", "path_" . $type);
     $opt = module::get_var("image_optimizer", "optlevel_" . $target . "_" . $type);
     $meta = module::get_var("image_optimizer", "metastrip_" . $target);
     $prog = module::get_var("image_optimizer", "progressive_" . $target);
     // make sure the toolkit is configured correctly and we want to use it - if not, return without doing anything.
     if ($configstatus) {
         if (!$prog && !$meta && !$opt) {
             // nothing to do!
             return;
         }
     } else {
         // not configured correctly
         return;
     }
     /**
      * do the actual optimization
      */
     // set parameters
     switch ($type) {
         case "jpg":
             $exec_args = $opt ? " -optimize" : "";
             $exec_args .= $meta ? " -copy none" : " -copy all";
             $exec_args .= $prog ? " -progressive" : "";
             $exec_args .= " -outfile ";
             break;
         case "png":
             $exec_args = $opt ? " -o" . $opt : "";
             $exec_args .= $meta ? " -strip all" : "";
             $exec_args .= $prog ? " -i 1" : "";
             $exec_args .= " -quiet -out ";
             break;
         case "gif":
             $exec_args = $opt ? " --optimize=3" : "";
             // levels 1 and 2 don't really help us
             $exec_args .= $meta ? " --no-comments --no-extensions --no-names" : " --same-comments --same-extensions --same-names";
             $exec_args .= $prog ? " --interlace" : " --same-interlace";
             $exec_args .= " --careful --output ";
             break;
     }
     // run it - from output_file to tmp_file.
     $tmp_file = image_optimizer::make_temp_name($output_file);
     exec(escapeshellcmd($path) . $exec_args . escapeshellarg($tmp_file) . " " . escapeshellarg($output_file), $exec_output, $exec_status);
     if ($exec_status || !filesize($tmp_file)) {
         // either a blank/nonexistant file or an error - do nothing to the output, but log an error and delete the temp (if any)
         Kohana_Log::add("error", "image_optimizer optimization failed on " . $output_file);
         unlink($tmp_file);
     } else {
         // worked - move temp to output
         rename($tmp_file, $output_file);
     }
 }