1 <?php rcs_id('$Id: WikiPluginCached.php,v 1.12 2004-09-07 13:26:31 rurban Exp $');
3 Copyright (C) 2002 Johannes Große (Johannes Große)
5 This file is part of PhpWiki.
7 PhpWiki is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 PhpWiki is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with PhpWiki; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * You should set up the options in config/config.ini at Part seven:
24 * $ pear install http://pear.php.net/get/Cache
25 * This file belongs to WikiPluginCached.
26 * @author Johannes Große
30 require_once "lib/WikiPlugin.php";
31 // require_once "lib/plugincache-config.php"; // replaced by config.ini settings!
33 // Try the system pear class. See newCache()
34 @require_once('Cache.php');
36 define('PLUGIN_CACHED_HTML',0);
37 define('PLUGIN_CACHED_IMG_INLINE',1);
38 define('PLUGIN_CACHED_IMG_ONDEMAND',2);
39 define('PLUGIN_CACHED_MAP',3);
42 * An extension of the WikiPlugin class to allow image output and
44 * There are several abstract functions to be overloaded.
45 * Have a look at the example files
46 * <ul><li>plugin/TexToPng.php</li>
47 * <li>plugin/CacheTest.php (extremely simple example)</li>
48 * <li>plugin/RecentChanges.php</li>
49 * <li>plugin/VisualWiki.php</li></ul>
51 * @author Johannes Große
54 class WikiPluginCached extends WikiPlugin
57 * Produces URL and id number from plugin arguments which later on,
58 * will allow to find a cached image or to reconstruct the complete
59 * plugin call to recreate the image.
61 * @param cache object the cache object used to store the images
62 * @param argarray array all parameters (including those set to
63 * default values) of the plugin call to be
66 * @return array(id,url)
68 function genUrl($cache, $argarray) {
70 //$cacheparams = $GLOBALS['CacheParams'];
72 $plugincall = serialize( array(
73 'pluginname' => $this->getName(),
74 'arguments' => $argarray ) );
75 $id = $cache->generateId( $plugincall );
77 $url = DATA_PATH . '/getimg.php?';
78 if (($lastchar = substr($url,-1)) == '/') {
79 $url = substr($url, 0, -1);
81 if (strlen($plugincall) > PLUGIN_CACHED_MAXARGLEN) {
82 // we can't send the data as URL so we just send the id
83 if (!$request->getSessionVar('imagecache'.$id)) {
84 $request->setSessionVar('imagecache'.$id, $plugincall);
86 $plugincall = false; // not needed anymore
89 if ($lastchar == '?') {
90 // this indicates that a direct call of the image creation
91 // script is wished ($url is assumed to link to the script)
92 $url .= "id=$id" . ($plugincall ? '&args='.rawurlencode($plugincall) : '');
95 // We are supposed to use the indirect 404 ErrorDocument method
96 // ($url is assumed to be the url of the image in
97 // cache_dir and the image creation script is referred to in the
98 // ErrorDocument 404 directive.)
99 $url .= '/' . PLUGIN_CACHED_FILENAME_PREFIX . $id . '.img'
100 . ($plugincall ? '?args='.rawurlencode($plugincall) : '');
102 if ($request->getArg("start_debug"))
103 $url .= "&start_debug=1";
104 return array($id, $url);
108 * Replaces the abstract run method of WikiPlugin to implement
109 * a cache check which can avoid redundant runs.
110 * <b>Do not override this method in a subclass. Instead you may
111 * rename your run method to getHtml, getImage or getMap.
112 * Have a close look on the arguments and required return values,
116 * @param dbi WikiDB database abstraction class
117 * @param argstr string plugin arguments in the call from PhpWiki
118 * @param request Request ???
119 * @param string basepage Pagename to use to interpret links [/relative] page names.
120 * @return string HTML output to be printed to browser
126 function run($dbi, $argstr, &$request, $basepage) {
127 $cache = WikiPluginCached::newCache();
128 //$cacheparams = $GLOBALS['CacheParams'];
130 $sortedargs = $this->getArgs($argstr, $request);
131 if (is_array($sortedargs) )
133 $this->_args =& $sortedargs;
134 list($id,$url) = $this->genUrl($cache, $sortedargs);
136 // ---------- html and img gen. -----------------
137 if ($this->getPluginType() == PLUGIN_CACHED_IMG_ONDEMAND) {
138 return $this->embedImg($url, $dbi, $sortedargs, $request);
142 $content = $cache->get($id, 'imagecache');
143 switch($this->getPluginType()) {
144 case PLUGIN_CACHED_HTML:
145 if (!$content || !$content['html']) {
147 $content['html'] = $this->getHtml($dbi,$sortedargs,$request,$basepage);
148 if ($errortext = $this->getError()) {
149 WikiPluginCached::printError($errortext,'html');
155 case PLUGIN_CACHED_IMG_INLINE:
156 if (PLUGIN_CACHED_USECACHE && (!$content || !$content['image'])) {
157 $do_save = WikiPluginCached::produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
158 $content['html'] = $do_save?$this->embedImg($url, $dbi, $sortedargs, $request) : false;
161 case PLUGIN_CACHED_MAP:
162 if (!$content || !$content['image'] || !$content['html'] ) {
163 $do_save = WikiPluginCached::produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
164 $content['html'] = $do_save
165 ? WikiPluginCached::embedMap($id,$url,$content['html'],$dbi,$sortedargs,$request)
171 $expire = $this->getExpire($dbi,$sortedargs,$request);
172 $cache->save($id, $content, $expire,'imagecache');
174 if ($content['html'])
175 return $content['html'];
180 /* --------------------- virtual or abstract functions ----------- */
183 * Sets the type of the plugin to html, image or map
187 * @return int determines the plugin to produce either html,
188 * an image or an image map; uses on of the
189 * following predefined values
191 * <li>PLUGIN_CACHED_HTML</li>
192 * <li>PLUGIN_CACHED_IMG_INLINE</li>
193 * <li>PLUGIN_CACHED_IMG_ONDEMAND</li>
194 * <li>PLUGIN_CACHED_MAP</li>
197 function getPluginType() {
198 return PLUGIN_CACHED_IMG_ONDEMAND;
202 * Creates an image handle from the given user arguments.
203 * This method is only called if the return value of
204 * <code>getPluginType</code> is set to
205 * PLUGIN_CACHED_IMG_INLINE or PLUGIN_CACHED_IMG_ONDEMAND.
207 * @access protected pure virtual
208 * @param dbi WikiDB database abstraction class
209 * @param argarray array complete (!) arguments to produce
210 * image. It is not necessary to call
211 * WikiPlugin->getArgs anymore.
212 * @param request Request ???
213 * @return imagehandle image handle if successful
214 * false if an error occured
216 function getImage($dbi,$argarray,$request) {
217 trigger_error('WikiPluginCached::getImage: pure virtual function in file '
218 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
223 * Sets the life time of a cache entry in seconds.
224 * Expired entries are not used anymore.
225 * During a garbage collection each expired entry is
226 * removed. If removing all expired entries is not
227 * sufficient, the expire time is ignored and removing
228 * is determined by the last "touch" of the entry.
230 * @access protected virtual
231 * @param dbi WikiDB database abstraction class
232 * @param argarray array complete (!) arguments. It is
233 * not necessary to call
234 * WikiPlugin->getArgs anymore.
235 * @param request Request ???
236 * @return string format: '+seconds'
239 function getExpire($dbi,$argarray,$request) {
240 return '0'; // persist forever
244 * Decides the image type of an image output.
245 * Always used unless plugin type is PLUGIN_CACHED_HTML.
247 * @access protected virtual
248 * @param dbi WikiDB database abstraction class
249 * @param argarray array complete (!) arguments. It is
250 * not necessary to call
251 * WikiPlugin->getArgs anymore.
252 * @param request Request ???
253 * @return string 'png', 'jpeg' or 'gif'
255 function getImageType(&$dbi, $argarray, &$request) {
256 if (in_array($argarray['imgtype'], preg_split('/\s*:\s*/', PLUGIN_CACHED_IMGTYPES)))
257 return $argarray['imgtype'];
263 * Produces the alt text for an image.
264 * <code> <img src=... alt="getAlt(...)"> </code>
266 * @access protected virtual
267 * @param dbi WikiDB database abstraction class
268 * @param argarray array complete (!) arguments. It is
269 * not necessary to call
270 * WikiPlugin->getArgs anymore.
271 * @param request Request ???
272 * @return string "alt" description of the image
274 function getAlt($dbi,$argarray,$request) {
275 return '<?plugin '.$this->getName().' '.$this->glueArgs($argarray).'?>';
279 * Creates HTML output to be cached.
280 * This method is only called if the plugin_type is set to
281 * PLUGIN_CACHED_HTML.
283 * @access protected pure virtual
284 * @param dbi WikiDB database abstraction class
285 * @param argarray array complete (!) arguments to produce
286 * image. It is not necessary to call
287 * WikiPlugin->getArgs anymore.
288 * @param request Request ???
289 * @param string $basepage Pagename to use to interpret links [/relative] page names.
290 * @return string html to be printed in place of the plugin command
291 * false if an error occured
293 function getHtml($dbi, $argarray, $request, $basepage) {
294 trigger_error('WikiPluginCached::getHtml: pure virtual function in file '
295 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
299 * Creates HTML output to be cached.
300 * This method is only called if the plugin_type is set to
301 * PLUGIN_CACHED_HTML.
303 * @access protected pure virtual
304 * @param dbi WikiDB database abstraction class
305 * @param argarray array complete (!) arguments to produce
306 * image. It is not necessary to call
307 * WikiPlugin->getArgs anymore.
308 * @param request Request ???
309 * @return array(html,handle) html for the map interior (to be specific,
310 * only <area;> tags defining hot spots)
311 * handle is an imagehandle to the corresponding
313 * array(false,false) if an error occured
315 function getMap($dbi, $argarray, $request) {
316 trigger_error('WikiPluginCached::getHtml: pure virtual function in file '
317 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
320 /* --------------------- produce Html ----------------------------- */
323 * Creates an HTML map hyperlinked to the image specified
324 * by url and containing the hotspots given by map.
327 * @param id string unique id for the plugin call
328 * @param url string url pointing to the image part of the map
329 * @param map string <area> tags defining active
331 * @param dbi WikiDB database abstraction class
332 * @param argarray array complete (!) arguments to produce
333 * image. It is not necessary to call
334 * WikiPlugin->getArgs anymore.
335 * @param request Request ???
336 * @return string html output
338 function embedMap($id,$url,$map,&$dbi,$argarray,&$request) {
339 // id is not unique if the same map is produced twice
340 $key = substr($id,0,8).substr(microtime(),0,6);
341 return HTML(HTML::map(array( 'name' => $key ), $map ),
344 // 'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request))
345 'usemap' => '#'.$key ))
350 * Creates an HTML <img> tag hyperlinking to the specified
351 * url and produces an alternative text for non-graphical
355 * @param url string url pointing to the image part of the map
356 * @param map string <area> tags defining active
358 * @param dbi WikiDB database abstraction class
359 * @param argarray array complete (!) arguments to produce
360 * image. It is not necessary to call
361 * WikiPlugin->getArgs anymore.
362 * @param request Request ???
363 * @return string html output
365 function embedImg($url,$dbi,$argarray,$request) {
366 return HTML::img( array(
368 'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request)) ) );
372 // --------------------------------------------------------------------------
373 // ---------------------- static member functions ---------------------------
374 // --------------------------------------------------------------------------
377 * Creates one static PEAR Cache object and returns copies afterwards.
378 * FIXME: There should be references returned
380 * @access static protected
381 * @return Cache copy of the cache object
383 function newCache() {
386 if (!is_object($staticcache)) {
387 if (!class_exists('Cache')) {
388 // uuh, pear not in include_path! should print a warning.
389 // search some possible pear paths.
390 $pearFinder = new PearFileFinder;
391 if ($lib = $pearFinder->findFile('Cache.php', 'missing_ok'))
393 else // fall back to our own copy
394 require_once('lib/pear/Cache.php');
396 $cacheparams = array();
397 foreach (explode(':','database:cache_dir:filename_prefix:highwater:lowwater'
398 .':maxlifetime:maxarglen:usecache:force_syncmap') as $key) {
399 $cacheparams[$key] = constant('PLUGIN_CACHED_'.strtoupper($key));
401 $cacheparams['imgtypes'] = preg_split('/\s*:\s*/', PLUGIN_CACHED_IMGTYPES);
402 $staticcache = new Cache(PLUGIN_CACHED_DATABASE, $cacheparams);
403 $staticcache->gc_maxlifetime = PLUGIN_CACHED_MAXLIFETIME;
405 if (! PLUGIN_CACHED_USECACHE ) {
406 $staticcache->setCaching(false);
409 return $staticcache; // FIXME: use references ?
413 * Determines whether a needed image type may is available
414 * from the GD library and gives an alternative otherwise.
416 * @access public static
417 * @param wish string one of 'png', 'gif', 'jpeg', 'jpg'
418 * @return string the image type to be used ('png', 'gif', 'jpeg')
419 * 'html' in case of an error
422 function decideImgType($wish) {
423 if ($wish=='html') return $wish;
424 if ($wish=='jpg') { $wish = 'jpeg'; }
426 $supportedtypes = array();
427 // Todo: swf, pdf, ...
434 /* // these do work but not with the ImageType bitmask
441 $presenttypes = ImageTypes();
442 foreach($imagetypes as $imgtype => $bitmask)
443 if ( $presenttypes && $bitmask )
444 array_push($supportedtypes, $imgtype);
446 if (in_array($wish, $supportedtypes))
448 elseif (!empty($supportedtypes))
449 return reset($supportedtypes);
457 * Writes an image into a file or to the browser.
458 * Note that there is no check if the image can
461 * @access public static
462 * @param imgtype string 'png', 'gif' or 'jpeg'
463 * @param imghandle string image handle containing the image
464 * @param imgfile string file name of the image to be produced
466 * @see decideImageType
468 function writeImage($imgtype, $imghandle, $imgfile=false) {
469 if ($imgtype != 'html') {
470 $func = "Image" . strtoupper($imgtype);
472 $func($imghandle,$imgfile);
481 * Sends HTTP Header for some predefined file types.
482 * There is no parameter check.
484 * @access public static
485 * @param doctype string 'gif', 'png', 'jpeg', 'html'
488 function writeHeader($doctype) {
489 static $IMAGEHEADER = array(
490 'gif' => 'Content-type: image/gif',
491 'png' => 'Content-type: image/png',
492 'jpeg' => 'Content-type: image/jpeg',
493 'xbm' => 'Content-type: image/xbm',
494 'xpm' => 'Content-type: image/xpm',
495 'gd' => 'Content-type: image/gd',
496 'gd2' => 'Content-type: image/gd2',
497 'wbmp' => 'Content-type: image/vnd.wap.wbmp', // wireless bitmaps for PDA's and such.
498 'html' => 'Content-type: text/html' );
500 Header($IMAGEHEADER[$doctype]);
505 * Converts argument array to a string of format option="value".
506 * This should only be used for displaying plugin options for
507 * the quoting of arguments is not safe, yet.
509 * @access public static
510 * @param argarray array contains all arguments to be converted
511 * @return string concated arguments
513 function glueArgs($argarray) {
514 if (!empty($argarray)) {
516 while (list($key,$value)=each($argarray)) {
517 $argstr .= $key. '=' . '"' . $value . '" ';
518 // FIXME FIXME: How are values quoted? Can a value contain " ?
520 return substr($argstr,0,strlen($argstr)-1);
525 // ---------------------- FetchImageFromCache ------------------------------
528 * Extracts the cache entry id from the url and the plugin call
529 * parameters if available.
531 * @access private static
532 * @param id string return value. Image is stored under this id.
533 * @param plugincall string return value. Only returned if present in url.
534 * Contains all parameters to reconstruct
536 * @param cache Cache PEAR Cache object
537 * @param request Request ???
538 * @param errorformat string format which should be used to
539 * output errors ('html', 'png', 'gif', 'jpeg')
540 * @return boolean false if an error occurs, true otherwise.
541 * Param id and param plugincall are
542 * also return values.
544 function checkCall1(&$id, &$plugincall,$cache,$request, $errorformat) {
545 $id=$request->getArg('id');
546 $plugincall=rawurldecode($request->getArg('args'));
550 // This should never happen, so do not gettextify.
551 $errortext = "Neither 'args' nor 'id' given. Cannot proceed without parameters.";
552 WikiPluginCached::printError($errorformat, $errortext);
555 $id = $cache->generateId( $plugincall );
563 * Extracts the parameters necessary to reconstruct the plugin
564 * call needed to produce the requested image.
566 * @access static private
567 * @param plugincall string reference to serialized array containing both
568 * name and parameters of the plugin call
569 * @param request Request ???
570 * @return boolean false if an error occurs, true otherwise.
573 function checkCall2(&$plugincall,$request) {
574 // if plugincall wasn't sent by URL, it must have been
575 // stored in a session var instead and we can retreive it from there
577 if (!$plugincall=$request->getSessionVar('imagecache'.$id)) {
578 // I think this is the only error which may occur
579 // without having written bad code. So gettextify it.
580 $errortext = sprintf(
581 gettext ("There is no image creation data available to id '%s'. Please reload referring page." ),
583 WikiPluginCached::printError($errorformat, $errortext);
587 $plugincall = unserialize($plugincall);
593 * Creates an image or image map depending on the plugin type.
594 * @access static private
595 * @param content array reference to created array which overwrite the keys
596 * 'image', 'imagetype' and possibly 'html'
597 * @param plugin WikiPluginCached plugin which is called to create image or map
598 * @param dbi WikiDB handle to database
599 * @param argarray array Contains all arguments needed by plugin
600 * @param request Request ????
601 * @param errorformat string outputs errors in 'png', 'gif', 'jpg' or 'html'
602 * @return boolean error status; true=ok; false=error
604 function produceImage(&$content, $plugin, $dbi, $argarray, $request, $errorformat) {
605 $plugin->resetError();
606 $content['html'] = $imagehandle = false;
607 if ($plugin->getPluginType() == PLUGIN_CACHED_MAP ) {
608 list($imagehandle,$content['html']) = $plugin->getMap($dbi, $argarray, $request);
610 $imagehandle = $plugin->getImage($dbi, $argarray, $request);
613 $content['imagetype']
614 = WikiPluginCached::decideImgType($plugin->getImageType($dbi, $argarray, $request));
615 $errortext = $plugin->getError();
617 if (!$imagehandle||$errortext) {
619 $errortext = "'<?plugin ".$plugin->getName(). ' '
620 . WikiPluginCached::glueArgs($argarray)." ?>' returned no image, "
621 . " although no error was reported.";
623 WikiPluginCached::printError($errorformat, $errortext);
627 // image handle -> image data
628 //$cacheparams = $GLOBALS['CacheParams'];
629 $tmpfile = $this->tempnam();
630 WikiPluginCached::writeImage($content['imagetype'], $imagehandle, $tmpfile);
631 ImageDestroy($imagehandle);
632 if (file_exists($tmpfile)) {
633 $fp = fopen($tmpfile,'rb');
634 $content['image'] = fread($fp,filesize($tmpfile));
637 if ($content['image'])
643 function tempnam($prefix = false) {
644 return tempnam(isWindows() ? str_replace('/', "\\", PLUGIN_CACHED_CACHE_DIR) : PLUGIN_CACHED_CACHE_DIR,
645 $prefix ? $prefix : PLUGIN_CACHED_FILENAME_PREFIX);
649 * Main function for obtaining images from cache or generating on-the-fly
650 * from parameters sent by url or session vars.
652 * @access static public
653 * @param dbi WikiDB handle to database
654 * @param request Request ???
655 * @param errorformat string outputs errors in 'png', 'gif', 'jpeg' or 'html'
657 function fetchImageFromCache($dbi,$request,$errorformat='png') {
658 $cache = WikiPluginCached::newCache();
659 $errorformat = WikiPluginCached::decideImgType($errorformat);
661 if (!WikiPluginCached::checkCall1($id,$plugincall,$cache,$request,$errorformat)) return false;
664 $content = $cache->get($id,'imagecache');
666 if ($content && $content['image']) {
667 WikiPluginCached::writeHeader($content['imagetype']);
668 print $content['image'];
672 // produce image, now. At first, we need plugincall parameters
673 if (!WikiPluginCached::checkCall2($plugincall,$request)) return false;
675 $pluginname = $plugincall['pluginname'];
676 $argarray = $plugincall['arguments'];
678 $loader = new WikiPluginLoader;
679 $plugin = $loader->getPlugin($pluginname);
681 // cache empty, but image maps have to be created _inline_
682 // so ask user to reload wiki page instead
683 //$cacheparams = $GLOBALS['CacheParams'];
684 if (($plugin->getPluginType() == PLUGIN_CACHED_MAP) && PLUGIN_CACHED_FORCE_SYNCMAP) {
685 $errortext = _("Image map expired. Reload wiki page to recreate its html part.");
686 WikiPluginCached::printError($errorformat, $errortext);
690 if (!WikiPluginCached::produceImage($content, $plugin, $dbi, $argarray,
691 $request, $errorformat) ) return false;
693 $expire = $plugin->getExpire($dbi,$argarray,$request);
695 if ($content['image']) {
696 $cache->save($id, $content, $expire,'imagecache');
697 WikiPluginCached::writeHeader($content['imagetype']);
698 print $content['image'];
702 $errortext = "Could not create image file from imagehandle.";
703 WikiPluginCached::printError($errorformat, $errortext);
705 } // FetchImageFromCache
707 // -------------------- error handling ----------------------------
710 * Resets buffer containing all error messages. This is allways
711 * done before invoking any abstract creation routines like
712 * <code>getImage</code>.
717 function resetError() {
718 $this->_errortext = '';
722 * Returns all accumulated error messages.
725 * @return string error messages printed with <code>complain</code>.
727 function getError() {
728 return $this->_errortext;
732 * Collects the error messages in a string for later output
733 * by WikiPluginCached. This should be used for any errors
734 * that occur during data (html,image,map) creation.
737 * @param addtext string errormessage to be printed (separate
738 * multiple lines with '\n')
741 function complain($addtext) {
742 $this->_errortext .= $addtext;
746 * Outputs the error as image if possible or as html text
747 * if wished or html header has already been sent.
749 * @access static protected
750 * @param imgtype string 'png', 'gif', 'jpeg' or 'html'
751 * @param errortext string guess what?
754 function printError($imgtype, $errortext) {
755 $imgtype = WikiPluginCached::decideImgType($imgtype);
757 $talkedallready = ob_get_contents() || headers_sent();
758 if (($imgtype=='html') || $talkedallready) {
759 trigger_error($errortext, E_USER_WARNING);
761 $red = array(255,0,0);
762 $grey = array(221,221,221);
763 $im = WikiPluginCached::text2img($errortext, 2, $red, $grey);
765 trigger_error($errortext, E_USER_WARNING);
768 WikiPluginCached::writeHeader($imgtype);
769 WikiPluginCached::writeImage($imgtype, $im);
776 * Basic text to image converter for error handling which allows
777 * multiple line output.
778 * It will only output the first 25 lines of 80 characters. Both
779 * values may be smaller if the chosen font is to big for there
780 * is further restriction to 600 pixel in width and 350 in height.
782 * @access static public
783 * @param txt string multi line text to be converted
784 * @param fontnr integer number (1-5) telling gd which internal font to use;
785 * I recommend font 2 for errors and 4 for help texts.
786 * @param textcol array text color as a list of the rgb components; array(red,green,blue)
787 * @param bgcol array background color; array(red,green,blue)
788 * @return string image handle for gd routines
790 function text2img($txt,$fontnr,$textcol,$bgcol) {
791 // basic (!) output for error handling
794 if ($fontnr<1 || $fontnr>5) {
797 if (!is_array($textcol) || !is_array($bgcol)) {
798 $textcol = array(0,0,0);
799 $bgcol = array(255,255,255);
801 foreach( array_merge($textcol,$bgcol) as $component) {
802 if ($component<0 || $component > 255) {
803 $textcol = array(0,0,0);
804 $bgcol = array(255,255,255);
809 // prepare Parameters
811 // set maximum values
818 if (function_exists('ImageFontWidth')) {
819 $charx = ImageFontWidth($fontnr);
820 $chary = ImageFontHeight($fontnr);
822 $charx = 10; $chary = 10;
825 $marginy = floor($chary/2);
827 $IMAGESIZE['cols'] = min($IMAGESIZE['cols'], floor(($IMAGESIZE['width'] - 2*$marginx )/$charx));
828 $IMAGESIZE['rows'] = min($IMAGESIZE['rows'], floor(($IMAGESIZE['height'] - 2*$marginy )/$chary));
835 $npos = strpos($txt, "\n");
838 $breaklen = min($IMAGESIZE['cols'],$len);
840 $breaklen = min($npos+1, $IMAGESIZE['cols']);
842 $lines[$y] = chop(substr($txt, 0, $breaklen));
843 $wx = max($wx,strlen($lines[$y++]));
844 $txt = substr($txt, $breaklen);
845 } while ($txt && ($y < $IMAGESIZE['rows']));
847 // recalculate image size
848 $IMAGESIZE['rows'] = $y;
849 $IMAGESIZE['cols'] = $wx;
851 $IMAGESIZE['width'] = $IMAGESIZE['cols'] * $charx + 2*$marginx;
852 $IMAGESIZE['height'] = $IMAGESIZE['rows'] * $chary + 2*$marginy;
854 // create blank image
855 $im = @ImageCreate($IMAGESIZE['width'],$IMAGESIZE['height']);
857 $col = ImageColorAllocate($im, $textcol[0], $textcol[1], $textcol[2]);
858 $bg = ImageColorAllocate($im, $bgcol[0], $bgcol[1], $bgcol[2]);
860 ImageFilledRectangle($im, 0, 0, $IMAGESIZE['width']-1, $IMAGESIZE['height']-1, $bg);
863 foreach($lines as $nr => $textstr) {
864 ImageString( $im, $fontnr, $marginx, $marginy+$nr*$chary,
870 function newFilterThroughCmd($input, $commandLine) {
871 $descriptorspec = array(
872 0 => array("pipe", "r"), // stdin is a pipe that the child will read from
873 1 => array("pipe", "w"), // stdout is a pipe that the child will write to
874 2 => array("pipe", "w"), // stdout is a pipe that the child will write to
877 $process = proc_open("$commandLine", $descriptorspec, $pipes);
878 if (is_resource($process)) {
879 // $pipes now looks like this:
880 // 0 => writeable handle connected to child stdin
881 // 1 => readable handle connected to child stdout
882 // 2 => readable handle connected to child stderr
883 fwrite($pipes[0], $input);
886 while(!feof($pipes[1])) {
887 $buf .= fgets($pipes[1], 1024);
891 while(!feof($pipes[2])) {
892 $stderr .= fgets($pipes[2], 1024);
895 // It is important that you close any pipes before calling
896 // proc_close in order to avoid a deadlock
897 $return_value = proc_close($process);
898 if (empty($buf)) printXML($this->error($stderr));
903 /* PHP versions < 4.3
904 * TODO: via temp file looks more promising
906 function OldFilterThroughCmd($input, $commandLine) {
907 $input = str_replace ("\\", "\\\\", $input);
908 $input = str_replace ("\"", "\\\"", $input);
909 $input = str_replace ("\$", "\\\$", $input);
910 $input = str_replace ("`", "\`", $input);
911 $input = str_replace ("'", "\'", $input);
912 //$input = str_replace (";", "\;", $input);
914 $pipe = popen("echo \"$input\"|$commandLine", 'r');
916 print "pipe failed.";
920 while (!feof($pipe)) {
921 $output .= fread($pipe, 1024);
927 // run "echo $source | $commandLine" and return result
928 function filterThroughCmd($source, $commandLine) {
929 if (check_php_version(4,3,0))
930 return $this->newFilterThroughCmd($source, $commandLine);
932 return $this->oldFilterThroughCmd($source, $commandLine);
935 } // WikiPluginCached
938 // $Log: not supported by cvs2svn $
939 // Revision 1.11 2004/09/06 09:12:46 rurban
940 // improve pear handling with silent fallback to ours
948 // c-hanging-comment-ender-p: nil
949 // indent-tabs-mode: nil