2bb5fd9fcb416a7dc784cdebbe48094a03ba866f
[atutor.git] / mods / wiki / plugins / feature / imgresize_gd2.php
1 <?php
2
3 #  if someone uploads an image, which is larger than the allowed
4 #  image size (EWIKI_IMAGE_MAXSIZE), then this plugin tries to
5 #  rescale that image until it fits; it utilizes the PHP libgd
6 #  functions to accomplish this
7
8 #  NOTE: It is currently disabled for Win32, because nobody knows, if
9 #  this will crash the PHP interpreter on those systems.
10
11 #  Changed on 6/8/04 by Alfred Sterphone, III
12 #  Types supported: gif, png, jpeg
13 #  All that are resized are converted to jpeg in the end
14 #  WBMP stands for wireless bitmap and not windows bitmap
15 #  Bitmap support for only 25KB files is folly anyway
16
17 #  Revamped again, later the same day (6/8/04) by me again
18 #  Implemented a binary search for the correct pixel width
19 #  Next step is to put PHPUnit around and test all cases
20
21 #  Refactoring and bullet-proofing completed (6/15/04) by me again
22
23 /*
24 Design Requirements
25 - Handled formats: jpeg, gif, png
26 - Images wider than 90% of our page width will be resized proportionally to 90% of page width at upload
27 - Images uploaded as images will be restricted to 25 kilobytes
28 - Images larger than this will:
29 -# Be scaled according to our page width
30 -# Be converted to JPEG
31 -# Be re-compressed as JPEG with compression settings that suits us
32 -# Rescaled via binary search for suitable size down to 20% of page width
33 -# Rejected as too large
34 - Image format may be changed in scaling
35 - Images uploaded as attachments will not be scaled in this manner
36
37
38 */
39
40 define("EWIKI_IMGRESIZE_WIN", 0);
41 define("EWIKI_IMAGE_MAX_PIXELS", 11000000); //11 megapixels
42 define("EWIKI_IMAGE_MAXSIZE", 65536);
43 define("EWIKI_IMAGE_TOLERANCE", 60000); //don't settle for smaller than this during resize
44 define("EWIKI_WORK_AREA", 640); //width of whitespace or work area within ewiki
45 define("EWIKI_IMAGE_MAX_X", (int)(.9*EWIKI_WORK_AREA)); //90% max
46 define("EWIKI_IMAGE_MIN_X", (int)(.2*EWIKI_WORK_AREA)); //20% min
47 define("EWIKI_IMAGE_MAX_Y",1000); //max y value if picture is "candy cane"
48 define("EWIKI_IMAGE_RATIO", 10); //maximum acceptable y:x ratio
49
50 if (!strstr(PHP_VERSION, "-dev") && !(extension_loaded("php_gd2.dll") or extension_loaded("gd.so")) && !function_exists("imagecreate") && function_exists("dl"))
51 {   #-- try to load gd lib
52     @dl("php_gd2.dll") or @dl("gd.so");
53 }
54
55 if (function_exists("imagecreate"))
56 {
57     $ewiki_plugins["image_resize"][] = "ewiki_binary_resize_image_gd";
58 }
59
60 function ewiki_binary_resize_image_gd(&$filename, &$mime, $return=0)
61 {
62     return resizeImage($filename, $mime, $return);
63 }
64
65 function getTypeFromMIME($mime)
66 {
67     #-- read orig image
68     strtok($mime, "/");
69     $type = strtok("/");
70     return($type);
71 }
72
73 function getXY($orig_image,&$orig_x,&$orig_y)
74 {
75     $orig_x = imagesx($orig_image);
76     $orig_y = imagesy($orig_image);
77 }
78
79 function isResizeNeeded($a_width, $a_fileName)
80 {
81     clearstatcache();
82     
83     if ($a_width > EWIKI_IMAGE_MAX_X)
84     {
85         return true;
86     }
87     
88     if (filesize($a_fileName) > EWIKI_IMAGE_MAXSIZE)
89     {
90         return true;
91     }
92     
93     return false;
94 }
95
96 function isImageTolerable($a_fileName)
97 {
98     clearstatcache();
99     
100     if (filesize($a_fileName) > EWIKI_IMAGE_MAXSIZE)
101     {
102         return false;
103     }
104     
105     if (filesize($a_fileName) < EWIKI_IMAGE_TOLERANCE)
106     {
107         return false;
108     }
109     
110     return true;
111 }
112
113 function getImageStream($filename,$type)
114 {
115     $retval=NULL;
116
117     if ((($type != "gif") && ($type !="jpeg") && ($type !="png") && ($type != "vnd.wap.wbmp")))
118     {
119         return($retval);
120     }
121     
122     if (!function_exists($pf = "imagecreatefrom$type"))
123     {
124         return($retval);
125     }
126     
127     $retval = $pf($filename);
128     
129     return($retval);    
130 }
131
132 function getInitialResize($orig_image,&$new_x,&$new_y)
133 {
134     getXY($orig_image,$orig_x,$orig_y);
135     
136     if ($orig_x <= EWIKI_IMAGE_MAX_X)
137     //keep original dimesions
138     {
139         $new_x=$orig_x;
140         $new_y=$orig_y;
141     }
142     else
143     //wider than max width, so resize
144     {
145         $new_x=EWIKI_IMAGE_MAX_X;
146         $new_y=(int)((EWIKI_IMAGE_MAX_X*$orig_y)/$orig_x);
147     }
148 }
149
150 function doResize($orig_image,$new_x,$new_y,&$type)
151 {
152     $tc = function_exists("imageistruecolor") && imageistruecolor($orig_image);
153     if (!$tc || ($type == "gif"))
154     {
155         $new_image = imagecreate($new_x, $new_y);
156         $white = imagecolorallocate($new_image,255,255,255);
157         imagefill($new_image, 0, 0, $white);
158         imagepalettecopy($new_image, $orig_image);
159     }
160     else
161     {
162         $new_image = imagecreatetruecolor($new_x, $new_y);
163         $white = imagecolorallocate($new_image,255,255,255);
164         imagefill($new_image, 0, 0, $white);
165     }
166
167     getXY($orig_image,$orig_x,$orig_y);
168     
169     #-- resize action
170     imagecopyresampled($new_image, $orig_image, 0,0, 0,0, $new_x,$new_y, $orig_x,$orig_y);
171     
172     $type = "jpeg";
173     
174     return($new_image);
175 }
176
177 function doSave(&$image,$filename,$type)
178 {
179     if (function_exists($pf = "image$type"))
180     {
181         $pf($image,$filename,70);
182     }
183     else
184     {
185         return(false);   # cannot save in orig format
186     }
187 }
188
189 function isCandyCane($filename)
190 {
191     clearstatcache();
192     
193     list($width, $height, $type, $attr) = getimagesize($filename);
194     $ratio = ($height/$width);
195     $fs = filesize($filename);
196     
197     if (($ratio > EWIKI_IMAGE_RATIO) && ($fs > EWIKI_IMAGE_MAXSIZE) && ($height > EWIKI_IMAGE_MAX_Y))
198     {
199         ewiki_log("$filename is a candy cane",3);
200         return true;
201     }
202     return false;
203 }
204
205 function isMemoryFriendly($filename)
206 {
207     list($width, $height, $type, $attr) = getimagesize($filename);
208     $pixels = (int)($height*$width);
209     
210     if ($pixels > EWIKI_IMAGE_MAX_PIXELS)
211     {
212         ewiki_log("$filename at $pixels pixels is too big!",3);
213         return false;
214     }
215     
216     return true;
217 }
218
219 function resizeImage(&$filename, &$mime, $return=0)
220 {
221     //start timing
222     $time_start = getmicrotime();
223     
224     /*** this disallows Win32 ***/
225     if ((DIRECTORY_SEPARATOR!="/") && !EWIKI_IMAGERESIZE_WIN || (strpos($mime, "image/")!==0))
226     {
227         return(false);
228     }
229     
230     if (!isMemoryFriendly($filename))
231     {
232         return false;
233     }
234
235     if (isCandyCane($filename))
236     {
237         return false;
238     }
239         
240     $rescaled_filename = $filename;
241
242     $type = getTypeFromMIME($mime);
243
244     $orig_image = getImageStream($rescaled_filename,$type);
245     if (!isset($orig_image))
246     {
247         return(false);
248     }
249     
250     getXY($orig_image,$orig_x,$orig_y);
251     
252     if (!isResizeNeeded($orig_x, $filename))
253         return true;
254     
255     getInitialResize($orig_image,$new_x,$new_y);
256     
257     $orig_image = doResize($orig_image,$new_x,$new_y,$type);
258     
259     $rescaled_filename = tempnam(EWIKI_TMP, "ewiki.img_resize_gd.tmp.");
260     doSave($orig_image,$rescaled_filename,$type);
261     
262     if (isResizeNeeded($new_x, $rescaled_filename))
263     //will only take cases that need to be resized
264     {
265         ewiki_log("Resize beyond initial resize is needed.  Carrying through.",3);
266         
267         //set starting points for binary search
268         $x_max=EWIKI_IMAGE_MAX_X-1;
269         $x_min=EWIKI_IMAGE_MIN_X;
270        
271         //set failsafe break to max number of iterations through the loop
272         $failsafe = (int)(log($orig_x)+1);
273         
274         while (($x_min <= $x_max) && !isImageTolerable($rescaled_filename))
275         //the resize while loop
276         {
277             ewiki_log("While loop initiated",3);
278             
279             //somehow made it to an infinite loop, so get out
280             if($failsafe < 0) return(false);
281             
282             //take a guess at the correct width
283             $x_guess=(int)(($x_max+$x_min)/2);
284             
285             if ($filename == $rescaled_filename)
286             {
287                 $rescaled_filename = tempnam(EWIKI_TMP, "ewiki.img_resize_gd.tmp.");
288             }
289             
290             #-- sizes
291             $new_x = (int)($x_guess);
292             $new_y = (int)(($x_guess*$orig_y)/$orig_x);
293             
294             $new_image = doResize($orig_image,$new_x,$new_y,$type);
295             
296             doSave($new_image,$rescaled_filename,$type);
297             
298             #-- prepare next run
299             imagedestroy($new_image);
300             clearstatcache();
301             
302             $failsafe--;
303             
304             $ftmp = filesize($rescaled_filename);
305             ewiki_log("xguess: $x_guess, xmin: $x_min, xmax: $x_max, filesize: $ftmp",3);
306             
307             if (filesize($rescaled_filename) < EWIKI_IMAGE_TOLERANCE)
308             {
309                 $x_min=$x_guess+1;
310             }
311             else if (filesize($rescaled_filename) > EWIKI_IMAGE_MAXSIZE)
312             {
313                 $x_max=$x_guess-1;
314             }
315         }
316         
317         ewiki_log("While loop ended",3);
318         
319     }
320     
321     #-- stop
322     imagedestroy($orig_image);
323     clearstatcache();
324     
325     #-- security check filesizes, abort
326     if (!filesize($filename) || !filesize($rescaled_filename) || (filesize($rescaled_filename) > EWIKI_IMAGE_MAXSIZE))
327     {
328         unlink($rescaled_filename);
329         return($false);
330     }
331     
332     #-- set $mime, as it may have changed (.gif)
333     $mime = strtok($mime, "/") . "/" . $type;
334     if (!strstr($filename, ".$type"))
335     {
336         unlink($filename);
337         $filename .= ".$type";
338     }
339     
340     #-- move tmp file to old name
341     copy($rescaled_filename, $filename);
342     unlink($rescaled_filename);
343     
344     //end timing
345     $time_end = getmicrotime();
346     $time = $time_end - $time_start;
347     ewiki_log("$time seconds to perform resizing", 3);
348     
349     return(true);
350 }
351 ?>