ImageMagick Engine - Version 1.0

Version Description

Download this release

Release Info

Developer orangelab
Plugin Icon 128x128 ImageMagick Engine
Version 1.0
Comparing to
See all releases

Version 1.0

css/ime-admin.css ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #regenbar {
2
+ position: relative;
3
+ height: 25px;
4
+
5
+ padding: 1px;
6
+ }
7
+
8
+ #regenbar-percent {
9
+ position: absolute;
10
+ left: 50%;
11
+ top: 50%;
12
+ width: 50px;
13
+ margin-left: -25px;
14
+ height: 25px;
15
+ margin-top: -9px;
16
+ font-weight: bold;
17
+ text-align: center;
18
+ }
19
+
20
+ .ui-widget-content {
21
+ border: 1px solid #000;
22
+ background-color: #FFF;
23
+ color: #000;
24
+ height: 25px;
25
+ }
26
+
27
+ .ui-progressbar .ui-progressbar-value {
28
+ height: 25px;
29
+ background-color: #FF6600;
30
+ }
31
+
32
+ #regen-message {
33
+ margin-top: 10px;
34
+ width: 200px;
35
+ background-color: #FFC9A5;
36
+ }
37
+
38
+ #regen-message-inner {
39
+ padding: 10px;
40
+ }
imagemagick-engine.php ADDED
@@ -0,0 +1,899 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: ImageMagick Engine
4
+ Plugin URI: http://wp.orangelab.se/imagemagick-engine/
5
+ Description: Improve the quality of re-sized images by replacing standard GD library with ImageMagick
6
+ Author: Orangelab
7
+ Author URI: http://www.orangelab.se
8
+ Version: 1.0
9
+
10
+ Copyright 2010 Orangelab
11
+
12
+ Licenced under the GNU GPL:
13
+
14
+ This program is free software; you can redistribute it and/or modify
15
+ it under the terms of the GNU General Public License as published by
16
+ the Free Software Foundation; either version 2 of the License, or
17
+ (at your option) any later version.
18
+
19
+ This program is distributed in the hope that it will be useful,
20
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ GNU General Public License for more details.
23
+
24
+ You should have received a copy of the GNU General Public License
25
+ along with this program; if not, write to the Free Software
26
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+
29
+ /*
30
+ * Current todo list:
31
+ * - readme.txt
32
+ * - screenshots
33
+ *
34
+ * - check localization, translate strings
35
+ *
36
+ * Future todo list:
37
+ * - AJAXify re-encode startup
38
+ * - admin: smarter find path to executable (maybe try 'which' or package handler?)
39
+ * - allow customization of command line / class functions (safely!), check memory limit
40
+ * - handle TIF and other IM formats if possible
41
+ * - can we use IM instead of GD in more places?
42
+ */
43
+
44
+ if (!defined('ABSPATH'))
45
+ die('Must not be called directly');
46
+
47
+
48
+ /*
49
+ * Constants
50
+ */
51
+ define('IME_OPTION_VERSION', 1);
52
+
53
+
54
+ /*
55
+ * Global variables
56
+ */
57
+
58
+ // Plugin options default values -- change on plugin admin page
59
+ $ime_options_default = array('enabled' => false
60
+ , 'mode' => null
61
+ , 'cli_path' => null
62
+ , 'handle_sizes' => array('thumbnail' => true, 'medium' => true, 'large' => true)
63
+ , 'quality' => ''
64
+ , 'version' => IME_OPTION_VERSION
65
+ );
66
+
67
+ // Available modes
68
+ $ime_available_modes = array('php' => "Imagick PHP module"
69
+ , 'cli' => "ImageMagick command-line");
70
+
71
+ // Current options
72
+ $ime_options = null;
73
+
74
+ // Keep track of attachment file & sizes between different filters
75
+ $ime_image_sizes = null;
76
+ $ime_image_file = null;
77
+
78
+
79
+ /*
80
+ * Functions
81
+ */
82
+ add_action('plugins_loaded', 'ime_init');
83
+
84
+ /* Plugin setup */
85
+ function ime_init() {
86
+ $plugin_url = trailingslashit(WP_PLUGIN_URL . '/' . dirname(plugin_basename(__FILE__)));
87
+
88
+ load_plugin_textdomain('ime', false, dirname(plugin_basename(__FILE__)) . '/languages');
89
+
90
+ if (ime_active()) {
91
+ add_filter('intermediate_image_sizes_advanced', 'ime_filter_image_sizes', 99, 1);
92
+ add_filter('wp_read_image_metadata', 'ime_filter_read_image_metadata', 10, 3);
93
+ add_filter('wp_generate_attachment_metadata', 'ime_filter_attachment_metadata', 10, 2);
94
+ }
95
+
96
+ if (is_admin()) {
97
+ add_action('admin_menu', 'ime_admin_menu');
98
+ add_filter('plugin_action_links', 'ime_filter_plugin_actions', 10, 2 );
99
+ add_filter('media_meta', 'ime_filter_media_meta', 10, 2);
100
+
101
+ add_action('wp_ajax_ime_test_im_path', 'ime_ajax_test_im_path');
102
+ add_action('wp_ajax_ime_process_image', 'ime_ajax_process_image');
103
+
104
+ wp_enqueue_script('ime-admin', $plugin_url . 'js/ime-admin.js', array('jquery'));
105
+ wp_enqueue_script('jquery-ui-progressbar', $plugin_url . 'js/ui.progressbar.js', array('jquery-ui-core'));
106
+
107
+ wp_enqueue_style( 'ime-admin-style', $plugin_url . 'css/ime-admin.css', array());
108
+ }
109
+ }
110
+
111
+ /* Are we enabled with valid mode? */
112
+ function ime_active() {
113
+ return ime_get_option("enabled") && ime_mode_valid();
114
+ }
115
+
116
+ /* Check if mode is valid */
117
+ function ime_mode_valid($mode = null) {
118
+ if (empty($mode))
119
+ $mode = ime_get_option("mode");
120
+ $fn = 'ime_im_' . $mode . '_valid';
121
+ return (!empty($mode) && function_exists($fn) && call_user_func($fn));
122
+ }
123
+
124
+
125
+ /*
126
+ * Plugin option handling
127
+ */
128
+
129
+ // Setup plugin options
130
+ function ime_setup_options() {
131
+ global $ime_options;
132
+
133
+ // Already setup?
134
+ if (is_array($ime_options))
135
+ return;
136
+
137
+ $ime_options = get_option("ime_options");
138
+
139
+ // No stored options yet?
140
+ if (!is_array($ime_options)) {
141
+ global $ime_options_default;
142
+ $ime_options = $ime_options_default;
143
+ }
144
+
145
+ // Do we need to upgrade options?
146
+ if (!array_key_exists('version', $ime_options)
147
+ || $ime_options['version'] < IME_OPTION_VERSION) {
148
+
149
+ /*
150
+ * Future compatability code goes here!
151
+ */
152
+
153
+ $ime_options['version'] = IME_OPTION_VERSION;
154
+ ime_store_options();
155
+ }
156
+ }
157
+
158
+ // Store plugin options
159
+ function ime_store_options() {
160
+ global $ime_options;
161
+
162
+ ime_setup_options();
163
+
164
+ $stored_options = get_option("ime_options");
165
+
166
+ if ($stored_options === false)
167
+ add_option("ime_options", $ime_options, null, false);
168
+ else
169
+ update_option("ime_options", $ime_options);
170
+ }
171
+
172
+ // Get plugin option
173
+ function ime_get_option($option_name, $default = null) {
174
+ ime_setup_options();
175
+
176
+ global $ime_options, $ime_options_default;
177
+
178
+ if (array_key_exists($option_name, $ime_options))
179
+ return $ime_options[$option_name];
180
+
181
+ if (!is_null($default))
182
+ return $default;
183
+
184
+ if (array_key_exists($option_name, $ime_options_default))
185
+ return $ime_options_default[$option_name];
186
+
187
+ return null;
188
+ }
189
+
190
+ // Set plugin option
191
+ function ime_set_option($option_name, $option_value, $store = false) {
192
+ ime_setup_options();
193
+
194
+ global $ime_options;
195
+
196
+ $ime_options[$option_name] = $option_value;
197
+
198
+ if ($store)
199
+ ime_store_options();
200
+ }
201
+
202
+
203
+ /*
204
+ * WP integration & image handling functions
205
+ */
206
+
207
+ /*
208
+ * Filter image sizes (in wp_generate_attachment_metadata()).
209
+ *
210
+ * We store the sizes we are interested in, and remove those sizes from the
211
+ * list so that WP doesn't handle them -- we will take care of them later.
212
+ *
213
+ * The reason we do things this way is so we do not resize image twize (once
214
+ * by WordPress using GD, and then again by us).
215
+ */
216
+ function ime_filter_image_sizes($sizes) {
217
+ global $ime_image_sizes;
218
+
219
+ $handle_sizes = ime_get_option('handle_sizes');
220
+ foreach ($handle_sizes AS $s => $handle) {
221
+ if (!$handle || !array_key_exists($s, $sizes))
222
+ continue;
223
+ $ime_image_sizes[$s] = $sizes[$s];
224
+ unset($sizes[$s]);
225
+ }
226
+ return $sizes;
227
+ }
228
+
229
+ /*
230
+ * Filter to get target file name.
231
+ *
232
+ * Function wp_generate_attachment_metadata calls wp_read_image_metadata which
233
+ * gives us a hook to get the target filename.
234
+ */
235
+ function ime_filter_read_image_metadata($metadata, $file, $ignore) {
236
+ global $ime_image_file;
237
+
238
+ $ime_image_file = $file;
239
+
240
+ return $metadata;
241
+ }
242
+
243
+ /*
244
+ * Filter new attachment metadata
245
+ *
246
+ * Resize image for the sizes we are interested in.
247
+ *
248
+ * Parts of function copied from wp-includes/media.php:image_resize()
249
+ */
250
+ function ime_filter_attachment_metadata($metadata, $attachment_id) {
251
+ global $ime_image_sizes, $ime_image_file;
252
+
253
+ // Any sizes we are interested in?
254
+ if (empty($ime_image_sizes))
255
+ return $metadata;
256
+
257
+ // Get size & image type of original image
258
+ $old_stats = @getimagesize($ime_image_file);
259
+ if (!$old_stats)
260
+ return $metadata;
261
+
262
+ list($orig_w, $orig_h, $orig_type) = $old_stats;
263
+
264
+ /*
265
+ * Sort out the filename, extension (and image type) of resized images
266
+ *
267
+ * If image type is PNG or GIF keep it, otherwise make sure it is JPG
268
+ */
269
+ $info = pathinfo($ime_image_file);
270
+ $dir = $info['dirname'];
271
+ $ext = $info['extension'];
272
+ $namebase = basename($ime_image_file, ".{$ext}");
273
+ if ($orig_type == IMAGETYPE_PNG || $orig_type == IMAGETYPE_GIF)
274
+ $new_ext = $ext;
275
+ else
276
+ $new_ext = "jpg";
277
+
278
+ /*
279
+ * Do the actual resize
280
+ */
281
+ foreach ($ime_image_sizes as $size => $size_data) {
282
+ $width = $size_data['width'];
283
+ $height = $size_data['height'];
284
+
285
+ // ignore sizes equal to or larger than original size
286
+ if ($orig_w <= $width && $orig_h <= $height)
287
+ continue;
288
+
289
+ $crop = $size_data['crop'];
290
+
291
+ $dims = image_resize_dimensions($orig_w, $orig_h, $width, $height, $crop);
292
+ if ( !$dims )
293
+ continue;
294
+ list($dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) = $dims;
295
+
296
+ $suffix = "{$dst_w}x{$dst_h}";
297
+ $new_filename = "{$dir}/{$namebase}-{$suffix}.{$new_ext}";
298
+
299
+ $resized = ime_im_resize($ime_image_file, $new_filename, $dst_w, $dst_h, $crop);
300
+ if (!$resized)
301
+ continue;
302
+
303
+ $metadata['sizes'][$size] = array('file' => basename($new_filename)
304
+ , 'width' => $dst_w
305
+ , 'height' => $dst_h
306
+ );
307
+ $metadata['image-converter'][$size] = 'IME';
308
+
309
+ // Set correct file permissions
310
+ $stat = stat( dirname( $new_filename ));
311
+ $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
312
+ @ chmod( $new_filename, $perms );
313
+ }
314
+
315
+ $ime_image_sizes = null;
316
+ return $metadata;
317
+ }
318
+
319
+ // Resize file by calling mode specific resize function
320
+ function ime_im_resize($old_file, $new_file, $width, $height, $crop) {
321
+ $mode = ime_get_option("mode");
322
+ $fn = 'ime_im_' . $mode . '_valid';
323
+ if (empty($mode) || !function_exists($fn) || !call_user_func($fn))
324
+ return false;
325
+
326
+ $fn = 'ime_im_' . $mode . '_resize';
327
+ return (function_exists($fn) && call_user_func($fn, $old_file, $new_file, $width, $height, $crop));
328
+ }
329
+
330
+ // Is this the filename of a jpeg?
331
+ function ime_im_filename_is_jpg($filename) {
332
+ $info = pathinfo($filename);
333
+ $ext = $info['extension'];
334
+ return (strcasecmp($ext, 'jpg') == 0) || (strcasecmp($ext, 'jpeg') == 0);
335
+ }
336
+
337
+ /*
338
+ * PHP ImageMagick ("Imagick") class handling
339
+ */
340
+
341
+ // Does class exist?
342
+ function ime_im_php_valid() {
343
+ return class_exists('Imagick');
344
+ }
345
+
346
+ // Resize file using PHP Imagick class
347
+ function ime_im_php_resize($old_file, $new_file, $width, $height, $crop) {
348
+ $im = new Imagick($old_file);
349
+ if (!$im->valid())
350
+ return false;
351
+
352
+ $quality = ime_get_option('quality', '-1');
353
+ if (is_numeric($quality) && $quality >= 0 && $quality <= 100 && ime_im_filename_is_jpg($new_file)) {
354
+ $im->setImageCompression(Imagick::COMPRESSION_JPEG);
355
+ $im->setImageCompressionQuality($quality);
356
+ }
357
+ $im->setImageOpacity(1.0);
358
+
359
+ if ($crop)
360
+ $im->cropThumbnailImage($width, $height);
361
+ else
362
+ $im->scaleImage($width, $height, true);
363
+
364
+ $im->setImagePage($width, $height, 0, 0); // to make sure canvas is correct
365
+ $im->writeImage($new_file);
366
+
367
+ return file_exists($new_file);
368
+ }
369
+
370
+ /*
371
+ * ImageMagick executable handling
372
+ */
373
+
374
+ // Do we have a valid ImageMagick executable set?
375
+ function ime_im_cli_valid() {
376
+ $cmd = ime_im_cli_command();
377
+ return !empty($cmd) && is_executable($cmd);
378
+ }
379
+
380
+ // Check if path leads to ImageMagic executable
381
+ function ime_im_cli_check_command($path) {
382
+ $path = realpath($path);
383
+ if (!is_dir($path))
384
+ return null;
385
+
386
+ $cmd = $path . '/convert';
387
+ if (is_executable($cmd))
388
+ return $cmd;
389
+
390
+ $cmd = $cmd . '.exe';
391
+ if (is_executable($cmd))
392
+ return $cmd;
393
+
394
+ return null;
395
+ }
396
+
397
+ // Try to find a valid ImageMagic executable
398
+ function ime_im_cli_find_command() {
399
+ $possible_paths = array("/usr/bin", "/usr/local/bin");
400
+
401
+ foreach ($possible_paths AS $path) {
402
+ $path = realpath($path);
403
+ if (ime_im_cli_check_command($path))
404
+ return $path;
405
+ }
406
+
407
+ return null;
408
+ }
409
+
410
+ // Get ImageMagic executable
411
+ function ime_im_cli_command() {
412
+ $path = ime_get_option("cli_path");
413
+ if (!empty($path))
414
+ return ime_im_cli_check_command($path);
415
+
416
+ $path = ime_im_cli_find_command();
417
+ if (empty($path))
418
+ return null;
419
+ ime_set_option("cli_path", $path, true);
420
+ return ime_im_cli_check_command($path);
421
+ }
422
+
423
+ // Resize using ImageMagick executable
424
+ function ime_im_cli_resize($old_file, $new_file, $width, $height, $crop) {
425
+ $cmd = ime_im_cli_command();
426
+ if (empty($cmd))
427
+ return false;
428
+
429
+ $old_file = addslashes($old_file);
430
+ $new_file = addslashes($new_file);
431
+
432
+ $cmd = "\"$cmd\" -limit memory 150mb -limit map 128mb -size {$width}x{$height} '{$old_file}' -resize {$width}x{$height}";
433
+ if ($crop)
434
+ $cmd .= "^ -gravity center -extent {$width}x{$height}";
435
+
436
+ $quality = ime_get_option('quality', '-1');
437
+ if (is_numeric($quality) && $quality >= 0 && $quality <= 100 && ime_im_filename_is_jpg($new_file))
438
+ $cmd .= " -quality " . intval($quality);
439
+
440
+ $cmd .= " '{$new_file}'";
441
+ exec($cmd);
442
+
443
+ return file_exists($new_file);
444
+ }
445
+
446
+
447
+ /*
448
+ * AJAX functions
449
+ */
450
+
451
+ // Test if a path is correct for IM binary
452
+ function ime_ajax_test_im_path() {
453
+ if (!current_user_can('manage_options'))
454
+ die();
455
+ $r = ime_im_cli_check_command($_REQUEST['cli_path']);
456
+ echo empty($r) ? "0" : "1";
457
+ die();
458
+ }
459
+
460
+ // Process single attachment ID
461
+ function ime_ajax_process_image() {
462
+ global $ime_image_sizes, $ime_image_file, $_wp_additional_image_sizes;
463
+
464
+ if (!current_user_can('manage_options') || !ime_mode_valid())
465
+ die('-1');
466
+
467
+ $id = intval($_REQUEST['id']);
468
+ if ($id <= 0)
469
+ die('-1');
470
+
471
+ $temp_sizes = $_REQUEST['sizes'];
472
+ if (empty($temp_sizes))
473
+ die('-1');
474
+ $temp_sizes = explode('|', $temp_sizes);
475
+ if (count($temp_sizes) < 1)
476
+ die('-1');
477
+
478
+ $temp_sizes = apply_filters( 'intermediate_image_sizes', $temp_sizes );
479
+
480
+ foreach ( $temp_sizes as $s ) {
481
+ $sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => FALSE );
482
+ if ( isset( $_wp_additional_image_sizes[$s]['width'] ) )
483
+ $sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
484
+ else
485
+ $sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
486
+ if ( isset( $_wp_additional_image_sizes[$s]['height'] ) )
487
+ $sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
488
+ else
489
+ $sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
490
+ if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) )
491
+ $sizes[$s]['crop'] = intval( $_wp_additional_image_sizes[$s]['crop'] ); // For theme-added sizes
492
+ else
493
+ $sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
494
+ }
495
+
496
+ remove_filter('intermediate_image_sizes_advanced', 'ime_filter_image_sizes', 99, 1);
497
+ $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
498
+
499
+ $force = isset($_REQUEST['force']) && !! $_REQUEST['force'];
500
+
501
+ $ime_image_file = get_attached_file($id);
502
+
503
+ if (false === $ime_image_file || !file_exists($ime_image_file))
504
+ die('-1');
505
+
506
+ $metadata = wp_get_attachment_metadata($id);
507
+
508
+ if (!$force) {
509
+ foreach ($sizes AS $s => $ignore) {
510
+ if ($metadata['image-converter'][$s] == 'IME')
511
+ unset($sizes[$s]);
512
+ }
513
+ if (count($sizes) < 1)
514
+ die(1);
515
+ }
516
+
517
+ $ime_image_sizes = $sizes;
518
+
519
+ set_time_limit(60);
520
+
521
+ $new_meta = ime_filter_attachment_metadata($metadata, $id);
522
+ wp_update_attachment_metadata($id, $new_meta);
523
+
524
+ /*
525
+ * Normally the old file gets overwritten by the new one when
526
+ * regenerating resized images.
527
+ *
528
+ * However, if the specifications of image sizes were changed this
529
+ * will result in different resized file names.
530
+ *
531
+ * Make sure they get deleted.
532
+ */
533
+
534
+ $dir = trailingslashit(dirname($ime_image_file));
535
+
536
+ foreach ($metadata['sizes'] as $size => $sizeinfo) {
537
+ $old_file = $sizeinfo['file'];
538
+
539
+ // Does file exist in new meta?
540
+ $exists = false;
541
+ foreach ($new_meta['sizes'] as $ignore => $new_sizeinfo) {
542
+ if ($old_file != $new_sizeinfo['file'])
543
+ continue;
544
+ $exists = true;
545
+ break;
546
+ }
547
+ if ($exists)
548
+ continue;
549
+
550
+ // Old file did not exist in new meta. Delete it!
551
+ @ unlink($dir . $old_file);
552
+ }
553
+
554
+ die('1');
555
+ }
556
+
557
+
558
+ /*
559
+ * Admin page functions
560
+ */
561
+
562
+ /* Add admin/status page */
563
+ function ime_admin_menu() {
564
+ add_options_page('ImageMagick Engine', 'ImageMagick Engine', 8, 'ime', 'ime_option_page');
565
+ }
566
+
567
+ /* Add settings to plugin action links */
568
+ function ime_filter_plugin_actions($links, $file) {
569
+ if($file == plugin_basename(__FILE__)) {
570
+ $settings_link = "<a href=\"options-general.php?page=ime\">"
571
+ . __('Settings', 'ime') . '</a>';
572
+ array_unshift( $links, $settings_link ); // before other links
573
+ }
574
+
575
+ return $links;
576
+ }
577
+
578
+ /*
579
+ * Add admin information if attachment is converted using plugin
580
+ */
581
+ function ime_filter_media_meta($content, $post) {
582
+ $metadata = wp_get_attachment_metadata($post->ID);
583
+
584
+ if (!array_key_exists('image-converter', $metadata))
585
+ return $content;
586
+
587
+ $converted = false;
588
+ foreach ($metadata['image-converter'] as $size => $converter) {
589
+ if ($converter == 'IME') {
590
+ $converted = true;
591
+ break;
592
+ }
593
+ }
594
+
595
+ if (!$converted)
596
+ return $content;
597
+
598
+ $content .= '</p><p><i>' . __('Resized using ImageMagic Engine', 'ime') . '</i>';
599
+
600
+ return $content;
601
+ }
602
+
603
+ // Url to admin images
604
+ function ime_option_admin_images_url() {
605
+ return get_bloginfo('wpurl') . '/wp-admin/images/';
606
+ }
607
+
608
+ // Url to status icon
609
+ function ime_option_status_icon($yes = true) {
610
+ return ime_option_admin_images_url() . ($yes ? 'yes' : 'no') . '.png';
611
+ }
612
+
613
+ // Display, or not
614
+ function ime_option_display($display = true, $echo = true) {
615
+ if ($display)
616
+ return '';
617
+ $s = ' style="display: none" ';
618
+ if ($echo)
619
+ echo $s;
620
+ return $s;
621
+ }
622
+
623
+ // Show HTML (and JS) to regenerate images
624
+ function ime_show_regenerate_images($sizes) {
625
+ global $wpdb;
626
+
627
+ echo '<div class="wrap"><h2>' . __('ImageMagick Engine','ime') . '</h2>';
628
+
629
+ // Query for the IDs only to reduce memory usage
630
+ $images = $wpdb->get_results( "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%'" );
631
+
632
+ // Make sure there are images to process
633
+ if ( empty($images) ) {
634
+ echo '<p>' . sprintf( __( "Unable to find any images. Are you sure <a href='%s'>some exist</a>?", 'ime' ), admin_url('upload.php?post_mime_type=image') ) . "</p>\n\n</div>";
635
+ return;
636
+ }
637
+
638
+ // Valid results
639
+ echo '<p>' . __( "Please be patient while images are regenerated. This can take a long time if you have many images.", 'ime' ) . '</p>';
640
+
641
+ // Generate the list of IDs
642
+ $ids = array();
643
+ foreach ( $images as $image )
644
+ $ids[] = $image->ID;
645
+ $ids = implode( ',', $ids );
646
+
647
+ $count = count( $images );
648
+
649
+ $force = isset($_POST['force']) ? 1 : 0;
650
+
651
+ $regen_sizes = array();
652
+ foreach ($sizes AS $s) {
653
+ $f = 'regen-size-' . $s;
654
+ if (isset($_POST[$f]) && !! $_POST[$f])
655
+ array_push($regen_sizes, $s);
656
+ }
657
+ $regen_sizes = implode('|', $regen_sizes);
658
+ ?>
659
+ <noscript><p><em><?php _e( 'You must enable Javascript in order to proceed!', 'wp-qui' ) ?></em></p></noscript>
660
+
661
+ <div id="regenbar">
662
+ <div id="regenbar-percent"></div>
663
+ </div>
664
+
665
+ <div id="regen-message"><div id="regen-message-inner"><?php _e('Working...', 'ime');?></div></div>
666
+
667
+ </div>
668
+
669
+ <script type="text/javascript">
670
+ // <![CDATA[
671
+ jQuery(document).ready(function($){
672
+ var i;
673
+ var rt_images = [<?php echo $ids; ?>];
674
+ var rt_total = rt_images.length;
675
+ var rt_count = 1;
676
+ var rt_percent = 0;
677
+ var rt_force = <?php echo $force; ?>;
678
+ var rt_sizes = '<?php echo $regen_sizes; ?>';
679
+
680
+ $("#regenbar").progressbar();
681
+ $("#regenbar-percent").html( "0%" );
682
+
683
+ function RegenImages( id ) {
684
+ $.post( "admin-ajax.php", { action: "ime_process_image", id: id, sizes: rt_sizes, force: rt_force }, function() {
685
+ rt_percent = ( rt_count / rt_total ) * 100;
686
+ $("#regenbar").progressbar( "value", rt_percent );
687
+ $("#regenbar-percent").html( Math.round(rt_percent) + "%" );
688
+ rt_count = rt_count + 1;
689
+
690
+ if ( rt_images.length ) {
691
+ RegenImages( rt_images.shift() );
692
+ } else {
693
+ $("#regen-message-inner").html("<strong><?php echo js_escape(__( 'All done!', 'ime' ) ); ?></strong><br /><?php echo js_escape( sprintf( __( 'Processed %d images.', 'ime' ), $count ) ); ?>");
694
+ }
695
+
696
+ });
697
+ }
698
+
699
+ RegenImages( rt_images.shift() );
700
+ });
701
+ // ]]>
702
+ </script>
703
+ <?php
704
+ }
705
+
706
+ /* Plugin admin / status page */
707
+ function ime_option_page() {
708
+ global $ime_available_modes;
709
+
710
+ if (!current_user_can('manage_options'))
711
+ wp_die('Sorry, but you do not have permissions to change settings.');
712
+
713
+ /* Make sure post was from this page */
714
+ if (count($_POST) > 0)
715
+ check_admin_referer('ime-options');
716
+
717
+ global $_wp_additional_image_sizes;
718
+ $sizes = array('thumbnail', 'medium', 'large'); // Standard sizes
719
+ if ( isset( $_wp_additional_image_sizes ) && count( $_wp_additional_image_sizes ) )
720
+ $sizes = array_merge( $sizes, array_keys( $_wp_additional_image_sizes ) );
721
+
722
+ if (isset($_POST['regenerate-images'])) {
723
+ ime_show_regenerate_images($sizes);
724
+ return;
725
+ }
726
+
727
+ /* Should we update settings? */
728
+ if (isset($_POST['update_settings'])) {
729
+ $new_enabled = isset($_POST['enabled']) && !! $_POST['enabled'];
730
+ ime_set_option('enabled', $new_enabled);
731
+ if (isset($_POST['mode']) && array_key_exists($_POST['mode'], $ime_available_modes))
732
+ ime_set_option('mode', $_POST['mode']);
733
+ if (isset($_POST['cli_path']))
734
+ ime_set_option('cli_path', realpath($_POST['cli_path']));
735
+ if (isset($_POST['quality'])) {
736
+ if (is_numeric($_POST['quality']))
737
+ ime_set_option('quality', min(100, max(0, intval($_POST['quality']))));
738
+ else if (empty($_POST['quality']))
739
+ ime_set_option('quality', '');
740
+ }
741
+ $new_handle_sizes = array();
742
+ foreach ($sizes AS $s) {
743
+ $f = 'handle-' . $s;
744
+ $new_handle_sizes[$s] = isset($_POST[$f]) && !! $_POST[$f];
745
+ }
746
+ ime_set_option('handle_sizes', $new_handle_sizes);
747
+
748
+ ime_store_options();
749
+
750
+ echo '<div id="message" class="updated fade"><p>'
751
+ . __('Settings updated', 'ime')
752
+ . '</p></div>';
753
+ }
754
+
755
+ $modes_valid = $ime_available_modes;
756
+ $any_valid = false;
757
+ foreach($modes_valid AS $m => $ignore) {
758
+ $modes_valid[$m] = ime_mode_valid($m);
759
+ if ($modes_valid[$m])
760
+ $any_valid = true;
761
+ }
762
+
763
+ $current_mode = ime_get_option('mode');
764
+ if (!$modes_valid[$current_mode])
765
+ $current_mode = null;
766
+ if (is_null($current_mode) && $any_valid) {
767
+ foreach ($modes_valid AS $m => $valid) {
768
+ if ($valid) {
769
+ $current_mode = $m;
770
+ break;
771
+ }
772
+ }
773
+ }
774
+
775
+ $enabled = ime_get_option('enabled') && $current_mode;
776
+
777
+ $cli_path = ime_get_option('cli_path');
778
+ if (is_null($cli_path))
779
+ $cli_path = ime_im_cli_command();
780
+ $cli_path_ok = ime_im_cli_check_command($cli_path);
781
+
782
+ $quality = ime_get_option('quality');
783
+ $handle_sizes = ime_get_option('handle_sizes');
784
+
785
+ if (!$any_valid)
786
+ echo '<div id="warning" class="error"><p>'
787
+ . sprintf(__('No valid ImageMagick mode found! Please check %sFAQ%s for installation instructions.', 'ime'), '<a href="http://wp.orangelab.se/imagemagick-engine/documentation#installation">', '</a>')
788
+ . '</p></div>';
789
+ elseif (!$enabled)
790
+ echo '<div id="warning" class="error"><p>'
791
+ . __('ImageMagick Engine is not enabled.', 'ime')
792
+ . '</p></div>';
793
+ ?>
794
+ <div class="wrap">
795
+ <h2><?php _e('ImageMagick Engine Settings','ime'); ?></h2>
796
+ <form action="options-general.php?page=ime" method="post" name="update_options">
797
+ <?php wp_nonce_field('ime-options'); ?>
798
+ <input type="hidden" name="ajax_url" id="ajax_url" value="<?php bloginfo('wpurl'); ?>/wp-admin/admin-ajax.php" />
799
+ <div id="poststuff" class="metabox-holder has-right-sidebar">
800
+ <div class="inner-sidebar">
801
+ <div class="meta-box-sortables ui-sortable">
802
+ <div class="postbox">
803
+ <div class="handlediv" title="Click to toggle"><br /></div>
804
+ <h3 class="hndle"><span><?php _e('Regenerate Images','ime'); ?></span></h3>
805
+ <div class="inside">
806
+ <div class="submitbox">
807
+ <table border=0>
808
+ <tr>
809
+ <td scope="row" valign="top" style="padding-right: 20px;"><?php _e('Sizes','ime'); ?>:</td>
810
+ <td>
811
+ <?php
812
+ foreach($sizes AS $s) {
813
+ echo '<input type="checkbox" name="regen-size-' . $s . '" value="1" ' . ($handle_sizes[$s] ? ' CHECKED ' : '') . ' /> ' . ucfirst($s) . '<br />';
814
+ }
815
+ ?>
816
+ </td>
817
+ </tr>
818
+ </table>
819
+ <p><?php _e('ImageMagick images too','ime'); ?>:
820
+ <input type="checkbox" name="force" value="1" /></p>
821
+ <p><input class="button-primary" type="submit" name="regenerate-images" value="<?php _e('Regenerate', 'ime'); ?>" /> <?php _e('(this can take a long time)', 'ime'); ?></p>
822
+ </div>
823
+ </div>
824
+ </div>
825
+ </div>
826
+ </div>
827
+ <div id="post-body">
828
+ <div id="post-body-content">
829
+ <table class="form-table">
830
+ <tr>
831
+ <th scope="row" valign="top"><?php _e('Enable enhanced image engine','ime'); ?>:</th>
832
+ <td>
833
+ <input type="checkbox" id="enabled" name="enabled" value="1" <?php echo $enabled ? " CHECKED " : ""; echo $any_valid ? '' : ' disabled=disabled '; ?> />
834
+ </td>
835
+ </tr>
836
+ <tr>
837
+ <th scope="row" valign="top"><?php _e('Image engine','ime'); ?>:</th>
838
+ <td>
839
+ <select name="mode">
840
+ <?php
841
+ foreach($modes_valid AS $m => $valid) {
842
+ echo '<option value="' . $m . '"';
843
+ if ($m == $current_mode)
844
+ echo ' selected=selected ';
845
+ if (!$valid)
846
+ echo ' disabled=disabled ';
847
+ echo '>' . $ime_available_modes[$m] . '</option>';
848
+ }
849
+ ?>
850
+ </select>
851
+ </td>
852
+ </tr>
853
+ <tr>
854
+ <th scope="row" valign="top"><?php _e('Imagick PHP module','ime'); ?>:</th>
855
+ <td>
856
+ <img src="<?php echo ime_option_status_icon($modes_valid['php']); ?>" />
857
+ <?php echo $modes_valid['php'] ? __('Imagick PHP module found', 'ime') : __('Imagick PHP module not found', 'ime'); ?>
858
+ </td>
859
+ </tr>
860
+ <tr>
861
+ <th scope="row" valign="top"><?php _e('ImageMagick path','ime'); ?>:</th>
862
+ <td>
863
+ <img id="cli_path_yes" class="cli_path_icon" src="<?php echo ime_option_status_icon(true); ?>" alt="" <?php ime_option_display($cli_path_ok); ?> />
864
+ <img id="cli_path_no" class="cli_path_icon" src="<?php echo ime_option_status_icon(false); ?>" alt="<?php _e('Command not found', 'qp-qie'); ?>" <?php ime_option_display(!$cli_path_ok); ?> />
865
+ <img id="cli_path_progress" src="<?php echo ime_option_admin_images_url(); ?>wpspin_light.gif" alt="<?php _e('Testing command...', 'qp-qie'); ?>" <?php ime_option_display(false); ?> />
866
+ <input id="cli_path" type="text" name="cli_path" size="<?php echo max(30, strlen($cli_path) + 5); ?>" value="<?php echo $cli_path; ?>" />
867
+ <input type="button" name="ime_cli_path_test" id="ime_cli_path_test" value="<?php _e('Test path', 'ime'); ?>" />
868
+ </td>
869
+ </tr>
870
+ <tr>
871
+ <th scope="row" valign="top"><?php _e('ImageMagick quality','ime'); ?>:</th>
872
+ <td>
873
+ <input id="quality" type="text" name="quality" size="3" value="<?php echo $quality; ?>" /> <?php _e('(0-100, leave empty for default value, computed dynamically)', 'ime'); ?>
874
+ </td>
875
+ </tr>
876
+ <tr>
877
+ <th scope="row" valign="top"><?php _e('Handle sizes','ime'); ?>:</th>
878
+ <td>
879
+ <?php
880
+ foreach($sizes AS $s) {
881
+ echo '<input type="checkbox" name="handle-' . $s . '" value="1" ' . ($handle_sizes[$s] ? ' CHECKED ' : '') . ' /> ' . ucfirst($s) . '<br />';
882
+ }
883
+ ?>
884
+ </td>
885
+ </tr>
886
+ <tr>
887
+ <td>
888
+ <input class="button-primary" type="submit" name="update_settings" value="<?php _e('Save Changes', 'ime'); ?>" />
889
+ </td>
890
+ </tr>
891
+ </table>
892
+ </div>
893
+ </div>
894
+ </form>
895
+ </div>
896
+ </div>
897
+ <?php
898
+ }
899
+ ?>
js/ime-admin.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Ajax test IM path
2
+ function ime_test_path () {
3
+ jQuery('.cli_path_icon').hide();
4
+ jQuery('#cli_path_progress').show();
5
+ jQuery.get(jQuery('#ajax_url').val(), {
6
+ action: "ime_test_im_path",
7
+ cli_path: jQuery('#cli_path').val()
8
+ }, function(data) {
9
+ jQuery('#cli_path_progress').hide();
10
+ r = parseInt(data);
11
+ if (r > 0) {
12
+ jQuery('#cli_path_yes').show();
13
+ jQuery('#cli_path_no').hide();
14
+ } else {
15
+ jQuery('#cli_path_yes').hide();
16
+ jQuery('#cli_path_no').show();
17
+ }
18
+ });
19
+ }
20
+
21
+ jQuery(document).ready(function($) {
22
+ jQuery('#ime_cli_path_test').click(function(){
23
+ ime_test_path();
24
+ });
25
+ });
js/ui.progressbar.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI Progressbar 1.7.2
3
+ *
4
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI/Progressbar
9
+ *
10
+ * Depends:
11
+ * ui.core.js
12
+ */
13
+ (function(a){a.widget("ui.progressbar",{_init:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this._valueMin(),"aria-valuemax":this._valueMax(),"aria-valuenow":this._value()});this.valueDiv=a('<div class="ui-progressbar-value ui-widget-header ui-corner-left"></div>').appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow").removeData("progressbar").unbind(".progressbar");this.valueDiv.remove();a.widget.prototype.destroy.apply(this,arguments)},value:function(b){if(b===undefined){return this._value()}this._setData("value",b);return this},_setData:function(b,c){switch(b){case"value":this.options.value=c;this._refreshValue();this._trigger("change",null,{});break}a.widget.prototype._setData.apply(this,arguments)},_value:function(){var b=this.options.value;if(b<this._valueMin()){b=this._valueMin()}if(b>this._valueMax()){b=this._valueMax()}return b},_valueMin:function(){var b=0;return b},_valueMax:function(){var b=100;return b},_refreshValue:function(){var b=this.value();this.valueDiv[b==this._valueMax()?"addClass":"removeClass"]("ui-corner-right");this.valueDiv.width(b+"%");this.element.attr("aria-valuenow",b)}});a.extend(a.ui.progressbar,{version:"1.7.2",defaults:{value:0}})})(jQuery);;
readme.txt ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === ImageMagick Engine ===
2
+ Contributors: orangelab
3
+ Tags: image images picture imagemagick gd
4
+ Requires at least: 2.9
5
+ Tested up to: 3.0.1
6
+ Stable tag: 1.0
7
+
8
+ Improve the quality of re-sized images by replacing standard GD library with ImageMagick.
9
+
10
+ == Description ==
11
+
12
+ Dramatically improve the quality of re-sized images by making WordPress use ImageMagick instead of standard GD image library.
13
+
14
+ Features
15
+
16
+ * Preserve embedded color profile in re-sized image
17
+ * Allow regeneration of existing images (optionally for selected image sizes only)
18
+ * Configure image quality or use dynamically computed default value.
19
+
20
+ Requires either ImageMagick binary or Imagick PHP module.
21
+
22
+ == Installation ==
23
+
24
+ 0. Install either ImageMagick or the Imagick PHP module (see FAQ for more information).
25
+ 1. Download and extract plugin files to a folder in your wp-content/plugin directory.
26
+ 2. Activate the plugin through the WordPress admin interface.
27
+ 3. Configure ImageMagick settings and enable it on plugin settings page.
28
+ 4. Regenerate existing images to take advantage of the new features.
29
+
30
+ If you have any questions or problems please make a comment here: http://wp.orangelab.se/imagemagick-engine/
31
+
32
+ == Frequently Asked Questions ==
33
+
34
+ = What difference does it make? =
35
+
36
+ ImageMagick can result in huge improvements in the quality of re-sized images.
37
+
38
+ Take a look at the supplied screenshot, or try it yourself. More examples will be available on the plugin website (http://wp.orangelab.se/imagemagick-engine/).
39
+
40
+ Note that the new images tend to be slightly larger than those of the standard GD library, especially if you specify a very high image quality (95+).
41
+
42
+ = How do I know if I have ImageMagick installed? =
43
+
44
+ If you have the PHP module installed the plugin will find it. You can check yourself using the phpinfo() function. We also automatically check a common location for the ImageMagick executable.
45
+
46
+ If you have shell access to a Linux/UNIX server you can use "which convert" to look for the ImageMagick executable.
47
+
48
+ = How do I install ImageMagick? =
49
+
50
+ You'll need full access to your server and a bit of technical know-how. If you do not have access you'll have to ask the server administrator.
51
+
52
+ Don't do it yourself unless you know what you are doing.
53
+
54
+ Most Linux distributions have a package for "ImageMagick". Some have a package for "php5-imagick". It is also possible to install the PHP module using the PEAR.
55
+
56
+ You can also find binary releases at http://www.imagemagick.org including Windows installer.
57
+
58
+ == Screenshots ==
59
+
60
+ 1. Example image: ImageMagick vs GD
61
+ 2. Administration interface
screenshot-1.jpg ADDED
Binary file
screenshot-2.jpg ADDED
Binary file