2 rcs_id('$Id: GraphViz.php,v 1.5 2005-09-26 06:39:14 rurban Exp $');
4 Copyright 2004 $ThePhpWikiProgrammingTeam
6 This file is part of PhpWiki.
8 PhpWiki is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 PhpWiki is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * The GraphViz plugin passes all its arguments to the grapviz dot
25 * binary and displays the result as cached image (PNG,GIF,SVG) or imagemap.
27 * @Author: Reini Urban
30 * - We support only images supported by GD so far (PNG most likely).
31 * EPS, PS, SWF, SVG or SVGZ and imagemaps need to be tested.
34 <?plugin GraphViz [options...]
35 multiline dot script ...
38 * See also: VisualWiki, which depends on GraphViz and WikiPluginCached.
42 * - expand embedded <!plugin-list pagelist !> within the digraph script.
45 if (PHP_OS == "Darwin") { // Mac OS X
46 if (!defined("GRAPHVIZ_EXE"))
47 define('GRAPHVIZ_EXE', '/sw/bin/dot'); // graphviz via Fink
48 // Name of the Truetypefont - at least LucidaSansRegular.ttf is always present on OS X
49 if (!defined('VISUALWIKIFONT'))
50 define('VISUALWIKIFONT', 'LucidaSansRegular');
51 // The default font paths do not find your fonts, set the path here:
52 $fontpath = "/System/Library/Frameworks/JavaVM.framework/Versions/1.3.1/Home/lib/fonts/";
53 //$fontpath = "/usr/X11R6/lib/X11/fonts/TTF/";
55 elseif (isWindows()) {
56 if (!defined("GRAPHVIZ_EXE"))
57 define('GRAPHVIZ_EXE','dot.exe');
58 if (!defined('VISUALWIKIFONT'))
59 define('VISUALWIKIFONT', 'Arial');
60 } elseif ($_SERVER["SERVER_NAME"] == 'phpwiki.sourceforge.net') { // sf.net hack
61 if (!defined("GRAPHVIZ_EXE"))
62 define('GRAPHVIZ_EXE','/home/groups/p/ph/phpwiki/bin/dot');
63 if (!defined('VISUALWIKIFONT'))
64 define('VISUALWIKIFONT', 'luximr');
66 if (!defined("GRAPHVIZ_EXE"))
67 define('GRAPHVIZ_EXE','/usr/local/bin/dot');
68 // Name of the Truetypefont - Helvetica is probably easier to read
69 if (!defined('VISUALWIKIFONT'))
70 define('VISUALWIKIFONT', 'Helvetica');
71 //define('VISUALWIKIFONT', 'Times');
72 //define('VISUALWIKIFONT', 'Arial');
73 // The default font paths do not find your fonts, set the path here:
74 //$fontpath = "/usr/X11R6/lib/X11/fonts/TTF/";
75 //$fontpath = "/usr/share/fonts/default/TrueType/";
78 require_once "lib/WikiPluginCached.php";
80 class WikiPlugin_GraphViz
81 extends WikiPluginCached
84 function _mapTypes() {
85 return array("imap", "cmapx", "ismap", "cmap");
89 * Sets plugin type to MAP
90 * or HTML if the imagetype is not supported by GD (EPS, SVG, SVGZ) (not yet)
91 * or IMG_INLINE if device = png, gif or jpeg
93 function getPluginType() {
94 $type = $this->decideImgType($this->_args['imgtype']);
95 if ($type == $this->_args['imgtype'])
96 return PLUGIN_CACHED_IMG_INLINE;
97 $device = strtolower($this->_args['imgtype']);
98 if (in_array($device, $this->_mapTypes()))
99 return PLUGIN_CACHED_MAP;
100 if (in_array($device, array('svg','swf','svgz','eps','ps'))) {
101 switch ($this->_args['imgtype']) {
104 return PLUGIN_CACHED_STATIC | PLUGIN_CACHED_SVG_PNG;
106 return PLUGIN_CACHED_STATIC | PLUGIN_CACHED_SWF;
108 return PLUGIN_CACHED_STATIC | PLUGIN_CACHED_HTML;
112 return PLUGIN_CACHED_IMG_INLINE; // normal cached libgd image handles
114 function getName () {
115 return _("GraphViz");
117 function getDescription () {
118 return _("GraphViz image or imagemap creation of directed graphs");
120 function managesValidators() {
123 function getVersion() {
124 return preg_replace("/[Revision: $]/", '',
125 "\$Revision: 1.5 $");
127 function getDefaultArguments() {
129 'imgtype' => 'png', // png,gif,svgz,svg,...
131 'pages' => false, // <!plugin-list !> support
136 function handle_plugin_args_cruft(&$argstr, &$args) {
137 $this->source = $argstr;
140 * Sets the expire time to one day (so the image producing
141 * functions are called seldomly) or to about two minutes
142 * if a help screen is created.
144 function getExpire($dbi, $argarray, $request) {
145 if (!empty($argarray['help']))
146 return '+120'; // 2 minutes
147 return sprintf('+%d', 3*86000); // approx 3 days
151 * Sets the imagetype according to user wishes and
152 * relies on WikiPluginCached to catch illegal image
154 * @return string 'png', 'jpeg', 'gif'
156 function getImageType($dbi, $argarray, $request) {
157 return $argarray['imgtype'];
161 * This gives an alternative text description of
164 function getAlt($dbi, $argstr, $request) {
165 return (!empty($this->_args['alt'])) ? $this->_args['alt']
166 : $this->getDescription();
170 * Returns an image containing a usage description of the plugin.
172 * TODO: *map features.
173 * @return string image handle
175 function helpImage() {
176 $def = $this->defaultArguments();
177 //$other_imgtypes = $GLOBALS['PLUGIN_CACHED_IMGTYPES'];
178 //unset ($other_imgtypes[$def['imgtype']]);
179 $imgtypes = $GLOBALS['PLUGIN_CACHED_IMGTYPES'];
180 $imgtypes = array_merge($imgtypes, array("svg", "svgz", "ps"), $this->_mapTypes());
182 '<?plugin GraphViz ' .
183 'imgtype' => ' = "' . $def['imgtype'] . "(default)|" . join('|',$imgtypes).'"',
184 'alt' => ' = "alternate text"',
185 'pages' => ' = "pages,*" or <!plugin-list !> pagelist as input',
186 'exclude' => ' = "pages,*" or <!plugin-list !> pagelist as input',
187 'help' => ' bool: displays this screen',
188 '...' => ' all further lines below the first plugin line ',
189 '' => ' and inside the tags are the dot script.',
193 foreach($helparr as $alignright => $alignleft) {
194 $length = max($length, strlen($alignright));
197 foreach($helparr as $alignright => $alignleft) {
198 $helptext .= substr(' '
199 . $alignright, -$length).$alignleft."\n";
201 return $this->text2img($helptext, 4, array(1, 0, 0),
202 array(255, 255, 255));
205 function processSource($argarray=false) {
206 $source = $this->source;
207 if (empty($source)) {
208 // create digraph from pages
209 $source = "digraph GraphViz {\n"; // }
210 foreach ($argarray['pages'] as $name) { // support <!plugin-list !> pagelists
211 // allow Page/SubPage
212 $url = str_replace(urlencode(SUBPAGE_SEPARATOR), SUBPAGE_SEPARATOR, rawurlencode($name));
213 $source .= " \"$name\" [URL=\"$url\"];\n";
218 /* //TODO: expand inlined plugin-list arg
220 foreach ($source as $data) {
223 $src .= ("\t" . join(" ", $data) . "\n");
225 $src .= ("\t" . '"' . $data . '" ' . $i++ . "\n");
233 function createDotFile($tempfile='', $argarray=false) {
234 $this->source = $this->processSource($argarray);
236 $tempfile = $this->tempnam($this->getName().".dot");
239 if (!$fp = fopen($tempfile, 'w'))
241 $ok = fwrite($fp, $this->source);
242 $ok = fclose($fp) && $ok; // close anyway
243 return $ok ? $tempfile : false;
246 function getImage($dbi, $argarray, $request) {
247 $dotbin = GRAPHVIZ_EXE;
248 $tempfiles = $this->tempnam($this->getName());
249 $gif = $argarray['imgtype'];
250 if (in_array($gif, array("imap", "cmapx", "ismap", "cmap"))) {
251 $this->_mapfile = "$tempfiles.map";
252 $gif = $this->decideImgType($argarray['imgtype']);
255 $ImageCreateFromFunc = "ImageCreateFrom$gif";
256 $outfile = $tempfiles.".".$gif;
257 $debug = $request->getArg('debug');
259 $tempdir = dirname($tempfiles);
260 $tempout = $tempdir . "/.debug";
262 $source = $this->processSource($argarray);
264 return $this->error(fmt("No dot graph given"));
266 //$dotfile = $this->createDotFile($tempfiles.'.dot', $argarray);
267 //$args = "-T$gif $dotfile -o $outfile";
268 $args = "-T$gif -o $outfile";
269 $cmdline = "$dotbin $args";
270 if ($debug) $cmdline .= " > $tempout";
271 //if (!isWindows()) $cmdline .= " 2>&1";
272 //$this->execute($cmdline, $outfile);
273 $code = $this->filterThroughCmd($source, $cmdline);
275 $this->complain(sprintf(_("Couldn't start commandline '%s'", $cmdline)));
277 if (! file_exists($outfile) ) {
278 $this->complain(sprintf(_("%s error: outputfile '%s' not created"),
279 "GraphViz", $outfile));
280 $this->complain("\ncmd-line: $cmdline");
283 if (function_exists($ImageCreateFromFunc)) {
284 $img = $ImageCreateFromFunc( $outfile );
285 // clean up tempfiles
286 if (!$argarray['debug'])
287 foreach (array('',".$gif",'.dot') as $ext) {
288 if (file_exists($tempfiles.$ext))
289 unlink($tempfiles.$ext);
296 // which argument must be set to 'png', for the fallback image when svg will fail on the client.
302 function getMap($dbi, $argarray, $request) {
303 return $this->invokeDot($argarray);
304 // $img = $this->getImage($dbi, $argarray, $request);
305 //return array($this->_mapfile, $img);
309 * Produces a dot file, calls dot twice to obtain an image and a
310 * text description of active areas for hyperlinking and returns
311 * an image and an html map.
313 * @param width float width of the output graph in inches
314 * @param height float height of the graph in inches
315 * @param colorby string color sceme beeing used ('age', 'revtime',
317 * @param shape string node shape; 'ellipse', 'box', 'circle', 'point'
318 * @param label string not used anymore
320 function invokeDot($argarray) {
321 $dotbin = GRAPHVIZ_EXE;
322 $tempfiles = $this->tempnam($this->getName());
323 $gif = $argarray['imgtype'];
324 $ImageCreateFromFunc = "ImageCreateFrom$gif";
325 $outfile = $tempfiles.".".$gif;
326 $debug = $GLOBALS['request']->getArg('debug');
328 $tempdir = dirname($tempfiles);
329 $tempout = $tempdir . "/.debug";
332 $source = $this->processSource($argarray);
334 return $this->error(fmt("No dot graph given"));
335 //$ok = $ok and $this->createDotFile($tempfiles.'.dot', $argarray);
337 $args = "-T$gif $tempfiles.dot -o $outfile";
338 $cmdline1 = "$dotbin $args";
339 if ($debug) $cmdline1 .= " > $tempout";
340 $ok = $ok and $this->filterThroughCmd($source, $cmdline1);
341 // $this->execute("$dotbin -T$gif $tempfiles.dot -o $outfile" .
342 // ($debug ? " > $tempout 2>&1" : " 2>&1"), $outfile)
344 $args = "-Timap $tempfiles.dot -o $tempfiles.map";
345 $cmdline2 = "$dotbin $args";
346 if ($debug) $cmdline2 .= " > $tempout";
347 $ok = $ok and $this->filterThroughCmd($source, $cmdline2);
348 // $this->execute("$dotbin -Timap $tempfiles.dot -o ".$tempfiles.".map" .
349 // ($debug ? " > $tempout 2>&1" : " 2>&1"), $tempfiles.".map")
350 $ok = $ok and file_exists( $outfile );
351 $ok = $ok and file_exists( $tempfiles.'.map' );
352 $ok = $ok and ($img = $ImageCreateFromFunc($outfile));
353 $ok = $ok and ($fp = fopen($tempfiles.'.map', 'r'));
356 if ($debug == 'static') {
357 // workaround for misconfigured WikiPluginCached (sf.net) or dot.
358 // present a static png and map file.
359 if (file_exists($outfile) and filesize($outfile) > 900)
362 $img = $tempdir . "/".$this->getName().".".$gif;
363 if (file_exists( $tempfiles.".map") and filesize($tempfiles.".map") > 20)
364 $map = $tempfiles.".map";
366 $map = $tempdir . "/".$this->getName().".map";
367 $img = $ImageCreateFromFunc($img);
368 $fp = fopen($map, 'r');
374 $line = fgets($fp, 1000);
375 if (substr($line, 0, 1) == '#')
377 list($shape, $url, $e1, $e2, $e3, $e4) = sscanf($line,
378 "%s %s %d,%d %d,%d");
379 if ($shape != 'rect')
382 // dot sometimes gives not always the right order so
383 // so we have to sort a bit
388 $map->pushContent(HTML::area(array(
390 'coords' => "$x1,$y1,$x2,$y2",
392 'title' => rawurldecode($url),
396 //trigger_error("url=".$url);
398 $this->complain("$outfile: "
399 . (file_exists($outfile) ? filesize($outfile):'missing')
402 . (file_exists("$tempfiles.map") ? filesize("$tempfiles.map"):'missing'));
403 $this->complain("\ncmd-line: $cmdline1");
404 $this->complain("\ncmd-line: $cmdline2");
405 trigger_error($this->GetError(), E_USER_WARNING);
406 return array(false, false);
409 // clean up tempfiles
410 if ($ok and !$argarray['debug'])
411 foreach (array('',".$gif",'.map','.dot') as $ext) {
412 //if (file_exists($tempfiles.$ext))
413 @unlink($tempfiles.$ext);
417 return array($img, $map);
419 return array(false, false);
424 // $Log: not supported by cvs2svn $
425 // Revision 1.4 2005/05/06 16:54:59 rurban
426 // add failing cmdline for .map
428 // Revision 1.3 2004/12/17 16:49:52 rurban
429 // avoid Invalid username message on Sign In button click
431 // Revision 1.2 2004/12/14 21:34:22 rurban
434 // Revision 1.1 2004/12/13 14:45:33 rurban
435 // new generic GraphViz plugin: similar to Ploticus
443 // c-hanging-comment-ender-p: nil
444 // indent-tabs-mode: nil