home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Image / Canvas / SVG.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  32.9 KB  |  979 lines

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * Class for handling output in SVG format.
  7.  *
  8.  * LICENSE: This library is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU Lesser General Public License as published by
  10.  * the Free Software Foundation; either version 2.1 of the License, or (at your
  11.  * option) any later version. This library is distributed in the hope that it
  12.  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  13.  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
  14.  * General Public License for more details. You should have received a copy of
  15.  * the GNU Lesser General Public License along with this library; if not, write
  16.  * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  17.  * 02111-1307 USA
  18.  *
  19.  * @category   Images
  20.  * @package    Image_Canvas
  21.  * @author     Jesper Veggerby <pear.nosey@veggerby.dk>
  22.  * @copyright  Copyright (C) 2003, 2004 Jesper Veggerby Hansen
  23.  * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
  24.  * @version    CVS: $Id: SVG.php,v 1.17 2007/06/22 20:15:35 nosey Exp $
  25.  * @link       http://pear.php.net/pepr/pepr-proposal-show.php?id=212
  26.  */
  27.  
  28. /**
  29.  * Include file Image/Canvas.php
  30.  */
  31. require_once 'Image/Canvas.php';
  32.  
  33. /**
  34.  * Include file Image/Canvas/Color.php
  35.  */
  36. require_once 'Image/Canvas/Color.php';
  37.  
  38. /**
  39.  * SVG Canvas class.
  40.  * 
  41.  * @category   Images
  42.  * @package    Image_Canvas
  43.  * @author     Jesper Veggerby <pear.nosey@veggerby.dk>
  44.  * @copyright  Copyright (C) 2003, 2004 Jesper Veggerby Hansen
  45.  * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
  46.  * @version    Release: @package_version@
  47.  * @link       http://pear.php.net/pepr/pepr-proposal-show.php?id=212
  48.  */
  49. class Image_Canvas_SVG extends Image_Canvas
  50. {
  51.  
  52.     /**
  53.      * The SVG elements
  54.      * @var string
  55.      * @access private
  56.      */
  57.     var $_elements = '';
  58.  
  59.     /**
  60.      * The SVG defines
  61.      * @var string
  62.      * @access private
  63.      */
  64.     var $_defs = '';
  65.  
  66.     /**
  67.      * The current indention level
  68.      * @var string
  69.      * @access private
  70.      */
  71.     var $_indent = '    ';
  72.  
  73.     /**
  74.      * A unieuq id counter
  75.      * @var int
  76.      * @access private
  77.      */
  78.     var $_id = 1;
  79.  
  80.     /**
  81.      * The current group ids
  82.      * @var array
  83.      * @access private
  84.      */
  85.     var $_groupIDs = array();
  86.     
  87.     /**
  88.      * The XML encoding (default iso-8859-1)
  89.      * @var string
  90.      * @access private
  91.      */
  92.     var $_encoding = 'iso-8859-1';   
  93.  
  94.     /**
  95.      * Create the SVG canvas.
  96.      *
  97.      * Parameters available:
  98.      *
  99.      * 'width' The width of the graph
  100.      *
  101.      * 'height' The height of the graph
  102.      *
  103.      * 'encoding' The encoding of the SVG document
  104.      *
  105.      * @param array $param Parameter array
  106.      */
  107.     function Image_Canvas_SVG($params)
  108.     {
  109.         parent::Image_Canvas($params);
  110.         $this->_reset();
  111.  
  112.         if (isset($params['encoding'])) {
  113.             $this->_encoding = $params['encoding'];
  114.         }
  115.     }
  116.  
  117.     /**
  118.      * Add a SVG "element" to the output
  119.      *
  120.      * @param string $element The element
  121.      * @access private
  122.      */
  123.     function _addElement($element, $params = array()) {
  124.         $elementdata = $this->_indent . $element . "\n";
  125.         
  126.         if (isset($params['url'])) {
  127.             $url = $params['url'];
  128.             $target = (isset($params['target']) ? $params['target'] : false);
  129.             $alt = (isset($params['alt']) ? $params['alt'] : false);
  130.             
  131.             $tags = '';
  132.             if (isset($params['htmltags'])) {
  133.                 foreach ($params['htmltags'] as $key => $value) {
  134.                     $tags .= ' ';
  135.                     if (strpos($value, '"') >= 0) {
  136.                         $tags .= $key . '=\'' . $value . '\'';
  137.                     } else {
  138.                         $tags .= $key . '="' . $value . '"';
  139.                     }
  140.                 }
  141.             }
  142.             
  143.             $elementdata =
  144.                 $this->_indent . '<a xlink:href="' . $url . '"' . ($target ? ' target="' . $target . '"' : '') . '>' . "\n" .
  145.                 '    ' . $elementdata .
  146.                 $this->_indent . '</a>' . "\n";                           
  147.         }
  148.        
  149.         
  150.         $this->_elements .= $elementdata;
  151.     }
  152.  
  153.     /**
  154.      * Add a SVG "define" to the output
  155.      *
  156.      * @param string $def The define
  157.      * @access private
  158.      */
  159.     function _addDefine($def) {
  160.         $this->_defs .= '        ' . $def . "\n";
  161.     }
  162.  
  163.     /**
  164.      * Get the color index for the RGB color
  165.      *
  166.      * @param int $color The color
  167.      * @return int A SVG compatible color
  168.      * @access private
  169.      */
  170.     function _color($color = false)
  171.     {
  172.         if ($color === false) {
  173.             return 'transparent';
  174.         } else {
  175.             $color = Image_Canvas_Color::color2RGB($color);
  176.             return 'rgb(' . $color[0] . ',' . $color[1] . ',' . $color[2] . ')';
  177.         }
  178.     }
  179.  
  180.     /**
  181.      * Get the opacity for the RGB color
  182.      *
  183.      * @param int $color The color
  184.      * @return int A SVG compatible opacity value
  185.      * @access private
  186.      */
  187.     function _opacity($color = false)
  188.     {
  189.         if ($color === false) {
  190.             return false;
  191.         } else {
  192.             $color = Image_Canvas_Color::color2RGB($color);
  193.             if ($color[3] != 255) {
  194.                 return sprintf('%0.1f', $color[3]/255);
  195.             } else {
  196.                 return false;
  197.             }
  198.         }
  199.     }
  200.  
  201.     /**
  202.      * Get the SVG applicable linestyle
  203.      *
  204.      * @param mixed $lineStyle The line style to return, false if the one
  205.      *   explicitly set
  206.      * @return mixed A SVG compatible linestyle
  207.      * @access private
  208.      */
  209.     function _getLineStyle($lineStyle = false)
  210.     {
  211.         $result = '';
  212.         if ($lineStyle === false) {
  213.             $lineStyle = $this->_lineStyle;
  214.         }
  215.  
  216.         // TODO Linestyles (i.e. fx. dotted) does not work
  217.  
  218.         if (($lineStyle != 'transparent') && ($lineStyle !== false)) {
  219.             $result = 'stroke-width:' . $this->_thickness . ';';
  220.             $result .= 'stroke:' .$this->_color($lineStyle) . ';';
  221.             if ($opacity = $this->_opacity($lineStyle)) {
  222.                 $result .= 'stroke-opacity:' . $opacity . ';';
  223.             }
  224.         }
  225.         return $result;
  226.     }
  227.  
  228.     /**
  229.      * Get the SVG applicable fillstyle
  230.      *
  231.      * @param mixed $fillStyle The fillstyle to return, false if the one
  232.      *   explicitly set
  233.      * @return mixed A SVG compatible fillstyle
  234.      * @access private
  235.      */
  236.     function _getFillStyle($fillStyle = false)
  237.     {
  238.         $result = '';
  239.         if ($fillStyle === false) {
  240.             $fillStyle = $this->_fillStyle;
  241.         }
  242.  
  243.         if (is_array($fillStyle)) {
  244.             if ($fillStyle['type'] == 'gradient') {
  245.                 $id = 'gradient_' . ($this->_id++);
  246.                 $startColor = $this->_color($fillStyle['start']);
  247.                 $endColor = $this->_color($fillStyle['end']);
  248.                 $startOpacity = $this->_opacity($fillStyle['start']);
  249.                 $endOpacity = $this->_opacity($fillStyle['end']);
  250.  
  251.                 switch ($fillStyle['direction']) {
  252.                 case 'horizontal':
  253.                 case 'horizontal_mirror':
  254.                     $x1 = '0%';
  255.                     $y1 = '0%';
  256.                     $x2 = '100%';
  257.                     $y2 = '0%';
  258.                     break;
  259.  
  260.                 case 'vertical':
  261.                 case 'vertical_mirror':
  262.                     $x1 = '0%';
  263.                     $y1 = '100%';
  264.                     $x2 = '0%';
  265.                     $y2 = '0%';
  266.                     break;
  267.  
  268.                 case 'diagonal_tl_br':
  269.                     $x1 = '0%';
  270.                     $y1 = '0%';
  271.                     $x2 = '100%';
  272.                     $y2 = '100%';
  273.                     break;
  274.  
  275.                 case 'diagonal_bl_tr':
  276.                     $x1 = '0%';
  277.                     $y1 = '100%';
  278.                     $x2 = '100%';
  279.                     $y2 = '0%';
  280.                     break;
  281.  
  282.                 case 'radial':
  283.                     $cx = '50%';
  284.                     $cy = '50%';
  285.                     $r = '100%';
  286.                     $fx = '50%';
  287.                     $fy = '50%';
  288.                     break;
  289.  
  290.                 }
  291.  
  292.                 if ($fillStyle['direction'] == 'radial') {
  293.                     $this->_addDefine(
  294.                         '<radialGradient id="' . $id . '" cx="' .
  295.                             $cx .'" cy="' . $cy .'" r="' . $r .'" fx="' .
  296.                             $fx .'" fy="' . $fy .'">'
  297.                     );
  298.                     $this->_addDefine(
  299.                         '    <stop offset="0%" style="stop-color:' .
  300.                             $startColor. ';' . ($startOpacity ? 'stop-opacity:' .
  301.                             $startOpacity . ';' : ''). '"/>'
  302.                     );
  303.                     $this->_addDefine(
  304.                         '    <stop offset="100%" style="stop-color:' .
  305.                             $endColor. ';' . ($endOpacity ? 'stop-opacity:' .
  306.                             $endOpacity . ';' : ''). '"/>'
  307.                     );
  308.                     $this->_addDefine(
  309.                         '</radialGradient>'
  310.                     );
  311.                 } elseif (($fillStyle['direction'] == 'vertical_mirror') ||
  312.                     ($fillStyle['direction'] == 'horizontal_mirror'))
  313.                 {
  314.                     $this->_addDefine(
  315.                         '<linearGradient id="' . $id . '" x1="' .
  316.                             $x1 .'" y1="' . $y1 .'" x2="' . $x2 .'" y2="' .
  317.                             $y2 .'">'
  318.                     );
  319.                     $this->_addDefine(
  320.                         '    <stop offset="0%" style="stop-color:' .
  321.                             $startColor. ';' . ($startOpacity ? 'stop-opacity:' .
  322.                             $startOpacity . ';' : ''). '"/>'
  323.                     );
  324.                     $this->_addDefine(
  325.                         '    <stop offset="50%" style="stop-color:' .
  326.                             $endColor. ';' . ($endOpacity ? 'stop-opacity:' .
  327.                             $endOpacity . ';' : ''). '"/>'
  328.                     );
  329.                     $this->_addDefine(
  330.                         '    <stop offset="100%" style="stop-color:' .
  331.                             $startColor. ';' . ($startOpacity ? 'stop-opacity:' .
  332.                             $startOpacity . ';' : ''). '"/>'
  333.                     );
  334.                     $this->_addDefine(
  335.                         '</linearGradient>'
  336.                     );
  337.                 } else {
  338.                     $this->_addDefine(
  339.                         '<linearGradient id="' . $id . '" x1="' .
  340.                             $x1 .'" y1="' . $y1 .'" x2="' . $x2 .'" y2="' .
  341.                             $y2 .'">'
  342.                     );
  343.                     $this->_addDefine(
  344.                         '    <stop offset="0%" style="stop-color:' .
  345.                             $startColor. ';' . ($startOpacity ? 'stop-opacity:' .
  346.                             $startOpacity . ';' : ''). '"/>'
  347.                     );
  348.                     $this->_addDefine(
  349.                         '    <stop offset="100%" style="stop-color:' .
  350.                             $endColor. ';' . ($endOpacity ? 'stop-opacity:' .
  351.                             $endOpacity . ';' : ''). '"/>'
  352.                     );
  353.                     $this->_addDefine(
  354.                         '</linearGradient>'
  355.                     );
  356.                 }
  357.  
  358.                 return 'fill:url(#' . $id . ');';
  359.             }
  360.         } elseif (($fillStyle != 'transparent') && ($fillStyle !== false)) {
  361.             $result = 'fill:' . $this->_color($fillStyle) . ';';
  362.             if ($opacity = $this->_opacity($fillStyle)) {
  363.                 $result .= 'fill-opacity:' . $opacity . ';';
  364.             }
  365.             return $result;
  366.         } else {
  367.             return 'fill:none;';
  368.         }
  369.     }
  370.  
  371.     /**
  372.      * Sets an image that should be used for filling
  373.      *
  374.      * @param string $filename The filename of the image to fill with
  375.      */
  376.     function setFillImage($filename)
  377.     {
  378.     }
  379.  
  380.     /**
  381.      * Sets a gradient fill
  382.      *
  383.      * @param array $gradient Gradient fill options
  384.      */
  385.     function setGradientFill($gradient)
  386.     {
  387.         $this->_fillStyle = $gradient;
  388.         $this->_fillStyle['type'] = 'gradient';
  389.     }
  390.  
  391.     /**
  392.      * Sets the font options.
  393.      *
  394.      * The $font array may have the following entries:
  395.      * 'type' = 'ttf' (TrueType) or omitted for default<br>
  396.      * If 'type' = 'ttf' then the following can be specified<br>
  397.      * 'size' = size in pixels<br>
  398.      * 'angle' = the angle with which to write the text
  399.      * 'file' = the .ttf file (either the basename, filename or full path)
  400.      *
  401.      * @param array $font The font options.
  402.      */
  403.     function setFont($fontOptions)
  404.     {
  405.         parent::setFont($fontOptions);
  406.         if (!isset($this->_font['size'])) {
  407.             $this->_font['size'] = 10;
  408.         }
  409.     }
  410.  
  411.     /**
  412.      * Parameter array:
  413.      * 'x0': int X start point
  414.      * 'y0': int Y start point
  415.      * 'x1': int X end point
  416.      * 'y1': int Y end point
  417.      * 'color': mixed [optional] The line color
  418.      * @param array $params Parameter array
  419.      */
  420.     function line($params)
  421.     {
  422.         $x0 = $this->_getX($params['x0']);
  423.         $y0 = $this->_getY($params['y0']);
  424.         $x1 = $this->_getX($params['x1']);
  425.         $y1 = $this->_getY($params['y1']);
  426.         $color = (isset($params['color']) ? $params['color'] : false);
  427.  
  428.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  429.  
  430.         $style = $this->_getLineStyle($color) . $this->_getFillStyle('transparent');
  431.         if ($style != '') {
  432.             $this->_addElement(
  433.                 '<line ' .
  434.                     'x1="' . round($x0) . '" ' .
  435.                     'y1="' . round($y0) . '" ' .
  436.                     'x2="' . round($x1) . '" ' .
  437.                     'y2="' . round($y1) . '" ' .
  438.                     'style="' . $style . '"' .
  439.                     ($attrs ? ' ' . $attrs : '') .
  440.                 '/>',
  441.                 $params
  442.             );
  443.         }
  444.         parent::line($params);
  445.     }
  446.  
  447.     /**
  448.      * Parameter array:
  449.      * 'connect': bool [optional] Specifies whether the start point should be
  450.      *   connected to the endpoint (closed polygon) or not (connected line)
  451.      * 'fill': mixed [optional] The fill color
  452.      * 'line': mixed [optional] The line color
  453.      * @param array $params Parameter array
  454.      */
  455.     function polygon($params = array())
  456.     {
  457.         $connectEnds = (isset($params['connect']) ? $params['connect'] : false);
  458.         $fillColor = (isset($params['fill']) ? $params['line'] : false);
  459.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  460.  
  461.         if (!$connectEnds) {
  462.             $fillColor = 'transparent';
  463.         }
  464.         $style = $this->_getLineStyle($lineColor) . $this->_getFillStyle($fillColor);
  465.  
  466.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  467.  
  468.         $first = true;
  469.         $spline = false;
  470.         $lastpoint = false;
  471.         foreach($this->_polygon as $point) {
  472.             if ($first) {
  473.                 $points = 'M';
  474.             } elseif (!$spline) {
  475.                 $points .= ' L';
  476.             }
  477.             
  478.             if (($spline) && ($lastpoint !== false)) {
  479.                 $points .= ' ' .round($lastpoint['P1X']) . ',' . round($lastpoint['P1Y']) . ' ' .
  480.                            round($lastpoint['P2X']) . ',' . round($lastpoint['P2Y']);
  481.             } 
  482.  
  483.             $points .= ' ' . round($point['X']) . ',' . round($point['Y']);
  484.  
  485.             if ((isset($point['P1X'])) && (isset($point['P1Y'])) &&
  486.                 (isset($point['P2X'])) && (isset($point['P2Y'])))
  487.             {
  488.                 if (($first) || (!$spline)) {
  489.                     $points .= ' C';
  490.                 }
  491.                 $lastpoint = $point;
  492.                 $spline = true;
  493.             } else {
  494.                 $spline = false;
  495.             }
  496.             $first = false;
  497.         }
  498.         if ($connectEnds) {
  499.             $points .= ' Z';
  500.         }
  501.         $this->_addElement(
  502.             '<path ' .
  503.                  'd="' . $points . '" ' .
  504.                  'style="' . $style . '"' .
  505.                  ($attrs ? ' ' . $attrs : '') .
  506.             '/>',
  507.             $params
  508.         );
  509.  
  510.         parent::polygon($params);
  511.     }
  512.  
  513.     /**
  514.      * Draw a rectangle
  515.      *
  516.      * Parameter array:
  517.      * 'x0': int X start point
  518.      * 'y0': int Y start point
  519.      * 'x1': int X end point
  520.      * 'y1': int Y end point
  521.      * 'fill': mixed [optional] The fill color
  522.      * 'line': mixed [optional] The line color
  523.      * @param array $params Parameter array
  524.      */
  525.     function rectangle($params)
  526.     {
  527.         $x0 = min($this->_getX($params['x0']), $this->_getX($params['x1']));
  528.         $y0 = min($this->_getY($params['y0']), $this->_getY($params['y1']));
  529.         $x1 = max($this->_getX($params['x0']), $this->_getX($params['x1']));
  530.         $y1 = max($this->_getY($params['y0']), $this->_getY($params['y1']));
  531.         $fillColor = (isset($params['fill']) ? $params['line'] : false);
  532.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  533.  
  534.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  535.         
  536.         $style = $this->_getLineStyle($lineColor) . $this->_getFillStyle($fillColor);
  537.         if ($style != '') {
  538.             $this->_addElement(
  539.                 '<rect ' .
  540.                     'x="' . round($x0) . '" ' .
  541.                     'y="' . round($y0) . '" ' .
  542.                     'width="' . round(abs($x1 - $x0)) . '" ' .
  543.                     'height="' . round(abs($y1 - $y0)) . '" ' .
  544.                     'style="' . $style . '"' .
  545.                     ($attrs ? ' ' . $attrs : '') .
  546.                 '/>',
  547.                 $params
  548.             );
  549.         }
  550.         parent::rectangle($params);
  551.     }
  552.  
  553.     /**
  554.      * Draw an ellipse
  555.      *
  556.      * Parameter array:
  557.      * 'x': int X center point
  558.      * 'y': int Y center point
  559.      * 'rx': int X radius
  560.      * 'ry': int Y radius
  561.      * 'fill': mixed [optional] The fill color
  562.      * 'line': mixed [optional] The line color
  563.      * @param array $params Parameter array
  564.      */
  565.     function ellipse($params)
  566.     {
  567.         $x = $this->_getX($params['x']);
  568.         $y = $this->_getY($params['y']);
  569.         $rx = $this->_getX($params['rx']);
  570.         $ry = $this->_getY($params['ry']);
  571.         $fillColor = (isset($params['fill']) ? $params['line'] : false);
  572.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  573.  
  574.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  575.  
  576.         $style = $this->_getLineStyle($lineColor) . $this->_getFillStyle($fillColor);
  577.         if ($style != '') {
  578.             $this->_addElement(
  579.                 '<ellipse ' .
  580.                     'cx="' . round($x) . '" ' .
  581.                     'cy="' . round($y) . '" ' .
  582.                     'rx="' . round($rx) . '" ' .
  583.                     'ry="' . round($ry) . '" ' .
  584.                     'style="' . $style . '"' .
  585.                     ($attrs ? ' ' . $attrs : '') .
  586.                 '/>',
  587.                 $params
  588.             );
  589.         }
  590.         parent::ellipse($params);
  591.     }
  592.  
  593.     /**
  594.      * Draw a pie slice
  595.      *
  596.      * Parameter array:
  597.      * 'x': int X center point
  598.      * 'y': int Y center point
  599.      * 'rx': int X radius
  600.      * 'ry': int Y radius
  601.      * 'v1': int The starting angle (in degrees)
  602.      * 'v2': int The end angle (in degrees)
  603.      * 'srx': int [optional] Starting X-radius of the pie slice (i.e. for a doughnut)
  604.      * 'sry': int [optional] Starting Y-radius of the pie slice (i.e. for a doughnut)
  605.      * 'fill': mixed [optional] The fill color
  606.      * 'line': mixed [optional] The line color
  607.      * @param array $params Parameter array
  608.      */
  609.     function pieslice($params)
  610.     {
  611.         $x = $this->_getX($params['x']);
  612.         $y = $this->_getY($params['y']);
  613.         $rx = $this->_getX($params['rx']);
  614.         $ry = $this->_getY($params['ry']);
  615.         $v1 = $this->_getX($params['v1']);
  616.         $v2 = $this->_getY($params['v2']);
  617.         $srx = (isset($params['srx']) ? $this->_getX($params['srx']) : false);
  618.         $sry = (isset($params['sry']) ? $this->_getX($params['sry']) : false);
  619.         $fillColor = (isset($params['fill']) ? $params['line'] : false);
  620.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  621.  
  622.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  623.  
  624.         $dv = max($v2, $v1) - min($v2, $v1);
  625.         if ($dv >= 360) {
  626.             $this->ellipse($params);
  627.         }
  628.         else {
  629.             $style = $this->_getLineStyle($lineColor) . $this->_getFillStyle($fillColor);
  630.             if ($style != '') {
  631.                 $x1 = ($x + $rx * cos(deg2rad(min($v1, $v2) % 360)));
  632.                 $y1 = ($y + $ry * sin(deg2rad(min($v1, $v2) % 360)));
  633.                 $x2 = ($x + $rx * cos(deg2rad(max($v1, $v2) % 360)));
  634.                 $y2 = ($y + $ry * sin(deg2rad(max($v1, $v2) % 360)));
  635.                 $this->_addElement(
  636.                     '<path d="' .
  637.                         'M' . round($x) . ',' . round($y) . ' ' .
  638.                         'L' . round($x1) . ',' . round($y1) . ' ' .
  639.                         'A' . round($rx) . ',' . round($ry) . ($dv > 180 ? ' 0 1,1 ' : ' 0 0,1 ') .
  640.                               round($x2) . ',' . round($y2) . ' ' .
  641.                         'Z" ' .
  642.                         'style="' . $style . '"' .
  643.                         ($attrs ? ' ' . $attrs : '') .
  644.                     '/>',
  645.                     $params
  646.                 );
  647.             }
  648.  
  649.             parent::pieslice($params);
  650.         }
  651.     }
  652.  
  653.     /**
  654.      * Get the width of a text,
  655.      *
  656.      * @param string $text The text to get the width of
  657.      * @return int The width of the text
  658.      */
  659.     function textWidth($text)
  660.     {
  661.         if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
  662.             return $this->_font['size'];
  663.         } else {
  664.             return round($this->_font['size'] * 0.7 * strlen($text));
  665.         }
  666.     }
  667.  
  668.     /**
  669.      * Get the height of a text,
  670.      *
  671.      * @param string $text The text to get the height of
  672.      * @return int The height of the text
  673.      */
  674.     function textHeight($text)
  675.     {
  676.         if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
  677.             return round($this->_font['size'] * 0.7 * strlen($text));
  678.         } else {
  679.             return $this->_font['size'];
  680.         }
  681.     }
  682.  
  683.     /**
  684.      * Writes text
  685.      *
  686.      * Parameter array:
  687.      * 'x': int X-point of text
  688.      * 'y': int Y-point of text
  689.      * 'text': string The text to add
  690.      * 'alignment': array [optional] Alignment
  691.      * 'color': mixed [optional] The color of the text
  692.      */
  693.     function addText($params)
  694.     {
  695.         $x = $this->_getX($params['x']);
  696.         $y = $this->_getY($params['y']);
  697.         $text = $params['text'];
  698.         $color = (isset($params['color']) ? $params['color'] : false);
  699.         $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
  700.  
  701.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  702.         
  703.         $textHeight = $this->textHeight($text);
  704.  
  705.         if (!is_array($alignment)) {
  706.             $alignment = array('vertical' => 'top', 'horizontal' => 'left');
  707.         }
  708.         
  709.         if (!isset($alignment['vertical'])) {
  710.             $alignment['vertical'] = 'top';
  711.         }
  712.         
  713.         if (!isset($alignment['horizontal'])) {
  714.             $alignment['horizontal'] = 'left';
  715.         }
  716.         
  717.         $align = '';
  718.  
  719.         if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
  720. //            $align .= 'writing-mode: tb-rl;';
  721.  
  722.             if ($alignment['vertical'] == 'bottom') {
  723.                 $align .= 'text-anchor:end;';
  724.                 //$y = $y + $textHeight;
  725.             } elseif ($alignment['vertical'] == 'center') {
  726.                 //$y = $y + ($textHeight / 2);
  727.                 $align .= 'text-anchor:middle;';
  728.             }
  729.         } else {
  730.             if ($alignment['horizontal'] == 'right') {
  731.                 $align .= 'text-anchor:end;';
  732.             } elseif ($alignment['horizontal'] == 'center') {
  733.                 $align .= 'text-anchor:middle;';
  734.             }
  735.  
  736.             if ($alignment['vertical'] == 'top') {
  737.                 $y = $y + $textHeight;
  738.             } elseif ($alignment['vertical'] == 'center') {
  739.                 $y = $y + ($textHeight / 2);
  740.             }
  741.         }
  742.  
  743.         if (($color === false) && (isset($this->_font['color']))) {
  744.             $color = $this->_font['color'];
  745.         }
  746.  
  747.         $textColor = $this->_color($color);
  748.         $textOpacity = $this->_opacity($color);
  749.  
  750.         $this->_addElement(
  751.             '<g transform="translate(' . round($x) . ', ' . round($y) . ')">' . "\n" .
  752.             $this->_indent . '    <text ' .
  753.                 'x="0" ' .
  754.                 'y="0" ' .
  755.                 (isset($this->_font['angle']) && ($this->_font['angle'] > 0) ?
  756.                     'transform="rotate(' . (($this->_font['angle'] + 180) % 360) . ')" ' :
  757.                     ''
  758.                 ) .
  759.                 'style="' .
  760.                 (isset($this->_font['name']) ?
  761.                     'font-family:' . $this->_font['name'] . ';' : '') .
  762.                         'font-size:' . $this->_font['size'] . 'px;fill:' .
  763.                         $textColor . ($textOpacity ? ';fill-opacity:' .
  764.                         $textOpacity :
  765.                     ''
  766.                 ) . ';' . $align . '"' . 
  767.                 ($attrs ? ' ' . $attrs : '') .
  768.                 '>' .
  769.                 htmlspecialchars($text) .
  770.             '</text>' . "\n" . 
  771.             $this->_indent . '</g>',
  772.             $params
  773.         );
  774.         parent::addText($params);
  775.     }
  776.  
  777.     /**
  778.      * Overlay image
  779.      *
  780.      * Parameter array:
  781.      * 'x': int X-point of overlayed image
  782.      * 'y': int Y-point of overlayed image
  783.      * 'filename': string The filename of the image to overlay
  784.      * 'width': int [optional] The width of the overlayed image (resizing if possible)
  785.      * 'height': int [optional] The height of the overlayed image (resizing if possible)
  786.      * 'alignment': array [optional] Alignment
  787.      */
  788.     function image($params)
  789.     {
  790.         $x = $this->_getX($params['x']);
  791.         $y = $this->_getY($params['y']);
  792.         $filename = $params['filename'];
  793.  
  794.         $attrs = (isset($params['attrs']) && is_array($params['attrs'])) ? $this->_getAttributes($params['attrs']) : null;
  795.         
  796.         list($width, $height, $type, $attr) = getimagesize($filename);        
  797.         $width = (isset($params['width']) ? $params['width'] : $width);
  798.         $height = (isset($params['height']) ? $params['height'] : $height);
  799.         $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
  800.  
  801.         $file = fopen($filename, 'rb');
  802.         $filedata = fread($file, filesize($filename));
  803.         fclose($file);
  804.                 
  805.         $data = 'data:' . image_type_to_mime_type($type) . ';base64,' . base64_encode($filedata);
  806.             
  807.         $this->_addElement(
  808.             '<image xlink:href="' . $data . '" x="' . $x . '" y="' . $y . '"' .
  809.                 ($width ? ' width="' . $width . '"' : '') .
  810.                 ($height ? ' height="' . $height . '"' : '') .
  811.                 ($attrs ? ' ' . $attrs : '') .
  812.             ' preserveAspectRatio="none"/>',
  813.             $params
  814.         );
  815.         parent::image($params);
  816.     }
  817.  
  818.     /**
  819.      * Start a group.
  820.      * 
  821.      * What this does, depends on the canvas/format.
  822.      *
  823.      * @param string $name The name of the group
  824.      */
  825.     function startGroup($name = false)
  826.     {
  827.         $name = strtolower(str_replace(' ', '_', $name));
  828.         if (in_array($name, $this->_groupIDs)) {
  829.             $name .= $this->_id;
  830.             $this->_id++;
  831.         }
  832.         $this->_groupIDs[] = $name;
  833.         $this->_addElement('<g id="' . htmlspecialchars($name) . '">');
  834.         $this->_indent .= '    ';        
  835.     }
  836.  
  837.     /**
  838.      * End the "current" group.
  839.      * 
  840.      * What this does, depends on the canvas/format.
  841.      */
  842.     function endGroup()
  843.     {
  844.         $this->_indent = substr($this->_indent, 0, -4);
  845.         $this->_addElement('</g>');
  846.     }
  847.  
  848.     /**
  849.      * Output the result of the canvas
  850.      *
  851.      * @param array $param Parameter array
  852.      */
  853.     function show($param = false)
  854.     {
  855.         parent::show($param);
  856.         
  857.         $attrs = (isset($param['attrs']) && is_array($param['attrs'])) ? $this->_getAttributes($param['attrs']) : null;
  858.         
  859.         $output = $this->getData($param);
  860.  
  861.         header('Content-Type: image/svg+xml');
  862.         header('Content-Disposition: inline; filename = "' . basename($_SERVER['PHP_SELF'], '.php') . '.svg"');
  863.         print $output;
  864.     }
  865.  
  866.     /**
  867.      * Output the result of the canvas
  868.      *
  869.      * @param array $param Parameter array
  870.      */
  871.     function save($param = false)
  872.     {
  873.         parent::save($param);
  874.  
  875.         $output = $this->getData($param);
  876.         
  877.         $file = fopen($param['filename'], 'w+');
  878.         fwrite($file, $output);
  879.         fclose($file);
  880.     }
  881.     
  882.     
  883.     /**
  884.      * Get SVG data
  885.      *
  886.      * @param array $param Parameter array
  887.      *
  888.      * @return string
  889.      */
  890.     function getData($param = false)
  891.     {
  892.         $attrs = (isset($param['attrs']) && is_array($param['attrs'])) ? $this->_getAttributes($param['attrs']) : null;
  893.  
  894.         return '<?xml version="1.0" encoding="'. $this->_encoding . '"?>' . "\n" .
  895.             '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"' . "\n\t" .
  896.             ' "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">' . "\n" .
  897.             '<svg width="' . $this->_width . '" height="' . $this->_height .
  898.                 '" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"' .
  899.                 ($attrs ? ' ' . $attrs : '') .
  900.                 '>' . "\n" .
  901.             ($this->_defs ?
  902.                 '    <defs>' . "\n" .
  903.                 $this->_defs .
  904.                 '    </defs>' . "\n" :
  905.                 ''
  906.             ) .
  907.             $this->_elements .
  908.             '</svg>';
  909.      }
  910.  
  911.     /**
  912.      * Set clipping to occur
  913.      * 
  914.      * Parameter array:
  915.      * 
  916.      * 'x0': int X point of Upper-left corner
  917.      * 'y0': int X point of Upper-left corner
  918.      * 'x1': int X point of lower-right corner
  919.      * 'y1': int Y point of lower-right corner
  920.      */
  921.     function setClipping($params = false) 
  922.     {
  923.         if ($params === false) {
  924.             $this->_addElement('</g>');
  925.         }        
  926.         else {
  927.             $group = "clipping_" . $this->_id;
  928.             $this->_id++;
  929.             $this->_addElement('<g clip-path="url(#' . $group . ')">');
  930.             
  931.             $this->_addDefine('<clipPath id="' . $group . '">');
  932.             $this->_addDefine('    <path d="' .
  933.                 'M' . $this->_getX($params['x0']) . ' ' . $this->_getY($params['y0']) . 
  934.                 ' H' . $this->_getX($params['x1']) . 
  935.                 ' V' . $this->_getY($params['y1']) . 
  936.                 ' H' . $this->_getX($params['x0']) . 
  937.                 ' Z"/>');
  938.             $this->_addDefine('</clipPath>');            
  939.         }
  940.     }
  941.     
  942.     /**
  943.      * Get a canvas specific HTML tag.
  944.      * 
  945.      * This method implicitly saves the canvas to the filename in the 
  946.      * filesystem path specified and parses it as URL specified by URL path
  947.      * 
  948.      * Parameter array:
  949.      * 'filename': string
  950.      * 'filepath': string Path to the file on the file system. Remember the final slash
  951.      * 'urlpath': string Path to the file available through an URL. Remember the final slash
  952.      * 'width': int The width in pixels
  953.      * 'height': int The height in pixels
  954.      */
  955.     function toHtml($params)
  956.     {
  957.         parent::toHtml($params);
  958.         return '<embed src="' . $params['urlpath'] . $params['filename'] . '" width=' . $params['width'] . ' height=' . $params['height'] . ' type="image/svg+xml">';        
  959.     }
  960.  
  961.     /**
  962.      * Converts array of attributes to string
  963.      *
  964.      * @param array $attrs Attributes array
  965.      * @return array
  966.      */
  967.     function _getAttributes($attrs)
  968.     {    
  969.         $string = '';
  970.         
  971.         foreach ($attrs as $key => $value) {
  972.             $string .= ' ' . $key . '="' . $value . '"';
  973.         }
  974.         
  975.         return $string;
  976.     }
  977. }
  978.  
  979. ?>