1: <?php
2: /**
3: * This class implements the Horde_Image:: API for SVG.
4: *
5: * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
6: *
7: * See the enclosed file COPYING for license information (LGPL). If you
8: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
9: *
10: * @author Chuck Hagenbuch <chuck@horde.org>
11: * @package Image
12: */
13: class Horde_Image_Svg extends Horde_Image_Base
14: {
15: protected $_svg;
16:
17: /**
18: * Capabilites of this driver.
19: *
20: * @var array
21: */
22: protected $_capabilities = array('canvas');
23:
24: public function __construct($params)
25: {
26: parent::__construct($params);
27: $this->_svg = new XML_SVG_Document(array('width' => $this->_width,
28: 'height' => $this->_height));
29: }
30:
31: public function getContentType()
32: {
33: return 'image/svg+xml';
34: }
35:
36: public function display()
37: {
38: $this->_svg->printElement();
39: }
40:
41: /**
42: * Return the raw data for this image.
43: *
44: * @return string The raw image data.
45: */
46: public function raw()
47: {
48: return $this->_svg->bufferObject();
49: }
50:
51: private function _createSymbol($s, $id)
52: {
53: $s->setParam('id', $id);
54: $defs = new XML_SVG_Defs();
55: $defs->addChild($s);
56: $this->_svg->addChild($defs);
57: }
58:
59: private function _createDropShadow($id = 'dropShadow')
60: {
61: $defs = new XML_SVG_Defs();
62: $filter = new XML_SVG_Filter(array('id' => $id));
63: $filter->addPrimitive('GaussianBlur', array('in' => 'SourceAlpha',
64: 'stdDeviation' => 2,
65: 'result' => 'blur'));
66: $filter->addPrimitive('Offset', array('in' => 'blur',
67: 'dx' => 4,
68: 'dy' => 4,
69: 'result' => 'offsetBlur'));
70: $merge = new XML_SVG_FilterPrimitive('Merge');
71: $merge->addMergeNode('offsetBlur');
72: $merge->addMergeNode('SourceGraphic');
73:
74: $filter->addChild($merge);
75: $defs->addChild($filter);
76: $this->_svg->addChild($defs);
77: }
78:
79: /**
80: * Draws a text string on the image in a specified location, with
81: * the specified style information.
82: *
83: * @param string $text The text to draw.
84: * @param integer $x The left x coordinate of the start of the text string.
85: * @param integer $y The top y coordinate of the start of the text string.
86: * @param string $font The font identifier you want to use for the text.
87: * @param string $color The color that you want the text displayed in.
88: * @param integer $direction An integer that specifies the orientation of the text.
89: */
90: public function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0)
91: {
92: $height = 12;
93: $style = 'font-family:' . $font . ';font-height:' . $height . 'px;fill:' . $this->getHexColor($color) . ';text-anchor:start;';
94: $transform = 'rotate(' . $direction . ',' . $x . ',' . $y . ')';
95: $this->_svg->addChild(new XML_SVG_Text(array('text' => $string,
96: 'x' => (int)$x,
97: 'y' => (int)$y + $height,
98: 'transform' => $transform,
99: 'style' => $style)));
100: }
101:
102: /**
103: * Draw a circle.
104: *
105: * @param integer $x The x coordinate of the centre.
106: * @param integer $y The y coordinate of the centre.
107: * @param integer $r The radius of the circle.
108: * @param string $color The line color of the circle.
109: * @param string $fill The color to fill the circle.
110: */
111: public function circle($x, $y, $r, $color, $fill = null)
112: {
113: if (!empty($fill)) {
114: $style = 'fill:' . $this->getHexColor($fill) . '; ';
115: } else {
116: $style = 'fill:none;';
117: }
118: $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
119:
120: $this->_svg->addChild(new XML_SVG_Circle(array('cx' => $x,
121: 'cy' => $y,
122: 'r' => $r,
123: 'style' => $style)));
124: }
125:
126: /**
127: * Draw a polygon based on a set of vertices.
128: *
129: * @param array $vertices An array of x and y labeled arrays
130: * (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
131: * @param string $color The color you want to draw the polygon with.
132: * @param string $fill The color to fill the polygon.
133: */
134: public function polygon($verts, $color, $fill = null)
135: {
136: if (!empty($fill)) {
137: $style = 'fill:' . $this->getHexColor($fill) . '; ';
138: } else {
139: $style = 'fill:none;';
140: }
141: $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
142:
143: $points = '';
144: foreach ($verts as $v) {
145: $points .= $v['x'] . ',' . $v['y'] . ' ';
146: }
147: $points = trim($points);
148:
149: $this->_svg->addChild(new XML_SVG_Polygon(array('points' => $points,
150: 'style' => $style)));
151: }
152:
153: /**
154: * Draw a rectangle.
155: *
156: * @param integer $x The left x-coordinate of the rectangle.
157: * @param integer $y The top y-coordinate of the rectangle.
158: * @param integer $width The width of the rectangle.
159: * @param integer $height The height of the rectangle.
160: * @param string $color The line color of the rectangle.
161: * @param string $fill The color to fill the rectangle.
162: */
163: public function rectangle($x, $y, $width, $height, $color, $fill = null)
164: {
165: if (!empty($fill)) {
166: $style = 'fill:' . $this->getHexColor($fill) . '; ';
167: } else {
168: $style = 'fill:none;';
169: }
170: $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
171:
172: $this->_svg->addChild(new XML_SVG_Rect(array('x' => $x,
173: 'y' => $y,
174: 'width' => $width,
175: 'height' => $height,
176: 'style' => $style)));
177: }
178:
179: /**
180: * Draw a rectangle.
181: *
182: * @param integer $x The left x-coordinate of the rectangle.
183: * @param integer $y The top y-coordinate of the rectangle.
184: * @param integer $width The width of the rectangle.
185: * @param integer $height The height of the rectangle.
186: * @param integer $round The width of the corner rounding.
187: * @param string $color The line color of the rectangle.
188: * @param string $fill The color to fill the rectangle.
189: */
190: public function roundedRectangle($x, $y, $width, $height, $round, $color, $fill)
191: {
192: if (!empty($fill)) {
193: $style = 'fill:' . $this->getHexColor($fill) . '; ';
194: } else {
195: $style = 'fill:none;';
196: }
197: $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
198:
199: $this->_svg->addChild(new XML_SVG_Rect(array('x' => $x,
200: 'y' => $y,
201: 'rx' => $round,
202: 'ry' => $round,
203: 'width' => $width,
204: 'height' => $height,
205: 'style' => $style)));
206: }
207:
208: /**
209: * Draw a line.
210: *
211: * @param integer $x0 The x coordinate of the start.
212: * @param integer $y0 The y coordinate of the start.
213: * @param integer $x1 The x coordinate of the end.
214: * @param integer $y1 The y coordinate of the end.
215: * @param string $color The line color.
216: * @param string $width The width of the line.
217: */
218: public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
219: {
220: $style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . (int)$width;
221: $this->_svg->addChild(new XML_SVG_Line(array('x1' => $x1,
222: 'y1' => $y1,
223: 'x2' => $x2,
224: 'y2' => $y2,
225: 'style' => $style)));
226: }
227:
228: /**
229: * Draw a dashed line.
230: *
231: * @param integer $x0 The x coordinate of the start.
232: * @param integer $y0 The y coordinate of the start.
233: * @param integer $x1 The x coordinate of the end.
234: * @param integer $y1 The y coordinate of the end.
235: * @param string $color The line color.
236: * @param string $width The width of the line.
237: * @param integer $dash_length The length of a dash on the dashed line
238: * @param integer $dash_space The length of a space in the dashed line
239: */
240: public function dashedLine($x1, $y1, $x2, $y2, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
241: {
242: $style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . (int)$width . '; stroke-dasharray:' . $dash_length . ',' . $dash_space . ';';
243: $this->_svg->addChild(new XML_SVG_Line(array('x1' => $x1,
244: 'y1' => $y1,
245: 'x2' => $x2,
246: 'y2' => $y2,
247: 'style' => $style)));
248: }
249:
250: /**
251: * Draw a polyline (a non-closed, non-filled polygon) based on a
252: * set of vertices.
253: *
254: * @param array $vertices An array of x and y labeled arrays
255: * (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
256: * @param string $color The color you want to draw the line with.
257: * @param string $width The width of the line.
258: */
259: public function polyline($verts, $color, $width = 1)
260: {
261: $style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . $width . ';fill:none;';
262:
263: // Calculate the path entry.
264: $path = '';
265:
266: $first = true;
267: foreach ($verts as $vert) {
268: if ($first) {
269: $first = false;
270: $path .= 'M ' . $vert['x'] . ',' . $vert['y'];
271: } else {
272: $path .= ' L ' . $vert['x'] . ',' . $vert['y'];
273: }
274: }
275:
276: $this->_svg->addChild(new XML_SVG_Path(array('d' => $path,
277: 'style' => $style)));
278: }
279:
280: /**
281: * Draw an arc.
282: *
283: * @param integer $x The x coordinate of the centre.
284: * @param integer $y The y coordinate of the centre.
285: * @param integer $r The radius of the arc.
286: * @param integer $start The start angle of the arc.
287: * @param integer $end The end angle of the arc.
288: * @param string $color The line color of the arc.
289: * @param string $fill The fill color of the arc (defaults to none).
290: */
291: public function arc($x, $y, $r, $start, $end, $color = 'black', $fill = null)
292: {
293: if (!empty($fill)) {
294: $style = 'fill:' . $this->getHexColor($fill) . '; ';
295: } else {
296: $style = 'fill:none;';
297: }
298: $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
299:
300: $mid = round(($start + $end) / 2);
301:
302: // Calculate the path entry.
303: $path = '';
304:
305: // If filled, draw the outline.
306: if (!empty($fill)) {
307: // Start at the center of the ellipse the arc is on.
308: $path .= "M $x,$y ";
309:
310: // Draw out to ellipse edge.
311: list($arcX, $arcY) = Horde_Image::circlePoint($start, $r * 2);
312: $path .= 'L ' . round($x + $arcX) . ',' .
313: round($y + $arcY) . ' ';
314: }
315:
316: // Draw arcs.
317: list($arcX, $arcY) = Horde_Image::circlePoint($mid, $r * 2);
318: $path .= "A $r,$r 0 0 1 " .
319: round($x + $arcX) . ',' .
320: round($y + $arcY) . ' ';
321:
322: list($arcX, $arcY) = Horde_Image::circlePoint($end, $r * 2);
323: $path .= "A $r,$r 0 0 1 " .
324: round($x + $arcX) . ',' .
325: round($y + $arcY) . ' ';
326:
327: // If filled, close the outline.
328: if (!empty($fill)) {
329: $path .= 'Z';
330: }
331:
332: $path = trim($path);
333:
334: $this->_svg->addChild(new XML_SVG_Path(array('d' => $path,
335: 'style' => $style)));
336: }
337:
338: }