wp/wp-admin/includes/image.php
changeset 0 d970ebf37754
child 5 5e2f62d02dcd
equal deleted inserted replaced
-1:000000000000 0:d970ebf37754
       
     1 <?php
       
     2 /**
       
     3  * File contains all the administration image manipulation functions.
       
     4  *
       
     5  * @package WordPress
       
     6  * @subpackage Administration
       
     7  */
       
     8 
       
     9 /**
       
    10  * Crop an Image to a given size.
       
    11  *
       
    12  * @since 2.1.0
       
    13  *
       
    14  * @param string|int $src The source file or Attachment ID.
       
    15  * @param int $src_x The start x position to crop from.
       
    16  * @param int $src_y The start y position to crop from.
       
    17  * @param int $src_w The width to crop.
       
    18  * @param int $src_h The height to crop.
       
    19  * @param int $dst_w The destination width.
       
    20  * @param int $dst_h The destination height.
       
    21  * @param int $src_abs Optional. If the source crop points are absolute.
       
    22  * @param string $dst_file Optional. The destination file to write to.
       
    23  * @return string|WP_Error New filepath on success, WP_Error on failure.
       
    24  */
       
    25 function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
       
    26 	$src_file = $src;
       
    27 	if ( is_numeric( $src ) ) { // Handle int as attachment ID
       
    28 		$src_file = get_attached_file( $src );
       
    29 
       
    30 		if ( ! file_exists( $src_file ) ) {
       
    31 			// If the file doesn't exist, attempt a url fopen on the src link.
       
    32 			// This can occur with certain file replication plugins.
       
    33 			$src = _load_image_to_edit_path( $src, 'full' );
       
    34 		} else {
       
    35 			$src = $src_file;
       
    36 		}
       
    37 	}
       
    38 
       
    39 	$editor = wp_get_image_editor( $src );
       
    40 	if ( is_wp_error( $editor ) )
       
    41 		return $editor;
       
    42 
       
    43 	$src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
       
    44 	if ( is_wp_error( $src ) )
       
    45 		return $src;
       
    46 
       
    47 	if ( ! $dst_file )
       
    48 		$dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
       
    49 
       
    50 	// The directory containing the original file may no longer exist when
       
    51 	// using a replication plugin.
       
    52 	wp_mkdir_p( dirname( $dst_file ) );
       
    53 
       
    54 	$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
       
    55 
       
    56 	$result = $editor->save( $dst_file );
       
    57 	if ( is_wp_error( $result ) )
       
    58 		return $result;
       
    59 
       
    60 	return $dst_file;
       
    61 }
       
    62 
       
    63 /**
       
    64  * Generate post thumbnail attachment meta data.
       
    65  *
       
    66  * @since 2.1.0
       
    67  *
       
    68  * @param int $attachment_id Attachment Id to process.
       
    69  * @param string $file Filepath of the Attached image.
       
    70  * @return mixed Metadata for attachment.
       
    71  */
       
    72 function wp_generate_attachment_metadata( $attachment_id, $file ) {
       
    73 	$attachment = get_post( $attachment_id );
       
    74 
       
    75 	$metadata = array();
       
    76 	$support = false;
       
    77 	if ( preg_match('!^image/!', get_post_mime_type( $attachment )) && file_is_displayable_image($file) ) {
       
    78 		$imagesize = getimagesize( $file );
       
    79 		$metadata['width'] = $imagesize[0];
       
    80 		$metadata['height'] = $imagesize[1];
       
    81 
       
    82 		// Make the file path relative to the upload dir
       
    83 		$metadata['file'] = _wp_relative_upload_path($file);
       
    84 
       
    85 		// make thumbnails and other intermediate sizes
       
    86 		global $_wp_additional_image_sizes;
       
    87 
       
    88 		$sizes = array();
       
    89 		foreach ( get_intermediate_image_sizes() as $s ) {
       
    90 			$sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false );
       
    91 			if ( isset( $_wp_additional_image_sizes[$s]['width'] ) )
       
    92 				$sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
       
    93 			else
       
    94 				$sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
       
    95 			if ( isset( $_wp_additional_image_sizes[$s]['height'] ) )
       
    96 				$sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
       
    97 			else
       
    98 				$sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
       
    99 			if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) )
       
   100 				$sizes[$s]['crop'] = intval( $_wp_additional_image_sizes[$s]['crop'] ); // For theme-added sizes
       
   101 			else
       
   102 				$sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
       
   103 		}
       
   104 
       
   105 		$sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
       
   106 
       
   107 		if ( $sizes ) {
       
   108 			$editor = wp_get_image_editor( $file );
       
   109 
       
   110 			if ( ! is_wp_error( $editor ) )
       
   111 				$metadata['sizes'] = $editor->multi_resize( $sizes );
       
   112 		} else {
       
   113 			$metadata['sizes'] = array();
       
   114 		}
       
   115 
       
   116 		// fetch additional metadata from exif/iptc
       
   117 		$image_meta = wp_read_image_metadata( $file );
       
   118 		if ( $image_meta )
       
   119 			$metadata['image_meta'] = $image_meta;
       
   120 
       
   121 	} elseif ( preg_match( '#^video/#', get_post_mime_type( $attachment ) ) ) {
       
   122 		$metadata = wp_read_video_metadata( $file );
       
   123 		$support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) && post_type_supports( 'attachment:video', 'thumbnail' );
       
   124 	} elseif ( preg_match( '#^audio/#', get_post_mime_type( $attachment ) ) ) {
       
   125 		$metadata = wp_read_audio_metadata( $file );
       
   126 		$support = current_theme_supports( 'post-thumbnails', 'attachment:audio' ) && post_type_supports( 'attachment:audio', 'thumbnail' );
       
   127 	}
       
   128 
       
   129 	if ( $support && ! empty( $metadata['image']['data'] ) ) {
       
   130 		$ext = '.jpg';
       
   131 		switch ( $metadata['image']['mime'] ) {
       
   132 		case 'image/gif':
       
   133 			$ext = '.gif';
       
   134 			break;
       
   135 		case 'image/png':
       
   136 			$ext = '.png';
       
   137 			break;
       
   138 		}
       
   139 		$basename = str_replace( '.', '-', basename( $file ) ) . '-image' . $ext;
       
   140 		$uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] );
       
   141 		if ( false === $uploaded['error'] ) {
       
   142 			$attachment = array(
       
   143 				'post_mime_type' => $metadata['image']['mime'],
       
   144 				'post_type' => 'attachment',
       
   145 				'post_content' => '',
       
   146 			);
       
   147 			$sub_attachment_id = wp_insert_attachment( $attachment, $uploaded['file'] );
       
   148 			$attach_data = wp_generate_attachment_metadata( $sub_attachment_id, $uploaded['file'] );
       
   149 			wp_update_attachment_metadata( $sub_attachment_id, $attach_data );
       
   150 			update_post_meta( $attachment_id, '_thumbnail_id', $sub_attachment_id );
       
   151 		}
       
   152 	}
       
   153 	// remove the blob of binary data from the array
       
   154 	unset( $metadata['image']['data'] );
       
   155 
       
   156 	return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id );
       
   157 }
       
   158 
       
   159 /**
       
   160  * Convert a fraction string to a decimal.
       
   161  *
       
   162  * @since 2.5.0
       
   163  *
       
   164  * @param string $str
       
   165  * @return int|float
       
   166  */
       
   167 function wp_exif_frac2dec($str) {
       
   168 	@list( $n, $d ) = explode( '/', $str );
       
   169 	if ( !empty($d) )
       
   170 		return $n / $d;
       
   171 	return $str;
       
   172 }
       
   173 
       
   174 /**
       
   175  * Convert the exif date format to a unix timestamp.
       
   176  *
       
   177  * @since 2.5.0
       
   178  *
       
   179  * @param string $str
       
   180  * @return int
       
   181  */
       
   182 function wp_exif_date2ts($str) {
       
   183 	@list( $date, $time ) = explode( ' ', trim($str) );
       
   184 	@list( $y, $m, $d ) = explode( ':', $date );
       
   185 
       
   186 	return strtotime( "{$y}-{$m}-{$d} {$time}" );
       
   187 }
       
   188 
       
   189 /**
       
   190  * Get extended image metadata, exif or iptc as available.
       
   191  *
       
   192  * Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso
       
   193  * created_timestamp, focal_length, shutter_speed, and title.
       
   194  *
       
   195  * The IPTC metadata that is retrieved is APP13, credit, byline, created date
       
   196  * and time, caption, copyright, and title. Also includes FNumber, Model,
       
   197  * DateTimeDigitized, FocalLength, ISOSpeedRatings, and ExposureTime.
       
   198  *
       
   199  * @todo Try other exif libraries if available.
       
   200  * @since 2.5.0
       
   201  *
       
   202  * @param string $file
       
   203  * @return bool|array False on failure. Image metadata array on success.
       
   204  */
       
   205 function wp_read_image_metadata( $file ) {
       
   206 	if ( ! file_exists( $file ) )
       
   207 		return false;
       
   208 
       
   209 	list( , , $sourceImageType ) = getimagesize( $file );
       
   210 
       
   211 	// exif contains a bunch of data we'll probably never need formatted in ways
       
   212 	// that are difficult to use. We'll normalize it and just extract the fields
       
   213 	// that are likely to be useful. Fractions and numbers are converted to
       
   214 	// floats, dates to unix timestamps, and everything else to strings.
       
   215 	$meta = array(
       
   216 		'aperture' => 0,
       
   217 		'credit' => '',
       
   218 		'camera' => '',
       
   219 		'caption' => '',
       
   220 		'created_timestamp' => 0,
       
   221 		'copyright' => '',
       
   222 		'focal_length' => 0,
       
   223 		'iso' => 0,
       
   224 		'shutter_speed' => 0,
       
   225 		'title' => '',
       
   226 	);
       
   227 
       
   228 	// read iptc first, since it might contain data not available in exif such
       
   229 	// as caption, description etc
       
   230 	if ( is_callable( 'iptcparse' ) ) {
       
   231 		getimagesize( $file, $info );
       
   232 
       
   233 		if ( ! empty( $info['APP13'] ) ) {
       
   234 			$iptc = iptcparse( $info['APP13'] );
       
   235 
       
   236 			// headline, "A brief synopsis of the caption."
       
   237 			if ( ! empty( $iptc['2#105'][0] ) )
       
   238 				$meta['title'] = trim( $iptc['2#105'][0] );
       
   239 			// title, "Many use the Title field to store the filename of the image, though the field may be used in many ways."
       
   240 			elseif ( ! empty( $iptc['2#005'][0] ) )
       
   241 				$meta['title'] = trim( $iptc['2#005'][0] );
       
   242 
       
   243 			if ( ! empty( $iptc['2#120'][0] ) ) { // description / legacy caption
       
   244 				$caption = trim( $iptc['2#120'][0] );
       
   245 				if ( empty( $meta['title'] ) ) {
       
   246 					// Assume the title is stored in 2:120 if it's short.
       
   247 					if ( strlen( $caption ) < 80 )
       
   248 						$meta['title'] = $caption;
       
   249 					else
       
   250 						$meta['caption'] = $caption;
       
   251 				} elseif ( $caption != $meta['title'] ) {
       
   252 					$meta['caption'] = $caption;
       
   253 				}
       
   254 			}
       
   255 
       
   256 			if ( ! empty( $iptc['2#110'][0] ) ) // credit
       
   257 				$meta['credit'] = trim( $iptc['2#110'][0] );
       
   258 			elseif ( ! empty( $iptc['2#080'][0] ) ) // creator / legacy byline
       
   259 				$meta['credit'] = trim( $iptc['2#080'][0] );
       
   260 
       
   261 			if ( ! empty( $iptc['2#055'][0] ) and ! empty( $iptc['2#060'][0] ) ) // created date and time
       
   262 				$meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] );
       
   263 
       
   264 			if ( ! empty( $iptc['2#116'][0] ) ) // copyright
       
   265 				$meta['copyright'] = trim( $iptc['2#116'][0] );
       
   266 		 }
       
   267 	}
       
   268 
       
   269 	// fetch additional info from exif if available
       
   270 	if ( is_callable( 'exif_read_data' ) && in_array( $sourceImageType, apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) ) ) ) {
       
   271 		$exif = @exif_read_data( $file );
       
   272 
       
   273 		if ( !empty( $exif['Title'] ) )
       
   274 			$meta['title'] = trim( $exif['Title'] );
       
   275 
       
   276 		if ( ! empty( $exif['ImageDescription'] ) ) {
       
   277 			if ( empty( $meta['title'] ) && strlen( $exif['ImageDescription'] ) < 80 ) {
       
   278 				// Assume the title is stored in ImageDescription
       
   279 				$meta['title'] = trim( $exif['ImageDescription'] );
       
   280 				if ( ! empty( $exif['COMPUTED']['UserComment'] ) && trim( $exif['COMPUTED']['UserComment'] ) != $meta['title'] )
       
   281 					$meta['caption'] = trim( $exif['COMPUTED']['UserComment'] );
       
   282 			} elseif ( trim( $exif['ImageDescription'] ) != $meta['title'] ) {
       
   283 				$meta['caption'] = trim( $exif['ImageDescription'] );
       
   284 			}
       
   285 		} elseif ( ! empty( $exif['Comments'] ) && trim( $exif['Comments'] ) != $meta['title'] ) {
       
   286 			$meta['caption'] = trim( $exif['Comments'] );
       
   287 		}
       
   288 
       
   289 		if ( ! empty( $exif['Artist'] ) )
       
   290 			$meta['credit'] = trim( $exif['Artist'] );
       
   291 		elseif ( ! empty($exif['Author'] ) )
       
   292 			$meta['credit'] = trim( $exif['Author'] );
       
   293 
       
   294 		if ( ! empty( $exif['Copyright'] ) )
       
   295 			$meta['copyright'] = trim( $exif['Copyright'] );
       
   296 		if ( ! empty($exif['FNumber'] ) )
       
   297 			$meta['aperture'] = round( wp_exif_frac2dec( $exif['FNumber'] ), 2 );
       
   298 		if ( ! empty($exif['Model'] ) )
       
   299 			$meta['camera'] = trim( $exif['Model'] );
       
   300 		if ( ! empty($exif['DateTimeDigitized'] ) )
       
   301 			$meta['created_timestamp'] = wp_exif_date2ts($exif['DateTimeDigitized'] );
       
   302 		if ( ! empty($exif['FocalLength'] ) )
       
   303 			$meta['focal_length'] = (string) wp_exif_frac2dec( $exif['FocalLength'] );
       
   304 		if ( ! empty($exif['ISOSpeedRatings'] ) ) {
       
   305 			$meta['iso'] = is_array( $exif['ISOSpeedRatings'] ) ? reset( $exif['ISOSpeedRatings'] ) : $exif['ISOSpeedRatings'];
       
   306 			$meta['iso'] = trim( $meta['iso'] );
       
   307 		}
       
   308 		if ( ! empty($exif['ExposureTime'] ) )
       
   309 			$meta['shutter_speed'] = (string) wp_exif_frac2dec( $exif['ExposureTime'] );
       
   310 	}
       
   311 
       
   312 	foreach ( array( 'title', 'caption', 'credit', 'copyright', 'camera', 'iso' ) as $key ) {
       
   313 		if ( $meta[ $key ] && ! seems_utf8( $meta[ $key ] ) )
       
   314 			$meta[ $key ] = utf8_encode( $meta[ $key ] );
       
   315 	}
       
   316 
       
   317 	return apply_filters( 'wp_read_image_metadata', $meta, $file, $sourceImageType );
       
   318 
       
   319 }
       
   320 
       
   321 /**
       
   322  * Validate that file is an image.
       
   323  *
       
   324  * @since 2.5.0
       
   325  *
       
   326  * @param string $path File path to test if valid image.
       
   327  * @return bool True if valid image, false if not valid image.
       
   328  */
       
   329 function file_is_valid_image($path) {
       
   330 	$size = @getimagesize($path);
       
   331 	return !empty($size);
       
   332 }
       
   333 
       
   334 /**
       
   335  * Validate that file is suitable for displaying within a web page.
       
   336  *
       
   337  * @since 2.5.0
       
   338  * @uses apply_filters() Calls 'file_is_displayable_image' on $result and $path.
       
   339  *
       
   340  * @param string $path File path to test.
       
   341  * @return bool True if suitable, false if not suitable.
       
   342  */
       
   343 function file_is_displayable_image($path) {
       
   344 	$info = @getimagesize($path);
       
   345 	if ( empty($info) )
       
   346 		$result = false;
       
   347 	elseif ( !in_array($info[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG)) )	// only gif, jpeg and png images can reliably be displayed
       
   348 		$result = false;
       
   349 	else
       
   350 		$result = true;
       
   351 
       
   352 	return apply_filters('file_is_displayable_image', $result, $path);
       
   353 }
       
   354 
       
   355 /**
       
   356  * Load an image resource for editing.
       
   357  *
       
   358  * @since 2.9.0
       
   359  *
       
   360  * @param string $attachment_id Attachment ID.
       
   361  * @param string $mime_type Image mime type.
       
   362  * @param string $size Optional. Image size, defaults to 'full'.
       
   363  * @return resource|false The resulting image resource on success, false on failure.
       
   364  */
       
   365 function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
       
   366 	$filepath = _load_image_to_edit_path( $attachment_id, $size );
       
   367 	if ( empty( $filepath ) )
       
   368 		return false;
       
   369 
       
   370 	switch ( $mime_type ) {
       
   371 		case 'image/jpeg':
       
   372 			$image = imagecreatefromjpeg($filepath);
       
   373 			break;
       
   374 		case 'image/png':
       
   375 			$image = imagecreatefrompng($filepath);
       
   376 			break;
       
   377 		case 'image/gif':
       
   378 			$image = imagecreatefromgif($filepath);
       
   379 			break;
       
   380 		default:
       
   381 			$image = false;
       
   382 			break;
       
   383 	}
       
   384 	if ( is_resource($image) ) {
       
   385 		$image = apply_filters('load_image_to_edit', $image, $attachment_id, $size);
       
   386 		if ( function_exists('imagealphablending') && function_exists('imagesavealpha') ) {
       
   387 			imagealphablending($image, false);
       
   388 			imagesavealpha($image, true);
       
   389 		}
       
   390 	}
       
   391 	return $image;
       
   392 }
       
   393 
       
   394 /**
       
   395  * Retrieve the path or url of an attachment's attached file.
       
   396  *
       
   397  * If the attached file is not present on the local filesystem (usually due to replication plugins),
       
   398  * then the url of the file is returned if url fopen is supported.
       
   399  *
       
   400  * @since 3.4.0
       
   401  * @access private
       
   402  *
       
   403  * @param string $attachment_id Attachment ID.
       
   404  * @param string $size Optional. Image size, defaults to 'full'.
       
   405  * @return string|false File path or url on success, false on failure.
       
   406  */
       
   407 function _load_image_to_edit_path( $attachment_id, $size = 'full' ) {
       
   408 	$filepath = get_attached_file( $attachment_id );
       
   409 
       
   410 	if ( $filepath && file_exists( $filepath ) ) {
       
   411 		if ( 'full' != $size && ( $data = image_get_intermediate_size( $attachment_id, $size ) ) ) {
       
   412 			$filepath = apply_filters( 'load_image_to_edit_filesystempath', path_join( dirname( $filepath ), $data['file'] ), $attachment_id, $size );
       
   413 		}
       
   414 	} elseif ( function_exists( 'fopen' ) && function_exists( 'ini_get' ) && true == ini_get( 'allow_url_fopen' ) ) {
       
   415 		$filepath = apply_filters( 'load_image_to_edit_attachmenturl', wp_get_attachment_url( $attachment_id ), $attachment_id, $size );
       
   416 	}
       
   417 
       
   418 	return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size );
       
   419 }
       
   420 
       
   421 /**
       
   422  * Copy an existing image file.
       
   423  *
       
   424  * @since 3.4.0
       
   425  * @access private
       
   426  *
       
   427  * @param string $attachment_id Attachment ID.
       
   428  * @return string|false New file path on success, false on failure.
       
   429  */
       
   430 function _copy_image_file( $attachment_id ) {
       
   431 	$dst_file = $src_file = get_attached_file( $attachment_id );
       
   432 	if ( ! file_exists( $src_file ) )
       
   433 		$src_file = _load_image_to_edit_path( $attachment_id );
       
   434 
       
   435 	if ( $src_file ) {
       
   436 		$dst_file = str_replace( basename( $dst_file ), 'copy-' . basename( $dst_file ), $dst_file );
       
   437 		$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
       
   438 
       
   439 		// The directory containing the original file may no longer exist when
       
   440 		// using a replication plugin.
       
   441 		wp_mkdir_p( dirname( $dst_file ) );
       
   442 
       
   443 		if ( ! @copy( $src_file, $dst_file ) )
       
   444 			$dst_file = false;
       
   445 	} else {
       
   446 		$dst_file = false;
       
   447 	}
       
   448 
       
   449 	return $dst_file;
       
   450 }