Advertisement
thenadz

WPROOT/wp-includes/class-wp-image-editor-imagick.php

Mar 12th, 2015
295
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 14.81 KB | None | 0 0
  1. <?php
  2. /**
  3.  * WordPress Imagick Image Editor
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Image_Editor
  7.  */
  8.  
  9. include_once ABSPATH . 'wp-content/plugins/document-gallery/document-gallery.php';
  10.  
  11. /**
  12.  * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module
  13.  *
  14.  * @since 3.5.0
  15.  * @package WordPress
  16.  * @subpackage Image_Editor
  17.  * @uses WP_Image_Editor Extends class
  18.  */
  19. class WP_Image_Editor_Imagick extends WP_Image_Editor {
  20.     /**
  21.      * @var Imagick
  22.      */
  23.     protected $image; // Imagick Object
  24.  
  25.     public function __destruct() {
  26.         if ( $this->image instanceof Imagick ) {
  27.             // we don't need the original in memory anymore
  28.             $this->image->clear();
  29.             $this->image->destroy();
  30.         }
  31.     }
  32.  
  33.     /**
  34.      * Checks to see if current environment supports Imagick.
  35.      *
  36.      * We require Imagick 2.2.0 or greater, based on whether the queryFormats()
  37.      * method can be called statically.
  38.      *
  39.      * @since 3.5.0
  40.      * @access public
  41.      *
  42.      * @return boolean
  43.      */
  44.     public static function test( $args = array() ) {
  45.  
  46.         // First, test Imagick's extension and classes.
  47.         if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick' ) || ! class_exists( 'ImagickPixel' ) ) {
  48.            if (!extension_loaded( 'imagick' )) {
  49.                DG_Logger::writeLog(DG_LogLevel::Detail, 'imagick extension not loaded.', false, true);
  50.            }
  51.            
  52.            if (! class_exists( 'Imagick' )) {
  53.               DG_Logger::writeLog(DG_LogLevel::Detail, 'Imagick class missing.', false, true);
  54.            }
  55.            
  56.            if (! class_exists( 'ImagickPixel' )) {
  57.               DG_Logger::writeLog(DG_LogLevel::Detail, 'ImagickPixel class missing.', false, true);
  58.            }
  59.            return false;
  60.         }
  61.  
  62.         if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) ) {
  63.             DG_Logger::writeLog(DG_LogLevel::Detail, 'imagick version must be greater than 2.2.0. Actual version: ' . phpversion( 'imagick' ), false, true);
  64.             return false;
  65.         }
  66.  
  67.         $required_methods = array(
  68.             'clear',
  69.             'destroy',
  70.             'valid',
  71.             'getimage',
  72.             'writeimage',
  73.             'getimageblob',
  74.             'getimagegeometry',
  75.             'getimageformat',
  76.             'setimageformat',
  77.             'setimagecompression',
  78.             'setimagecompressionquality',
  79.             'setimagepage',
  80.             'scaleimage',
  81.             'cropimage',
  82.             'rotateimage',
  83.             'flipimage',
  84.             'flopimage',
  85.         );
  86.  
  87.         // Now, test for deep requirements within Imagick.
  88.         if ( ! defined( 'imagick::COMPRESSION_JPEG' ) ) {
  89.             DG_Logger::writeLog(DG_LogLevel::Detail, 'imagick::COMPRESSION_JPEG not defined', false, true);
  90.             return false;
  91.         }
  92.  
  93.         $diff = array_diff( $required_methods, get_class_methods( 'Imagick' ) );
  94.         if ( $diff ) {
  95.             DG_Logger::writeLog(DG_LogLevel::Detail, 'Imagick missing class method(s): ' . implode(', ', $diff), false, true);
  96.             return false;
  97.         }
  98.  
  99.         return true;
  100.     }
  101.  
  102.     /**
  103.      * Checks to see if editor supports the mime-type specified.
  104.      *
  105.      * @since 3.5.0
  106.      * @access public
  107.      *
  108.      * @param string $mime_type
  109.      * @return boolean
  110.      */
  111.     public static function supports_mime_type( $mime_type ) {
  112.         $imagick_extension = strtoupper( self::get_extension( $mime_type ) );
  113.  
  114.         if ( ! $imagick_extension )
  115.             return false;
  116.  
  117.         // setIteratorIndex is optional unless mime is an animated format.
  118.         // Here, we just say no if you are missing it and aren't loading a jpeg.
  119.         if ( ! method_exists( 'Imagick', 'setIteratorIndex' ) && $mime_type != 'image/jpeg' )
  120.                 return false;
  121.  
  122.         try {
  123.             return ( (bool) @Imagick::queryFormats( $imagick_extension ) );
  124.         }
  125.         catch ( Exception $e ) {
  126.             return false;
  127.         }
  128.     }
  129.  
  130.     /**
  131.      * Loads image from $this->file into new Imagick Object.
  132.      *
  133.      * @since 3.5.0
  134.      * @access protected
  135.      *
  136.      * @return boolean|WP_Error True if loaded; WP_Error on failure.
  137.      */
  138.     public function load() {
  139.         if ( $this->image instanceof Imagick )
  140.             return true;
  141.  
  142.         if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) )
  143.             return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
  144.  
  145.         /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */
  146.         // Even though Imagick uses less PHP memory than GD, set higher limit for users that have low PHP.ini limits
  147.         @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
  148.  
  149.         try {
  150.             $this->image = new Imagick( $this->file );
  151.  
  152.             if( ! $this->image->valid() )
  153.                 return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file);
  154.  
  155.             // Select the first frame to handle animated images properly
  156.             if ( is_callable( array( $this->image, 'setIteratorIndex' ) ) )
  157.                 $this->image->setIteratorIndex(0);
  158.  
  159.             $this->mime_type = $this->get_mime_type( $this->image->getImageFormat() );
  160.         }
  161.         catch ( Exception $e ) {
  162.             return new WP_Error( 'invalid_image', $e->getMessage(), $this->file );
  163.         }
  164.  
  165.         $updated_size = $this->update_size();
  166.         if ( is_wp_error( $updated_size ) ) {
  167.             return $updated_size;
  168.         }
  169.  
  170.         return $this->set_quality();
  171.     }
  172.  
  173.     /**
  174.      * Sets Image Compression quality on a 1-100% scale.
  175.      *
  176.      * @since 3.5.0
  177.      * @access public
  178.      *
  179.      * @param int $quality Compression Quality. Range: [1,100]
  180.      * @return boolean|WP_Error True if set successfully; WP_Error on failure.
  181.      */
  182.     public function set_quality( $quality = null ) {
  183.         $quality_result = parent::set_quality( $quality );
  184.         if ( is_wp_error( $quality_result ) ) {
  185.             return $quality_result;
  186.         } else {
  187.             $quality = $this->get_quality();
  188.         }
  189.  
  190.         try {
  191.             if ( 'image/jpeg' == $this->mime_type ) {
  192.                 $this->image->setImageCompressionQuality( $quality );
  193.                 $this->image->setImageCompression( imagick::COMPRESSION_JPEG );
  194.             }
  195.             else {
  196.                 $this->image->setImageCompressionQuality( $quality );
  197.             }
  198.         }
  199.         catch ( Exception $e ) {
  200.             return new WP_Error( 'image_quality_error', $e->getMessage() );
  201.         }
  202.  
  203.         return true;
  204.     }
  205.  
  206.     /**
  207.      * Sets or updates current image size.
  208.      *
  209.      * @since 3.5.0
  210.      * @access protected
  211.      *
  212.      * @param int $width
  213.      * @param int $height
  214.      */
  215.     protected function update_size( $width = null, $height = null ) {
  216.         $size = null;
  217.         if ( !$width || !$height ) {
  218.             try {
  219.                 $size = $this->image->getImageGeometry();
  220.             }
  221.             catch ( Exception $e ) {
  222.                 return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file );
  223.             }
  224.         }
  225.  
  226.         if ( ! $width )
  227.             $width = $size['width'];
  228.  
  229.         if ( ! $height )
  230.             $height = $size['height'];
  231.  
  232.         return parent::update_size( $width, $height );
  233.     }
  234.  
  235.     /**
  236.      * Resizes current image.
  237.      *
  238.      * At minimum, either a height or width must be provided.
  239.      * If one of the two is set to null, the resize will
  240.      * maintain aspect ratio according to the provided dimension.
  241.      *
  242.      * @since 3.5.0
  243.      * @access public
  244.      *
  245.      * @param  int|null $max_w Image width.
  246.      * @param  int|null $max_h Image height.
  247.      * @param  boolean  $crop
  248.      * @return boolean|WP_Error
  249.      */
  250.     public function resize( $max_w, $max_h, $crop = false ) {
  251.         if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) )
  252.             return true;
  253.  
  254.         $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
  255.         if ( ! $dims )
  256.             return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
  257.         list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
  258.  
  259.         if ( $crop ) {
  260.             return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h );
  261.         }
  262.  
  263.         try {
  264.             /**
  265.              * @TODO: Thumbnail is more efficient, given a newer version of Imagemagick.
  266.              * $this->image->thumbnailImage( $dst_w, $dst_h );
  267.              */
  268.             $this->image->scaleImage( $dst_w, $dst_h );
  269.         }
  270.         catch ( Exception $e ) {
  271.             return new WP_Error( 'image_resize_error', $e->getMessage() );
  272.         }
  273.  
  274.         return $this->update_size( $dst_w, $dst_h );
  275.     }
  276.  
  277.     /**
  278.      * Resize multiple images from a single source.
  279.      *
  280.      * @since 3.5.0
  281.      * @access public
  282.      *
  283.      * @param array $sizes {
  284.      *     An array of image size arrays. Default sizes are 'small', 'medium', 'large'.
  285.      *
  286.      *     Either a height or width must be provided.
  287.      *     If one of the two is set to null, the resize will
  288.      *     maintain aspect ratio according to the provided dimension.
  289.      *
  290.      *     @type array $size {
  291.      *         @type int  ['width']  Optional. Image width.
  292.      *         @type int  ['height'] Optional. Image height.
  293.      *         @type bool $crop   Optional. Whether to crop the image. Default false.
  294.      *     }
  295.      * }
  296.      * @return array An array of resized images' metadata by size.
  297.      */
  298.     public function multi_resize( $sizes ) {
  299.         $metadata = array();
  300.         $orig_size = $this->size;
  301.         $orig_image = $this->image->getImage();
  302.  
  303.         foreach ( $sizes as $size => $size_data ) {
  304.             if ( ! $this->image )
  305.                 $this->image = $orig_image->getImage();
  306.  
  307.             if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
  308.                 continue;
  309.             }
  310.  
  311.             if ( ! isset( $size_data['width'] ) ) {
  312.                 $size_data['width'] = null;
  313.             }
  314.             if ( ! isset( $size_data['height'] ) ) {
  315.                 $size_data['height'] = null;
  316.             }
  317.  
  318.             if ( ! isset( $size_data['crop'] ) ) {
  319.                 $size_data['crop'] = false;
  320.             }
  321.  
  322.             $resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
  323.  
  324.             if( ! is_wp_error( $resize_result ) ) {
  325.                 $resized = $this->_save( $this->image );
  326.  
  327.                 $this->image->clear();
  328.                 $this->image->destroy();
  329.                 $this->image = null;
  330.  
  331.                 if ( ! is_wp_error( $resized ) && $resized ) {
  332.                     unset( $resized['path'] );
  333.                     $metadata[$size] = $resized;
  334.                 }
  335.             }
  336.  
  337.             $this->size = $orig_size;
  338.         }
  339.  
  340.         $this->image = $orig_image;
  341.  
  342.         return $metadata;
  343.     }
  344.  
  345.     /**
  346.      * Crops Image.
  347.      *
  348.      * @since 3.5.0
  349.      * @access public
  350.      *
  351.      * @param string|int $src The source file or Attachment ID.
  352.      * @param int $src_x The start x position to crop from.
  353.      * @param int $src_y The start y position to crop from.
  354.      * @param int $src_w The width to crop.
  355.      * @param int $src_h The height to crop.
  356.      * @param int $dst_w Optional. The destination width.
  357.      * @param int $dst_h Optional. The destination height.
  358.      * @param boolean $src_abs Optional. If the source crop points are absolute.
  359.      * @return boolean|WP_Error
  360.      */
  361.     public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
  362.         if ( $src_abs ) {
  363.             $src_w -= $src_x;
  364.             $src_h -= $src_y;
  365.         }
  366.  
  367.         try {
  368.             $this->image->cropImage( $src_w, $src_h, $src_x, $src_y );
  369.             $this->image->setImagePage( $src_w, $src_h, 0, 0);
  370.  
  371.             if ( $dst_w || $dst_h ) {
  372.                 // If destination width/height isn't specified, use same as
  373.                 // width/height from source.
  374.                 if ( ! $dst_w )
  375.                     $dst_w = $src_w;
  376.                 if ( ! $dst_h )
  377.                     $dst_h = $src_h;
  378.  
  379.                 $this->image->scaleImage( $dst_w, $dst_h );
  380.                 return $this->update_size();
  381.             }
  382.         }
  383.         catch ( Exception $e ) {
  384.             return new WP_Error( 'image_crop_error', $e->getMessage() );
  385.         }
  386.         return $this->update_size();
  387.     }
  388.  
  389.     /**
  390.      * Rotates current image counter-clockwise by $angle.
  391.      *
  392.      * @since 3.5.0
  393.      * @access public
  394.      *
  395.      * @param float $angle
  396.      * @return boolean|WP_Error
  397.      */
  398.     public function rotate( $angle ) {
  399.         /**
  400.          * $angle is 360-$angle because Imagick rotates clockwise
  401.          * (GD rotates counter-clockwise)
  402.          */
  403.         try {
  404.             $this->image->rotateImage( new ImagickPixel('none'), 360-$angle );
  405.  
  406.             // Since this changes the dimensions of the image, update the size.
  407.             $result = $this->update_size();
  408.             if ( is_wp_error( $result ) )
  409.                 return $result;
  410.  
  411.             $this->image->setImagePage( $this->size['width'], $this->size['height'], 0, 0 );
  412.         }
  413.         catch ( Exception $e ) {
  414.             return new WP_Error( 'image_rotate_error', $e->getMessage() );
  415.         }
  416.         return true;
  417.     }
  418.  
  419.     /**
  420.      * Flips current image.
  421.      *
  422.      * @since 3.5.0
  423.      * @access public
  424.      *
  425.      * @param boolean $horz Flip along Horizontal Axis
  426.      * @param boolean $vert Flip along Vertical Axis
  427.      * @returns boolean|WP_Error
  428.      */
  429.     public function flip( $horz, $vert ) {
  430.         try {
  431.             if ( $horz )
  432.                 $this->image->flipImage();
  433.  
  434.             if ( $vert )
  435.                 $this->image->flopImage();
  436.         }
  437.         catch ( Exception $e ) {
  438.             return new WP_Error( 'image_flip_error', $e->getMessage() );
  439.         }
  440.         return true;
  441.     }
  442.  
  443.     /**
  444.      * Saves current image to file.
  445.      *
  446.      * @since 3.5.0
  447.      * @access public
  448.      *
  449.      * @param string $destfilename
  450.      * @param string $mime_type
  451.      * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
  452.      */
  453.     public function save( $destfilename = null, $mime_type = null ) {
  454.         $saved = $this->_save( $this->image, $destfilename, $mime_type );
  455.  
  456.         if ( ! is_wp_error( $saved ) ) {
  457.             $this->file = $saved['path'];
  458.             $this->mime_type = $saved['mime-type'];
  459.  
  460.             try {
  461.                 $this->image->setImageFormat( strtoupper( $this->get_extension( $this->mime_type ) ) );
  462.             }
  463.             catch ( Exception $e ) {
  464.                 return new WP_Error( 'image_save_error', $e->getMessage(), $this->file );
  465.             }
  466.         }
  467.  
  468.         return $saved;
  469.     }
  470.  
  471.     protected function _save( $image, $filename = null, $mime_type = null ) {
  472.         list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
  473.  
  474.         if ( ! $filename )
  475.             $filename = $this->generate_filename( null, null, $extension );
  476.  
  477.         try {
  478.             // Store initial Format
  479.             $orig_format = $this->image->getImageFormat();
  480.  
  481.             $this->image->setImageFormat( strtoupper( $this->get_extension( $mime_type ) ) );
  482.             $this->make_image( $filename, array( $image, 'writeImage' ), array( $filename ) );
  483.  
  484.             // Reset original Format
  485.             $this->image->setImageFormat( $orig_format );
  486.         }
  487.         catch ( Exception $e ) {
  488.             return new WP_Error( 'image_save_error', $e->getMessage(), $filename );
  489.         }
  490.  
  491.         // Set correct file permissions
  492.         $stat = stat( dirname( $filename ) );
  493.         $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
  494.         @ chmod( $filename, $perms );
  495.  
  496.         /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */
  497.         return array(
  498.             'path'      => $filename,
  499.             'file'      => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
  500.             'width'     => $this->size['width'],
  501.             'height'    => $this->size['height'],
  502.             'mime-type' => $mime_type,
  503.         );
  504.     }
  505.  
  506.     /**
  507.      * Streams current image to browser.
  508.      *
  509.      * @since 3.5.0
  510.      * @access public
  511.      *
  512.      * @param string $mime_type
  513.      * @return boolean|WP_Error
  514.      */
  515.     public function stream( $mime_type = null ) {
  516.         list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type );
  517.  
  518.         try {
  519.             // Temporarily change format for stream
  520.             $this->image->setImageFormat( strtoupper( $extension ) );
  521.  
  522.             // Output stream of image content
  523.             header( "Content-Type: $mime_type" );
  524.             print $this->image->getImageBlob();
  525.  
  526.             // Reset Image to original Format
  527.             $this->image->setImageFormat( $this->get_extension( $this->mime_type ) );
  528.         }
  529.         catch ( Exception $e ) {
  530.             return new WP_Error( 'image_stream_error', $e->getMessage() );
  531.         }
  532.  
  533.         return true;
  534.     }
  535. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement