25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 

491 satır
12 KiB

  1. <?php
  2. /**
  3. * Base WordPress Image Editor
  4. *
  5. * @package WordPress
  6. * @subpackage Image_Editor
  7. */
  8. /**
  9. * Base image editor class from which implementations extend
  10. *
  11. * @since 3.5.0
  12. */
  13. abstract class WP_Image_Editor {
  14. protected $file = null;
  15. protected $size = null;
  16. protected $mime_type = null;
  17. protected $default_mime_type = 'image/jpeg';
  18. protected $quality = false;
  19. protected $default_quality = 82;
  20. /**
  21. * Each instance handles a single file.
  22. *
  23. * @param string $file Path to the file to load.
  24. */
  25. public function __construct( $file ) {
  26. $this->file = $file;
  27. }
  28. /**
  29. * Checks to see if current environment supports the editor chosen.
  30. * Must be overridden in a sub-class.
  31. *
  32. * @since 3.5.0
  33. *
  34. * @static
  35. * @access public
  36. * @abstract
  37. *
  38. * @param array $args
  39. * @return bool
  40. */
  41. public static function test( $args = array() ) {
  42. return false;
  43. }
  44. /**
  45. * Checks to see if editor supports the mime-type specified.
  46. * Must be overridden in a sub-class.
  47. *
  48. * @since 3.5.0
  49. *
  50. * @static
  51. * @access public
  52. * @abstract
  53. *
  54. * @param string $mime_type
  55. * @return bool
  56. */
  57. public static function supports_mime_type( $mime_type ) {
  58. return false;
  59. }
  60. /**
  61. * Loads image from $this->file into editor.
  62. *
  63. * @since 3.5.0
  64. * @access protected
  65. * @abstract
  66. *
  67. * @return bool|WP_Error True if loaded; WP_Error on failure.
  68. */
  69. abstract public function load();
  70. /**
  71. * Saves current image to file.
  72. *
  73. * @since 3.5.0
  74. * @access public
  75. * @abstract
  76. *
  77. * @param string $destfilename
  78. * @param string $mime_type
  79. * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
  80. */
  81. abstract public function save( $destfilename = null, $mime_type = null );
  82. /**
  83. * Resizes current image.
  84. *
  85. * At minimum, either a height or width must be provided.
  86. * If one of the two is set to null, the resize will
  87. * maintain aspect ratio according to the provided dimension.
  88. *
  89. * @since 3.5.0
  90. * @access public
  91. * @abstract
  92. *
  93. * @param int|null $max_w Image width.
  94. * @param int|null $max_h Image height.
  95. * @param bool $crop
  96. * @return bool|WP_Error
  97. */
  98. abstract public function resize( $max_w, $max_h, $crop = false );
  99. /**
  100. * Resize multiple images from a single source.
  101. *
  102. * @since 3.5.0
  103. * @access public
  104. * @abstract
  105. *
  106. * @param array $sizes {
  107. * An array of image size arrays. Default sizes are 'small', 'medium', 'large'.
  108. *
  109. * @type array $size {
  110. * @type int $width Image width.
  111. * @type int $height Image height.
  112. * @type bool $crop Optional. Whether to crop the image. Default false.
  113. * }
  114. * }
  115. * @return array An array of resized images metadata by size.
  116. */
  117. abstract public function multi_resize( $sizes );
  118. /**
  119. * Crops Image.
  120. *
  121. * @since 3.5.0
  122. * @access public
  123. * @abstract
  124. *
  125. * @param int $src_x The start x position to crop from.
  126. * @param int $src_y The start y position to crop from.
  127. * @param int $src_w The width to crop.
  128. * @param int $src_h The height to crop.
  129. * @param int $dst_w Optional. The destination width.
  130. * @param int $dst_h Optional. The destination height.
  131. * @param bool $src_abs Optional. If the source crop points are absolute.
  132. * @return bool|WP_Error
  133. */
  134. abstract public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false );
  135. /**
  136. * Rotates current image counter-clockwise by $angle.
  137. *
  138. * @since 3.5.0
  139. * @access public
  140. * @abstract
  141. *
  142. * @param float $angle
  143. * @return bool|WP_Error
  144. */
  145. abstract public function rotate( $angle );
  146. /**
  147. * Flips current image.
  148. *
  149. * @since 3.5.0
  150. * @access public
  151. * @abstract
  152. *
  153. * @param bool $horz Flip along Horizontal Axis
  154. * @param bool $vert Flip along Vertical Axis
  155. * @return bool|WP_Error
  156. */
  157. abstract public function flip( $horz, $vert );
  158. /**
  159. * Streams current image to browser.
  160. *
  161. * @since 3.5.0
  162. * @access public
  163. * @abstract
  164. *
  165. * @param string $mime_type
  166. * @return bool|WP_Error
  167. */
  168. abstract public function stream( $mime_type = null );
  169. /**
  170. * Gets dimensions of image.
  171. *
  172. * @since 3.5.0
  173. * @access public
  174. *
  175. * @return array {'width'=>int, 'height'=>int}
  176. */
  177. public function get_size() {
  178. return $this->size;
  179. }
  180. /**
  181. * Sets current image size.
  182. *
  183. * @since 3.5.0
  184. * @access protected
  185. *
  186. * @param int $width
  187. * @param int $height
  188. * @return true
  189. */
  190. protected function update_size( $width = null, $height = null ) {
  191. $this->size = array(
  192. 'width' => (int) $width,
  193. 'height' => (int) $height
  194. );
  195. return true;
  196. }
  197. /**
  198. * Gets the Image Compression quality on a 1-100% scale.
  199. *
  200. * @since 4.0.0
  201. * @access public
  202. *
  203. * @return int $quality Compression Quality. Range: [1,100]
  204. */
  205. public function get_quality() {
  206. if ( ! $this->quality ) {
  207. $this->set_quality();
  208. }
  209. return $this->quality;
  210. }
  211. /**
  212. * Sets Image Compression quality on a 1-100% scale.
  213. *
  214. * @since 3.5.0
  215. * @access public
  216. *
  217. * @param int $quality Compression Quality. Range: [1,100]
  218. * @return true|WP_Error True if set successfully; WP_Error on failure.
  219. */
  220. public function set_quality( $quality = null ) {
  221. if ( null === $quality ) {
  222. /**
  223. * Filters the default image compression quality setting.
  224. *
  225. * Applies only during initial editor instantiation, or when set_quality() is run
  226. * manually without the `$quality` argument.
  227. *
  228. * set_quality() has priority over the filter.
  229. *
  230. * @since 3.5.0
  231. *
  232. * @param int $quality Quality level between 1 (low) and 100 (high).
  233. * @param string $mime_type Image mime type.
  234. */
  235. $quality = apply_filters( 'wp_editor_set_quality', $this->default_quality, $this->mime_type );
  236. if ( 'image/jpeg' == $this->mime_type ) {
  237. /**
  238. * Filters the JPEG compression quality for backward-compatibility.
  239. *
  240. * Applies only during initial editor instantiation, or when set_quality() is run
  241. * manually without the `$quality` argument.
  242. *
  243. * set_quality() has priority over the filter.
  244. *
  245. * The filter is evaluated under two contexts: 'image_resize', and 'edit_image',
  246. * (when a JPEG image is saved to file).
  247. *
  248. * @since 2.5.0
  249. *
  250. * @param int $quality Quality level between 0 (low) and 100 (high) of the JPEG.
  251. * @param string $context Context of the filter.
  252. */
  253. $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' );
  254. }
  255. if ( $quality < 0 || $quality > 100 ) {
  256. $quality = $this->default_quality;
  257. }
  258. }
  259. // Allow 0, but squash to 1 due to identical images in GD, and for backward compatibility.
  260. if ( 0 === $quality ) {
  261. $quality = 1;
  262. }
  263. if ( ( $quality >= 1 ) && ( $quality <= 100 ) ) {
  264. $this->quality = $quality;
  265. return true;
  266. } else {
  267. return new WP_Error( 'invalid_image_quality', __('Attempted to set image quality outside of the range [1,100].') );
  268. }
  269. }
  270. /**
  271. * Returns preferred mime-type and extension based on provided
  272. * file's extension and mime, or current file's extension and mime.
  273. *
  274. * Will default to $this->default_mime_type if requested is not supported.
  275. *
  276. * Provides corrected filename only if filename is provided.
  277. *
  278. * @since 3.5.0
  279. * @access protected
  280. *
  281. * @param string $filename
  282. * @param string $mime_type
  283. * @return array { filename|null, extension, mime-type }
  284. */
  285. protected function get_output_format( $filename = null, $mime_type = null ) {
  286. $new_ext = null;
  287. // By default, assume specified type takes priority
  288. if ( $mime_type ) {
  289. $new_ext = $this->get_extension( $mime_type );
  290. }
  291. if ( $filename ) {
  292. $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
  293. $file_mime = $this->get_mime_type( $file_ext );
  294. }
  295. else {
  296. // If no file specified, grab editor's current extension and mime-type.
  297. $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) );
  298. $file_mime = $this->mime_type;
  299. }
  300. // Check to see if specified mime-type is the same as type implied by
  301. // file extension. If so, prefer extension from file.
  302. if ( ! $mime_type || ( $file_mime == $mime_type ) ) {
  303. $mime_type = $file_mime;
  304. $new_ext = $file_ext;
  305. }
  306. // Double-check that the mime-type selected is supported by the editor.
  307. // If not, choose a default instead.
  308. if ( ! $this->supports_mime_type( $mime_type ) ) {
  309. /**
  310. * Filters default mime type prior to getting the file extension.
  311. *
  312. * @see wp_get_mime_types()
  313. *
  314. * @since 3.5.0
  315. *
  316. * @param string $mime_type Mime type string.
  317. */
  318. $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type );
  319. $new_ext = $this->get_extension( $mime_type );
  320. }
  321. if ( $filename ) {
  322. $dir = pathinfo( $filename, PATHINFO_DIRNAME );
  323. $ext = pathinfo( $filename, PATHINFO_EXTENSION );
  324. $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}";
  325. }
  326. return array( $filename, $new_ext, $mime_type );
  327. }
  328. /**
  329. * Builds an output filename based on current file, and adding proper suffix
  330. *
  331. * @since 3.5.0
  332. * @access public
  333. *
  334. * @param string $suffix
  335. * @param string $dest_path
  336. * @param string $extension
  337. * @return string filename
  338. */
  339. public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
  340. // $suffix will be appended to the destination filename, just before the extension
  341. if ( ! $suffix )
  342. $suffix = $this->get_suffix();
  343. $dir = pathinfo( $this->file, PATHINFO_DIRNAME );
  344. $ext = pathinfo( $this->file, PATHINFO_EXTENSION );
  345. $name = wp_basename( $this->file, ".$ext" );
  346. $new_ext = strtolower( $extension ? $extension : $ext );
  347. if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
  348. $dir = $_dest_path;
  349. return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
  350. }
  351. /**
  352. * Builds and returns proper suffix for file based on height and width.
  353. *
  354. * @since 3.5.0
  355. * @access public
  356. *
  357. * @return false|string suffix
  358. */
  359. public function get_suffix() {
  360. if ( ! $this->get_size() )
  361. return false;
  362. return "{$this->size['width']}x{$this->size['height']}";
  363. }
  364. /**
  365. * Either calls editor's save function or handles file as a stream.
  366. *
  367. * @since 3.5.0
  368. * @access protected
  369. *
  370. * @param string|stream $filename
  371. * @param callable $function
  372. * @param array $arguments
  373. * @return bool
  374. */
  375. protected function make_image( $filename, $function, $arguments ) {
  376. if ( $stream = wp_is_stream( $filename ) ) {
  377. ob_start();
  378. } else {
  379. // The directory containing the original file may no longer exist when using a replication plugin.
  380. wp_mkdir_p( dirname( $filename ) );
  381. }
  382. $result = call_user_func_array( $function, $arguments );
  383. if ( $result && $stream ) {
  384. $contents = ob_get_contents();
  385. $fp = fopen( $filename, 'w' );
  386. if ( ! $fp )
  387. return false;
  388. fwrite( $fp, $contents );
  389. fclose( $fp );
  390. }
  391. if ( $stream ) {
  392. ob_end_clean();
  393. }
  394. return $result;
  395. }
  396. /**
  397. * Returns first matched mime-type from extension,
  398. * as mapped from wp_get_mime_types()
  399. *
  400. * @since 3.5.0
  401. *
  402. * @static
  403. * @access protected
  404. *
  405. * @param string $extension
  406. * @return string|false
  407. */
  408. protected static function get_mime_type( $extension = null ) {
  409. if ( ! $extension )
  410. return false;
  411. $mime_types = wp_get_mime_types();
  412. $extensions = array_keys( $mime_types );
  413. foreach ( $extensions as $_extension ) {
  414. if ( preg_match( "/{$extension}/i", $_extension ) ) {
  415. return $mime_types[$_extension];
  416. }
  417. }
  418. return false;
  419. }
  420. /**
  421. * Returns first matched extension from Mime-type,
  422. * as mapped from wp_get_mime_types()
  423. *
  424. * @since 3.5.0
  425. *
  426. * @static
  427. * @access protected
  428. *
  429. * @param string $mime_type
  430. * @return string|false
  431. */
  432. protected static function get_extension( $mime_type = null ) {
  433. $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types() ) );
  434. if ( empty( $extensions[0] ) )
  435. return false;
  436. return $extensions[0];
  437. }
  438. }