1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8/* This library is LGPL
9 * written by Louis-Philippe Huberdeau
10 *
11 * vim: fdm=marker tabstop=4 shiftwidth=4 noet:
12 *
13 * This file contains the GD graphic renderer.
14 */
15require_once('lib/graph-engine/core.php');
16
17class GD_GRenderer extends GRenderer // {{{1
18{
19	var $gd;
20	var $styles;
21	var $colors;
22	var $fonts;
23
24	var $format;
25	var $width;
26	var $height;
27
28	var $imageMap;
29
30	function __construct($width = 0, $height = 0, $format = 'png') // {{{2
31	{
32		// Null size does not create a graphic.
33		$this->styles = [];
34		$this->colors = [];
35		$this->fonts = [];
36
37		if ($width !== 0 && $height !== 0) {
38			$this->gd = imagecreate($width, $height);
39			$this->_getColor('white');
40		}
41
42		$this->format = $format;
43		$this->width = $width;
44		$this->height = $height;
45	}
46
47	function addLink($target, $left, $top, $right, $bottom, $title = null) // {{{2
48	{
49		$this->_convertPosition($left, $top);
50		$this->_convertPosition($right, $bottom);
51		$target = htmlspecialchars($target);
52		$title = htmlspecialchars($title);
53
54		$this->imageMap .= "<area shape=\"rect\" coords=\"$left,$top,$right,$bottom\" href=\"$target\" alt=\"$title\" title=\"$title\"/>\n";
55	}
56
57	function drawLine($x1, $y1, $x2, $y2, $style) // {{{2
58	{
59		$this->_convertPosition($x1, $y1);
60		$this->_convertPosition($x2, $y2);
61		imagesetthickness($this->gd, $style['line-width']);
62		imageline($this->gd, $x1, $y1, $x2, $y2, $style['line']);
63	}
64
65	function drawRectangle($left, $top, $right, $bottom, $style) // {{{2
66	{
67		if ($top > $bottom) {
68			// Filled rect has a problem when coordinates are inverted.
69			$a = $top;
70			$top = $bottom;
71			$bottom = $a;
72		}
73		if ($left > $right) {
74			// Filled rect has a problem when coordinates are inverted.
75			$a = $left;
76			$left = $right;
77			$right = $a;
78		}
79
80		$this->_convertPosition($left, $top);
81		$this->_convertPosition($right, $bottom);
82
83		if (isset($style['fill'])) {
84			imagefilledrectangle($this->gd, $left, $top, $right, $bottom, $style['fill']);
85		}
86
87		imagesetthickness($this->gd, $style['line-width']);
88		imagerectangle($this->gd, $left, $top, $right, $bottom, $style['line']);
89	}
90
91	function drawPie($centerX, $centerY, $radius, $begin, $end, $style) // {{{2
92	{
93		$radius = $radius * 2;
94		if ($begin != 0 || $end != 360) {
95			$tmp = -$begin;
96			$begin = -$end;
97			$end = $tmp;
98		}
99
100		$this->_convertPosition($centerX, $centerY);
101		$radius = $radius * min($this->width, $this->height);
102		imagefilledarc($this->gd, $centerX, $centerY, $radius, $radius, $begin, $end, $style['fill'], IMG_ARC_PIE);
103
104		imagesetthickness($this->gd, $style['line-width']);
105		imagefilledarc($this->gd, $centerX, $centerY, $radius, $radius, $begin, $end, $style['line'], IMG_ARC_NOFILL | IMG_ARC_EDGED);
106	}
107
108	function drawText($text, $left, $right, $height, $style) // {{{2
109	{
110		$h = $height; // Creating duplicate (temp)
111		$this->_convertPosition($left, $height);
112		$this->_convertPosition($right, $h);
113		switch ($style['align']) {
114			case 'left':
115				$this->_drawLeftText($text, $left, $height, $style);
116				break;
117			case 'center':
118				$this->_drawCenterText($text, $left, $right, $height, $style);
119				break;
120			case 'right':
121				$this->_drawRightText($text, $right, $height, $style);
122				break;
123		}
124	}
125
126	function getTextWidth($text, $style) // {{{2
127	{
128		return imagefontwidth($style['font']) * strlen($text) / $this->width;
129	}
130
131	function getTextHeight($style) // {{{2
132	{
133		return imagefontheight($style['font']) / $this->height;
134	}
135
136	function getStyle($name) // {{{2
137	{
138		if (isset($this->styles[$name])) {
139			return $this->styles[$name];
140		}
141
142		return $this->styles[$name] = $this->_findStyle($name);
143	}
144
145	function httpOutput($filename) // {{{2
146	{
147		switch ($this->format) {
148			case 'png':
149				header("Content-type: image/png");
150				imagepng($this->gd);
151				break;
152			case 'jpg':
153				header("Content-type: image/jpeg");
154				imagejpeg($this->gd);
155				break;
156			default:
157				echo "Unknown Format: {$this->format}\n";
158		}
159
160		imagedestroy($this->gd);
161	}
162
163	function writeToStream($stream) // {{{2
164	{
165		ob_start();
166		switch ($this->format) {
167			case 'png':
168				imagepng($this->gd);
169				break;
170			case 'jpg':
171				imagejpeg($this->gd);
172				break;
173			default:
174				echo "Unknown Format: {$this->format}\n";
175		}
176		fwrite($stream, ob_get_contents());
177		ob_end_clean();
178		imagedestroy($this->gd);
179	}
180
181	function getMapContent() // {{{2
182	{
183		return $this->imageMap;
184	}
185
186	function _convertLength($value, $type) // {{{2
187	{
188		// $type is either 'width' or 'height'
189		// $value is a 0-1 float
190		return floor($value * $this->$type);
191	}
192
193	function _convertPosition(&$x, &$y) // {{{2
194	{
195		// Parameters passed by ref!
196		$x = $this->_convertLength($x, 'width');
197		$y = $this->_convertLength($y, 'height');
198	}
199
200	function _findStyle($name) // {{{2
201	{
202		$parts = explode('-', $name);
203		$style = [];
204
205		switch ($parts[0]) {
206			case 'Thin':
207				$style['line-width'] = 1;
208				array_shift($parts);
209				break;
210			case 'Bold':
211				$style['line-width'] = 2;
212				array_shift($parts);
213				break;
214			case 'Bolder':
215				$style['line-width'] = 3;
216				array_shift($parts);
217				break;
218			case 'Large':
219				$style['font'] = 5;
220				array_shift($parts);
221				break;
222			case 'Small':
223				$style['font'] = 2;
224				array_shift($parts);
225				break;
226			case 'Normal':
227				array_shift($parts);
228			default:
229				if ($parts[0] == 'Text') {
230					$style['font'] = 4;
231				} else {
232					$style['line-width'] = 1;
233				}
234				break;
235		}
236
237		switch ($parts[0]) {
238			case 'LineStroke':
239				$style['line'] = $this->_getColor($parts[1]);
240				break;
241			case 'FillStroke':
242				$style['fill'] = $this->_getColor($parts[1]);
243				$style['line'] = $this->_getColor('Black');
244				break;
245			case 'Text':
246				if (! isset($parts[1])) {
247					$parts[1] = null;
248				}
249				switch ($parts[1]) {
250					case 'Center':
251						$style['align'] = 'center';
252						break;
253					case 'Right':
254						$style['align'] = 'right';
255						break;
256					case 'Left':
257					default:
258						$style['align'] = 'left';
259						break;
260				}
261				break;
262			default:
263				return GRenderer::getStyle($name);
264		}
265
266		return $style;
267	}
268
269	function _getColor($name) // {{{2
270	{
271		$name = strtolower($name);
272
273		if (isset($this->colors[$name])) {
274			return $this->colors[$name];
275		}
276
277		return $this->colors[$name] = $this->_findColor($name);
278	}
279
280	function _findColor($name) // {{{2
281	{
282		$color = $this->_getRawColor($name);
283		return imagecolorallocate($this->gd, (int)$color['r'], (int)$color['g'], (int)$color['b']);
284	}
285
286	function _drawLeftText($string, $left, $height, $style) // {{{2
287	{
288		imagestring($this->gd, $style['font'], $left, $height, $string, $this->_getColor('Black'));
289	}
290
291	function _drawCenterText($string, $left, $right, $height, $style) // {{{2
292	{
293		$width = imagefontwidth($style['font']) * strlen($string);
294		$x = ( $right - $left ) / 2 + $left - $width / 2;
295
296		imagestring($this->gd, $style['font'], $x, $height, $string, $this->_getColor('Black'));
297	}
298
299	function _drawRightText($string, $right, $height, $style) // {{{2
300	{
301		$width = imagefontwidth($style['font']) * strlen($string);
302		$x = $right - $width;
303
304		imagestring($this->gd, $style['font'], $x, $height, $string, $this->_getColor('Black'));
305	}
306} // }}}1
307