1 <?php rcs_id('$Id: WikiPluginCached.php,v 1.7 2004-02-17 12:17:34 rurban Exp $');
3 Copyright (C) 2002 Johannes Große (Johannes Große)
5 This file is (not yet) 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 plugincache-config.php
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";
32 // Try the installed pear class first. It might be newer. Not yet.
33 // @require_once('Cache.php');
34 // if (!class_exists('Cache'))
35 require_once('lib/pear/Cache.php'); // We have to create your own copy here.
37 define('PLUGIN_CACHED_HTML',0);
38 define('PLUGIN_CACHED_IMG_INLINE',1);
39 define('PLUGIN_CACHED_IMG_ONDEMAND',2);
40 define('PLUGIN_CACHED_MAP',3);
43 * An extension of the WikiPlugin class to allow image output and
45 * There are several abstract functions to be overloaded.
46 * Have a look at the example files
47 * <ul><li>plugin/TexToPng.php</li>
48 * <li>plugin/CacheTest.php (extremely simple example)</li>
49 * <li>plugin/RecentChanges.php</li>
50 * <li>plugin/VisualWiki.php</li></ul>
52 * @author Johannes Große
55 class WikiPluginCached extends WikiPlugin
58 * Produces URL and id number from plugin arguments which later on,
59 * will allow to find a cached image or to reconstruct the complete
60 * plugin call to recreate the image.
62 * @param cache object the cache object used to store the images
63 * @param argarray array all parameters (including those set to
64 * default values) of the plugin call to be
67 * @return array(id,url)
69 function genUrl($cache,$argarray) {
71 $cacheparams = $GLOBALS['CacheParams'];
73 $plugincall = serialize( array(
74 'pluginname' => $this->getName(),
75 'arguments' => $argarray ) );
76 $id = $cache->generateId( $plugincall );
78 $url = $cacheparams['cacheurl']; // FIXME: SERVER_URL ?
79 if (($lastchar = substr($url,-1)) == '/') {
80 $url = substr($url, 0, -1);
82 if (strlen($plugincall)>$cacheparams['maxarglen']) {
83 // we can't send the data as URL so we just send the id
85 if (!$request->getSessionVar('imagecache'.$id)) {
86 $request->setSessionVar('imagecache'.$id, $plugincall);
88 $plugincall = false; // not needed anymore
92 // this indicates that a direct call of the image creation
93 // script is wished ($url is assumed to link to the script)
94 $url .= "id=$id" . ($plugincall ? '&args='.rawurlencode($plugincall) : '');
96 // we are supposed to use the indirect 404 ErrorDocument method
97 // ($url is assumed to be the url of the image in
98 // cache_dir and the image creation script is referred to in the
99 // ErrorDocument 404 directive.)
100 $url .= '/' . $cacheparams['filename_prefix'] . $id . '.img'
101 . ($plugincall ? '?args='.rawurlencode($plugincall) : '');
103 if ($request->getArg("start_debug"))
104 $url .= "&start_debug=1";
105 return array($id, $url);
109 * Replaces the abstract run method of WikiPlugin to implement
110 * a cache check which can avoid redundant runs.
111 * <b>Do not override this method in a subclass. Instead you may
112 * rename your run method to getHtml, getImage or getMap.
113 * Have a close look on the arguments and required return values,
117 * @param dbi WikiDB database abstraction class
118 * @param argstr string plugin arguments in the call from PhpWiki
119 * @param request Request ???
120 * @param string basepage Pagename to use to interpret links [/relative] page names.
121 * @return string HTML output to be printed to browser
127 function run($dbi, $argstr, &$request, $basepage) {
128 $cache = WikiPluginCached::newCache();
129 $cacheparams = $GLOBALS['CacheParams'];
131 $sortedargs = $this->getArgs($argstr, $request);
132 if (is_array($sortedargs) )
135 list($id,$url) = $this->genUrl($cache, $sortedargs);
137 // ---------- html and img gen. -----------------
138 if ($this->getPluginType() == PLUGIN_CACHED_IMG_ONDEMAND) {
139 return $this->embedImg($url, $dbi, $sortedargs, $request);
143 $content = $cache->get($id, 'imagecache');
144 switch($this->getPluginType()) {
145 case PLUGIN_CACHED_HTML:
146 if (!$content || !$content['html']) {
148 $content['html'] = $this->getHtml($dbi,$sortedargs,$request,$basepage);
149 if ($errortext = $this->getError()) {
150 WikiPluginCached::printError($errortext,'html');
156 case PLUGIN_CACHED_IMG_INLINE:
157 if ($cacheparams['usecache']&&(!$content || !$content['image'])) {
158 $do_save = WikiPluginCached::produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
159 $content['html'] = $do_save?$this->embedImg($url, $dbi, $sortedargs, $request) : false;
162 case PLUGIN_CACHED_MAP:
163 if (!$content || !$content['image'] || !$content['html'] ) {
164 $do_save = WikiPluginCached::produceImage($content, $this, $dbi, $sortedargs, $request, 'html');
165 $content['html'] = $do_save?WikiPluginCached::embedMap($id,
166 $url,$content['html'],$dbi,$sortedargs,$request):false;
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;
204 * Creates an image handle from the given user arguments.
205 * This method is only called if the return value of
206 * <code>getPluginType</code> is set to
207 * PLUGIN_CACHED_IMG_INLINE or PLUGIN_CACHED_IMG_ONDEMAND.
209 * @access protected pure virtual
210 * @param dbi WikiDB database abstraction class
211 * @param argarray array complete (!) arguments to produce
212 * image. It is not necessary to call
213 * WikiPlugin->getArgs anymore.
214 * @param request Request ???
215 * @return imagehandle image handle if successful
216 * false if an error occured
218 function getImage($dbi,$argarray,$request) {
219 trigger_error('WikiPluginCached::getImage: pure virtual function in file '
220 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
225 * Sets the life time of a cache entry in seconds.
226 * Expired entries are not used anymore.
227 * During a garbage collection each expired entry is
228 * removed. If removing all expired entries is not
229 * sufficient, the expire time is ignored and removing
230 * is determined by the last "touch" of the entry.
232 * @access protected virtual
233 * @param dbi WikiDB database abstraction class
234 * @param argarray array complete (!) arguments. It is
235 * not necessary to call
236 * WikiPlugin->getArgs anymore.
237 * @param request Request ???
238 * @return string format: '+seconds'
241 function getExpire($dbi,$argarray,$request) {
242 return '0'; // persist forever
246 * Decides the image type of an image output.
247 * Always used unless plugin type is PLUGIN_CACHED_HTML.
249 * @access protected virtual
250 * @param dbi WikiDB database abstraction class
251 * @param argarray array complete (!) arguments. It is
252 * not necessary to call
253 * WikiPlugin->getArgs anymore.
254 * @param request Request ???
255 * @return string 'png', 'jpeg' or 'gif'
257 function getImageType($dbi,$argarray,$request) {
258 if (in_array($argarray['imgtype'],$GLOBAL['CacheParams']['imgtypes']))
259 return $argarray['imgtype'];
266 * Produces the alt text for an image.
267 * <code> <img src=... alt="getAlt(...)"> </code>
269 * @access protected virtual
270 * @param dbi WikiDB database abstraction class
271 * @param argarray array complete (!) arguments. It is
272 * not necessary to call
273 * WikiPlugin->getArgs anymore.
274 * @param request Request ???
275 * @return string "alt" description of the image
277 function getAlt($dbi,$argarray,$request) {
278 return '<?plugin '.$this->getName().' '.$this->glueArgs($argarray).'?>';
282 * Creates HTML output to be cached.
283 * This method is only called if the plugin_type is set to
284 * PLUGIN_CACHED_HTML.
286 * @access protected pure virtual
287 * @param dbi WikiDB database abstraction class
288 * @param argarray array complete (!) arguments to produce
289 * image. It is not necessary to call
290 * WikiPlugin->getArgs anymore.
291 * @param request Request ???
292 * @param string $basepage Pagename to use to interpret links [/relative] page names.
293 * @return string html to be printed in place of the plugin command
294 * false if an error occured
296 function getHtml($dbi, $argarray, $request, $basepage) {
297 trigger_error('WikiPluginCached::getHtml: pure virtual function in file '
298 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
303 * Creates HTML output to be cached.
304 * This method is only called if the plugin_type is set to
305 * PLUGIN_CACHED_HTML.
307 * @access protected pure virtual
308 * @param dbi WikiDB database abstraction class
309 * @param argarray array complete (!) arguments to produce
310 * image. It is not necessary to call
311 * WikiPlugin->getArgs anymore.
312 * @param request Request ???
313 * @return array(html,handle) html for the map interior (to be specific,
314 * only <area;> tags defining hot spots)
315 * handle is an imagehandle to the corresponding
317 * array(false,false) if an error occured
319 function getMap($dbi, $argarray, $request) {
320 trigger_error('WikiPluginCached::getHtml: pure virtual function in file '
321 . __FILE__ . ' line ' . __LINE__, E_USER_ERROR);
324 /* --------------------- produce Html ----------------------------- */
327 * Creates an HTML map hyperlinked to the image specified
328 * by url and containing the hotspots given by map.
331 * @param id string unique id for the plugin call
332 * @param url string url pointing to the image part of the map
333 * @param map string <area> tags defining active
335 * @param dbi WikiDB database abstraction class
336 * @param argarray array complete (!) arguments to produce
337 * image. It is not necessary to call
338 * WikiPlugin->getArgs anymore.
339 * @param request Request ???
340 * @return string html output
342 function embedMap($id,$url,$map,$dbi,$argarray,$request) {
343 // id is not unique if the same map is produced twice
344 $key = substr($id,0,8).substr(microtime(),0,6);
345 return HTML(HTML::map(array( 'name' => $key ), $map ),
348 // 'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request))
349 'usemap' => '#'.$key ))
354 * Creates an HTML <img> tag hyperlinking to the specified
355 * url and produces an alternative text for non-graphical
359 * @param url string url pointing to the image part of the map
360 * @param map string <area> tags defining active
362 * @param dbi WikiDB database abstraction class
363 * @param argarray array complete (!) arguments to produce
364 * image. It is not necessary to call
365 * WikiPlugin->getArgs anymore.
366 * @param request Request ???
367 * @return string html output
369 function embedImg($url,$dbi,$argarray,$request) {
370 return HTML::img( array(
372 'alt' => htmlspecialchars($this->getAlt($dbi,$argarray,$request)) ) );
376 // --------------------------------------------------------------------------
377 // ---------------------- static member functions ---------------------------
378 // --------------------------------------------------------------------------
381 * Creates one static PEAR Cache object and returns copies afterwards.
382 * FIXME: There should be references returned
384 * @access static protected
385 * @return Cache copy of the cache object
387 function newCache() {
390 $cacheparams = $GLOBALS['CacheParams'];
392 if (!is_object($staticcache)) {
393 /* $pearFinder = new PearFileFinder;
394 $pearFinder->includeOnce('Cache.php');*/
396 $staticcache = new Cache($cacheparams['database'],$cacheparams);
397 $staticcache->gc_maxlifetime = $cacheparams['maxlifetime'];
399 if (!$cacheparams['usecache']) {
400 $staticcache->setCaching(false);
403 return $staticcache; // FIXME: use references ?
407 * Determines whether a needed image type may is available
408 * from the GD library and gives an alternative otherwise.
410 * @access public static
411 * @param wish string one of 'png', 'gif', 'jpeg', 'jpg'
412 * @return string the image type to be used ('png', 'gif', 'jpeg')
413 * 'html' in case of an error
416 function decideImgType($wish) {
417 if ($wish=='html') return $wish;
418 if ($wish=='jpg') { $wish = 'jpeg'; }
420 $supportedtypes = array();
421 // Todo: swf, pdf, ...
428 /* // these do work but not with the ImageType bitmask
435 $presenttypes = ImageTypes();
436 foreach($imagetypes as $imgtype => $bitmask)
437 if ( $presenttypes && $bitmask )
438 array_push($supportedtypes, $imgtype);
440 if (in_array($wish, $supportedtypes))
442 elseif (!empty($supportedtypes))
443 return reset($supportedtypes);
451 * Writes an image into a file or to the browser.
452 * Note that there is no check if the image can
455 * @access public static
456 * @param imgtype string 'png', 'gif' or 'jpeg'
457 * @param imghandle string image handle containing the image
458 * @param imgfile string file name of the image to be produced
460 * @see decideImageType
462 function writeImage($imgtype, $imghandle, $imgfile=false) {
463 if ($imgtype != 'html') {
464 $func = "Image" . strtoupper($imgtype);
466 $func($imghandle,$imgfile);
475 * Sends HTTP Header for some predefined file types.
476 * There is no parameter check.
478 * @access public static
479 * @param doctype string 'gif', 'png', 'jpeg', 'html'
482 function writeHeader($doctype) {
483 $IMAGEHEADER = array(
484 'gif' => 'Content-type: image/gif',
485 'png' => 'Content-type: image/png',
486 'jpeg' => 'Content-type: image/jpeg',
487 'xbm' => 'Content-type: image/xbm',
488 'xpm' => 'Content-type: image/xpm',
489 'gd' => 'Content-type: image/gd',
490 'gd2' => 'Content-type: image/gd2',
491 'wbmp' => 'Content-type: image/vnd.wap.wbmp', // wireless bitmaps for PDA's and such.
492 'html' => 'Content-type: text/html' );
494 Header($IMAGEHEADER[$doctype]);
499 * Converts argument array to a string of format option="value".
500 * This should only be used for displaying plugin options for
501 * the quoting of arguments is not safe, yet.
503 * @access public static
504 * @param argarray array contains all arguments to be converted
505 * @return string concated arguments
507 function glueArgs($argarray) {
508 if (!empty($argarray)) {
510 while (list($key,$value)=each($argarray)) {
511 $argstr .= $key. '=' . '"' . $value . '" ';
512 // FIXME FIXME: How are values quoted? Can a value contain " ?
514 return substr($argstr,0,strlen($argstr)-1);
519 // ---------------------- FetchImageFromCache ------------------------------
522 * Extracts the cache entry id from the url and the plugin call
523 * parameters if available.
525 * @access private static
526 * @param id string return value. Image is stored under this id.
527 * @param plugincall string return value. Only returned if present in url.
528 * Contains all parameters to reconstruct
530 * @param cache Cache PEAR Cache object
531 * @param request Request ???
532 * @param errorformat string format which should be used to
533 * output errors ('html', 'png', 'gif', 'jpeg')
534 * @return boolean false if an error occurs, true otherwise.
535 * Param id and param plugincall are
536 * also return values.
538 function checkCall1(&$id, &$plugincall,$cache,$request, $errorformat) {
539 $id=$request->getArg('id');
540 $plugincall=rawurldecode($request->getArg('args'));
544 // This should never happen, so do not gettextify.
545 $errortext = "Neither 'args' nor 'id' given. Cannot proceed without parameters.";
546 WikiPluginCached::printError($errorformat, $errortext);
549 $id = $cache->generateId( $plugincall );
557 * Extracts the parameters necessary to reconstruct the plugin
558 * call needed to produce the requested image.
560 * @access static private
561 * @param plugincall string reference to serialized array containing both
562 * name and parameters of the plugin call
563 * @param request Request ???
564 * @return boolean false if an error occurs, true otherwise.
567 function checkCall2(&$plugincall,$request) {
568 // if plugincall wasn't sent by URL, it must have been
569 // stored in a session var instead and we can retreive it from there
571 if (!$plugincall=$request->getSessionVar('imagecache'.$id)) {
572 // I think this is the only error which may occur
573 // without having written bad code. So gettextify it.
574 $errortext = sprintf(
575 gettext ("There is no image creation data available to id '%s'. Please reload referring page." ),
577 WikiPluginCached::printError($errorformat, $errortext);
581 $plugincall = unserialize($plugincall);
587 * Creates an image or image map depending on the plugin type.
588 * @access static private
589 * @param content array reference to created array which overwrite the keys
590 * 'image', 'imagetype' and possibly 'html'
591 * @param plugin WikiPluginCached plugin which is called to create image or map
592 * @param dbi WikiDB handle to database
593 * @param argarray array Contains all arguments needed by plugin
594 * @param request Request ????
595 * @param errorformat string outputs errors in 'png', 'gif', 'jpg' or 'html'
596 * @return boolean error status; true=ok; false=error
598 function produceImage(&$content, $plugin, $dbi, $argarray, $request, $errorformat) {
599 $plugin->resetError();
600 $content['html'] = $imagehandle = false;
601 if ($plugin->getPluginType() == PLUGIN_CACHED_MAP ) {
602 list($imagehandle,$content['html']) = $plugin->getMap($dbi, $argarray, $request);
604 $imagehandle = $plugin->getImage($dbi, $argarray, $request);
607 $content['imagetype']
608 = WikiPluginCached::decideImgType($plugin->getImageType($dbi, $argarray, $request));
609 $errortext = $plugin->getError();
611 if (!$imagehandle||$errortext) {
613 $errortext = "'<?plugin ".$plugin->getName(). ' '
614 . WikiPluginCached::glueArgs($argarray)." ?>' returned no image, "
615 . " although no error was reported.";
617 WikiPluginCached::printError($errorformat, $errortext);
621 // image handle -> image data
622 $cacheparams = $GLOBALS['CacheParams'];
623 $tmpfile = tempnam($cacheparams['cache_dir'],$cacheparams['filename_prefix']);
624 WikiPluginCached::writeImage($content['imagetype'], $imagehandle, $tmpfile);
625 ImageDestroy($imagehandle);
626 if (file_exists($tmpfile)) {
627 $fp = fopen($tmpfile,'rb');
628 $content['image'] = fread($fp,filesize($tmpfile));
631 if ($content['image'])
639 * Main function for obtaining images from cache or generating on-the-fly
640 * from parameters sent by url or session vars.
642 * @access static public
643 * @param dbi WikiDB handle to database
644 * @param request Request ???
645 * @param errorformat string outputs errors in 'png', 'gif', 'jpeg' or 'html'
647 function fetchImageFromCache($dbi,$request,$errorformat='png') {
648 $cache = WikiPluginCached::newCache();
649 $errorformat = WikiPluginCached::decideImgType($errorformat);
651 if (!WikiPluginCached::checkCall1($id,$plugincall,$cache,$request,$errorformat)) return false;
654 $content = $cache->get($id,'imagecache');
656 if ($content && $content['image']) {
657 WikiPluginCached::writeHeader($content['imagetype']);
658 print $content['image'];
662 // produce image, now. At first, we need plugincall parameters
663 if (!WikiPluginCached::checkCall2($plugincall,$request)) return false;
665 $pluginname = $plugincall['pluginname'];
666 $argarray = $plugincall['arguments'];
668 $loader = new WikiPluginLoader;
669 $plugin = $loader->getPlugin($pluginname);
671 // cache empty, but image maps have to be created _inline_
672 // so ask user to reload wiki page instead
673 $cacheparams = $GLOBALS['CacheParams'];
674 if (($plugin->getPluginType() == PLUGIN_CACHED_MAP) && $cacheparams['force_syncmap']) {
675 $errortext = gettext('Image map expired. Reload wiki page to recreate its html part.');
676 WikiPluginCached::printError($errorformat, $errortext);
680 if (!WikiPluginCached::produceImage($content, $plugin, $dbi, $argarray, $request, $errorformat) ) return false;
682 $expire = $plugin->getExpire($dbi,$argarray,$request);
684 if ($content['image']) {
685 $cache->save($id, $content, $expire,'imagecache');
686 WikiPluginCached::writeHeader($content['imagetype']);
687 print $content['image'];
691 $errortext = "Could not create image file from imagehandle.";
692 WikiPluginCached::printError($errorformat, $errortext);
694 } // FetchImageFromCache
696 // -------------------- error handling ----------------------------
699 * Resets buffer containing all error messages. This is allways
700 * done before invoking any abstract creation routines like
701 * <code>getImage</code>.
706 function resetError() {
707 $this->_errortext = '';
711 * Returns all accumulated error messages.
714 * @return string error messages printed with <code>complain</code>.
716 function getError() {
717 return $this->_errortext;
721 * Collects the error messages in a string for later output
722 * by WikiPluginCached. This should be used for any errors
723 * that occur during data (html,image,map) creation.
726 * @param addtext string errormessage to be printed (separate
727 * multiple lines with '\n')
730 function complain($addtext) {
731 $this->_errortext .= $addtext;
735 * Outputs the error as image if possible or as html text
736 * if wished or html header has already been sent.
738 * @access static protected
739 * @param imgtype string 'png', 'gif', 'jpeg' or 'html'
740 * @param errortext string guess what?
743 function printError($imgtype, $errortext) {
744 $imgtype = WikiPluginCached::decideImgType($imgtype);
746 $talkedallready = ob_get_contents() || headers_sent();
747 if (($imgtype=='html') || $talkedallready) {
748 trigger_error($errortext, E_USER_WARNING);
750 $red = array(255,0,0);
751 $grey = array(221,221,221);
752 $im = WikiPluginCached::text2img($errortext, 2, $red, $grey);
754 trigger_error($errortext, E_USER_WARNING);
757 WikiPluginCached::writeHeader($imgtype);
758 WikiPluginCached::writeImage($imgtype, $im);
765 * Basic text to image converter for error handling which allows
766 * multiple line output.
767 * It will only output the first 25 lines of 80 characters. Both
768 * values may be smaller if the chosen font is to big for there
769 * is further restriction to 600 pixel in width and 350 in height.
771 * @access static public
772 * @param txt string multi line text to be converted
773 * @param fontnr integer number (1-5) telling gd which internal font to use;
774 * I recommend font 2 for errors and 4 for help texts.
775 * @param textcol array text color as a list of the rgb components; array(red,green,blue)
776 * @param bgcol array background color; array(red,green,blue)
777 * @return string image handle for gd routines
779 function text2img($txt,$fontnr,$textcol,$bgcol) {
780 // basic (!) output for error handling
783 if ($fontnr<1 || $fontnr>5) {
786 if (!is_array($textcol) || !is_array($bgcol)) {
787 $textcol = array(0,0,0);
788 $bgcol = array(255,255,255);
790 foreach( array_merge($textcol,$bgcol) as $component) {
791 if ($component<0 || $component > 255) {
792 $textcol = array(0,0,0);
793 $bgcol = array(255,255,255);
798 // prepare Parameters
800 // set maximum values
807 $charx = ImageFontWidth($fontnr);
808 $chary = ImageFontHeight($fontnr);
810 $marginy = floor($chary/2);
812 $IMAGESIZE['cols'] = min($IMAGESIZE['cols'], floor(($IMAGESIZE['width'] - 2*$marginx )/$charx));
813 $IMAGESIZE['rows'] = min($IMAGESIZE['rows'], floor(($IMAGESIZE['height'] - 2*$marginy )/$chary));
820 $npos = strpos($txt, "\n");
823 $breaklen = min($IMAGESIZE['cols'],$len);
825 $breaklen = min($npos+1, $IMAGESIZE['cols']);
827 $lines[$y] = chop(substr($txt, 0, $breaklen));
828 $wx = max($wx,strlen($lines[$y++]));
829 $txt = substr($txt, $breaklen);
830 } while ($txt && ($y < $IMAGESIZE['rows']));
832 // recalculate image size
833 $IMAGESIZE['rows'] = $y;
834 $IMAGESIZE['cols'] = $wx;
836 $IMAGESIZE['width'] = $IMAGESIZE['cols'] * $charx + 2*$marginx;
837 $IMAGESIZE['height'] = $IMAGESIZE['rows'] * $chary + 2*$marginy;
839 // create blank image
840 $im = @ImageCreate($IMAGESIZE['width'],$IMAGESIZE['height']);
842 $col = ImageColorAllocate($im, $textcol[0], $textcol[1], $textcol[2]);
843 $bg = ImageColorAllocate($im, $bgcol[0], $bgcol[1], $bgcol[2]);
845 ImageFilledRectangle($im, 0, 0, $IMAGESIZE['width']-1, $IMAGESIZE['height']-1, $bg);
848 foreach($lines as $nr => $textstr) {
849 ImageString( $im, $fontnr, $marginx, $marginy+$nr*$chary,
855 } // WikiPluginCached
863 // c-hanging-comment-ender-p: nil
864 // indent-tabs-mode: nil