439 lines
14 KiB
PHP
439 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2002-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file LICENSE for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Chuck Hagenbuch <chuck@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Image
|
|
*/
|
|
|
|
/**
|
|
* This class implements the Horde_Image:: API for SVG.
|
|
*
|
|
* @author Chuck Hagenbuch <chuck@horde.org>
|
|
* @category Horde
|
|
* @copyright 2002-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Image
|
|
*
|
|
* Requires PEAR XML_SVG
|
|
*/
|
|
class Horde_Image_Svg extends Horde_Image_Base
|
|
{
|
|
/**
|
|
* SVG document handle.
|
|
*
|
|
* @var XML_SVG
|
|
*/
|
|
protected $_svg;
|
|
|
|
/**
|
|
* Capabilites of this driver.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_capabilities = array(
|
|
'canvas',
|
|
'circle',
|
|
'dashedLine',
|
|
'line',
|
|
'polygon',
|
|
'polyline',
|
|
'rectangle',
|
|
'roundedRectangle',
|
|
'text',
|
|
);
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @see Horde_Image_Base::_construct
|
|
*/
|
|
public function __construct($params, $context = array())
|
|
{
|
|
parent::__construct($params, $context);
|
|
$this->_svg = new XML_SVG_Document(array(
|
|
'width' => $this->_width,
|
|
'height' => $this->_height,
|
|
));
|
|
if ($this->_background != 'none') {
|
|
$this->rectangle(
|
|
0, 0,
|
|
$this->_width, $this->_height,
|
|
$this->_background, $this->_background
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the MIME type for this image.
|
|
*
|
|
* @return string The MIME type for this image.
|
|
*/
|
|
public function getContentType()
|
|
{
|
|
return 'image/svg+xml';
|
|
}
|
|
|
|
/**
|
|
* Displays the current image.
|
|
*/
|
|
public function display()
|
|
{
|
|
$this->headers();
|
|
$this->_svg->printElement();
|
|
}
|
|
|
|
/**
|
|
* Return raw image data.
|
|
*
|
|
* @param boolean $convert Unused in SVG driver.
|
|
* @param array $options Array of options:
|
|
* NONE USED in SVG driver.
|
|
*
|
|
* @return string The raw image data.
|
|
*/
|
|
public function raw($convert = false, $options = array())
|
|
{
|
|
return $this->_svg->bufferObject();
|
|
}
|
|
|
|
private function _createSymbol($s, $id)
|
|
{
|
|
$s->setParam('id', $id);
|
|
$defs = new XML_SVG_Defs();
|
|
$defs->addChild($s);
|
|
$this->_svg->addChild($defs);
|
|
}
|
|
|
|
private function _createDropShadow($id = 'dropShadow')
|
|
{
|
|
$defs = new XML_SVG_Defs();
|
|
$filter = new XML_SVG_Filter(array('id' => $id));
|
|
$filter->addPrimitive(
|
|
'GaussianBlur',
|
|
array(
|
|
'in' => 'SourceAlpha',
|
|
'stdDeviation' => 2,
|
|
'result' => 'blur'
|
|
)
|
|
);
|
|
$filter->addPrimitive(
|
|
'Offset',
|
|
array(
|
|
'in' => 'blur',
|
|
'dx' => 4,
|
|
'dy' => 4,
|
|
'result' => 'offsetBlur'
|
|
)
|
|
);
|
|
$merge = new XML_SVG_FilterPrimitive('Merge');
|
|
$merge->addMergeNode('offsetBlur');
|
|
$merge->addMergeNode('SourceGraphic');
|
|
|
|
$filter->addChild($merge);
|
|
$defs->addChild($filter);
|
|
$this->_svg->addChild($defs);
|
|
}
|
|
|
|
/**
|
|
* Draws a text string on the image in a specified location, with the
|
|
* specified style information.
|
|
*
|
|
* @param string $text The text to draw.
|
|
* @param integer $x The left x coordinate of the start of the
|
|
* text string.
|
|
* @param integer $y The top y coordinate of the start of the text
|
|
* string.
|
|
* @param string $font The font identifier you want to use for the
|
|
* text.
|
|
* @param string $color The color that you want the text displayed in.
|
|
* @param integer $direction An integer that specifies the orientation of
|
|
* the text.
|
|
* @param string $fontsize Size of the font (small, medium, large, giant)
|
|
*/
|
|
public function text(
|
|
$string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0
|
|
)
|
|
{
|
|
$height = 12;
|
|
$style = 'font-family:' . $font . ';font-height:' . $height
|
|
. 'px;fill:' . Horde_Image::getHexColor($color) . ';text-anchor:start;';
|
|
$transform = 'rotate(' . $direction . ',' . $x . ',' . $y . ')';
|
|
$this->_svg->addChild(new XML_SVG_Text(array(
|
|
'text' => $string,
|
|
'x' => (int)$x,
|
|
'y' => (int)$y + $height,
|
|
'transform' => $transform,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a circle.
|
|
*
|
|
* @param integer $x The x coordinate of the centre.
|
|
* @param integer $y The y coordinate of the centre.
|
|
* @param integer $r The radius of the circle.
|
|
* @param string $color The line color of the circle.
|
|
* @param string $fill The color to fill the circle.
|
|
*/
|
|
public function circle($x, $y, $r, $color, $fill = null)
|
|
{
|
|
if (!empty($fill)) {
|
|
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
|
|
} else {
|
|
$style = 'fill:none;';
|
|
}
|
|
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
|
|
|
|
$this->_svg->addChild(new XML_SVG_Circle(array(
|
|
'cx' => $x,
|
|
'cy' => $y,
|
|
'r' => $r,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a polygon based on a set of vertices.
|
|
*
|
|
* @param array $vertices An array of x and y labeled arrays
|
|
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
|
|
* @param string $color The color you want to draw the polygon with.
|
|
* @param string $fill The color to fill the polygon.
|
|
*/
|
|
public function polygon($verts, $color, $fill = null)
|
|
{
|
|
if (!empty($fill)) {
|
|
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
|
|
} else {
|
|
$style = 'fill:none;';
|
|
}
|
|
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
|
|
|
|
$points = '';
|
|
foreach ($verts as $v) {
|
|
$points .= $v['x'] . ',' . $v['y'] . ' ';
|
|
}
|
|
$points = trim($points);
|
|
|
|
$this->_svg->addChild(new XML_SVG_Polygon(array(
|
|
'points' => $points,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a rectangle.
|
|
*
|
|
* @param integer $x The left x-coordinate of the rectangle.
|
|
* @param integer $y The top y-coordinate of the rectangle.
|
|
* @param integer $width The width of the rectangle.
|
|
* @param integer $height The height of the rectangle.
|
|
* @param string $color The line color of the rectangle.
|
|
* @param string $fill The color to fill the rectangle.
|
|
*/
|
|
public function rectangle($x, $y, $width, $height, $color, $fill = null)
|
|
{
|
|
if (!empty($fill) && $fill != 'none') {
|
|
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
|
|
} else {
|
|
$style = 'fill:none;';
|
|
}
|
|
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
|
|
|
|
$this->_svg->addChild(new XML_SVG_Rect(array(
|
|
'x' => $x,
|
|
'y' => $y,
|
|
'width' => $width,
|
|
'height' => $height,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a rounded rectangle.
|
|
*
|
|
* @param integer $x The left x-coordinate of the rectangle.
|
|
* @param integer $y The top y-coordinate of the rectangle.
|
|
* @param integer $width The width of the rectangle.
|
|
* @param integer $height The height of the rectangle.
|
|
* @param integer $round The width of the corner rounding.
|
|
* @param string $color The line color of the rectangle.
|
|
* @param string $fill The color to fill the rounded rectangle with.
|
|
*/
|
|
public function roundedRectangle(
|
|
$x, $y, $width, $height, $round, $color, $fill
|
|
)
|
|
{
|
|
if (!empty($fill)) {
|
|
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
|
|
} else {
|
|
$style = 'fill:none;';
|
|
}
|
|
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
|
|
|
|
$this->_svg->addChild(new XML_SVG_Rect(
|
|
array('x' => $x,
|
|
'y' => $y,
|
|
'rx' => $round,
|
|
'ry' => $round,
|
|
'width' => $width,
|
|
'height' => $height,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a line.
|
|
*
|
|
* @param integer $x0 The x coordinate of the start.
|
|
* @param integer $y0 The y coordinate of the start.
|
|
* @param integer $x1 The x coordinate of the end.
|
|
* @param integer $y1 The y coordinate of the end.
|
|
* @param string $color The line color.
|
|
* @param string $width The width of the line.
|
|
*/
|
|
public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
|
|
{
|
|
$style = 'stroke:' . Horde_Image::getHexColor($color)
|
|
. '; stroke-width:' . (int)$width;
|
|
$this->_svg->addChild(new XML_SVG_Line(array(
|
|
'x1' => $x1,
|
|
'y1' => $y1,
|
|
'x2' => $x2,
|
|
'y2' => $y2,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a dashed line.
|
|
*
|
|
* @param integer $x0 The x coordinate of the start.
|
|
* @param integer $y0 The y coordinate of the start.
|
|
* @param integer $x1 The x coordinate of the end.
|
|
* @param integer $y1 The y coordinate of the end.
|
|
* @param string $color The line color.
|
|
* @param string $width The width of the line.
|
|
* @param integer $dash_length The length of a dash on the dashed line
|
|
* @param integer $dash_space The length of a space in the dashed line
|
|
*/
|
|
public function dashedLine(
|
|
$x1, $y1, $x2, $y2, $color = 'black', $width = 1, $dash_length = 2,
|
|
$dash_space = 2
|
|
)
|
|
{
|
|
$style = 'stroke:' . Horde_Image::getHexColor($color)
|
|
. '; stroke-width:' . (int)$width
|
|
. '; stroke-dasharray:' . $dash_length . ',' . $dash_space . ';';
|
|
$this->_svg->addChild(new XML_SVG_Line(array(
|
|
'x1' => $x1,
|
|
'y1' => $y1,
|
|
'x2' => $x2,
|
|
'y2' => $y2,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
|
|
* vertices.
|
|
*
|
|
* @param array $vertices An array of x and y labeled arrays
|
|
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
|
|
* @param string $color The color you want to draw the line with.
|
|
* @param string $width The width of the line.
|
|
*/
|
|
public function polyline($verts, $color, $width = 1)
|
|
{
|
|
$style = 'stroke:' . Horde_Image::getHexColor($color)
|
|
. '; stroke-width:' . $width . ';fill:none;';
|
|
|
|
// Calculate the path entry.
|
|
$path = '';
|
|
|
|
$first = true;
|
|
foreach ($verts as $vert) {
|
|
if ($first) {
|
|
$first = false;
|
|
$path .= 'M ' . $vert['x'] . ',' . $vert['y'];
|
|
} else {
|
|
$path .= ' L ' . $vert['x'] . ',' . $vert['y'];
|
|
}
|
|
}
|
|
|
|
$this->_svg->addChild(new XML_SVG_Path(array(
|
|
'd' => $path,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
/**
|
|
* Draws an arc.
|
|
*
|
|
* @param integer $x The x coordinate of the centre.
|
|
* @param integer $y The y coordinate of the centre.
|
|
* @param integer $r The radius of the arc.
|
|
* @param integer $start The start angle of the arc.
|
|
* @param integer $end The end angle of the arc.
|
|
* @param string $color The line color of the arc.
|
|
* @param string $fill The fill color of the arc (defaults to none).
|
|
*/
|
|
public function arc(
|
|
$x, $y, $r, $start, $end, $color = 'black', $fill = null
|
|
)
|
|
{
|
|
if (!empty($fill)) {
|
|
$style = 'fill:' . Horde_Image::getHexColor($fill) . '; ';
|
|
} else {
|
|
$style = 'fill:none;';
|
|
}
|
|
$style .= 'stroke:' . Horde_Image::getHexColor($color) . '; stroke-width:1';
|
|
|
|
$mid = round(($start + $end) / 2);
|
|
|
|
// Calculate the path entry.
|
|
$path = '';
|
|
|
|
// If filled, draw the outline.
|
|
if (!empty($fill)) {
|
|
// Start at the center of the ellipse the arc is on.
|
|
$path .= "M $x,$y ";
|
|
|
|
// Draw out to ellipse edge.
|
|
list($arcX, $arcY) = Horde_Image::circlePoint($start, $r * 2);
|
|
$path .= 'L ' . round($x + $arcX) . ',' .
|
|
round($y + $arcY) . ' ';
|
|
}
|
|
|
|
// Draw arcs.
|
|
list($arcX, $arcY) = Horde_Image::circlePoint($mid, $r * 2);
|
|
$path .= "A $r,$r 0 0 1 " .
|
|
round($x + $arcX) . ',' .
|
|
round($y + $arcY) . ' ';
|
|
|
|
list($arcX, $arcY) = Horde_Image::circlePoint($end, $r * 2);
|
|
$path .= "A $r,$r 0 0 1 " .
|
|
round($x + $arcX) . ',' .
|
|
round($y + $arcY) . ' ';
|
|
|
|
// If filled, close the outline.
|
|
if (!empty($fill)) {
|
|
$path .= 'Z';
|
|
}
|
|
|
|
$path = trim($path);
|
|
|
|
$this->_svg->addChild(new XML_SVG_Path(array(
|
|
'd' => $path,
|
|
'style' => $style
|
|
)));
|
|
}
|
|
|
|
} |