<?php

/***********************************************************
* pmgraphviz.php, requires PmWiki 2.1
* Allows insertion of graphviz dot language to display
* graphs.  Will cache resulting graphs to prevent
* re-rendering.                      
*
* Copyright (c) 2006, Chris Cox ccox@airmail.net
* All Rights, Reserved.
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your
* option) any later version.
*
Installation:

Place pmgraphviz.php (this file) into your cookbook 
directory (e.g. /srv/www/htdocs/pmwiki/cookbook)

Include the cookbook in your local/config.php 
include_once('cookbook/pmgraphviz.php');

Create a cache directory at pub/pmgraphviz under your
your PmWiki base directory.  Needs to be writable by
the web http server username.

On a page include the markup (the -- is important).
(:pmgraphviz -- [=
   graph {
      "hello" -> "world";
   }
=] :)

You set these variables to override defaults (usually
set inside your config.php before including the recipe):

$PmGraphVizCache = Path to cache dir ($FarmD/pub/pmgraphviz)
$PmGraphVizDotPgm = Path to graphviz dot (/usr/bin/dot)
$PmGraphVizWebCache = Url to cache dir ($FarmPubUrl/pmgraphviz);

-<0.01: Initial release.

-<0.02: Bug fixes, use Keep, added client side image map handling.

->Variables:
     [=
     mapname      Set this to the graphname if you include URL's for image mapping.
     =]
***********************************************************/
/* pmgraphviz */
Markup('pmgraphviz','directives',"/^\(:pmgraphviz[ 	]*(.*?) -- (.*?):\)\s*$/e",
	"pmgraphviz('$1','$2')");

function pmgraphviz($opts, $dotcmds) {
	global $KPV, $KeepToken, $FarmD, $FarmPubDirUrl, $pagename, $MaxIncludes, $HTMLHeaderFmt, $PmGraphicVizCache, $PmGraphVizDotPgm, $PmGraphVizWebCache, $PmGraphVizConvertPgm;


	SDV($PmGraphVizCache, $FarmD.'/pub/pmgraphviz');
	SDV($PmGraphVizDotPgm, '/usr/bin/dot');
	SDV($PmGraphVizWebCache, $FarmPubDirUrl.'/pmgraphviz');
	// If PmGraphVizConvertPgm is empty, then don't process with convert
	SDV($PmGraphVizConvertPgm, '/usr/bin/convert');

	// Enabling debug will write the dot file to  /tmp/pmgraphviz.out
	$debug=0;
	// Problem scaling using ImageMagick convert... disable this for now.
	$convert=0;
	if ($PmGraphVizConvertPgm != '')
		$convert=0;

	// Determine this Group
	//
	$group = FmtPageName('$Group',$pagename);
	$name = FmtPageName('$Name',$pagename);

	// Process markup arguments first
	//
	$defaults = array(
		'typeext'=>'png',
		'unsafe'=>'false',
		'overrides'=>'true',
		'mapname'=>'_anonymous_0'
	);

	$args = array_merge($defaults, ParseArgs($opts));
	$urladd='';


	// Allows overrides=false in the :pmgraphviz: markup to disallow
	// settings on the URL line.
	//
	$overrides = $args['overrides'];
	if ($overrides == 'false') {
		$_GET = NULL;
	}

	$typeext = isset($_GET['typeext']) ? $_GET['typeext'] :
		$args['typeext'];
	if (isset($_GET['typeext']))
		$urladd.="&amp;typeext=".urlencode($_GET['typeext']);

	// For unsafe (?) things
	// Like handling creating of arbitrarily named files from the
	// url line.
	$unsafe=$args['unsafe'];
	if ($unsafe == 'true') {
		$mapname = isset($_GET['mapname']) ? $_GET['mapname'] :
			$args['mapname'];
		if (isset($_GET['mapname']))
			$urladd.="&amp;mapname=".urlencode($_GET['mapname']);

	} else {
		$mapname=$args['mapname'];
	}

	$out='';

	// Compute the md5 of the opts plus dotcmds, if we
	// find a match in the cache, then return back
	// the graphic file there.
	//
	// Files may collect there over time.  You may
	// want to create a skulking routine to clean
	// out old entries in order to remove orphans.

    	$dotcmds = preg_replace( "/$KeepToken(\\d+?)$KeepToken/e", '$KPV[$1]', $dotcmds);
   	$dotcmds = preg_replace( "/&gt;/", ">", $dotcmds);
   	$dotcmds = preg_replace( "/&lt;/", "<", $dotcmds);
 
	$checkmd5 = md5("$opts"."$dotcmds");
	$filename = $checkmd5.".".$typeext;

	if (is_readable($PmGraphVizCache."/".$filename)) {
		$out.="<img border=\"0\" src=\"".$PmGraphVizWebCache."/".$filename."\" usemap=\"#".$mapname."\" ismap=\"ismap\">";
		$html_map=file_get_contents($PmGraphVizCache."/".$filename."-map");
		$out.="\n".$html_map;
		return Keep($out);
	}

	// Write dot file if debugging enabled.
	//
	if ($debug) {
		if ($f = fopen("/tmp/pmgraphviz.out", "w")) {
			fputs($f, "$dotcmds");
			pclose($f);
		}
	}
	// If we didn't return there, then we regen
	// the graphic.
	
	if (preg_match("/^http/", $PmGraphVizDotPgm)) {
		// Using a public server, no support for maps (yet)
		$dotfn = $checkmd5 .".dot";
		$fp = fopen($PmGraphVizCache."/".$dotfn, "w");
		fputs($fp, "$dotcmds");
		fclose($fp);
		$doturl = $PmGraphVizWebCache."/".$dotfn.".dot.".$typeext;
		$imgurl = $PmGraphVizDotPgm .'/'. $doturl;
		$out = "<img border=\"0\" src=\"$imgurl\" />";
		return Keep($out);
	}
	
	// Produce client side image map
	//
	if ($pf = popen("$PmGraphVizDotPgm -Tcmapx -o $PmGraphVizCache/$filename-map 2>&1",'wb')) {
		fputs($pf, "$dotcmds");
		pclose($pf);
	}
	// Produce image
	//
	// If convert worked right, we would get antialiasing.
	//
	$html_map=file_get_contents($PmGraphVizCache."/".$filename."-map");
	if ($convert) {
		$dotpgmline="$PmGraphVizDotPgm -Tps | $PmGraphVizConvertPgm - $PmGraphVizCache/$filename";
	} else {
		$dotpgmline="$PmGraphVizDotPgm -T $typeext -o $PmGraphVizCache/$filename 2>&1";
	}
	if ($pf = popen($dotpgmline,'wb')) {
		fputs($pf, "$dotcmds");
		pclose($pf);
		if (is_readable($PmGraphVizCache."/".$filename)) {
			$out.="<img border=\"0\" src=\"".$PmGraphVizWebCache."/".$filename."\" usemap=\"#".$mapname."\" ismap=\"ismap\">";
			$out.="\n".$html_map;
			return Keep($out);
		} else {
			return "!! pmgraphvis: Error\n";
		}
	}
	// don't want to make it here.
	return "!! pmgraphvis: How did I get here?\n";
}

?>