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 / GD.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  63.3 KB  |  1,793 lines

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * Image_Canvas
  7.  *
  8.  * Class for handling output in GD compatible format.
  9.  * 
  10.  * Supported formats are PNG, JPEG, GIF and WBMP.
  11.  *
  12.  * Requires PHP extension GD
  13.  *
  14.  * PHP versions 4 and 5
  15.  *
  16.  * LICENSE: This library is free software; you can redistribute it and/or modify
  17.  * it under the terms of the GNU Lesser General Public License as published by
  18.  * the Free Software Foundation; either version 2.1 of the License, or (at your
  19.  * option) any later version. This library is distributed in the hope that it
  20.  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  21.  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
  22.  * General Public License for more details. You should have received a copy of
  23.  * the GNU Lesser General Public License along with this library; if not, write
  24.  * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  25.  * 02111-1307 USA
  26.  *
  27.  * @category   Images
  28.  * @package    Image_Canvas
  29.  * @author     Jesper Veggerby <pear.nosey@veggerby.dk>
  30.  * @copyright  Copyright (C) 2003, 2004 Jesper Veggerby Hansen
  31.  * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
  32.  * @version    CVS: $Id: GD.php,v 1.16 2007/06/22 20:19:54 nosey Exp $
  33.  * @link       http://pear.php.net/pepr/pepr-proposal-show.php?id=212
  34.  */
  35.  
  36. /**
  37.  * Include file Image/Canvas.php
  38.  */
  39. require_once 'Image/Canvas/WithMap.php';
  40.  
  41. /**
  42.  * Include file Image/Canvas/Color.php
  43.  */
  44. require_once 'Image/Canvas/Color.php';
  45.  
  46. /**
  47.  * Canvas class to output using PHP GD support.
  48.  * 
  49.  * @category   Images
  50.  * @package    Image_Canvas
  51.  * @author     Jesper Veggerby <pear.nosey@veggerby.dk>
  52.  * @copyright  Copyright (C) 2003, 2004 Jesper Veggerby Hansen
  53.  * @license    http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
  54.  * @version    Release: @package_version@
  55.  * @link       http://pear.php.net/pepr/pepr-proposal-show.php?id=212
  56.  * @abstract
  57.  */
  58. class Image_Canvas_GD extends Image_Canvas_WithMap
  59. {
  60.  
  61.     /**
  62.      * The canvas of the graph
  63.      * @var resource
  64.      * @access private
  65.      */
  66.     var $_canvas;
  67.  
  68.     /**
  69.      * The canvas to use for tiled filling
  70.      * @var resource
  71.      * @access private
  72.      */
  73.     var $_tileImage = null;
  74.  
  75.     /**
  76.      * Is version GD2 installed?
  77.      * @var bool
  78.      * @access private
  79.      */
  80.     var $_gd2 = true;
  81.  
  82.     /**
  83.      * Antialiasing?
  84.      * 
  85.      * Possible values 'off', 'driver' and 'native'
  86.      * 
  87.      * @var string
  88.      * @access private
  89.      */
  90.     var $_antialias = 'off';
  91.     
  92.     var $_alpha = false;
  93.         
  94.     var $_clipping = array();
  95.  
  96.     /**
  97.      * Create the GD canvas.
  98.      *
  99.      * Parameters available:
  100.      *
  101.      * 'width' The width of the graph on the canvas
  102.      *
  103.      * 'height' The height of the graph on the canvas
  104.      *
  105.      * 'left' The left offset of the graph on the canvas
  106.      *
  107.      * 'top' The top offset of the graph on the canvas
  108.      * 
  109.      * 'antialias' = 'native' enables native GD antialiasing - this
  110.      * method has no severe impact on performance (approx +5%). Requires PHP
  111.      * 4.3.2 (with bundled GD2)
  112.      * 
  113.      * 'antialias' = {true|'driver'} Image_Graph implemented method. This method
  114.      * has a severe impact on performance, drawing an antialiased line this
  115.      * way is about XX times slower, with an overall performance impact of
  116.      * about +40%. The justification for this method is that if native support
  117.      * is not available this can be used, it is also a future feature that this
  118.      * method for antialiasing will support line styles.
  119.      * 
  120.      * Use antialiased for best results with a line/area chart having just a few
  121.      * datapoints. Native antialiasing does not provide a good appearance with
  122.      * short lines, as for example with smoothed charts. Antialiasing does not
  123.      * (currently) work with linestyles, neither native nor driver method!
  124.      * 
  125.      * 'noalpha' = true If alpha blending is to be disabled
  126.      *
  127.      * 'filename' An image to open, on which the graph is created on
  128.      *
  129.      * 'gd' A GD resource to add the image to, use this option to continue
  130.      * working on an already existing GD resource. Make sure this is passed 'by-
  131.      * reference' (using &)
  132.      * 
  133.      * 'usemap' Initialize an image map
  134.      *
  135.      * 'gd' and 'filename' are mutually exclusive with 'gd' as preference
  136.      *
  137.      * 'width' and 'height' are required unless 'filename' or 'gd' are
  138.      * specified, in which case the width and height are taken as the actual
  139.      * image width/height. If the latter is the case and 'left' and/or 'top' was
  140.      * also specified, the actual 'width'/'height' are altered so that the graph
  141.      * fits inside the canvas (i.e 'height' = actual height - top, etc.)
  142.      *
  143.      * @param array $param Parameter array
  144.      */
  145.     function Image_Canvas_GD($param)
  146.     {
  147.         include_once 'Image/Canvas/Color.php';
  148.  
  149.         parent::Image_Canvas_WithMap($param);
  150.         
  151.         $this->_gd2 = ($this->_version() == 2);
  152.         $this->_font = array('font' => 1, 'color' => 'black');
  153.  
  154.         if ((isset($param['gd'])) && (is_resource($param['gd']))) {
  155.             $this->_canvas =& $param['gd'];
  156.         } elseif (isset($param['filename'])) {
  157.             $this->_canvas =& $this->_getGD($param['filename']);
  158.         } else {
  159.             if ($this->_gd2) {
  160.                 $this->_canvas = ImageCreateTrueColor(
  161.                     $this->_width,
  162.                     $this->_height
  163.                 );
  164.                 if ((!isset($param['noalpha'])) || ($param['noalpha'] !== true)) {
  165.                     ImageAlphaBlending($this->_canvas, true);
  166.                     $this->_alpha = true;
  167.                 }
  168.             } else {
  169.                 $this->_canvas = ImageCreate($this->_width, $this->_height);
  170.             }
  171.         }
  172.         
  173.         if (isset($param['antialias'])) {
  174.             $this->_antialias = $param['antialias'];
  175.         }
  176.         
  177.         if ($this->_antialias === true) {
  178.             $this->_antialias = 'driver';
  179.         }
  180.         
  181.         if (($this->_gd2) && ($this->_antialias === 'native') && (function_exists('ImageAntialias'))) {
  182.             ImageAntialias($this->_canvas, true);
  183.         }
  184.     }
  185.  
  186.     /**
  187.      * Get an GD image resource from a file
  188.      *
  189.      * @param string $filename
  190.      * @return mixed The GD image resource
  191.      * @access private
  192.      */
  193.     function &_getGD($filename)
  194.     {
  195.         $info = getimagesize($filename);
  196.         
  197.         $result = null;
  198.         switch($info[2]) {
  199.         case IMG_PNG:
  200.             $result =& ImageCreateFromPNG($filename);
  201.             break;
  202.             
  203.         case IMG_JPG:
  204.             $result =& ImageCreateFromJPEG($filename);
  205.             break;
  206.             
  207.         case IMG_GIF:
  208.             $result =& ImageCreateFromGIF($filename);
  209.             break;
  210.         }
  211.         return $result;
  212.     }
  213.  
  214.     /**
  215.      * Get the color index for the RGB color
  216.      *
  217.      * @param int $color The color
  218.      * @return int The GD image index of the color
  219.      * @access private
  220.      */
  221.     function _color($color = false)
  222.     {
  223.         if (($color === false) || ($color === 'opague') || ($color === 'transparent')) {
  224.             return ImageColorTransparent($this->_canvas);
  225.         } else {
  226.             return Image_Canvas_Color::allocateColor($this->_canvas, $color);
  227.         }
  228.     }
  229.  
  230.     /**
  231.      * Get the GD applicable linestyle
  232.      *
  233.      * @param mixed $lineStyle The line style to return, false if the one
  234.      *   explicitly set
  235.      * @return mixed A GD compatible linestyle
  236.      * @access private
  237.      */
  238.     function _getLineStyle($lineStyle = false)
  239.     {
  240.         if ($this->_gd2) {
  241.             ImageSetThickness($this->_canvas, $this->_thickness);
  242.         }
  243.  
  244.         if ($lineStyle == 'transparent') {
  245.             return false;
  246.         } elseif ($lineStyle === false) {
  247.             if (is_array($this->_lineStyle)) {
  248.                 $colors = array();
  249.                 foreach ($this->_lineStyle as $color) {
  250.                     if ($color === 'transparent') {
  251.                         $color = false;
  252.                     }
  253.                     $colors[] = $this->_color($color);
  254.                 }
  255.                 ImageSetStyle($this->_canvas, $colors);
  256.                 return IMG_COLOR_STYLED;
  257.             } else {
  258.                 return $this->_color($this->_lineStyle);
  259.             }
  260.         } else {
  261.             return $this->_color($lineStyle);
  262.         }
  263.     }
  264.  
  265.     /**
  266.      * Get the GD applicable fillstyle
  267.      *
  268.      * @param mixed $fillStyle The fillstyle to return, false if the one
  269.      *   explicitly set
  270.      * @return mixed A GD compatible fillstyle
  271.      * @access private
  272.      */
  273.     function _getFillStyle($fillStyle = false, $x0 = 0, $y0 = 0, $x1 = 0, $y1 = 0)
  274.     {
  275.         if ($this->_tileImage != null) {
  276.             ImageDestroy($this->_tileImage);
  277.             $this->_tileImage = null;
  278.         }
  279.         if ($fillStyle == 'transparent') {
  280.             return false;
  281.         } elseif ($fillStyle === false) {
  282.             if (is_resource($this->_fillStyle)) {
  283.                 $x = min($x0, $x1);
  284.                 $y = min($y0, $y1);
  285.                 $w = abs($x1 - $x0) + 1;
  286.                 $h = abs($y1 - $y0) + 1;
  287.                 if ($this->_gd2) {
  288.                     $this->_tileImage = ImageCreateTrueColor(
  289.                         $this->getWidth(),
  290.                         $this->getHeight()
  291.                     );
  292.  
  293.                     ImageCopyResampled(
  294.                         $this->_tileImage,
  295.                         $this->_fillStyle,
  296.                         $x,
  297.                         $y,
  298.                         0,
  299.                         0,
  300.                         $w,
  301.                         $h,
  302.                         ImageSX($this->_fillStyle),
  303.                         ImageSY($this->_fillStyle)
  304.                     );
  305.                 } else {
  306.                     $this->_tileImage = ImageCreate(
  307.                         $this->getWidth(),
  308.                         $this->getHeight()
  309.                     );
  310.  
  311.                     ImageCopyResized(
  312.                         $this->_tileImage,
  313.                         $this->_fillStyle,
  314.                         $x,
  315.                         $y,
  316.                         0,
  317.                         0,
  318.                         $w,
  319.                         $h,
  320.                         ImageSX($this->_fillStyle),
  321.                         ImageSY($this->_fillStyle)
  322.                     );
  323.                 }
  324.                 ImageSetTile($this->_canvas, $this->_tileImage);
  325.                 return IMG_COLOR_TILED;
  326.             } elseif ((is_array($this->_fillStyle)) && (isset($this->_fillStyle['direction']))) {
  327.                 $width = abs($x1 - $x0) + 1;
  328.                 $height = abs($y1 - $y0) + 1;
  329.  
  330.                 switch ($this->_fillStyle['direction']) {
  331.                 case 'horizontal':
  332.                     $count = $width;
  333.                     break;
  334.  
  335.                 case 'vertical':
  336.                     $count = $height;
  337.                     break;
  338.  
  339.                 case 'horizontal_mirror':
  340.                     $count = $width / 2;
  341.                     break;
  342.  
  343.                 case 'vertical_mirror':
  344.                     $count = $height / 2;
  345.                     break;
  346.  
  347.                 case 'diagonal_tl_br':
  348.                 case 'diagonal_bl_tr':
  349.                     $count = sqrt($width * $width + $height * $height);
  350.                     break;
  351.  
  352.                 case 'radial':
  353.                     $count = max($width, $height, sqrt($width * $width + $height * $height)) + 1;
  354.                     break;
  355.  
  356.                 }
  357.  
  358.                 $count = round($count);
  359.  
  360.                 if ($this->_gd2) {
  361.                     $this->_tileImage = ImageCreateTrueColor(
  362.                         $this->getWidth(),
  363.                         $this->getHeight()
  364.                     );
  365.                 } else {
  366.                     $this->_tileImage = ImageCreate(
  367.                         $this->getWidth(),
  368.                         $this->getHeight()
  369.                     );
  370.                 }
  371.  
  372.  
  373.                 $startColor = Image_Canvas_Color::color2RGB(
  374.                     ($this->_fillStyle['direction'] == 'radial' ?
  375.                         $this->_fillStyle['end'] :
  376.                         $this->_fillStyle['start']
  377.                     )
  378.                 );
  379.                 $endColor = Image_Canvas_Color::color2RGB(
  380.                     ($this->_fillStyle['direction'] == 'radial' ?
  381.                         $this->_fillStyle['start'] :
  382.                         $this->_fillStyle['end']
  383.                     )
  384.                 );
  385.  
  386.                 $redIncrement = ($endColor[0] - $startColor[0]) / $count;
  387.                 $greenIncrement = ($endColor[1] - $startColor[1]) / $count;
  388.                 $blueIncrement = ($endColor[2] - $startColor[2]) / $count;
  389.  
  390.                 $color = false;
  391.                 for ($i = 0; $i < $count; $i ++) {
  392.                     unset($color);
  393.                     if ($i == 0) {
  394.                         $color = $startColor;
  395.                         unset($color[3]);
  396.                     } else {
  397.                         $color[0] = round(($redIncrement * $i) +
  398.                             $redIncrement + $startColor[0]);
  399.                         $color[1] = round(($greenIncrement * $i) +
  400.                             $greenIncrement + $startColor[1]);
  401.                         $color[2] = round(($blueIncrement * $i) +
  402.                             $blueIncrement + $startColor[2]);
  403.                     }
  404.                     $color = Image_Canvas_Color::allocateColor(
  405.                         $this->_tileImage,
  406.                         $color
  407.                     );
  408.  
  409.                     switch ($this->_fillStyle['direction']) {
  410.                     case 'horizontal':
  411.                         ImageLine($this->_tileImage,
  412.                             $x0 + $i,
  413.                             $y0,
  414.                             $x0 + $i,
  415.                             $y1, $color);
  416.                         break;
  417.  
  418.                     case 'vertical':
  419.                         ImageLine($this->_tileImage,
  420.                             $x0,
  421.                             $y1 - $i,
  422.                             $x1,
  423.                             $y1 - $i, $color);
  424.                         break;
  425.  
  426.                     case 'horizontal_mirror':
  427.                         if (($x0 + $i) <= ($x1 - $i)) {
  428.                             ImageLine($this->_tileImage,
  429.                                 $x0 + $i,
  430.                                 $y0,
  431.                                 $x0 + $i,
  432.                                 $y1, $color);
  433.  
  434.                             ImageLine($this->_tileImage,
  435.                                 $x1 - $i,
  436.                                 $y0,
  437.                                 $x1 - $i,
  438.                                 $y1, $color);
  439.                         }
  440.                         break;
  441.  
  442.                     case 'vertical_mirror':
  443.                         if (($y0 + $i) <= ($y1 - $i)) {
  444.                             ImageLine($this->_tileImage,
  445.                                 $x0,
  446.                                 $y0 + $i,
  447.                                 $x1,
  448.                                 $y0 + $i, $color);
  449.                             ImageLine($this->_tileImage,
  450.                                 $x0,
  451.                                 $y1 - $i,
  452.                                 $x1,
  453.                                 $y1 - $i, $color);
  454.                         }
  455.                         break;
  456.  
  457.                     case 'diagonal_tl_br':
  458.                         if (($i > $width) && ($i > $height)) {
  459.                             $polygon = array (
  460.                                 $x1, $y0 + $i - $width - 1,
  461.                                 $x1, $y1,
  462.                                 $x0 + $i - $height - 1, $y1);
  463.                         } elseif ($i > $width) {
  464.                             $polygon = array (
  465.                                 $x0, $y0 + $i,
  466.                                 $x0, $y1,
  467.                                 $x1, $y1,
  468.                                 $x1, $y0 + $i - $width - 1);
  469.                         } elseif ($i > $height) {
  470.                             $polygon = array (
  471.                                 $x0 + $i - $height - 1, $y1,
  472.                                 $x1, $y1,
  473.                                 $x1, $y0,
  474.                                 $x0 + $i, $y0);
  475.                         } else {
  476.                             $polygon = array (
  477.                                 $x0, $y0 + $i,
  478.                                 $x0, $y1,
  479.                                 $x1, $y1,
  480.                                 $x1, $y0,
  481.                                 $x0 + $i, $y0);
  482.                         }
  483.                         ImageFilledPolygon(
  484.                             $this->_tileImage,
  485.                             $polygon,
  486.                             count($polygon) / 2,
  487.                             $color
  488.                         );
  489.                         break;
  490.  
  491.                     case 'diagonal_bl_tr':
  492.                         if (($i > $width) && ($i > $height)) {
  493.                             $polygon = array (
  494.                                 $x1, $y1 - $i + $width - 1,
  495.                                 $x1, $y0,
  496.                                 $x0 + $i - $height - 1, $y0);
  497.                         } elseif ($i > $width) {
  498.                             $polygon = array (
  499.                                 $x0, $y1 - $i,
  500.                                 $x0, $y0,
  501.                                 $x1, $y0,
  502.                                 $x1, $y1 - $i + $width - 1);
  503.                         } elseif ($i > $height) {
  504.                             $polygon = array (
  505.                                 $x0 + $i - $height - 1, $y0,
  506.                                 $x1, $y0,
  507.                                 $x1, $y1,
  508.                                 $x0 + $i, $y1);
  509.                         } else {
  510.                             $polygon = array (
  511.                                 $x0, $y1 - $i,
  512.                                 $x0, $y0,
  513.                                 $x1, $y0,
  514.                                 $x1, $y1,
  515.                                 $x0 + $i, $y1);
  516.                         }
  517.                         ImageFilledPolygon(
  518.                             $this->_tileImage,
  519.                             $polygon,
  520.                             count($polygon) / 2,
  521.                             $color
  522.                         );
  523.                         break;
  524.  
  525.                     case 'radial':
  526.                         if (($this->_gd2) && ($i < $count)) {
  527.                             ImageFilledEllipse(
  528.                                 $this->_tileImage,
  529.                                 $x0 + $width / 2,
  530.                                 $y0 + $height / 2,
  531.                                 $count - $i,
  532.                                 $count - $i,
  533.                                 $color
  534.                             );
  535.                         }
  536.                         break;
  537.                     }
  538.                 }
  539.                 ImageSetTile($this->_canvas, $this->_tileImage);
  540.                 return IMG_COLOR_TILED;
  541.             } else {
  542.                 return $this->_color($this->_fillStyle);
  543.             }
  544.         } else {
  545.             return $this->_color($fillStyle);
  546.         }
  547.     }
  548.  
  549.     /**
  550.      * Sets an image that should be used for filling
  551.      *
  552.      * @param string $filename The filename of the image to fill with
  553.      */
  554.     function setFillImage($filename)
  555.     {
  556.         $this->_fillStyle =& $this->_getGD($filename);
  557.     }
  558.  
  559.     /**
  560.      * Sets the font options.
  561.      *
  562.      * The $font array may have the following entries:
  563.      *
  564.      * 'ttf' = the .ttf file (either the basename, filename or full path)
  565.      * If 'ttf' is specified, then the following can be specified
  566.      *
  567.      * 'size' = size in pixels
  568.      *
  569.      * 'angle' = the angle with which to write the text
  570.      *
  571.      * @param array $font The font options.
  572.      */
  573.     function setFont($fontOptions)
  574.     {
  575.         parent::setFont($fontOptions);
  576.  
  577.         if (isset($this->_font['ttf'])) {
  578.             $this->_font['file'] = str_replace('\\', '/', Image_Canvas_Tool::fontMap($this->_font['ttf']));
  579.         } elseif (!isset($this->_font['font'])) {
  580.             $this->_font['font'] = 1;
  581.         }
  582.  
  583.         if (!isset($this->_font['color'])) {
  584.             $this->_font['color'] = 'black';
  585.         }
  586.  
  587.         if ((isset($this->_font['angle'])) && ($this->_font['angle'] === false)) {
  588.             $this->_font['angle'] = 0;
  589.         }
  590.     }
  591.  
  592.     /**
  593.      * Calculate pixels on a line
  594.      *
  595.      * @param int $x0 X start point
  596.      * @param int $y0 X start point
  597.      * @param int $x1 X end point
  598.      * @param int $y1 Y end point
  599.      * @return array An associated array of x,y points with all pixels on the
  600.      * line    
  601.      * @access private
  602.      */
  603.     function &_linePixels($x0, $y0, $x1, $y1)
  604.     {
  605.         $pixels = array();
  606.         if (abs($x0 - $x1) > abs($y0 - $y1)) {
  607.             if ($x1 != $x0) {
  608.                 $m = ($y1 - $y0) / ($x1 - $x0);
  609.             } else {
  610.                 $m = 0;
  611.             }  
  612.             $b = $y0 - $m * $x0;
  613.             $strx = min($x0, $x1);
  614.             $endx = max($x0, $x1);
  615.             for ($x = $strx; $x <= $endx; $x++) {
  616.                 $pixels[] = array('X' => $x, 'Y' => ($m * $x + $b));                
  617.             }
  618.         } else {
  619.             if ($y1 != $y0) {
  620.                 $m = ($x1 - $x0) / ($y1 - $y0);
  621.             } else {
  622.                 $m = 0;
  623.             }
  624.             $b = $x0 - $m * $y0;
  625.             $stry = min($y0, $y1);
  626.             $endy = max($y0, $y1);
  627.             for ($y = $stry; $y <= $endy; $y++) {
  628.                 $pixels[] = array('X' => ($m * $y + $b), 'Y' => $y);                
  629.             }
  630.         }
  631.         return $pixels;
  632.     }
  633.  
  634.     /**
  635.      * Draws an antialiased line
  636.      *
  637.      * @param int $x0 X start point
  638.      * @param int $y0 X start point
  639.      * @param int $x1 X end point
  640.      * @param int $y1 Y end point
  641.      * @param mixed $color The line color, can be omitted
  642.      * @access private
  643.      */
  644.     function _antialiasedLine($x0, $y0, $x1, $y1, $color = false)
  645.     {
  646.         if (($line = $this->_getLineStyle($color)) !== false) {
  647.             if ($line >= 0) {
  648.                 $line = ImageColorsForIndex($this->_canvas, $line);
  649.                 $pixels = &$this->_linePixels($x0, $y0, $x1, $y1);
  650.                 foreach ($pixels as $point) {
  651.                     $this->_antialiasedPixel($point['X'], $point['Y'], $line);
  652.                 }
  653.                 unset($pixels);
  654.             }
  655.         }
  656.     }
  657.  
  658.  
  659.     /**
  660.      * Draws an antialiased pixel
  661.      *
  662.      * @param int $x X point
  663.      * @param int $y Y point
  664.      * @param mixed $color The pixel color
  665.      * @access private
  666.      */
  667.     function _antialiasedPixel($x, $y, $color)
  668.     {
  669.         $fx = floor($x);
  670.         $fy = floor($y);
  671.         $cx = ceil($x);
  672.         $cy = ceil($y);
  673.         $xa = $x - $fx;
  674.         $xb = $cx - $x;
  675.         $ya = $y - $fy;
  676.         $yb = $cy - $y;
  677.         if (($cx == $fx) && ($cy == $fy)) {
  678.             $this->_antialisedSubPixel($fx, $fy, 0.0, 1.0, $color);
  679.         } else {
  680.             $this->_antialisedSubPixel($fx, $fy, $xa + $ya, $xb + $yb, $color);
  681.             if ($cy != $fy) {
  682.                 $this->_antialisedSubPixel($fx, $cy, $xa + $yb, $xb + $ya, $color);
  683.             }
  684.             if ($cx != $fx) {
  685.                 $this->_antialisedSubPixel($cx, $fy, $xb + $ya, $xa + $yb, $color);
  686.                 if ($cy != $fy) {
  687.                     $this->_antialisedSubPixel($cx, $cy, $xb + $yb, $xa + $ya, $color);
  688.                 }
  689.             }
  690.         }
  691.     }
  692.  
  693.     /**
  694.      * Antialias'es the pixel around x,y with weights a,b
  695.      *
  696.      * @param int $x X point
  697.      * @param int $y Y point
  698.      * @param int $a The weight of the current color
  699.      * @param int $b The weight of the applied/wanted color
  700.      * @param mixed $color The pixel color
  701.      * @access private
  702.      */
  703.     function _antialisedSubPixel($x, $y, $a, $b, $color)
  704.     {
  705.         $x = $this->_getX($x);
  706.         $y = $this->_getX($y);
  707.         if (($x >=0 ) && ($y >= 0) && ($x < $this->getWidth()) && ($y < $this->getHeight())) {
  708.             $tempColor = ImageColorsForIndex($this->_canvas, ImageColorAt($this->_canvas, $x, $y));                
  709.             
  710.             $newColor[0] = min(255, round($tempColor['red'] * $a + $color['red'] * $b));        
  711.             $newColor[1] = min(255, round($tempColor['green'] * $a + $color['green'] * $b));        
  712.             $newColor[2] = min(255, round($tempColor['blue'] * $a + $color['blue'] * $b));
  713.             //$newColor[3] = 0;
  714.             $color = '#';
  715.             foreach ($newColor as $acolor) {
  716.                 $color .= sprintf('%02s', dechex($acolor));
  717.             }
  718.             $newColor = $this->_color($color);//,'rgb(' . $newColor[0] . ',' . $newColor[1] . ','  . $newColor[2] .')';        
  719.     
  720.             ImageSetPixel($this->_canvas, $x, $y, $newColor);
  721.         }
  722.     }
  723.     
  724.     
  725.     /**
  726.      * Draw a line end
  727.      *
  728.      * Parameter array:
  729.      * 
  730.      * 'x': int X point
  731.      * 
  732.      * 'y': int Y point
  733.      * 
  734.      * 'end': string The end type of the end
  735.      * 
  736.      * 'size': int The size of the end
  737.      * 
  738.      * 'color': string The color of the end
  739.      * 
  740.      * 'angle': int [optional] The angle with which to draw the end
  741.      * 
  742.      * @param array $params Parameter array
  743.      */
  744.     function drawEnd($params) 
  745.     {        
  746.         $x = $this->_getX($params['x']);
  747.         $y = $this->_getY($params['y']);
  748.         $size = $params['size'];
  749.         //var_dump($params);
  750.         $angle = deg2rad((isset($params['angle']) ? $params['angle'] : 0));
  751.         $pi2 = pi() / 2;
  752.         switch ($params['end']) {
  753.         case 'lollipop':
  754.         case 'circle':
  755.             $this->ellipse(
  756.                 array(
  757.                     'x' => $x,
  758.                     'y' => $y,
  759.                     'rx' => $size / 2,
  760.                     'ry' => $size / 2,
  761.                     'fill' => $params['color'],
  762.                     'line' => $params['color']                    
  763.                 )
  764.             );
  765.             break;
  766.         case 'diamond':
  767.             $x0 = round($params['x'] + cos($angle) * $size * 0.65);
  768.             $y0 = round($params['y'] - sin($angle) * $size * 0.65);
  769.             $shape = array(
  770.                 $x0 + round(cos($angle) * $size * 0.65),
  771.                 $y0 - round(sin($angle) * $size * 0.65),
  772.                 $x0 + round(cos($angle + $pi2) * $size * 0.65),
  773.                 $y0 - round(sin($angle + $pi2) * $size * 0.65),
  774.                 $x0 + round(cos($angle + pi()) * $size * 0.65),
  775.                 $y0 - round(sin($angle + pi()) * $size * 0.65),
  776.                 $x0 + round(cos($angle + 3 * $pi2) * $size * 0.65),
  777.                 $y0 - round(sin($angle + 3 * $pi2) * $size * 0.65)
  778.             );
  779.             break;
  780.         case 'line':
  781.             $this->line(
  782.                 array(
  783.                     'x0' => $x + round(cos($angle + $pi2) * $size / 2),
  784.                     'y0' => $y - round(sin($angle + $pi2) * $size / 2),
  785.                     'x1' => $x + round(cos($angle + 3 * $pi2) * $size / 2),
  786.                     'y1' => $y - round(sin($angle + 3 * $pi2) * $size / 2),
  787.                     'color' => $params['color']
  788.                 )
  789.             );
  790.             break;
  791.         case 'box':
  792.         case 'rectangle':
  793.             $x0 = round($params['x'] + cos($angle) * $size / 2);
  794.             $y0 = round($params['y'] - sin($angle) * $size / 2);
  795.             $pi4 = pi() / 4;            
  796.             $shape = array(
  797.                 $x0 + round(cos($angle + $pi4) * $size / 2),
  798.                 $y0 - round(sin($angle + $pi4) * $size / 2),
  799.                 $x0 + round(cos($angle + $pi2 + $pi4) * $size / 2),
  800.                 $y0 - round(sin($angle + $pi2 + $pi4) * $size / 2),
  801.                 $x0 + round(cos($angle + pi() + $pi4) * $size / 2),
  802.                 $y0 - round(sin($angle + pi() + $pi4) * $size / 2),
  803.                 $x0 + round(cos($angle + 3 * $pi2 + $pi4) * $size / 2),
  804.                 $y0 - round(sin($angle + 3 * $pi2 + $pi4) * $size / 2)
  805.             );
  806.             break;
  807.         case 'arrow':  
  808.             $shape = array(
  809.                 $x + cos($angle) * $size,
  810.                 $y - sin($angle) * $size,
  811.                 $x + cos($angle + $pi2) * $size * 0.4,
  812.                 $y - sin($angle + $pi2) * $size * 0.4,
  813.                 $x + cos($angle + 3 * $pi2) * $size * 0.4,
  814.                 $y - sin($angle + 3 * $pi2) * $size * 0.4,                
  815.             );
  816.             break;
  817.         case 'arrow2':  
  818.             $shape = array(
  819.                 $x + round(cos($angle) * $size),
  820.                 $y - round(sin($angle) * $size),
  821.                 $x + round(cos($angle + $pi2 + deg2rad(45)) * $size),
  822.                 $y - round(sin($angle + $pi2 + deg2rad(45)) * $size),
  823.                 $x,
  824.                 $y,
  825.                 $x + round(cos($angle + 3 * $pi2 - deg2rad(45)) * $size),
  826.                 $y - round(sin($angle + 3 * $pi2 - deg2rad(45)) * $size),                
  827.             );
  828.             break;
  829.         }
  830.         
  831.         if (isset($shape)) {
  832.             // output the shape
  833.             if (($fill = $this->_getFillStyle($params['color'])) !== false) {
  834.                 ImageFilledPolygon($this->_canvas, $shape, count($shape)/2, $fill);
  835.             }
  836.         }
  837.         parent::drawEnd($params);
  838.     }    
  839.     
  840.     /**
  841.      * Draw a line
  842.      *
  843.      * Parameter array:
  844.      * 
  845.      * 'x0': int X start point
  846.      * 
  847.      * 'y0': int Y start point
  848.      * 
  849.      * 'x1': int X end point
  850.      * 
  851.      * 'y1': int Y end point
  852.      * 
  853.      * 'color': mixed [optional] The line color
  854.      * 
  855.      * @param array $params Parameter array
  856.      */
  857.     function line($params)
  858.     {
  859.         $x0 = $this->_getX($params['x0']);
  860.         $y0 = $this->_getY($params['y0']);
  861.         $x1 = $this->_getX($params['x1']);
  862.         $y1 = $this->_getY($params['y1']);
  863.         $color = (isset($params['color']) ? $params['color'] : false);
  864.         
  865.         $x0 = $this->_getX($x0);
  866.         $y0 = $this->_getY($y0);
  867.         $x1 = $this->_getX($x1);
  868.         $y1 = $this->_getY($y1);
  869.         if (($this->_antialias === 'driver') && ($x0 != $x1) && ($y0 != $y1)) {
  870.             $this->_antialiasedLine($x0, $y0, $x1, $y1, $color);
  871.         } elseif (($line = $this->_getLineStyle($color)) !== false) {            
  872.             ImageLine($this->_canvas, $x0, $y0, $x1, $y1, $line);
  873.         }
  874.         parent::line($params);
  875.     }
  876.  
  877.     /**
  878.      * Parameter array:
  879.      * 
  880.      * 'connect': bool [optional] Specifies whether the start point should be
  881.      * connected  to the endpoint (closed polygon) or not (connected line)
  882.      * 
  883.      * 'fill': mixed [optional] The fill color
  884.      * 
  885.      * 'line': mixed [optional] The line color
  886.      * @param array $params Parameter array
  887.      */
  888.     function polygon($params)
  889.     {
  890.         include_once 'Image/Canvas/Tool.php';
  891.  
  892.         $connectEnds = (isset($params['connect']) ? $params['connect'] : false);
  893.         $fillColor = (isset($params['fill']) ? $params['fill'] : false);
  894.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  895.  
  896.         if (!$connectEnds) {
  897.             $fillColor = 'transparent';
  898.         }
  899.         $style = $this->_getLineStyle($lineColor) . $this->_getFillStyle($fillColor);
  900.  
  901.         $lastPoint = false;
  902.         foreach ($this->_polygon as $point) {
  903.             if (($lastPoint) && (isset($lastPoint['P1X'])) &&
  904.                 (isset($lastPoint['P1Y'])) && (isset($lastPoint['P2X'])) &&
  905.                 (isset($lastPoint['P2Y'])))
  906.             {
  907.                 $dx = abs($point['X'] - $lastPoint['X']);
  908.                 $dy = abs($point['Y'] - $lastPoint['Y']);
  909.                 $d = sqrt($dx * $dx + $dy * $dy);
  910.                 if ($d > 0) {
  911.                     $interval = 1 / $d;
  912.                     for ($t = 0; $t <= 1; $t = $t + $interval) {
  913.                         $x = Image_Canvas_Tool::bezier(
  914.                             $t,
  915.                             $lastPoint['X'],
  916.                             $lastPoint['P1X'],
  917.                             $lastPoint['P2X'],
  918.                             $point['X']
  919.                         );
  920.     
  921.                         $y = Image_Canvas_Tool::bezier(
  922.                             $t,
  923.                             $lastPoint['Y'],
  924.                             $lastPoint['P1Y'],
  925.                             $lastPoint['P2Y'],
  926.                             $point['Y']
  927.                         );
  928.     
  929.                         if (!isset($low['X'])) {
  930.                             $low['X'] = $x;
  931.                         } else {
  932.                             $low['X'] = min($x, $low['X']);
  933.                         }
  934.                         if (!isset($high['X'])) {
  935.                             $high['X'] = $x;
  936.                         } else {
  937.                             $high['X'] = max($x, $high['X']);
  938.                         }
  939.                         if (!isset($low['Y'])) {
  940.                             $low['Y'] = $y;
  941.                         } else {
  942.                             $low['Y'] = min($y, $low['Y']);
  943.                         }
  944.                         if (!isset($high['Y'])) {
  945.                             $high['Y'] = $y;
  946.                         } else {
  947.                             $high['Y'] = max($y, $high['Y']);
  948.                         }
  949.                         $polygon[] = $x;
  950.                         $polygon[] = $y;
  951.                     }
  952.                     if (($t - $interval) < 1) {
  953.                         $x = Image_Canvas_Tool::bezier(
  954.                             1,
  955.                             $lastPoint['X'],
  956.                             $lastPoint['P1X'],
  957.                             $lastPoint['P2X'],
  958.                             $point['X']
  959.                         );
  960.     
  961.                         $y = Image_Canvas_Tool::bezier(
  962.                             1,
  963.                             $lastPoint['Y'],
  964.                             $lastPoint['P1Y'],
  965.                             $lastPoint['P2Y'],
  966.                             $point['Y']
  967.                         );
  968.     
  969.                         $polygon[] = $x;
  970.                         $polygon[] = $y;
  971.                     }
  972.                 }
  973.             } else {
  974.                 if (!isset($low['X'])) {
  975.                     $low['X'] = $point['X'];
  976.                 } else {
  977.                     $low['X'] = min($point['X'], $low['X']);
  978.                 }
  979.                 if (!isset($high['X'])) {
  980.                     $high['X'] = $point['X'];
  981.                 } else {
  982.                     $high['X'] = max($point['X'], $high['X']);
  983.                 }
  984.                 if (!isset($low['Y'])) {
  985.                     $low['Y'] = $point['Y'];
  986.                 } else {
  987.                     $low['Y'] = min($point['Y'], $low['Y']);
  988.                 }
  989.                 if (!isset($high['Y'])) {
  990.                     $high['Y'] = $point['Y'];
  991.                 } else {
  992.                     $high['Y'] = max($point['Y'], $high['Y']);
  993.                 }
  994.  
  995.                 $polygon[] = $point['X'];
  996.                 $polygon[] = $point['Y'];
  997.             }
  998.             $lastPoint = $point;
  999.         }
  1000.  
  1001.         if ((isset($polygon)) && (is_array($polygon))) {
  1002.             if ($connectEnds) {
  1003.                 if (($fill = $this->_getFillStyle($fillColor, $low['X'], $low['Y'], $high['X'], $high['Y'])) !== false) {
  1004.                     ImageFilledPolygon($this->_canvas, $polygon, count($polygon)/2, $fill);
  1005.                 }
  1006.                 if ($this->_antialias === 'driver') {
  1007.                     $pfirst = $p0 = false; 
  1008.                     reset($polygon);
  1009.                     
  1010.                     while (list(, $x) = each($polygon)) {
  1011.                         list(, $y) = each($polygon);
  1012.                         if ($p0 !== false) {
  1013.                             $this->_antialiasedLine($p0['X'], $p0['Y'], $x, $y, $lineColor);
  1014.                         }
  1015.                         if ($pfirst === false) {
  1016.                             $pfirst = array('X' => $x, 'Y' => $y);
  1017.                         }                        
  1018.                         $p0 = array('X' => $x, 'Y' => $y);;
  1019.                     }
  1020.                     
  1021.                     $this->_antialiasedLine($p0['X'], $p0['Y'], $pfirst['X'], $pfirst['Y'], $lineColor);
  1022.                 } elseif (($line = $this->_getLineStyle($lineColor)) !== false) {
  1023.                     ImagePolygon($this->_canvas, $polygon, count($polygon)/2, $line);
  1024.                 }
  1025.             } else {
  1026.                 $prev_point = false;
  1027.                 if ($this->_antialias === 'driver') {
  1028.                     reset($polygon);
  1029.                     while (list(, $x) = each($polygon)) {
  1030.                         list(, $y) = each($polygon);
  1031.                         if ($prev_point) {
  1032.                             $this->_antialiasedLine(
  1033.                                 $prev_point['X'],
  1034.                                 $prev_point['Y'],
  1035.                                 $x,
  1036.                                 $y,
  1037.                                 $lineColor
  1038.                             );                            
  1039.                         }
  1040.                         $prev_point = array('X' => $x, 'Y' => $y);;
  1041.                     }
  1042.                 } elseif (($line = $this->_getLineStyle($lineColor)) !== false) {
  1043.                     reset($polygon);
  1044.                     while (list(, $x) = each($polygon)) {
  1045.                         list(, $y) = each($polygon);
  1046.                         if ($prev_point) {
  1047.                             ImageLine(
  1048.                                 $this->_canvas,
  1049.                                 $prev_point['X'],
  1050.                                 $prev_point['Y'],
  1051.                                 $x,
  1052.                                 $y,
  1053.                                 $line
  1054.                             );
  1055.                         }
  1056.                         $prev_point = array('X' => $x, 'Y' => $y);;
  1057.                     }
  1058.                 }
  1059.             }
  1060.         }
  1061.  
  1062.         parent::polygon($params);
  1063.     }
  1064.  
  1065.     /**
  1066.      * Draw a rectangle
  1067.      *
  1068.      * Parameter array:
  1069.      * 
  1070.      * 'x0': int X start point
  1071.      * 
  1072.      * 'y0': int Y start point
  1073.      * 
  1074.      * 'x1': int X end point
  1075.      * 
  1076.      * 'y1': int Y end point
  1077.      * 
  1078.      * 'fill': mixed [optional] The fill color
  1079.      * 
  1080.      * 'line': mixed [optional] The line color
  1081.      * 
  1082.      * @param array $params Parameter array
  1083.      */
  1084.     function rectangle($params)
  1085.     {
  1086.         $x0 = $this->_getX($params['x0']);
  1087.         $y0 = $this->_getY($params['y0']);
  1088.         $x1 = $this->_getX($params['x1']);
  1089.         $y1 = $this->_getY($params['y1']);
  1090.         $fillColor = (isset($params['fill']) ? $params['fill'] : false);
  1091.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  1092.  
  1093.         if (($fill = $this->_getFillStyle($fillColor, $x0, $y0, $x1, $y1)) !== false) {
  1094.             ImageFilledRectangle($this->_canvas, $x0, $y0, $x1, $y1, $fill);
  1095.         }
  1096.  
  1097.         if (($line = $this->_getLineStyle($lineColor)) !== false) {
  1098.             ImageRectangle($this->_canvas, $x0, $y0, $x1, $y1, $line);
  1099.         }
  1100.  
  1101.         parent::rectangle($params);
  1102.     }
  1103.  
  1104.     /**
  1105.      * Draw an ellipse
  1106.      *
  1107.      * Parameter array:
  1108.      * 
  1109.      * 'x': int X center point
  1110.      * 
  1111.      * 'y': int Y center point
  1112.      * 
  1113.      * 'rx': int X radius
  1114.      * 
  1115.      * 'ry': int Y radius
  1116.      * 
  1117.      * 'fill': mixed [optional] The fill color
  1118.      * 
  1119.      * 'line': mixed [optional] The line color
  1120.      * 
  1121.      * @param array $params Parameter array
  1122.      */
  1123.     function ellipse($params)
  1124.     {
  1125.         $x = $this->_getX($params['x']);
  1126.         $y = $this->_getY($params['y']);
  1127.         $rx = $this->_getX($params['rx']);
  1128.         $ry = $this->_getY($params['ry']);
  1129.         $fillColor = (isset($params['fill']) ? $params['fill'] : false);
  1130.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  1131.  
  1132.         if (($fill = $this->_getFillStyle($fillColor, $x - $rx, $y - $ry, $x + $rx, $y + $ry)) !== false) {
  1133.             ImageFilledEllipse($this->_canvas, $x, $y, $rx * 2, $ry * 2, $fill);
  1134.         }
  1135.  
  1136.         if (($line = $this->_getLineStyle($lineColor)) !== false) {
  1137.             ImageEllipse($this->_canvas, $x, $y, $rx * 2, $ry * 2, $line);
  1138.         }
  1139.         parent::ellipse($params);
  1140.     }
  1141.  
  1142.     /**
  1143.      * Draw a pie slice
  1144.      *
  1145.      * Parameter array:
  1146.      * 
  1147.      * 'x': int X center point
  1148.      * 
  1149.      * 'y': int Y center point
  1150.      * 
  1151.      * 'rx': int X radius
  1152.      * 
  1153.      * 'ry': int Y radius
  1154.      * 
  1155.      * 'v1': int The starting angle (in degrees)
  1156.      * 
  1157.      * 'v2': int The end angle (in degrees)
  1158.      * 
  1159.      * 'srx': int [optional] Starting X-radius of the pie slice (i.e. for a doughnut)
  1160.      * 
  1161.      * 'sry': int [optional] Starting Y-radius of the pie slice (i.e. for a doughnut)
  1162.      * 
  1163.      * 'fill': mixed [optional] The fill color
  1164.      * 
  1165.      * 'line': mixed [optional] The line color
  1166.      * 
  1167.      * @param array $params Parameter array
  1168.      */
  1169.     function pieslice($params)
  1170.     {
  1171.         $x = $this->_getX($params['x']);
  1172.         $y = $this->_getY($params['y']);
  1173.         $rx = $params['rx'];
  1174.         $ry = $params['ry'];
  1175.         $v1 = $params['v1'];
  1176.         $v2 = $params['v2'];
  1177.         $srx = (isset($params['srx']) ? $params['srx'] : 0);
  1178.         $sry = (isset($params['sry']) ? $params['sry'] : 0);
  1179.         $fillColor = (isset($params['fill']) ? $params['fill'] : false);
  1180.         $lineColor = (isset($params['line']) ? $params['line'] : false);
  1181.  
  1182.         $dA = 0.1;
  1183.  
  1184.         if (($srx !== false) && ($sry !== false)) {
  1185.             $angle = max($v1, $v2);
  1186.             while ($angle >= min($v1, $v2)) {
  1187.                 $polygon[] = ($x + $srx * cos(deg2rad($angle % 360)));
  1188.                 $polygon[] = ($y + $sry * sin(deg2rad($angle % 360)));
  1189.                 $angle -= $dA;
  1190.             }
  1191.             if (($angle + $dA) > min($v1, $v2)) {
  1192.                 $polygon[] = ($x + $srx * cos(deg2rad(min($v1, $v2) % 360)));
  1193.                 $polygon[] = ($y + $sry * sin(deg2rad(min($v1, $v2) % 360)));
  1194.             }
  1195.         } else {
  1196.             $polygon[] = $x;
  1197.             $polygon[] = $y;
  1198.         }
  1199.  
  1200.         $angle = min($v1, $v2);
  1201.         while ($angle <= max($v1, $v2)) {
  1202.             $polygon[] = ($x + $rx * cos(deg2rad($angle % 360)));
  1203.             $polygon[] = ($y + $ry * sin(deg2rad($angle % 360)));
  1204.             $angle += $dA;
  1205.         }
  1206.  
  1207.         if (($angle - $dA) < max($v1, $v2)) {
  1208.             $polygon[] = ($x + $rx * cos(deg2rad(max($v1, $v2) % 360)));
  1209.             $polygon[] = ($y + $ry * sin(deg2rad(max($v1, $v2) % 360)));
  1210.         }
  1211.  
  1212.         if ((($fill = $this->_getFillStyle($fillColor, $x - $rx - 1, $y - $ry - 1, $x + $rx + 1, $y + $ry + 1)) !== false) && (count($polygon) > 2)) {
  1213.             ImageFilledPolygon($this->_canvas, $polygon, count($polygon) / 2, $fill);
  1214.         }
  1215.  
  1216.         if (($line = $this->_getLineStyle($lineColor)) !== false) {
  1217.             ImagePolygon($this->_canvas, $polygon, count($polygon) / 2, $line);
  1218.         }
  1219.  
  1220.         parent::pieSlice($params);
  1221.     }
  1222.  
  1223.     /**
  1224.      * Get the width of a text,
  1225.      *
  1226.      * @param string $text The text to get the width of
  1227.      * @return int The width of the text
  1228.      */
  1229.     function textWidth($text)
  1230.     {
  1231.         if (isset($this->_font['file'])) {
  1232.             $angle = 0;
  1233.             if (isset($this->_font['angle'])) {
  1234.                 $angle = $this->_font['angle'];
  1235.             }
  1236.             
  1237.             $width = 0;
  1238.             $lines = explode("\n", $text);
  1239.             foreach ($lines as $line) {
  1240.                 $bounds = ImageTTFBBox(
  1241.                     $this->_font['size'],
  1242.                     $angle,
  1243.                     $this->_font['file'],
  1244.                     $text
  1245.                 );
  1246.     
  1247.                 $x0 = min($bounds[0], $bounds[2], $bounds[4], $bounds[6]);
  1248.                 $x1 = max($bounds[0], $bounds[2], $bounds[4], $bounds[6]);
  1249.                 $width = max(abs($x0 - $x1), $width);
  1250.             }
  1251.             return $width;
  1252.         } else {
  1253.             if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
  1254.                 return ImageFontHeight($this->_font['font']) * (substr_count($text, "\n") + 1);
  1255.             } else {
  1256.                 $width = 0;
  1257.                 $lines = explode("\n", $text);
  1258.                 foreach ($lines as $line) {
  1259.                     $width = max($width, ImageFontWidth($this->_font['font']) * strlen($line));
  1260.                 }
  1261.                 return $width;
  1262.             }
  1263.         }
  1264.     }
  1265.  
  1266.     /**
  1267.      * Get the height of a text.
  1268.      * 
  1269.      * Note! This method can give some peculiar results, since ImageTTFBBox() returns the total
  1270.      * bounding box of a text, where ImageTTF() writes the text on the baseline of the text, that
  1271.      * is 'g', 'p', 'q' and other letters that dig under the baseline will appear to have a larger
  1272.      * height than they actually do. Have a look at the tests/text.php test case - the first two
  1273.      * columns, 'left and 'center', both look alright, whereas the last column, 'right', appear
  1274.      * with a larger space between the first text and the second. This is because the total height
  1275.      * is actually smaller by exactly the number of pixels that the 'g' digs under the baseline.
  1276.      * Remove the 'g' from the text and they appear correct. 
  1277.      *
  1278.      * @param string $text The text to get the height of
  1279.      * @param bool $force Force the method to calculate the size
  1280.      * @return int The height of the text
  1281.      */
  1282.     function textHeight($text, $force = false)
  1283.     {
  1284.         if (isset($this->_font['file'])) {
  1285.             $angle = 0;
  1286.             if (isset($this->_font['angle'])) {
  1287.                 $angle = $this->_font['angle'];
  1288.             }
  1289.             
  1290.             $linebreaks = substr_count($text, "\n"); 
  1291.             if (($angle == 0) && ($force === false)) {
  1292.                 /*
  1293.                  * if the angle is 0 simply return the size, due to different
  1294.                  * heights for example for x-axis labels, making the labels
  1295.                  * _not_ appear as written on the same baseline
  1296.                  */ 
  1297.                 return $this->_font['size'] + ($this->_font['size'] + 2) * $linebreaks;
  1298.             }
  1299.  
  1300.             $height = 0;
  1301.             $lines = explode("\n", $text);
  1302.             foreach ($lines as $line) {            
  1303.                 $bounds = ImageTTFBBox(
  1304.                     $this->_font['size'],
  1305.                     $angle,
  1306.                     $this->_font['file'],
  1307.                     $line
  1308.                 );
  1309.     
  1310.                 $y0 = min($bounds[1], $bounds[3], $bounds[5], $bounds[7]);
  1311.                 $y1 = max($bounds[1], $bounds[3], $bounds[5], $bounds[7]);
  1312.                 $height += abs($y0 - $y1);
  1313.             }
  1314.             return $height + $linebreaks * 2;
  1315.         } else {
  1316.             if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {
  1317.                 $width = 0;
  1318.                 $lines = explode("\n", $text);
  1319.                 foreach ($lines as $line) {
  1320.                     $width = max($width, ImageFontWidth($this->_font['font']) * strlen($line));
  1321.                 }
  1322.                 return $width;
  1323.             } else {
  1324.                 return ImageFontHeight($this->_font['font']) * (substr_count($text, "\n") + 1);
  1325.             }
  1326.         }
  1327.     }
  1328.     
  1329.     /**
  1330.      * Calculated the absolute bottom-left position of the text, by simulating
  1331.      * the calculation of the baseline drop.
  1332.      * @param int $x The relative x position to write the text 
  1333.      * @param int $y The relative y position to write the text
  1334.      * @param string $text The text to write 
  1335.      * @param array $align The alignment of the text relative to (x, y)
  1336.      * @returns array An array containing the absolute x and y positions
  1337.      * @access private
  1338.      */
  1339.     function _getAbsolutePosition($x, $y, $text, $align) 
  1340.     {
  1341.         if ($this->_font['angle'] > 0) {
  1342.             $w0 = $this->textWidth($text);
  1343.             $h0 = $this->textHeight($text);
  1344.             
  1345.             if ($align['vertical'] == 'bottom') {
  1346.                 $dy = $y - $h0;
  1347.             }
  1348.             else if ($align['vertical'] == 'center') {
  1349.                 $dy = $y - $h0 / 2;
  1350.             }
  1351.             else if ($align['vertical'] == 'top') {
  1352.                 $dy = $y;
  1353.             }
  1354.  
  1355.             if ($align['horizontal'] == 'right') {
  1356.                 $dx = $x - $w0;
  1357.             }
  1358.             else if ($align['horizontal'] == 'center') {
  1359.                 $dx = $x - $w0 / 2;
  1360.             }
  1361.             else if ($align['horizontal'] == 'left') {
  1362.                 $dx = $x;
  1363.             }
  1364.             
  1365.             if (($this->_font['angle'] < 180) && ($this->_font['angle'] >= 0)) {
  1366.                 $dy += $h0;
  1367.             }
  1368.             if (($this->_font['angle'] >= 90) && ($this->_font['angle'] < 270)) {
  1369.                 $dx += $w0;
  1370.             }            
  1371.         }
  1372.         else {       
  1373.             // get the maximum size of normal text above base line - sampled by 'Al'
  1374.             $size1 = imagettfbbox($this->_font['size'], 0, $this->_font['file'], 'Al');    
  1375.             $height1 = abs($size1[7] - $size1[1]);
  1376.             
  1377.             // get the maximum size of all text above base and below line - sampled by 'AlgjpqyQ'
  1378.             $size2 = imagettfbbox($this->_font['size'], 0, $this->_font['file'], 'AlgjpqyQ');
  1379.             $height2 = abs($size2[7] - $size2[1]);
  1380.         
  1381.             // get the size of the text, simulating height above baseline beinh max, by sampling using 'Al'
  1382.             $size = imagettfbbox($this->_font['size'], 0, $this->_font['file'], 'Al' . $text);
  1383.             $height = abs($size[7] - $size[1]);
  1384.             
  1385.             // if all text is above baseline, i.e. height of text compares to max height above (within 10%)     
  1386.             if (abs($height - $height1)/$height1 < 0.1) { 
  1387.                 $dHeight = 0;        
  1388.             }
  1389.             else {
  1390.                 $dHeight = abs($height2 - $height1);
  1391.             }
  1392.  
  1393.             // specifies the bottom-left corner!
  1394.             $dx = $x + sin(deg2rad($this->_font['angle'])) * $dHeight;
  1395.             $dy = $y - cos(deg2rad($this->_font['angle'])) * $dHeight;
  1396.  
  1397.             if ($align['vertical'] == 'top') {
  1398.                 $dy += $height;
  1399.             }
  1400.             else if ($align['vertical'] == 'center') {
  1401.                 $dy += ($height + $dHeight) / 2;
  1402.             }
  1403.             else if ($align['vertical'] == 'bottom') {
  1404.                 $dy += $dHeight;
  1405.             }
  1406.         
  1407.             if ($align['horizontal'] == 'center') {
  1408.                 $factor = 0.5;
  1409.             }
  1410.             else if ($align['horizontal'] == 'right') {
  1411.                 $factor = 1;
  1412.             }
  1413.             else {
  1414.                 $factor = 0;
  1415.             }
  1416.             
  1417.             if ($factor != 0) {
  1418.                 $size = imagettfbbox($this->_font['size'], 0, $this->_font['file'], $text);
  1419.                 $w0 = abs($size[2] - $size[0]);                
  1420.                 $dx -= cos(deg2rad($this->_font['angle'])) * $w0 * $factor;
  1421.                 $dy += sin(deg2rad($this->_font['angle'])) * $w0 * $factor;
  1422.             }
  1423.         }
  1424.                 
  1425.         return array('x' => $dx, 'y' => $dy);  
  1426.     }
  1427.  
  1428.     /**
  1429.      * Writes text
  1430.      *
  1431.      * Parameter array:
  1432.      * 
  1433.      * 'x': int X-point of text
  1434.      * 
  1435.      * 'y': int Y-point of text
  1436.      * 
  1437.      * 'text': string The text to add
  1438.      * 
  1439.      * 'alignment': array [optional] Alignment
  1440.      * 
  1441.      * 'color': mixed [optional] The color of the text
  1442.      */
  1443.     function addText($params)
  1444.     {
  1445.         $x0 = $this->_getX($params['x']);
  1446.         $y0 = $this->_getY($params['y']);
  1447.         $text = $params['text'];
  1448.         $color = (isset($params['color']) ? $params['color'] : false);
  1449.         $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
  1450.  
  1451.         $text = str_replace("\r", '', $text);
  1452.  
  1453.         if (!is_array($alignment)) {
  1454.             $alignment = array('vertical' => 'top', 'horizontal' => 'left');
  1455.         }
  1456.  
  1457.         if (!isset($alignment['vertical'])) {
  1458.             $alignment['vertical'] = 'top';
  1459.         }
  1460.         
  1461.         if (!isset($alignment['horizontal'])) {
  1462.             $alignment['horizontal'] = 'left';
  1463.         }
  1464.  
  1465.         if (isset($this->_font['size'])) {            
  1466.             $textHeight = $this->_font['size'] + 2;
  1467.         }
  1468.         else {
  1469.             $textHeight = $this->textHeight('A');
  1470.         }
  1471.         $lines = explode("\n", $text);                
  1472.         foreach ($lines as $line) {          
  1473.             $x = $x0; 
  1474.             $y = $y0;
  1475.             
  1476.             $y0 += $textHeight + 2;
  1477.                     
  1478.             if (($color === false) && (isset($this->_font['color']))) {
  1479.                 $color = $this->_font['color'];
  1480.             }
  1481.  
  1482.             if ($color != 'transparent') {
  1483.                 if (isset($this->_font['file'])) {                   
  1484.                     $result = $this->_getAbsolutePosition($x, $y, $line, $alignment);                    
  1485.                     ImageTTFText(
  1486.                         $this->_canvas,
  1487.                         $this->_font['size'],
  1488.                         $this->_font['angle'],
  1489.                         $result['x'],
  1490.                         $result['y'],
  1491.                         $this->_color($color),
  1492.                         $this->_font['file'],
  1493.                         $line
  1494.                     );
  1495.  
  1496.                 } else {
  1497.                     $width = $this->textWidth($line);
  1498.                     $height = $this->textHeight($line);
  1499.                     if ($alignment['horizontal'] == 'right') {
  1500.                         $x -= $width;
  1501.                     }
  1502.                     else if ($alignment['horizontal'] == 'center') {
  1503.                         $x -= $width / 2;
  1504.                     }
  1505.                     if ($alignment['vertical'] == 'bottom') {
  1506.                         $y -= $height;
  1507.                     }
  1508.                     else if ($alignment['vertical'] == 'center') {
  1509.                         $y -= $height / 2;
  1510.                     }
  1511.                     if ((isset($this->_font['vertical'])) && ($this->_font['vertical'])) {                      
  1512.                         ImageStringUp(
  1513.                             $this->_canvas,
  1514.                             $this->_font['font'],
  1515.                             $x,
  1516.                             $y + $this->textHeight($text),
  1517.                             $line,
  1518.                             $this->_color($color)
  1519.                         );
  1520.                     } else {
  1521.                         ImageString(
  1522.                             $this->_canvas,
  1523.                             $this->_font['font'],
  1524.                             $x,
  1525.                             $y,
  1526.                             $line,
  1527.                             $this->_color($color)
  1528.                         );
  1529.                     }
  1530.                 }
  1531.             }
  1532.         }
  1533.         parent::addText($params);
  1534.     }
  1535.  
  1536.     /**
  1537.      * Overlay image
  1538.      *
  1539.      * Parameter array:
  1540.      * 
  1541.      * 'x': int X-point of overlayed image
  1542.      * 
  1543.      * 'y': int Y-point of overlayed image
  1544.      * 
  1545.      * 'filename': string The filename of the image to overlay
  1546.      * 
  1547.      * 'width': int [optional] The width of the overlayed image (resizing if possible)
  1548.      * 
  1549.      * 'height': int [optional] The height of the overlayed image (resizing if possible)
  1550.      * 
  1551.      * 'alignment': array [optional] Alignment
  1552.      */
  1553.     function image($params)
  1554.     {
  1555.         $x = $this->_getX($params['x']);
  1556.         $y = $this->_getY($params['y']);
  1557.         $filename = $params['filename'];
  1558.         $width = (isset($params['width']) ? $params['width'] : false);
  1559.         $height = (isset($params['height']) ? $params['height'] : false);
  1560.         $alignment = (isset($params['alignment']) ? $params['alignment'] : false);
  1561.  
  1562.         if (!is_array($alignment)) {
  1563.             $alignment = array('vertical' => 'top', 'horizontal' => 'left');
  1564.         }
  1565.  
  1566.         if (!isset($alignment['vertical'])) {
  1567.             $alignment['vertical'] = 'top';
  1568.         }
  1569.         
  1570.         if (!isset($alignment['horizontal'])) {
  1571.             $alignment['horizontal'] = 'left';
  1572.         }
  1573.  
  1574.         if (file_exists($filename)) {
  1575.             if (strtolower(substr($filename, -4)) == '.png') {
  1576.                 $image = ImageCreateFromPNG($filename);
  1577.             } elseif (strtolower(substr($filename, -4)) == '.gif') {
  1578.                 $image = ImageCreateFromGIF($filename);
  1579.             } else {
  1580.                 $image = ImageCreateFromJPEG($filename);
  1581.             }
  1582.  
  1583.             $imgWidth = ImageSX($image);
  1584.             $imgHeight = ImageSY($image);
  1585.  
  1586.             $outputWidth = ($width !== false ? $width : $imgWidth);
  1587.             $outputHeight = ($height !== false ? $height : $imgHeight);
  1588.             
  1589.             if ($alignment['horizontal'] == 'right') {
  1590.                 $x -= $outputWidth;
  1591.             } elseif ($alignment['horizontal'] == 'center') {
  1592.                 $x -= $outputWidth / 2;
  1593.             }
  1594.  
  1595.             if ($alignment['vertical'] == 'bottom') {
  1596.                 $y -= $outputHeight;
  1597.             } elseif ($alignment['vertical'] == 'center') {
  1598.                 $y -= $outputHeight / 2;
  1599.             }
  1600.  
  1601.             if ((($width !== false) && ($width != $imgWidth)) ||
  1602.                 (($height !== false) && ($height != $imgHeight)))
  1603.             {
  1604.                 if ($this->_gd2) {
  1605.                     ImageCopyResampled(
  1606.                         $this->_canvas,
  1607.                         $image,
  1608.                         $x,
  1609.                         $y,
  1610.                         0,
  1611.                         0,
  1612.                         $width,
  1613.                         $height,
  1614.                         $imgWidth,
  1615.                         $imgHeight
  1616.                     );
  1617.                 } else {
  1618.                     ImageCopyResized(
  1619.                         $this->_canvas,
  1620.                         $image,
  1621.                         $x,
  1622.                         $y,
  1623.                         0,
  1624.                         0,
  1625.                         $width,
  1626.                         $height,
  1627.                         $imgWidth,
  1628.                         $imgHeight
  1629.                     );
  1630.                 }
  1631.             } else {
  1632.                 ImageCopy(
  1633.                     $this->_canvas,
  1634.                     $image,
  1635.                     $x,
  1636.                     $y,
  1637.                     0,
  1638.                     0,
  1639.                     $imgWidth,
  1640.                     $imgHeight
  1641.                 );
  1642.             }
  1643.             ImageDestroy($image);
  1644.         }
  1645.         parent::image($params);
  1646.     }
  1647.     
  1648.     /**
  1649.      * Set clipping to occur
  1650.      * 
  1651.      * Parameter array:
  1652.      * 
  1653.      * 'x0': int X point of Upper-left corner
  1654.      * 'y0': int X point of Upper-left corner
  1655.      * 'x1': int X point of lower-right corner
  1656.      * 'y1': int Y point of lower-right corner
  1657.      */
  1658.     function setClipping($params = false) 
  1659.     {
  1660.         if ($params === false) {
  1661.             $index = count($this->_clipping) - 1;
  1662.             if (isset($this->_clipping[$index])) {                
  1663.                 $params = $this->_clipping[$index];
  1664.                 $canvas = $params['canvas'];
  1665.                 ImageCopy(
  1666.                     $canvas, 
  1667.                     $this->_canvas,
  1668.                     min($params['x0'], $params['x1']),
  1669.                     min($params['y0'], $params['y1']),
  1670.                     min($params['x0'], $params['x1']),
  1671.                     min($params['y0'], $params['y1']),
  1672.                     abs($params['x1'] - $params['x0'] + 1),
  1673.                     abs($params['y1'] - $params['y0'] + 1)
  1674.                 );
  1675.                 $this->_canvas = $canvas;
  1676.                 unset($this->_clipping[$index]);
  1677.             }
  1678.         }
  1679.         else {
  1680.             $params['canvas'] = $this->_canvas;
  1681.  
  1682.             if ($this->_gd2) {
  1683.                 $this->_canvas = ImageCreateTrueColor(
  1684.                     $this->_width,
  1685.                     $this->_height
  1686.                 );
  1687.                 if ($this->_alpha) {
  1688.                     ImageAlphaBlending($this->_canvas, true);
  1689.                 }
  1690.             } else {
  1691.                 $this->_canvas = ImageCreate($this->_width, $this->_height);
  1692.             }
  1693.             
  1694.             if (($this->_gd2) && ($this->_antialias === 'native')) {
  1695.                 ImageAntialias($this->_canvas, true);
  1696.             }
  1697.             
  1698.             ImageCopy($this->_canvas, $params['canvas'], 0, 0, 0, 0, $this->_width, $this->_height);                
  1699.             
  1700.             $this->_clipping[count($this->_clipping)] = $params;
  1701.         }
  1702.     }
  1703.     
  1704.     /**
  1705.      * Get a canvas specific HTML tag.
  1706.      * 
  1707.      * This method implicitly saves the canvas to the filename in the 
  1708.      * filesystem path specified and parses it as URL specified by URL path
  1709.      * 
  1710.      * Parameter array:
  1711.      * 
  1712.      * 'filename' string
  1713.      * 
  1714.      * 'filepath': string Path to the file on the file system. Remember the final slash
  1715.      * 
  1716.      * 'urlpath': string Path to the file available through an URL. Remember the final slash
  1717.      * 
  1718.      * 'alt': string [optional] Alternative text on image
  1719.      * 
  1720.      * 'cssclass': string [optional] The CSS Stylesheet class
  1721.      * 
  1722.      * 'border': int [optional] The border width on the image 
  1723.      */
  1724.     function toHtml($params)
  1725.     {
  1726.         parent::toHtml($params);
  1727.         return '<img src="' . $params['urlpath'] . $params['filename'] . '"' .
  1728.             (isset($params['alt']) ? ' alt="' . $params['alt'] . '"' : '') .
  1729.             (isset($params['cssclass']) ? ' class="' . $params['cssclass'] . '"' : '') .
  1730.             (isset($params['border']) ? ' border="' . $params['border'] . '"' : '') .
  1731.             (isset($this->_imageMap) ? ' usemap="#' . $params['filename'] . '"' : '') . '>' .
  1732.             (isset($this->_imageMap) ? "\n" . $this->_imageMap->toHtml(array('name' => $params['filename'])) : '');
  1733.     }
  1734.  
  1735.     /**
  1736.      * Resets the canvas.
  1737.      *
  1738.      * Include fillstyle, linestyle, thickness and polygon
  1739.      * @access private
  1740.      */
  1741.     function _reset()
  1742.     {
  1743.         if ($this->_gd2) {
  1744.             ImageSetThickness($this->_canvas, 1);
  1745.         }
  1746.         if ($this->_tileImage != null) {
  1747.             ImageDestroy($this->_tileImage);
  1748.             $this->_tileImage = null;
  1749.         }
  1750.         parent::_reset();
  1751.         $this->_font = array('font' => 1, 'color' => 'black');
  1752.     }
  1753.  
  1754.     /**
  1755.      * Check which version of GD is installed
  1756.      *
  1757.      * @return int 0 if GD isn't installed, 1 if GD 1.x is installed and 2 if GD
  1758.      * 2.x is installed
  1759.      * @access private
  1760.      */
  1761.     function _version()
  1762.     {
  1763.         $result = false;
  1764.         if (function_exists('gd_info')) {
  1765.             $info = gd_info();
  1766.             $version = $info['GD Version'];
  1767.         } else {
  1768.             ob_start();
  1769.             phpinfo(8);
  1770.             $php_info = ob_get_contents();
  1771.             ob_end_clean();
  1772.  
  1773.             if (ereg("<td[^>]*>GD Version *<\/td><td[^>]*>([^<]*)<\/td>",
  1774.                 $php_info, $result))
  1775.             {
  1776.                 $version = $result[1];
  1777.             } else {
  1778.                 $version = null;
  1779.             }
  1780.         }
  1781.  
  1782.         if (ereg('1\.[0-9]{1,2}', $version)) {
  1783.             return 1;
  1784.         } elseif (ereg('2\.[0-9]{1,2}', $version)) {
  1785.             return 2;
  1786.         } else {
  1787.             return 0;
  1788.         }
  1789.     }
  1790.  
  1791. }
  1792.  
  1793. ?>