本文使用PHP实现了一种与Google GraphViz Charts参数兼容的API,相较于Google GraphViz Charts API的优点是支持中文。
Contents
环境搭建
基于Debian,需要安装GraphViz及gd库
apt-get install php5 apt-get install graphviz apt-get install php5-gd
root@server1:/var/www/gv# dot -V dot - graphviz version 2.26.3 (20100126.1600)
API代码
<?php /** * graphviz api * * $Id api.php i@annhe.net 2015-6-8 $ **/ $host = $_SERVER['HTTP_HOST']; $siteurl = "http://" . $host . "/gv/"; $cht = "dot"; $chl = "graph {fontname=\"SimSun\";node[shape=box];a[label=\"nothing to do~\"];}"; $error = "error.png"; $engine = array("dot", "neato", "fdp", "sfdp", "twopi", "circo"); $imgtype = "png"; $imgtype_arr = array("png", "gif", "jpeg"); if(isset($_GET['cht'])) { $cht = $_GET['cht']; $arr = explode(':', $cht); if(!array_key_exists("1", $arr)) { $cht = "dot"; } else { $cht = $arr['1']; } if(!in_array($cht, $engine)) { $cht = "dot"; } } if(isset($_GET['chl'])) { $chl = $_GET['chl']; } if(isset($_GET['chof'])) { $imgtype = $_GET['chof']; if(!in_array($imgtype, $imgtype_arr)) { $imgtype = "png"; } } $gvname = md5($chl) . $cht; $gvpath = "./gv/" . $gvname . ".gv"; $imgpath = "img/" . $gvname . "." . $imgtype; $file = fopen("$gvpath", "w"); $encode = mb_detect_encoding($chl, array("ASCII","UTF-8","GB2312", "GBK", "EUC-CN")); if($encode != "UTF-8") { $chl = iconv("$encode", "UTF-8", $chl); } $chl = str_replace(">", ">", $chl); $chl = str_replace("<", "<", $chl); $chl = str_replace(""", "\"", $chl); fwrite($file, "$chl"); fclose($file); if(!file_exists($imgpath)) { exec("$cht -T$imgtype $gvpath -o $imgpath",$out, $ret); if($ret != 0) { $imgpath = $error; $imgtype = "png"; } } $imgstrout = "image$imgtype(imagecreatefrom$imgtype('$imgpath'));"; header("Content-Type: image/$imgtype; charset=UTF-8"); eval($imgstrout); ?>
演示
参数与Google GraphViz Charts一致,及
- cht=gv[:<opt_engine>] , 可选参数 , 如果没有,这默认为gv:dot。其他可用引擎有 neato twopi circo fdp sfdp (比Google GraphViz Charts多一个sfdp)
- chl=<DOT_string>, 必选参数。支持中文
- chof=<png|gif|jpeg>, 可选参数,默认为png
Discuz插件形式的演示:http://www.tecbbs.com/forum.php?mod=viewthread&tid=6724
WordPress尚未做成插件,暂时直接URL插入图片
中文图片问题
直接返回图片内容
API一开始返回的是图片URL,这样程序需要有2次HTTP请求才能确定图片链接,会严重拖慢网页加载速度。因此改为直接返回图片内容,只需要一次HTTP请求。
$imgstrout = "image$imgtype(imagecreatefrom$imgtype('$imgpath'));"; header("Content-Type: image/$imgtype; charset=UTF-8"); eval($imgstrout);
eval()函数将字符串作为PHP代码来执行。
Gif not recognized问题
如果API搭建在Centos上,可能需要rpm方式安装GraphViz,yum方式的GraphViz版本太旧,不支持gif输出。 需要安装 graphviz-gd。
[root@HADOOP-215 gv]# dot -Tgif 05acaa447bc10f15991d19580ccc0d41dot.gv -o test.gif Format: "gif" not recognized. Use one of: canon cmap cmapx cmapx_np dot eps fig gv imap imap_np ismap pic plain plain-ext pov ps ps2 svg svgz tk vml vmlz xdot xdot1.2 xdot1.4[root@HADOOP-215 gv]# yum install graphviz-gd Loaded plugins: fastestmirror, priorities Loading mirror speeds from cached hostfile ===================================================================================================================================== Package Arch Version Repository Size ===================================================================================================================================== Installing: graphviz-gd x86_64 2.38.0-1.el6 graphviz-stable 6.6 k Installing for dependencies: graphviz-plugins-gd x86_64 2.38.0-1.el6 graphviz-stable 20 k Transaction Summary ===================================================================================================================================== Install 2 Package(s) [root@HADOOP-215 gv]# dot -Tgif 05acaa447bc10f15991d19580ccc0d41dot.gv -o test.gif [root@HADOOP-215 gv]# ls 05acaa447bc10f15991d19580ccc0d41circo.gv 05acaa447bc10f15991d19580ccc0d41fdp.gv test.gif 05acaa447bc10f15991d19580ccc0d41dot.gv 76fadd7c49a49f7a08d97a34c111d534dot.gv
另外,GraphViz 的repo文件链接是:wget http://www.graphviz.org/graphviz-rhel.repo,下载之后可以直接yum安装最新稳定版本的GraphViz。
urlencode问题
dot源码需要进行urlencode,但是考虑到和Google GraphViz Charts保持一致,只进行部分字符的encode
$texcode = str_replace('#', "%23", $texcode); //'#' 需要urlencode
如果不encode,获得的dot源码不完整
digraph example3 { Server1 -> Server2 Server2 -> Server3 Server3 -> Server1 Server1 [shape=box, label="Server1\nWeb Server", fillcolo r="
完整的代码是
digraph example3 { Server1 -> Server2 Server2 -> Server3 Server3 -> Server1 Server1 [shape=box, label="Server1\nWeb Server", fillcolo r="#ABACBA", style=filled] Server2 [shape=triangle, label="Server2\nApp Server", fillcolor="#DDBCBC", style=filled] Server3 [shape=circle, label="Server3\nDatabase Server", fillcolor="#FFAA22",style=filled] }
中文字体问题
需要安装中文字体,否则中文将乱码,可以直接拷贝其他机器的wqy-microhei字体。
cd /usr/share/fonts/ tar zxvf wqy.tar.gz rm -f wqy.tar.gz mv usr/share/fonts/wqy-microhei/ . cd wqy-microhei/
$_GET解码问题
PHP官网明确指出:超全局变量 $_GET 和 $_REQUEST 已经被解码了。对 $_GET 或 $_REQUEST 里的元素使用 urldecode() 将会导致不可预计和危险的结果。
本文实现的API会将dot源码保持为文件,文件名使用dot源码的md5值
$gvname = md5($chl) . $cht;
由于$_GET已经经过了urldecode,所以客户端程序使用dot源码文件名时也需要urldecode,这样才能获取正确的文件名
$p = str_replace(">", ">", $texcode); $p = str_replace("<", "<", $p); $p = str_replace(""", "\"", $p); $gv_md5 = md5(urldecode($p)) . $engine . ".gv";
2.38中文问题
Debian7和Centos6.5软件源里的GraphViz都是2.26版本的,目前(15年6月)最新稳定版本是2.38,手贱升级到了2.38,发现该版本子图中文显示有问题。上文演示的图片,代码如下:
digraph idp_modules{ rankdir = TB; fontname = "Microsoft YaHei"; fontsize = 12; node [ fontname = "Microsoft YaHei", fontsize = 12, shape = "record" ]; edge [ fontname = "Microsoft YaHei", fontsize = 12 ]; subgraph cluster_sl{ label="IDP支持层"; bgcolor="mintcream"; node [shape="Mrecord", color="skyblue", style="filled"]; network_mgr [label="网络管理器"]; log_mgr [label="日志管理器"]; module_mgr [label="模块管理器"]; conf_mgr [label="配置管理器"]; db_mgr [label="数据库管理器"]; }; subgraph cluster_md{ label="可插拔模块集"; bgcolor="lightcyan"; node [color="chartreuse2", style="filled"]; mod_dev [label="开发支持模块"]; mod_dm [label="数据建模模块"]; mod_dp [label="部署发布模块"]; }; mod_dp -> mod_dev [label="依赖..."]; mod_dp -> mod_dm [label="依赖..."]; mod_dp -> module_mgr [label="安装...", color="yellowgreen", arrowhead="none"]; mod_dev -> mod_dm [label="依赖..."]; mod_dev -> module_mgr [label="安装...", color="yellowgreen", arrowhead="none"]; mod_dm -> module_mgr [label="安装...", color="yellowgreen", arrowhead="none"]; }
使用2.38生成的图片
网上搜索的结果,一个说是引号问题
macbook darwin下安装了graphviz 2.38.0。
画有向图digraph的时候,如果label中包含中文字符,生成的图片中会缺文字。
网上的解释是fontname没有设置的原因,可设置fontname后还是会出现同样的问题。例如:
digraph Geely {
node [shape=record,fontname="SimSun.ttf"];
edge [fontname="SimSun.ttf"];jlwy [label="吉利万源"];
jlzy [label="吉利兆园"];
volv [label="沃尔沃"];jlwy -> jlzy [label="87.65%"];
jlzy -> volv [label="100%"];}
$ dot -Tpng geely.dot -o geely.png $ open geely.png
生成的图像中,连接线上的百分比有,但节点中的内容是空的。
偶然原因,在引号中的中文前加了个空格,字符就能显示出来了,可能是个bug吧,总之问题解决了。
volv [label=" 沃尔沃"];
按照此方法能够正常显示图片。
另一个说是shape问题
可是子图里面的节点却依然是空(不是乱码,只是没有内容)。于是我查找半天,最终发现罪魁祸首是:
zongshu_title [label = "研究综述" shape=record]
看到了吧?就是最后这句shape=record。为什么有问题?不知道。但是把它删去,一点不改变原先的图形结构,中文却又都出来了。
按照此方法中文是可以显示了,但是形状效果都没了
参考资料
[1]. http://php.net/manual/zh/function.urldecode.php [2]. http://php.net/manual/zh/function.imagecreatefromjpeg.php [3]. http://blog.sina.com.cn/s/blog_b09d4602010195ky.html [4]. 2.38中文问题 http://wshuyi.github.io/2014/05/03/graphvizchinesewindows/ [5]. 2.38中文问题 http://blog.csdn.net/spacecraft/article/details/25163293
Pingback: Discuz插件处理大型dot源码 | 知行近思