root/image-headlines/tags/2.6/image-headlines.php

Revision 2125, 47.8 kB (checked in by coldforged, 3 years ago)

Added functionality to manage the image cache and delete image files older than a configurable time.

Line 
1 <?php
2 /*
3 Plugin Name: Headline Images
4 Plugin URI: http://www.coldforged.org/image-headlines-plugin-for-wordpress-15/
5 Description: Replaces Post headlines with PNG images of text, from ALA's <a href="http://www.alistapart.com/articles/dynatext/">Dynamic Text Replacement</a>. Includes soft-shadows, improved configuration, and previews. Configure on the <a href="../wp-admin/admin.php?page=image-headlines.php">Headline Images Configuration</a> page.
6 Version: 2.6
7 Author: Brian "ColdForged" Dupuis
8 Author URI: http://www.coldforged.org
9 */
10
11 /*
12     Bundled font is the beautiful Warp 1 by Alex Gollner. Take a
13     gander at some of his other fonts at
14
15     http://www.project.com/alex/fonts/index.html
16 */   
17
18 /*
19     Plugin originally by Joel �Jaykul� Bennett, but heavily modified
20     to the point seen here.
21
22     Dynamic Heading Generator
23     By Stewart Rosenberger
24     http://www.stewartspeak.com/headings/
25     http://www.alistapart.com/articles/dynatext/
26
27     This script generates PNG images of text, written in
28     the font/size that you specify. These PNG images are passed
29     back to the browser. Optionally, they can be cached for later use.
30     If a cached image is found, a new image will not be generated,
31     and the existing copy will be sent to the browser.
32
33     Additional documentation on PHP's image handling capabilities can
34     be found at http://www.php.net/image/
35 */
36
37 define( "IMAGEHEADLINES_VERSION", "2.5" );
38
39 if (! isset($wp_version))
40 {
41     require_once (dirname(dirname(dirname(__FILE__))) . "/wp-config.php");
42     global $wp_version;
43 }
44
45 if (substr($wp_version, 0, 3) == "1.1" || substr($wp_version, 0, 3) == "1.0" || substr($wp_version, 0, 3) == "1.2" )
46 {
47     echo "The Image Headlines Plugin requires at least WordPress version 1.3";
48     return;
49 }
50
51 define( "FONT_DIRECTORY", dirname(dirname(__FILE__))."/image-headlines" );
52
53 if (function_exists('load_plugin_textdomain'))
54 {
55     load_plugin_textdomain('headlinedomain');
56 }
57
58 if (! function_exists('ImageHeadline_update_option'))
59 {
60     function ImageHeadline_update_option($option, $new_settings)
61     {
62         update_option($option, $new_settings);
63     }
64     function ImageHeadline_get_settings($option)
65     {
66         $settings = get_settings($option);
67
68         // HACK for problems with some WordPress 1.3 installations.
69         if( is_string($settings) )
70         {
71             $unserialized = @ unserialize(stripslashes($settings));
72             if( $unserialized !== FALSE )
73                 $settings = $unserialized;
74         }
75         return $settings;
76     }
77 }
78
79 if (! function_exists('ImageHeadline_option_set'))
80 {
81     function ImageHeadline_option_set($option)
82     {
83         if (! $options = ImageHeadline_get_settings('ImageHeadline_options'))
84             return false;
85         else
86             return (in_array($option, $options));
87     }
88
89 }
90
91 if (! function_exists('ImageHeadline_add_options_page'))
92 {
93     function ImageHeadline_add_options_page()
94     {
95         if (function_exists('add_options_page'))
96             add_options_page(__("Headline Options Page"), __('Headlines'), 7, basename(__FILE__));
97     }
98
99 }
100
101 if (! function_exists('ImageHeadline_option_set'))
102 {
103     function ImageHeadline_option_set($option)
104     {
105         if (! $options = ImageHeadline_get_settings('ImageHeadline_options'))
106             return false;
107         else
108             return (in_array($option, $options));
109     }
110
111 }
112
113 if (!function_exists('ImageHeadline_check_flag'))
114 {
115     function ImageHeadline_check_flag($flagname, $allflags)
116     {
117         echo (in_array($flagname, $allflags) ? 'checked="checked"' : '');
118     }
119 }
120
121 if (!function_exists('ImageHeadline_check_select'))
122 {
123     function ImageHeadline_check_select($flagname, $allflags, $value)
124     {
125         echo ($allflags[$flagname] == $value ? ' selected="selected"' : '');
126     }
127 }
128
129
130 if (!function_exists('ImageHeadline_check_radio'))
131 {
132     function ImageHeadline_check_radio($flagname, $allflags, $value)
133     {
134         echo ($allflags[$flagname] == $value ? 'checked="checked"' : '');
135     }
136 }
137
138 if( !function_exists('ImageHeadline_gd_version' ) )
139 {
140     function ImageHeadline_gd_version() {
141        static $gd_version_number = null;
142        if ($gd_version_number === null) {
143            // Use output buffering to get results from phpinfo()
144            // without disturbing the page we're in.  Output
145            // buffering is "stackable" so we don't even have to
146            // worry about previous or encompassing buffering.
147            ob_start();
148            phpinfo(8);
149            $module_info = ob_get_contents();
150            ob_end_clean();
151            if (preg_match("/\bgd\s+version\b[^\d\n\r]+?([\d\.]+)/i",
152                    $module_info,$matches)) {
153                $gd_version_number = $matches[1];
154            } else {
155                $gd_version_number = 0;
156            }
157        }
158        return $gd_version_number;
159     }
160 }
161
162 if( !function_exists('ImageHeadline_readint'))
163 {
164     function ImageHeadline_readint( $file, $offset, $size )
165     {
166         @fseek( $file, $offset, SEEK_SET );
167         $string = @fread( $file, $size );
168         $myarray = @unpack( (( $size == 2 ) ? "n" : "N")."*", $string );
169         return intval($myarray[1]);
170     }
171 }
172
173 if( !function_exists('ImageHeadline_get_ttf_font_name'))
174 {
175     function ImageHeadline_get_ttf_font_name( $fullpath )
176     {
177         $return_string = '';
178         
179         $thefile = @fopen( $fullpath, "rb" );
180         if( $thefile )
181         {
182             // Read the number of records.
183             $num_tables = ImageHeadline_readint( $thefile, 4, 2 );
184
185             // Loop through looking for the name record.
186             $offset = 12;
187             $name_offset = 0;
188             $name_length = 0;
189             for( $x = 0; ( $x < $num_tables ) && !feof( $thefile ) && ( $name_offset == 0 ); $x++ )
190             {
191                 @fseek( $thefile, $offset, SEEK_SET );
192                 $tag = @fread( $thefile, 4 );
193
194                 if( !strcmp( $tag, 'name' ) )
195                 {
196                     // Found the 'name' tag so read the offset into the file of
197                     // the name table.
198                     $offset += 8;
199                     $name_offset = ImageHeadline_readint( $thefile, $offset, 4 );
200                     $name_length = ImageHeadline_readint( $thefile, $offset+4, 4 );
201                 } else {
202                     $offset += 16;
203                 }   
204             }
205
206             // See if we have an offset to the name table.
207             if( $name_offset != 0 )
208             {
209                 // Yay, likely this is a valid TTF file. That's nice. See how many name entries
210                 // we have.
211                 $num_names = ImageHeadline_readint( $thefile, $name_offset+2, 2 );
212                 $string_storage_offset = ImageHeadline_readint( $thefile, $name_offset+4, 2 );
213                 $name_id_offset = $name_offset + 12;
214                 
215                 // Let's find the name record that we desire. We're looking for a name ID
216                 // of 4.
217                 $name_string_offset = 0;
218                 $good_count = 0;
219                 $preferred = 0;
220                 for( $x = 0; ( $x < $num_names ) && !feof( $thefile ) /*&& ( $name_string_offset == 0 )*/; $x++ )
221                 {
222                     $name_id = ImageHeadline_readint( $thefile, $name_id_offset, 2 );
223                     if( $name_id == 4 )
224                     {
225                         $good_names[$good_count]["platform_id"] = ImageHeadline_readint( $thefile, $name_id_offset-6, 2 );
226                         $good_names[$good_count]["encoding_id"] = ImageHeadline_readint( $thefile, $name_id_offset-4, 2 );
227                         $good_names[$good_count]["language_id"] = ImageHeadline_readint( $thefile, $name_id_offset-2, 2 );
228
229                         // Odd I know... we're searching for a Windows platform string with these
230                         // precise parameters. It's the most common among fonts that I've seen. Not that this is a
231                         // Unicode string so we'll have to deal with that rather naively below. The problem with
232                         // the other formats is that many shareware/freeware fonts -- which a lot of people
233                         // will probably use -- is that they're inconsistent with their string settings (like the
234                         // bundled font... one of the strings is left at "Arial".
235                         if( ( $good_names[$good_count]["platform_id"] == 3 ) &&
236                             ( $good_names[$good_count]["encoding_id"] == 1 ) &&
237                             ( $good_names[$good_count]["language_id"] == 1033 ) ) {
238                             $preferred = $good_count;
239                         }                           
240                             
241                         $good_names[$good_count]["string_length"] = ImageHeadline_readint( $thefile, $name_id_offset+2, 2 );
242                         $good_names[$good_count++]["string_offset"] = ImageHeadline_readint( $thefile, $name_id_offset+4, 2 );
243                     }                       
244                     $name_id_offset += 12;
245                 }
246
247                 // Did we find one?
248                 if( $good_count )
249                 {
250                     // This getting old yet? What a goofy file format, and PHP is far from the most
251                     // efficient binary file parsers available. Anyway, we apparently have our string.
252                     // Let's read out the damned thing and have done with it.
253                     @fseek( $thefile, $name_offset + $string_storage_offset + $good_names[$preferred]["string_offset"], SEEK_SET );
254                     $return_string = @fread( $thefile, $good_names[$preferred]["string_length"] );
255                     for( $x = 0; $x < 32; $x++ )
256                         $unicode_chars[] = chr($x);
257                     $return_string = str_replace( $unicode_chars, "", $return_string );
258                 }
259             }
260             fclose( $thefile );
261         }
262
263         return $return_string;   
264     }
265 }
266
267 if( !function_exists( 'ImageHeadline_clear_cache' ) )
268 {
269     function ImageHeadline_clear_cache( $dirname, $lifetime )
270     {
271         if( ( $mydir = @opendir( $dirname ) ) !== false )
272         {
273             while( ( $file = @readdir( $mydir ) ) !== false )
274             {
275                if( preg_match("/[a-f0-9]{32}\.png/i", $file) )
276                {
277                    $difftime = ( time() - filectime( "$dirname/$file" ) ) / 60 / 60 / 24;
278                    if( $difftime > $lifetime )
279                    {
280                        @unlink( "$dirname/$file" );
281                    }
282                }
283             }
284         }
285     }
286 }
287
288 if( !function_exists( 'ImageHeadline_maybe_clear_cache' ) )
289 {
290     function ImageHeadline_maybe_clear_cache( $dirname, $lifetime )
291     {
292         $clearit = false;
293
294         // Determine if the cache needs clearing. We do this every 12 hours.
295         if( file_exists( "$dirname/cache.info" ) )
296         {
297             $difftime = time() - filectime( "$dirname/cache.info" );
298             if( $difftime > ( 12 * 60 * 60 ) )
299             {
300                 $clearit = true;
301                 @touch( "$dirname/cache.info" );
302             }
303         }
304         else
305         {
306             if( ( $file = @fopen( "$dirname/cache.info", "wb" ) ) !== false )
307             {
308                 @fwrite( $file, "Hi there." );
309                 @fclose( $file );
310             }
311             $clearit = true;
312         }
313
314         if( $clearit )
315         {
316             ImageHeadline_clear_cache($dirname, $lifetime);
317         }
318     }
319 }
320 if( !function_exists( 'ImageHeadline_traverse_directory' ) )
321 {
322     function ImageHeadline_traverse_directory($dirname, &$return_array)
323     {
324         $current_num = count($return_array);
325
326         // Get a list of files in the directory.
327         if( ($mydir = @opendir( $dirname )) !== false )
328         {
329             while(($file = readdir($mydir))!== false )
330             {
331                 if ($file != "." && $file != "..")
332                 {
333                     $font_name = ImageHeadline_get_ttf_font_name( $dirname."/".$file );
334                     if( $font_name != '' )
335                     {
336                         $return_array[$current_num]["font_name"] = $font_name;
337                         $return_array[$current_num++]["font_file"] = $dirname."/".$file;
338                     }
339                 }
340             }
341
342             closedir($mydir);
343         }
344         return $current_num;
345     }
346 }
347
348 if( !function_exists( 'ImageHeadline_get_fonts' ) )
349 {
350     /* Returns an associative array of font file names and the corresponding font name */
351     function ImageHeadline_get_fonts()
352     {
353         $num_fonts = 0;
354         $return_array = Array();
355
356         // Get a list of files in the directory. We'll also check the upload directory
357         // since that's an easy place for WordPress administrators to put new files.
358         ImageHeadline_traverse_directory( FONT_DIRECTORY, $return_array );
359         ImageHeadline_traverse_directory( get_settings('fileupload_realpath'), $return_array );
360
361         // For each supposed font file parse out a font name string.
362         return $return_array;
363     }
364 }
365
366 if( !function_exists( 'ImageHeadline_gaussian' ) ) {
367     function ImageHeadline_gaussian( &$image, $width, $height, $spread )
368     {
369         $use_GD1 = ImageHeadline_option_set( 'only_use_imagecreate' );
370         if( $use_GD1 )
371             return;
372
373         // Check for silly spreads
374         if( $spread == 0 )
375             return;
376
377         if( $spread > 10 )
378             $spread = 10;
379
380         if( strlen( $memory_limit = trim(ini_get('memory_limit' )) ) > 0 )
381         {
382             $last = strtolower($memory_limit{strlen($memory_limit)-1});
383             switch($last) {
384                 // The 'G' modifier is available since PHP 5.1.0
385                 case 'g':
386                     $memory_limit *= 1024;
387                 case 'm':
388                     $memory_limit *= 1024;
389                 case 'k':
390                     $memory_limit *= 1024;
391             }
392
393             if( $memory_limit <= 8 * 1024 * 1024 )
394             {
395                 $use_low_memory_method = true;
396             }
397             
398         } else {
399             $use_low_memory_method = false;
400         }
401
402         // Perform gaussian blur convlution. First, prepare the convolution
403         // kernel and precalculated multiplication array. Algorithm
404         // adapted from the simply exceptional code by Mario Klingemann
405         // <http://incubator.quasimondo.com>. Kernel is essentially an
406         // approximation of a gaussian distribution by utilizing squares.
407         $kernelsize = $spread*2-1;
408         $kernel = array_fill( 0, $kernelsize, 0 );
409         $mult = array_fill( 0, $kernelsize, array_fill( 0, 256, 0 ) );
410         for( $i = 1; $i < $spread; $i++ )
411         {
412             $smi = $spread - $i;
413             $kernel[$smi-1]=$kernel[$spread+$i-1]=$smi*$smi;
414             for( $j = 0; $j < 256; $j++ )
415             {
416                 $mult[$smi-1][$j] = $mult[$spread+$i-1][$j] = $kernel[$smi-1]*$j;
417             }
418         }
419         $kernel[$spread-1]=$spread*$spread;
420         for( $j = 0; $j < 256; $j++ )
421         {
422             $mult[$spread-1][$j] = $kernel[$spread-1]*$j;
423         }
424
425         if( !$use_low_memory_method ) {
426
427             // Kernel and multiplication array calculated, let's get the image
428             // read out into a usable format.
429             $imagebytes = $width*$height;
430             $i = 0;
431             for( $x = 0; $x < $width; $x++ )
432             {
433                 for( $y = 0; $y < $height; $y++ )
434                 {
435                     $rgb = imagecolorat( $image, $x, $y );
436                     $imagearray[$i++] = $rgb;
437                 }               
438             }
439         }
440             
441         // Everything's set. Let's run the first pass. Our first pass will be a
442         // vertical pass.
443         for( $x = 0; $x < $width; $x++ )
444         {
445             for( $y = 0; $y < $height; $y++ )
446             {
447                 $sum = 0;
448                 $cr = $cg = $cb = 0;
449                 for( $j = 0; $j < $kernelsize; $j++ )
450                 {
451                     $kernely = $y + ( $j - ( $spread - 1 ) );
452                     if( ( $kernely >= 0 ) && ( $kernely < $height ) )
453                     {
454                         if( !$use_low_memory_method ) {
455                             $ci = ( $x * $height ) + $kernely;
456                             $rgb = $imagearray[$ci];
457                         } else {
458                             $rgb = imagecolorat( $image, $x, $kernely );
459                         }
460                         $cr += $mult[$j][($rgb >> 16 ) & 0xFF];
461                         $cg += $mult[$j][($rgb >> 8 ) & 0xFF];
462                         $cb += $mult[$j][$rgb & 0xFF];
463                         $sum += $kernel[$j];
464                     }
465                 }
466                 $ci = ( $x * $height ) + $y;
467                 $shadowarray[$ci] = ( ( intval(round($cr/$sum)) & 0xff ) << 16 ) | ( ( intval(round($cg/$sum)) & 0xff ) << 8 ) | ( intval(round($cb/$sum)) & 0xff );
468             }
469         }           
470
471         // Free up some memory
472         if( isset( $imagearray ) ) {
473             unset( $imagearray );
474         }
475
476         // Now let's make with the horizontal passing. That sentence
477         // contruct never gets old: "make with the". Oh the humor.
478         for( $x = 0; $x < $width; $x++ )
479         {
480             for( $y = 0; $y < $height; $y++ )
481             {
482                 $sum = 0;
483                 $cr = $cg = $cb = 0;
484                 for( $j = 0; $j < $kernelsize; $j++ )
485                 {
486                     $kernelx = $x + ( $j - ( $spread - 1 ) );
487                     if( ( $kernelx >= 0 ) && ( $kernelx < $width ) )
488                     {
489                         $ci = ( $kernelx * $height ) + $y;
490                         $cr += $mult[$j][($shadowarray[$ci] >> 16 ) & 0xFF];
491                         $cg += $mult[$j][($shadowarray[$ci] >> 8 ) & 0xFF];
492                         $cb += $mult[$j][$shadowarray[$ci] &