steffen: server/kolab-horde-framework/kolab-horde-framework/Image/Image Decorator.php, NONE, 1.1 gd.php, NONE, 1.1 im.php, NONE, 1.1 png.php, NONE, 1.1 rgb.php, NONE, 1.1 svg.php, NONE, 1.1 swf.php, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Fri Oct 14 16:33:07 CEST 2005


Author: steffen

Update of /kolabrepository/server/kolab-horde-framework/kolab-horde-framework/Image/Image
In directory doto:/tmp/cvs-serv28903/kolab-horde-framework/kolab-horde-framework/Image/Image

Added Files:
	Decorator.php gd.php im.php png.php rgb.php svg.php swf.php 
Log Message:
Separated Horde Framework from kolab-resource-handlers

--- NEW FILE: Decorator.php ---
<?php
/**
 * The Horde_Image_Decorator parent class defines a general API for
 * ways to "decorate" Horde_Image objects.
 *
 * $Horde: framework/Image/Image/Decorator.php,v 1.3 2004/05/04 21:35:10 chuck Exp $
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @package Horde_Image
 */
class Horde_Image_Decorator {

    /**
     * Decorator parameters.
     *
     * @var array $_params
     */
    var $_params = array();

    /**
     * Decorator constructor.
     *
     * @param array $params  (optional) Any parameters for the decoration.
     *                       Parameters are documented in each subclass.
     */
    function Horde_Image_Decorator($params = array())
    {
        foreach ($params as $key => $val) {
            $this->_params[$key] = $val;
        }
    }

}

--- NEW FILE: gd.php ---
<?php

require_once dirname(__FILE__) . '/../Image.php';

/**
 * This class implements the Horde_Image:: API for the PHP GD
 * extension. It mainly provides some utility functions, such as the
 * ability to make pixels, for now.
 *
 * $Horde: framework/Image/Image/gd.php,v 1.45 2004/05/26 12:45:41 jan Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Image
 */
class Horde_Image_gd extends Horde_Image {

    /**
     * Capabilites of this driver.
     * @var array $_capabilities
     */
    var $_capabilities = array('resize',
                               'crop',
                               'rotate',
                               'flip',
                               'mirror',
                               'grayscale',
                               'sepia',
                               'yellowize',
                               'watermark',
                               'canvas',
                         );

    /**
     * What kind of images should GD generate? Defaults to 'png'.
     * @var string $_type
     */
    var $_type = 'png';

    /**
     * GD Image resource for the current image data.
     * @var resource $_im
     */
    var $_im;

    /**
     * String identifier of the current image. New image data will not
     * be loaded if the same id is already loaded.
     * @var string $_id
     */
    var $_id = '';

    function Horde_Image_gd($params)
    {
        parent::Horde_Image($params);
        if (!empty($params['type'])) {
            $this->_type = $params['type'];
        }

        if (!empty($params['width'])) {
            $this->_im = @imageCreateTrueColor($this->_width, $this->_height);
            if (!is_resource($this->_im)) {
                $this->_im = imageCreate($this->_width, $this->_height);
            }

            imageFill($this->_im, 0, 0, $this->allocateColor($this->_background));
        }
    }

    function getContentType()
    {
        return 'image/' . $this->_type;
    }

    /**
     * Display the current image.
     */
    function display()
    {
        $this->headers();
        $function = 'image' . $this->_type;
        $function($this->_im);
    }

    /**
     * Return the raw data for this image.
     *
     * @return string  The raw image data.
     */
    function raw()
    {
        return Util::bufferOutput('image' . $this->_type, $this->_im);
    }

    /**
     * Reset the image data.
     */
    function reset()
    {
        parent::reset();
        @imageDestroy($this->_im);
    }

    /**
     * Get the height and width of the current image.
     *
     * @return array  An hash with 'width' containing the width,
     *                'height' containing the height of the image.
     */
    function getDimensions()
    {
        if ($this->_im) {
            return array('width' => imageSX($this->_im),
                         'height' => imageSY($this->_im));
        } else {
            return array('width' => 0, 'height' => 0);
        }
    }

    /**
     * Creates a color that can be accessed in this object. When a
     * color is set, the integer resource of it is returned.
     *
     * @param string $name  The name of the color.
     *
     * @return integer  The resource of the color that can be passed to GD.
     */
    function allocateColor($name)
    {
        static $colors = array();

        if (empty($colors[$name])) {
            list($r, $g, $b) = $this->getRGB($name);
            $colors[$name] = imageColorAllocate($this->_im, $r, $g, $b);
        }

        return $colors[$name];
    }

    function getFont($font)
    {
        switch ($font) {
        case 'tiny':
            return 1;

        case 'medium':
            return 3;

        case 'large':
            return 4;

        case 'giant':
            return 5;

        case 'small':
        default:
            return 2;
        }
    }

    /**
     * Load the image data from a string.
     *
     * @access public
     *
     * @params string $id          An arbitrary id for the image.
     * @params string $image_data  The data to use for the image.
     */
    function loadString($id, $image_data)
    {
        if ($id != $this->_id) {
            if ($this->_im) {
                $this->reset();
            }
            $this->_im = @imageCreateFromString($image_data);
            $this->_id = $id;
        }
    }

    /**
     * Load the image data from a file.
     *
     * @access public
     *
     * @params string $filename  The full path and filename to the file to load
     *                           the image data from. The filename will also be
     *                           used for the image id.
     *
     * @return mixed  PEAR Error if file does not exist or could not be loaded
     *                otherwise NULL if successful or already loaded.
     */
    function loadFile($filename)
    {
        $this->reset();

        $info = getimagesize($filename);
        if (is_array($info)) {
            switch ($info[2]) {
            case 1:
                if (function_exists('imagecreatefromgif')) {
                    $this->_im = @imagecreatefromgif($filename);
                }
                break;
            case 2:
                $this->_im = @imagecreatefromjpeg($filename);
                break;
            case 3:
                $this->_im = @imagecreatefrompng($filename);
                break;
            case 15:
                if (function_exists('imagecreatefromgwbmp')) {
                    $this->_im = @imagecreatefromgwbmp($filename);
                }
                break;
            case 16:
                $this->_im = @imagecreatefromxbm($filename);
                break;
            }
        }

        if (is_resource($this->_im)) {
            return;
        }

        $result = parent::loadFile($filename);
        if (is_a($result, 'PEAR_Error')) {
            return $result;
        }
        $this->_im = @imageCreateFromString($this->_data);
    }

    /**
     * Resize the current image.
     *
     * @param integer $width      The new width.
     * @param integer $height     The new height.
     * @param boolean $ratio      Maintain original aspect ratio.
     */
    function resize($width, $height, $ratio = true)
    {
        /* Abort if we're asked to divide by zero, or truncate the
         * image completely in either direction. */
        if (!$width || !$height) {
            return;
        }

        if ($ratio) {
            if ($width / $height > imageSX($this->_im) / imageSY($this->_im)) {
                $width = $height * imageSX($this->_im) / imageSY($this->_im);
            } else {
                $height = $width * imageSY($this->_im) / imageSX($this->_im);
            }
        }

        $im = $this->_im;
        $this->_im = @imageCreateTrueColor($width, $height);
        if (!is_resource($this->_im)) {
            $this->_im = imageCreate($width, $height);
        }
        imageFill($this->_im, 0, 0, imageColorAllocate($this->_im, 255, 255, 255));
        if (!@imageCopyResampled($this->_im, $im, 0, 0, 0, 0, $width, $height, imageSX($im), imageSY($im))) {
            imageCopyResized($this->_im, $im, 0, 0, 0, 0, $width, $height, imageSX($im), imageSY($im));
        }
    }

    /**
     * Crop the current image.
     *
     * @param integer $x1  The top left corner of the cropped image.
     * @param integer $y1  The top right corner of the cropped image.
     * @param integer $x2  The bottom left corner of the cropped image.
     * @param integer $y2  The bottom right corner of the cropped image.
     */
    function crop($x1, $y1, $x2, $y2)
    {
        $im = $this->_im;
        $this->_im = @imageCreateTrueColor($x2 - $x1, $y2 - $y1);
        if (!is_resource($this->_im)) {
            $this->_im = imageCreate($x2 - $x1, $y2 - $y1);
        }
        imageCopy($this->_im, $im, 0, 0, $x1, $y1, $x2 - $x1, $y2 - $y1);
    }

    /**
     * Rotate the current image.
     *
     * @param integer $angle       The angle to rotate the image by,
     *                             in the clockwise direction
     * @param integer $background  The background color to fill any triangles
     */
    function rotate($angle, $background = 'white')
    {
        if (!function_exists('imagerotate')) {
            return;
        }

        $background = $this->allocateColor($background);

        switch ($angle) {
        case '90':
            $x = imageSX($this->_im);
            $y = imageSY($this->_im);
            $xymax = max($x, $y);

            $im = @imageCreateTrueColor($xymax, $xymax);
            if (!is_resource($im)) {
                $im = imageCreate($xymax, $xymax);
            }
            imageCopy($im, $this->_im, 0, 0, 0, 0, $x, $y);
            $im = imageRotate($im, 270, $background);
            $this->_im = $im;
            $im = @imageCreateTrueColor($y, $x);
            if (!is_resource($im)) {
                $im = imageCreate($y, $x);
            }
            if ($x < $y) {
                imageCopy($im, $this->_im, 0, 0, 0, 0, $xymax, $xymax);
            } elseif ($x > $y) {
                imageCopy($im, $this->_im, 0, 0, $xymax - $y, $xymax - $x, $xymax, $xymax);
            }
            $this->_im = $im;
            break;

        default:
            $this->_im = imageRotate($this->_im, 360 - $angle, $background);
            break;
        }
    }

    /**
     * Flip the current image.
     */
    function flip()
    {
        $x = imageSX($this->_im);
        $y = imageSY($this->_im);

        $im = @imageCreateTrueColor($x, $y);
        if (!is_resource($im)) {
            $im = imageCreate($x, $y);
        }
        for ($curY = 0; $curY < $y; $curY++) {
            imageCopy($im, $this->_im, 0, $y - ($curY + 1), 0, $curY, $x, 1);
        }

        $this->_im = $im;
    }

    /**
     * Mirror the current image.
     */
    function mirror()
    {
        $x = imageSX($this->_im);
        $y = imageSY($this->_im);

        $im = @imageCreateTrueColor($x, $y);
        if (!is_resource($im)) {
            $im = imageCreate($x, $y);
        }
        for ($curX = 0; $curX < $x; $curX++) {
            imageCopy($im, $this->_im, $x - ($curX + 1), 0, $curX, 0, 1, $y);
        }

        $this->_im = $im;
    }

    /**
     * Convert the current image to grayscale.
     */
    function grayscale()
    {
        $rateR = .229;
        $rateG = .587;
        $rateB = .114;
        $whiteness = 3;

        if (function_exists('imageistruecolor') && imageIsTrueColor($this->_im)) {
            @imageTrueColorToPalette($this->_im, true, 256);
        }

        $colors = min(256, imageColorsTotal($this->_im));
        for ($x = 0; $x < $colors; $x++) {
            $src = imageColorsForIndex($this->_im, $x);
            $new = min(255, abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
            imageColorSet($this->_im, $x, $new, $new, $new);
        }
    }

    /**
     * Sepia filter.
     *
     * Basically turns the image to grayscale and then adds some
     * defined tint on it (R += 30, G += 43, B += -23) so it will
     * appear to be a very old picture.
     */
    function sepia()
    {
        $tintR = 80;
        $tintG = 43;
        $tintB = -23;
        $rateR = .229;
        $rateG = .587;
        $rateB = .114;
        $whiteness = 3;

        if (imageIsTrueColor($this->_im)) {
            imageTrueColorToPalette($this->_im, true, 256);
        }

        $colors = max(256, imageColorsTotal($this->_im));
        for ($x = 0; $x < $colors; $x++) {
            $src = imageColorsForIndex($this->_im, $x);
            $new = min(255, abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
            $r = min(255, $new + $tintR);
            $g = min(255, $new + $tintG);
            $b = min(255, $new + $tintB);
            imageColorSet($this->_im, $x, $r, $g, $b);
        }
    }

    /**
     * Yellowize filter.
     *
     * Adds a layer of yellow that can be transparent or solid. If
     * $intensityA is 255 the image will be 0% transparent (solid).
     *
     * @param integer $intensityY  How strong should the yellow (red and green) be? (0-255)
     * @param integer $intensityB  How weak should the blue be? (>= 2, in the positive limit it will be make BLUE 0)
     */
    function yellowize($intensityY = 50, $intensityB = 3)
    {
        if (imageIsTrueColor($this->_im)) {
            imageTrueColorToPalette($this->_im, true, 256);
        }

        $colors = max(256, imageColorsTotal($this->_im));
        for ($x = 0; $x < $colors; $x++) {
            $src = imageColorsForIndex($this->_im, $x);
            $r = min($src['red'] + $intensityY, 255);
            $g = min($src['green'] + $intensityY, 255);
            $b = max(($r + $g) / max($intensityB, 2), 0);
            imageColorSet($this->_im, $x, $r, $g, $b);
        }
    }

    function watermark($text, $halign = 'right', $valign = 'bottom', $font = 'small')
    {
        $color = imageColorClosest($this->_im, 255, 255, 255);
        $shadow = imageColorClosest($this->_im, 0, 0, 0);

        // Shadow offset in pixels.
        $drop = 1;

        // Maximum text width.
        $maxwidth = 200;

        // Amount of space to leave between the text and the image
        // border.
        $padding = 10;

        $f = $this->getFont($font);
        $fontwidth = imageFontWidth($f);
        $fontheight = imageFontHeight($f);

        // So that shadow is not off the image with right align and
        // bottom valign.
        $margin = floor($padding + $drop) / 2;

        if ($maxwidth) {
            $maxcharsperline = floor(($maxwidth - ($margin * 2)) / $fontwidth);
            $text = wordwrap($text, $maxcharsperline, "\n", 1);
        }

        // Split $text into individual lines.
        $lines = explode("\n", $text);

        switch ($valign){
        case 'center':
            $y = (imageSY($this->_im) - ($fontheight * count($lines))) / 2;
            break;

        case 'bottom':
            $y = imageSY($this->_im) - (($fontheight * count($lines)) + $margin);
            break;

        default:
            $y = $margin;
            break;
        }

        switch ($halign) {
        case 'right':
            foreach ($lines as $line) {
                imageString($this->_im, $f, (imageSX($this->_im) - $fontwidth * strlen($line)) - $margin + $drop, ($y + $drop), $line, $shadow);
                imageString($this->_im, $f, (imageSX($this->_im) - $fontwidth * strlen($line)) - $margin, $y, $line, $color);
                $y += $fontheight;
            }
            break;

        case 'center':
            foreach ($lines as $line) {
                imageString($this->_im, $f, floor((imageSX($this->_im) - $fontwidth * strlen($line)) / 2) + $drop, ($y + $drop), $line, $shadow);
                imageString($this->_im, $f, floor((imageSX($this->_im) - $fontwidth * strlen($line)) / 2), $y, $line, $color);
                $y += $fontheight;
            }
            break;

        default:
            foreach ($lines as $line) {
                imageString($this->_im, $f, $margin + $drop, ($y + $drop), $line, $shadow);
                imageString($this->_im, $f, $margin, $y, $line, $color);
                $y += $fontheight;
            }
            break;
        }
    }

    /**
     * Draws a text string on the image in a specified location, with
     * the specified style information.
     *
     * @param string  $text       The text to draw.
     * @param integer $x          The left x coordinate of the start of the text string.
     * @param integer $y          The top y coordinate of the start of the text string.
     * @param string  $font       The font identifier you want to use for the text.
     * @param string  $color      The color that you want the text displayed in.
     * @param integer $direction  An integer that specifies the orientation of the text.
     */
    function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0)
    {
        $c = $this->allocateColor($color);
        $f = $this->getFont($font);
        switch ($direction) {
        case -90:
        case 270:
            imageStringUp($this->_im, $f, $x, $y, $string, $c);
            break;

        case 0:
        default:
            imageString($this->_im, $f, $x, $y, $string, $c);
        }
    }

    /**
     * Draw a circle.
     *
     * @param integer $x      The x co-ordinate of the centre.
     * @param integer $y      The y co-ordinate of the centre.
     * @param integer $r      The radius of the circle.
     * @param string  $color  The line color of the circle.
     * @param string  $fill   (optional) The color to fill the circle.
     */
    function circle($x, $y, $r, $color, $fill = null)
    {
        $c = $this->allocateColor($color);
        if (is_null($fill)) {
            imageEllipse($this->_im, $x, $y, $r * 2, $r * 2, $c);
        } else {
            if ($fill !== $color) {
                $fillColor = $this->allocateColor($fill);
                imageFilledEllipse($this->_im, $x, $y, $r * 2, $r * 2, $fillColor);
                imageEllipse($this->_im, $x, $y, $r * 2, $r * 2, $c);
            } else {
                imageFilledEllipse($this->_im, $x, $y, $r * 2, $r * 2, $c);
            }
        }
    }

    /**
     * Draw a polygon based on a set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the polygon with.
     * @param string  $fill      (optional) The color to fill the polygon.
     */
    function polygon($verts, $color, $fill = 'none')
    {
        $vertices = array();
        foreach ($verts as $vert) {
            $vertices[] = $vert['x'];
            $vertices[] = $vert['y'];
        }

        if ($fill != 'none') {
            $f = $this->allocateColor($fill);
            imageFilledPolygon($this->_im, $vertices, count($verts), $f);
        }

        if ($fill == 'none' || $fill != $color) {
            $c = $this->allocateColor($color);
            imagePolygon($this->_im, $vertices, count($verts), $c);
        }
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param string  $color   (optional) The line color of the rectangle. Defaults to black.
     * @param string  $fill    (optional) The color to fill the rectangle with. Defaults to none.
     */
    function rectangle($x, $y, $width, $height, $color = 'black', $fill = 'none')
    {
        if ($fill != 'none') {
            $f = $this->allocateColor($fill);
            imageFilledRectangle($this->_im, $x, $y, $x + $width, $y + $height, $f);
        }

        if ($fill == 'none' || $fill != $color) {
            $c = $this->allocateColor($color);
            imageRectangle($this->_im, $x, $y, $x + $width, $y + $height, $c);
        }
    }

    /**
     * Draw a rounded rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param integer $round   The width of the corner rounding.
     * @param string  $color   (optional) The line color of the rectangle. Defaults to black.
     * @param string  $fill    (optional) The color to fill the rounded rectangle with. Defaults to none.
     */
    function roundedRectangle($x, $y, $width, $height, $round, $color = 'black', $fill = 'none')
    {
        if ($round <= 0) {
            // Optimize out any calls with no corner rounding.
            return $this->rectangle($x, $y, $width, $height, $color, $fill);
        }

        $c = $this->allocateColor($color);

        // Set corner points to avoid lots of redundant math.
        $x1 = $x + $round;
        $y1 = $y + $round;

        $x2 = $x + $width - $round;
        $y2 = $y + $round;

        $x3 = $x + $width - $round;
        $y3 = $y + $height - $round;

        $x4 = $x + $round;
        $y4 = $y + $height - $round;

        $r = $round * 2;

        // Calculate the upper left arc.
        $p1 = $this->_arcPoints($round, 180, 225);
        $p2 = $this->_arcPoints($round, 225, 270);

        // Calculate the upper right arc.
        $p3 = $this->_arcPoints($round, 270, 315);
        $p4 = $this->_arcPoints($round, 315, 360);

        // Calculate the lower right arc.
        $p5 = $this->_arcPoints($round, 0, 45);
        $p6 = $this->_arcPoints($round, 45, 90);

        // Calculate the lower left arc.
        $p7 = $this->_arcPoints($round, 90, 135);
        $p8 = $this->_arcPoints($round, 135, 180);

        // Draw the corners - upper left, upper right, lower right,
        // lower left.
        imageArc($this->_im, $x1, $y1, $r, $r, 180, 270, $c);
        imageArc($this->_im, $x2, $y2, $r, $r, 270, 360, $c);
        imageArc($this->_im, $x3, $y3, $r, $r, 0, 90, $c);
        imageArc($this->_im, $x4, $y4, $r, $r, 90, 180, $c);

        // Draw the connecting sides - top, right, bottom, left.
        imageLine($this->_im, $x1 + $p2['x2'], $y1 + $p2['y2'], $x2 + $p3['x1'], $y2 + $p3['y1'], $c);
        imageLine($this->_im, $x2 + $p4['x2'], $y2 + $p4['y2'], $x3 + $p5['x1'], $y3 + $p5['y1'], $c);
        imageLine($this->_im, $x3 + $p6['x2'], $y3 + $p6['y2'], $x4 + $p7['x1'], $y4 + $p7['y1'], $c);
        imageLine($this->_im, $x4 + $p8['x2'], $y4 + $p8['y2'], $x1 + $p1['x1'], $y1 + $p1['y1'], $c);

        if ($fill != 'none') {
            $f = $this->allocateColor($fill);
            imageFillToBorder($this->_im, $x + ($width / 2), $y + ($height / 2), $c, $f);
        }
    }

    /**
     * Draw a line.
     *
     * @param integer $x0     The x co-ordinate of the start.
     * @param integer $y0     The y co-ordinate of the start.
     * @param integer $x1     The x co-ordinate of the end.
     * @param integer $y1     The y co-ordinate of the end.
     * @param string  $color  (optional) The line color.
     * @param string  $width  (optional) The width of the line.
     */
    function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
    {
        $c = $this->allocateColor($color);

        // Don't need to do anything special for single-width lines.
        if ($width == 1) {
            imageLine($this->_im, $x1, $y1, $x2, $y2, $c);
        } elseif ($x1 == $x2) {
            // For vertical lines, we can just draw a vertical
            // rectangle.
            $left = $x1 - floor(($width - 1) / 2);
            $right = $x1 + floor($width / 2);
            imageFilledRectangle($this->_im, $left, $y1, $right, $y2, $c);
        } elseif ($y1 == $y2) {
            // For horizontal lines, we can just draw a horizontal
            // filled rectangle.
            $top = $y1 - floor($width / 2);
            $bottom = $y1 + floor(($width - 1) / 2);
            imageFilledRectangle($this->_im, $x1, $top, $x2, $bottom, $c);
        } else {
            // Angled lines.

            // Make sure that the end points of the line are
            // perpendicular to the line itself.
            $a = atan2($y1 - $y2, $x2 - $x1);
            $dx = (sin($a) * $width / 2);
            $dy = (cos($a) * $width / 2);

            $verts = array($x2 + $dx, $y2 + $dy, $x2 - $dx, $y2 - $dy, $x1 - $dx, $y1 - $dy, $x1 + $dx, $y1 + $dy);
            imageFilledPolygon($this->_im, $verts, count($verts) / 2, $c);
        }
    }

    /**
     * Draw a dashed line.
     *
     * @param integer $x0           The x co-ordinate of the start.
     * @param integer $y0           The y co-ordinate of the start.
     * @param integer $x1           The x co-ordinate of the end.
     * @param integer $y1           The y co-ordinate of the end.
     * @param string  $color        (optional) The line color.
     * @param string  $width        (optional) The width of the line.
     * @param integer $dash_length  The length of a dash on the dashed line
     * @param integer $dash_space   The length of a space in the dashed line
     */
    function dashedLine($x1, $y1, $x2, $y2, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
    {
        $c = $this->allocateColor($color);
        $w = $this->allocateColor('white');

        // Set up the style array according to the $dash_* parameters.
        $style = array();
        for ($i = 0; $i < $dash_length; $i++) {
            $style[] = $c;
        }
        for ($i = 0; $i < $dash_space; $i++) {
            $style[] = $w;
        }

        imageSetStyle($this->_im, $style);
        imageSetThickness($this->_im, $width);
        imageLine($this->_im, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
    }

    /**
     * Draw a polyline (a non-closed, non-filled polygon) based on a
     * set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the line with.
     * @param string  $width     (optional) The width of the line.
     */
    function polyline($verts, $color, $width = 1)
    {
        $first = true;
        foreach ($verts as $vert) {
            if (!$first) {
                $this->line($lastX, $lastY, $vert['x'], $vert['y'], $color, $width);
            } else {
                $first = false;
            }
            $lastX = $vert['x'];
            $lastY = $vert['y'];
        }
    }

    /**
     * Draw an arc.
     *
     * @param integer $x      The x co-ordinate of the centre.
     * @param integer $y      The y co-ordinate of the centre.
     * @param integer $r      The radius of the arc.
     * @param integer $start  The start angle of the arc.
     * @param integer $end    The end angle of the arc.
     * @param string  $color  The line color of the arc.
     * @param string  $fill   The fill color of the arc (defaults to none).
     */
    function arc($x, $y, $r, $start, $end, $color = 'black', $fill = null)
    {
        $c = $this->allocateColor($color);
        if (is_null($fill)) {
            imageArc($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c);
        } else {
            if ($fill !== $color) {
                $f = $this->allocateColor($fill);
                imageFilledArc($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $f, IMG_ARC_PIE);
                imageFilledArc($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c, IMG_ARC_EDGED | IMG_ARC_NOFILL);
            } else {
                imageFilledArc($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c, IMG_ARC_PIE);
            }
        }
    }

}

--- NEW FILE: im.php ---
<?php

require_once 'Horde/Image.php';

/**
 * This class implements the Horde_Image:: API for ImageMagick.
 *
 * $Horde: framework/Image/Image/im.php,v 1.33 2004/05/07 19:16:20 chuck Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 * Copyright 2003-2004 Mike Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @author  Mike Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Image
 */
class Horde_Image_im extends Horde_Image {

    /**
     * Capabilites of this driver.
     * @var array $_capabilities
     */
    var $_capabilities = array('resize',
                               'crop',
                               'rotate',
                               'grayscale',
                               'flip',
                               'mirror',
                               'sepia',
                               'canvas'
                         );

    /**
     * What kind of images should ImageMagick generate? Defaults to
     * 'png'.
     * @var string $_type
     */
    var $_type = 'png';

    /**
     * Operations to be performed.
     * @var array $_operations
     */
    var $_operations = array();

    /**
     * Current stroke color; cached so we don't issue more -stroke
     * commands than necessary.
     * @var string $_strokeColor
     */
    var $_strokeColor = null;

    /**
     * Current stroke width; cached so we don't issue more -strokewidth
     * commands than necessary.
     * @var string $_strokeWidth
     */
    var $_strokeWidth = null;

    /**
     * Current fill color; cached so we don't issue more -fill
     * commands than necessary.
     * @var string $_fillColor
     */
    var $_fillColor = null;

    /**
     * Constructor.
     */
    function Horde_Image_im($params)
    {
        parent::Horde_Image($params);

        if (!empty($params['type'])) {
            $this->_type = $params['type'];
        }

        // Make sure we start with a white background to be consistent
        // with other drivers.
        if (!empty($params['background'])) {
            $bg = $params['background'];
        } else {
            $bg = 'white';
        }
        $this->rectangle(0, 0, $this->_width, $this->_height, $bg, $bg);
    }

    /**
     * Return the content type for this image.
     *
     * @return string  The content type for this image.
     */
    function getContentType()
    {
        return 'image/' . $this->_type;
    }

    /**
     * Return the raw data for this image.
     *
     * @return string  The raw image data.
     */
    function raw()
    {
        global $conf;

        if (!empty($this->_data)) {
            // If there are no operations, and we already have data,
            // don't bother writing out files, just return the current
            // data.
            if (!count($this->_operations)) {
                return $this->_data;
            }

            $tmpin = $this->toFile($this->_data);
        } else {
            // Create an empty PPM file to load.
            $tmpin = Util::getTempFile('img', false, $this->_tmpdir);
            $fp = fopen($tmpin, 'wb');
            fwrite($fp, sprintf("P3\n%d %d\n255\n ", $this->_width, $this->_height));
            fclose($fp);
        }

        $tmpout = Util::getTempFile('img', false, $this->_tmpdir);

        $command  = $conf['image']['convert'];
        $command .= ' ' . implode(' ', $this->_operations);
        $command .= ' "' . $tmpin . '" +profile "*" ' . $this->_type . ':"' . $tmpout . '" 2>&1';

        exec($command, $output, $retval);

        $fp = fopen($tmpout, 'rb');
        $this->_data = fread($fp, filesize($tmpout));
        fclose($fp);

        @unlink($tmpin);
        @unlink($tmpout);

        return $this->_data;
    }

    /**
     * Reset the image data.
     */
    function reset()
    {
        parent::reset();
        $this->_operations = array();
    }

    /**
     * Resize the current image.
     *
     * @param integer $width   The new width.
     * @param integer $height  The new height.
     * @param boolean $ratio   Maintain original aspect ratio.
     */
    function resize($width, $height, $ratio = true)
    {
        if ($ratio) {
            $this->_operations[] = "-resize {$width}x{$height}";
        } else {
            $this->_operations[] = "-resize {$width}x{$height}!";
        }
    }

    /**
     * Crop the current image.
     *
     * @param integer $x1  The top left corner of the cropped image.
     * @param integer $y1  The top right corner of the cropped image.
     * @param integer $x2  The bottom left corner of the cropped image.
     * @param integer $y2  The bottom right corner of the cropped image.
     */
    function crop($x1, $y1, $x2, $y2)
    {
        $line = ($x2 - $x1) . 'x' . ($y2 - $y1) . '+' . $x1 . '+' . $y2;
        $this->_operations[] = '-crop ' . $line;
    }

    /**
     * Rotate the current image.
     *
     * @param integer $angle       The angle to rotate the image by,
     *                             in the clockwise direction.
     * @param integer $background  The background color to fill any triangles.
     */
    function rotate($angle, $background = 'white')
    {
        $this->_operations[] = "-rotate {$angle} -background $background";
    }

    /**
     * Flip the current image.
     */
    function flip()
    {
        $this->_operations[] = '-flip';
    }

    /**
     * Mirror the current image.
     */
    function mirror()
    {
        $this->_operations[] = '-flop';
    }

    /**
     * Convert the current image to grayscale.
     */
    function grayscale()
    {
        $this->_operations[] = '-colorspace GRAY -colors 256';
    }

    /**
     * Sepia filter.
     */
    function sepia()
    {
        $this->_operations[] = '-modulate 110 -colorspace GRAY -colors 256 -gamma 1.25/1/0.66';
    }

    /**
     * Draws a text string on the image in a specified location, with
     * the specified style information.
     *
     * @param string  $text       The text to draw.
     * @param integer $x          The left x coordinate of the start of the text string.
     * @param integer $y          The top y coordinate of the start of the text string.
     * @param string  $font       The font identifier you want to use for the text.
     * @param string  $color      The color that you want the text displayed in.
     * @param integer $direction  An integer that specifies the orientation of the text.
     */
    function text($string, $x, $y, $font = '_sans', $color = 'black', $direction = 0)
    {
        $this->setStrokeColor($color);
        $this->setStrokeWidth(1);

        $string = addslashes('"' . $string . '"');
        $y = $y + 12;
        $this->_operations[] = "-draw \"text $x,$y $string\"";
    }

    /**
     * Draw a circle.
     *
     * @param integer $x      The x coordinate of the centre.
     * @param integer $y      The y coordinate of the centre.
     * @param integer $r      The radius of the circle.
     * @param string  $color  The line color of the circle.
     * @param string  $fill   (optional) The color to fill the circle.
     */
    function circle($x, $y, $r, $color, $fill = 'none')
    {
        $this->setStrokeColor($color);
        $this->setFillColor($fill);

        $xMax = $x + $r;
        $this->_operations[] = "-draw \"circle $x,$y $xMax,$y\"";
    }

    /**
     * Draw a polygon based on a set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the polygon with.
     * @param string  $fill      (optional) The color to fill the polygon.
     */
    function polygon($verts, $color, $fill = 'none')
    {
        $this->setStrokeColor($color);
        $this->setFillColor($fill);

        $command = '';
        foreach ($verts as $vert) {
            $command .= sprintf(' %d,%d', $vert['x'], $vert['y']);
        }
        $this->_operations[] = "-draw \"polygon $command\"";
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param string  $color   The line color of the rectangle.
     * @param string  $fill    (optional) The color to fill the rectangle.
     */
    function rectangle($x, $y, $width, $height, $color, $fill = 'none')
    {
        $this->setStrokeColor($color);
        $this->setFillColor($fill);

        $xMax = $x + $width;
        $yMax = $y + $height;
        $this->_operations[] = "-draw \"rectangle $x,$y $xMax,$yMax\"";
    }

    /**
     * Draw a rounded rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param integer $round   The width of the corner rounding.
     * @param string  $color   The line color of the rectangle.
     * @param string  $fill    The color to fill the rounded rectangle with.
     */
    function roundedRectangle($x, $y, $width, $height, $round, $color, $fill)
    {
        $this->setStrokeColor($color);
        $this->setFillColor($fill);

        $x1 = $x + $width;
        $y1 = $y + $height;
        $this->_operations[] = "-draw \"roundRectangle $x,$y $x1,$y1, $round,$round\"";
    }

    /**
     * Draw a line.
     *
     * @param integer $x0     The x coordinate of the start.
     * @param integer $y0     The y coordinate of the start.
     * @param integer $x1     The x coordinate of the end.
     * @param integer $y1     The y coordinate of the end.
     * @param string  $color  (optional) The line color.
     * @param string  $width  (optional) The width of the line.
     */
    function line($x0, $y0, $x1, $y1, $color = 'black', $width = 1)
    {
        $this->setStrokeColor($color);
        $this->setStrokeWidth($width);
        $this->_operations[] = "-draw \"line $x0,$y0 $x1,$y1\"";
    }

    /**
     * Draw a polyline (a non-closed, non-filled polygon) based on a
     * set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the line with.
     * @param string  $width     (optional) The width of the line.
     */
    function polyline($verts, $color, $width = 1)
    {
        $this->setStrokeColor($color);
        $this->setStrokeWidth($width);
        $this->setFillColor('none');

        $command = '';
        foreach ($verts as $vert) {
            $command .= sprintf(' %d,%d', $vert['x'], $vert['y']);
        }
        $this->_operations[] = "-draw \"polyline $command\"";
    }

    /**
     * Draw an arc.
     *
     * @param integer $x      The x coordinate of the centre.
     * @param integer $y      The y coordinate of the centre.
     * @param integer $r      The radius of the arc.
     * @param integer $start  The start angle of the arc.
     * @param integer $end    The end angle of the arc.
     * @param string  $color  The line color of the arc.
     * @param string  $fill   The fill color of the arc (defaults to none).
     */
    function arc($x, $y, $r, $start, $end, $color = 'black', $fill = 'none')
    {
        $this->setStrokeColor($color);
        $this->setFillColor($fill);

        // Split up arcs greater than 180 degrees into two pieces.
        $mid = round(($start + $end) / 2);
        if ($mid > 90) {
            $this->_operations[] = "-draw \"ellipse $x,$y $r,$r $start,$mid\"";
            $this->_operations[] = "-draw \"ellipse $x,$y $r,$r $mid,$end\"";
        } else {
            $this->_operations[] = "-draw \"ellipse $x,$y $r,$r $start,$end\"";
        }

        // If filled, draw the outline.
        if (!empty($fill)) {
            list($x1, $y1) = $this->_circlePoint($start, $r * 2);
            list($x2, $y2) = $this->_circlePoint($mid, $r * 2);
            list($x3, $y3) = $this->_circlePoint($end, $r * 2);

            // This seems to result in slightly better placement of
            // pie slices.
            $x++;
            $y++;

            $verts = array(array('x' => $x + $x3, 'y' => $y + $y3),
                           array('x' => $x, 'y' => $y),
                           array('x' => $x + $x1, 'y' => $y + $y1));

            if ($mid > 90) {
                $verts1 = array(array('x' => $x + $x2, 'y' => $y + $y2),
                                array('x' => $x, 'y' => $y),
                                array('x' => $x + $x1, 'y' => $y + $y1));
                $verts2 = array(array('x' => $x + $x3, 'y' => $y + $y3),
                                array('x' => $x, 'y' => $y),
                                array('x' => $x + $x2, 'y' => $y + $y2));

                $this->polygon($verts1, $fill, $fill);
                $this->polygon($verts2, $fill, $fill);
            } else {
                $this->polygon($verts, $fill, $fill);
            }

            $this->polyline($verts, $color);
        }
    }

    /**
     * Change the current stroke color. Will only affect the command
     * string if $stroke is different from the previous stroke color
     * (stored in $this->_strokeColor).
     *
     * @access private
     * @see $_strokeColor
     *
     * @param string $color  The new stroke color.
     */
    function setStrokeColor($color)
    {
        if ($color != $this->_strokeColor) {
            $this->_operations[] = "-stroke $color";
            $this->_strokeColor = $color;
        }
    }

    /**
     * Change the current stroke width. Will only affect the command
     * string if $width is different from the previous stroke width
     * (stored in $this->_strokeWidth).
     *
     * @access private
     * @see $_stroke
     *
     * @param string $width  The new stroke width.
     */
    function setStrokeWidth($width)
    {
        if ($width != $this->_strokeWidth) {
            $this->_operations[] = "-strokewidth $width";
            $this->_strokeWidth = $width;
        }
    }

    /**
     * Change the current fill color. Will only affect the command
     * string if $color is different from the previous fill color
     * (stored in $this->_fillColor).
     *
     * @access private
     * @see $_fill
     *
     * @param string $color  The new fill color.
     */
    function setFillColor($color)
    {
        if ($color != $this->_fillColor) {
            $this->_operations[] = "-fill $color";
            $this->_fillColor = $color;
        }
    }

}

--- NEW FILE: png.php ---
<?php

require_once dirname(__FILE__) . '/../Image.php';

/**
 * This class implements the Horde_Image:: API for PNG images. It
 * mainly provides some utility functions, such as the ability to make
 * pixels or solid images for now.
 *
 * $Horde: framework/Image/Image/png.php,v 1.19 2004/05/07 20:51:36 chuck Exp $
 *
 * Copyright 2003-2004 Mike Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Mike Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Image
 */
class Horde_Image_png extends Horde_Image {

    /**
     * The array of pixel data.
     * @var array $_img
     */
    var $_img = array();

    /**
     * Color depth (only 8 and 16 implemented).
     * @var integer $_colorDepth
     */
    var $_colorDepth = 8;

    /**
     * Color type (only 2 (true color) implemented).
     * @var integer $_colorType
     */
    var $_colorType = 2;

    /**
     * Compression method (0 is the only current valid value).
     * @var integer $_compressionMethod
     */
    var $_compressionMethod = 0;

    /**
     * Filter method (0 is the only current valid value).
     * @var integer $_filterMethod
     */
    var $_filterMethod = 0;

    /**
     * Interlace method (only 0 (no interlace) implemented).
     * @var integer $_interlaceMethod
     */
    var $_interlaceMethod = 0;

    /**
     * PNG image constructor.
     */
    function Horde_Image_png($params)
    {
        parent::Horde_Image($params);

        if (!empty($params['width'])) {
            $this->rectangle(0, 0, $params['width'], $params['height'], $this->_background, $this->_background);
        }
    }

    function getContentType()
    {
        return 'image/png';
    }

    /**
     * Return the raw data for this image.
     *
     * @return string  The raw image data.
     */
    function raw()
    {
        return
            $this->_header() .
            $this->_IHDR() .

            /* Say what created the image file. */
            $this->_tEXt('Software', 'Horde Framework Image_png Class') .

            /* Set the last modified date/time. */
            $this->_tIME() .

            $this->_IDAT() .
            $this->_IEND();
    }

    /**
     * Reset the image data.
     */
    function reset()
    {
        parent::reset();
        $this->_img = array();
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param string  $color   (optional) The line color of the rectangle. Defaults to black.
     * @param string  $fill    (optional) The color to fill the rectangle with. Defaults to none.
     */
    function rectangle($x, $y, $width, $height, $color = 'black', $fill = 'none')
    {
        list($r, $g, $b) = $this->getRGB($color);
        if ($fill != 'none') {
            list($fR, $fG, $fB) = $this->getRGB($fill);
        }

        $x2 = $x + $width;
        $y2 = $y + $height;

        for ($h = $y; $h <= $y2; $h++) {
            for ($w = $x; $w <= $x2; $w++) {
                // See if we're on an edge.
                if ($w == $x || $h == $y || $w == $x2 || $h == $y2) {
                    $this->_img[$h][$w] = array('r' => $r, 'g' => $g, 'b' => $b);
                } elseif ($fill != 'none') {
                    $this->_img[$h][$w] = array('r' => $fR, 'g' => $fG, 'b' => $fB);
                }
            }
        }
    }

    /**
     * Create the PNG file header.
     */
    function _header()
    {
        return pack('CCCCCCCC', 137, 80, 78, 71, 13, 10, 26, 10);
    }

    /**
     * Create Image Header block.
     */
    function _IHDR()
    {
        $data = pack('a4NNCCCCC', 'IHDR', $this->_width, $this->_height, $this->_colorDepth, $this->_colorType, $this->_compressionMethod, $this->_filterMethod, $this->_interlaceMethod);
        return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
    }

    /**
     * Create IEND block.
     */
    function _IEND()
    {
        $data = 'IEND';
        return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
    }

    /**
     * Create Image Data block.
     */
    function _IDAT()
    {
        $data = '';
        $prevscanline = null;
        $filter = 0;
        for ($i = 0; $i < $this->_height; $i++) {
            $scanline = array();
            $data .= chr($filter);
            for ($j = 0; $j < $this->_width; $j++) {
                if ($this->_colorDepth == 8) {
                    $scanline[$j] = pack('CCC', $this->_img[$i][$j]['r'], $this->_img[$i][$j]['g'], $this->_img[$i][$j]['b']);
                } elseif ($this->_colorDepth == 16) {
                    $scanline[$j] = pack('nnn', $this->_img[$i][$j]['r'] << 8, $this->_img[$i][$j]['g'] << 8 , $this->_img[$i][$j]['b'] << 8 );
                }

                if ($filter == 0) {
                    /* No Filter. */
                    $data .= $scanline[$j];
                } elseif ($filter == 2) {
                    /* Up Filter. */
                    $pixel = $scanline[$j] - $prevscanline[$j];
                    if ($this->_colorDepth == 8) {
                        $data .= pack('CCC', $pixel >> 16, ($pixel >> 8) & 0xFF, $pixel & 0xFF);
                    } elseif ($this->_colorDepth == 16) {
                        $data .= pack('nnn', ($pixel >> 32), ($pixel >> 16) & 0xFFFF , $pixel & 0xFFFF);
                    }
                }
            }
            $prevscanline = $scanline;
        }
        $compressed = gzdeflate($data, 9);

        $data = 'IDAT' . pack('CCa' . strlen($compressed) . 'a4', 0x78, 0x01, $compressed, $this->_Adler32($data));
        return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
    }

    /**
     * Create tEXt block.
     */
    function _tEXt($keyword, $text)
    {
        $data = 'tEXt' . $keyword . "\0" . $text;
        return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
    }

    /**
     * Create last modified time block.
     */
    function _tIME($date = null)
    {
        if (is_null($date)) {
            $date = time();
        }

        $data = 'tIME' . pack('nCCCCC', intval(date('Y', $date)), intval(date('m', $date)), intval(date('j', $date)), intval(date('G', $date)), intval(date('i', $date)), intval(date('s', $date)));
        return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
    }

    /**
     * Calculate an Adler32 checksum for a string.
     */
    function _Adler32($input)
    {
        $s1 = 1;
        $s2 = 0;
        $iMax = strlen($input);
        for ($i = 0; $i < $iMax; $i++) {
            $s1 = ($s1 + ord($input[$i])) % 0xFFF1;
            $s2 = ($s2 + $s1) % 0xFFF1;
        }
        return pack('N', (($s2 << 16) | $s1));
    }

}

--- NEW FILE: rgb.php ---
<?php
/**
 * RGB color names/values.
 *
 * $Horde: framework/Image/Image/rgb.php,v 1.2 2003/11/06 21:03:22 chuck Exp $
 *
 * @package Horde_Image
 */
$GLOBALS['horde_image_rgb_colors'] = array(
    'aqua' => array(0, 255, 255), 		
    'lime' => array(0, 255, 0), 		
    'teal' => array(0, 128, 128), 
    'whitesmoke' => array(245, 245, 245), 
    'gainsboro' => array(220, 220, 220), 
    'oldlace' => array(253, 245, 230), 
    'linen' => array(250, 240, 230), 
    'antiquewhite' => array(250, 235, 215), 
    'papayawhip' => array(255, 239, 213), 
    'blanchedalmond' => array(255, 235, 205), 
    'bisque' => array(255, 228, 196), 
    'peachpuff' => array(255, 218, 185), 
    'navajowhite' => array(255, 222, 173), 
    'moccasin' => array(255, 228, 181), 
    'cornsilk' => array(255, 248, 220), 
    'ivory' => array(255, 255, 240), 
    'lemonchiffon' => array(255, 250, 205), 
    'seashell' => array(255, 245, 238), 
    'mintcream' => array(245, 255, 250), 
    'azure' => array(240, 255, 255), 
    'aliceblue' => array(240, 248, 255), 
    'lavender' => array(230, 230, 250), 
    'lavenderblush' => array(255, 240, 245), 
    'mistyrose' => array(255, 228, 225), 
    'white' => array(255, 255, 255), 
    'black' => array(0, 0, 0), 
    'darkslategray' => array(47, 79, 79), 
    'dimgray' => array(105, 105, 105), 
    'slategray' => array(112, 128, 144), 
    'lightslategray' => array(119, 136, 153), 
    'gray' => array(190, 190, 190), 
    'lightgray' => array(211, 211, 211), 
    'midnightblue' => array(25, 25, 112), 
    'navy' => array(0, 0, 128), 
    'cornflowerblue' => array(100, 149, 237), 
    'darkslateblue' => array(72, 61, 139), 
    'slateblue' => array(106, 90, 205), 
    'mediumslateblue' => array(123, 104, 238), 
    'lightslateblue' => array(132, 112, 255), 
    'mediumblue' => array(0, 0, 205), 
    'royalblue' => array(65, 105, 225), 
    'blue' => array(0, 0, 255), 
    'dodgerblue' => array(30, 144, 255), 
    'deepskyblue' => array(0, 191, 255), 
    'skyblue' => array(135, 206, 235), 
    'lightskyblue' => array(135, 206, 250), 
    'steelblue' => array(70, 130, 180), 
    'lightred' => array(211, 167, 168), 
    'lightsteelblue' => array(176, 196, 222), 
    'lightblue' => array(173, 216, 230), 
    'powderblue' => array(176, 224, 230), 
    'paleturquoise' => array(175, 238, 238), 
    'darkturquoise' => array(0, 206, 209), 
    'mediumturquoise' => array(72, 209, 204), 
    'turquoise' => array(64, 224, 208), 
    'cyan' => array(0, 255, 255), 
    'lightcyan' => array(224, 255, 255), 
    'cadetblue' => array(95, 158, 160), 
    'mediumaquamarine' => array(102, 205, 170), 
    'aquamarine' => array(127, 255, 212), 
    'darkgreen' => array(0, 100, 0), 
    'darkolivegreen' => array(85, 107, 47), 
    'darkseagreen' => array(143, 188, 143), 
    'seagreen' => array(46, 139, 87), 
    'mediumseagreen' => array(60, 179, 113), 
    'lightseagreen' => array(32, 178, 170), 
    'palegreen' => array(152, 251, 152), 
    'springgreen' => array(0, 255, 127), 
    'lawngreen' => array(124, 252, 0), 
    'green' => array(0, 255, 0), 
    'chartreuse' => array(127, 255, 0), 
    'mediumspringgreen' => array(0, 250, 154), 
    'greenyellow' => array(173, 255, 47), 
    'limegreen' => array(50, 205, 50), 
    'yellowgreen' => array(154, 205, 50), 
    'forestgreen' => array(34, 139, 34), 
    'olivedrab' => array(107, 142, 35), 
    'darkkhaki' => array(189, 183, 107), 
    'khaki' => array(240, 230, 140), 
    'palegoldenrod' => array(238, 232, 170), 
    'lightgoldenrodyellow' => array(250, 250, 210), 
    'lightyellow' => array(255, 255, 200), 
    'yellow' => array(255, 255, 0), 
    'gold' => array(255, 215, 0), 
    'lightgoldenrod' => array(238, 221, 130), 
    'goldenrod' => array(218, 165, 32), 
    'darkgoldenrod' => array(184, 134, 11), 
    'rosybrown' => array(188, 143, 143), 
    'indianred' => array(205, 92, 92), 
    'saddlebrown' => array(139, 69, 19), 
    'sienna' => array(160, 82, 45), 
    'peru' => array(205, 133, 63), 
    'burlywood' => array(222, 184, 135), 
    'beige' => array(245, 245, 220), 
    'wheat' => array(245, 222, 179), 
    'sandybrown' => array(244, 164, 96), 
    'tan' => array(210, 180, 140), 
    'chocolate' => array(210, 105, 30), 
    'firebrick' => array(178, 34, 34), 
    'brown' => array(165, 42, 42), 
    'darksalmon' => array(233, 150, 122), 
    'salmon' => array(250, 128, 114), 
    'lightsalmon' => array(255, 160, 122), 
    'orange' => array(255, 165, 0), 
    'darkorange' => array(255, 140, 0), 
    'coral' => array(255, 127, 80), 
    'lightcoral' => array(240, 128, 128), 
    'tomato' => array(255, 99, 71), 
    'orangered' => array(255, 69, 0), 
    'red' => array(255, 0, 0), 
    'hotpink' => array(255, 105, 180), 
    'deeppink' => array(255, 20, 147), 
    'pink' => array(255, 192, 203), 
    'lightpink' => array(255, 182, 193), 
    'palevioletred' => array(219, 112, 147), 
    'maroon' => array(176, 48, 96), 
    'mediumvioletred' => array(199, 21, 133), 
    'violetred' => array(208, 32, 144), 
    'magenta' => array(255, 0, 255), 
    'violet' => array(238, 130, 238), 
    'plum' => array(221, 160, 221), 
    'orchid' => array(218, 112, 214), 
    'mediumorchid' => array(186, 85, 211), 
    'darkorchid' => array(153, 50, 204), 
    'darkviolet' => array(148, 0, 211), 
    'blueviolet' => array(138, 43, 226), 
    'purple' => array(160, 32, 240), 
    'mediumpurple' => array(147, 112, 219), 
    'thistle' => array(216, 191, 216), 
    'snow1' => array(255, 250, 250), 
    'snow2' => array(238, 233, 233), 
    'snow3' => array(205, 201, 201), 
    'snow4' => array(139, 137, 137), 
    'seashell1' => array(255, 245, 238), 
    'seashell2' => array(238, 229, 222), 
    'seashell3' => array(205, 197, 191), 
    'seashell4' => array(139, 134, 130), 
    'AntiqueWhite1' => array(255, 239, 219), 
    'AntiqueWhite2' => array(238, 223, 204), 
    'AntiqueWhite3' => array(205, 192, 176), 
    'AntiqueWhite4' => array(139, 131, 120), 
    'bisque1' => array(255, 228, 196), 
    'bisque2' => array(238, 213, 183), 
    'bisque3' => array(205, 183, 158), 
    'bisque4' => array(139, 125, 107), 
    'peachPuff1' => array(255, 218, 185), 
    'peachpuff2' => array(238, 203, 173), 
    'peachpuff3' => array(205, 175, 149), 
    'peachpuff4' => array(139, 119, 101), 
    'navajowhite1' => array(255, 222, 173), 
    'navajowhite2' => array(238, 207, 161), 
    'navajowhite3' => array(205, 179, 139), 
    'navajowhite4' => array(139, 121, 94), 
    'lemonchiffon1' => array(255, 250, 205), 
    'lemonchiffon2' => array(238, 233, 191), 
    'lemonchiffon3' => array(205, 201, 165), 
    'lemonchiffon4' => array(139, 137, 112), 
    'ivory1' => array(255, 255, 240), 
    'ivory2' => array(238, 238, 224), 
    'ivory3' => array(205, 205, 193), 
    'ivory4' => array(139, 139, 131), 
    'honeydew' => array(193, 205, 193), 
    'lavenderblush1' => array(255, 240, 245), 
    'lavenderblush2' => array(238, 224, 229), 
    'lavenderblush3' => array(205, 193, 197), 
    'lavenderblush4' => array(139, 131, 134), 
    'mistyrose1' => array(255, 228, 225), 
    'mistyrose2' => array(238, 213, 210), 
    'mistyrose3' => array(205, 183, 181), 
    'mistyrose4' => array(139, 125, 123), 
    'azure1' => array(240, 255, 255), 
    'azure2' => array(224, 238, 238), 
    'azure3' => array(193, 205, 205), 
    'azure4' => array(131, 139, 139), 
    'slateblue1' => array(131, 111, 255), 
    'slateblue2' => array(122, 103, 238), 
    'slateblue3' => array(105, 89, 205), 
    'slateblue4' => array(71, 60, 139), 
    'royalblue1' => array(72, 118, 255), 
    'royalblue2' => array(67, 110, 238), 
    'royalblue3' => array(58, 95, 205), 
    'royalblue4' => array(39, 64, 139), 
    'dodgerblue1' => array(30, 144, 255), 
    'dodgerblue2' => array(28, 134, 238), 
    'dodgerblue3' => array(24, 116, 205), 
    'dodgerblue4' => array(16, 78, 139), 
    'steelblue1' => array(99, 184, 255), 
    'steelblue2' => array(92, 172, 238), 
    'steelblue3' => array(79, 148, 205), 
    'steelblue4' => array(54, 100, 139), 
    'deepskyblue1' => array(0, 191, 255), 
    'deepskyblue2' => array(0, 178, 238), 
    'deepskyblue3' => array(0, 154, 205), 
    'deepskyblue4' => array(0, 104, 139), 
    'skyblue1' => array(135, 206, 255), 
    'skyblue2' => array(126, 192, 238), 
    'skyblue3' => array(108, 166, 205), 
    'skyblue4' => array(74, 112, 139), 
    'lightskyblue1' => array(176, 226, 255), 
    'lightskyblue2' => array(164, 211, 238), 
    'lightskyblue3' => array(141, 182, 205), 
    'lightskyblue4' => array(96, 123, 139), 
    'slategray1' => array(198, 226, 255), 
    'slategray2' => array(185, 211, 238), 
    'slategray3' => array(159, 182, 205), 
    'slategray4' => array(108, 123, 139), 
    'lightsteelblue1' => array(202, 225, 255), 
    'lightsteelblue2' => array(188, 210, 238), 
    'lightsteelblue3' => array(162, 181, 205), 
    'lightsteelblue4' => array(110, 123, 139), 
    'lightblue1' => array(191, 239, 255), 
    'lightblue2' => array(178, 223, 238), 
    'lightblue3' => array(154, 192, 205), 
    'lightblue4' => array(104, 131, 139), 
    'lightcyan1' => array(224, 255, 255), 
    'lightcyan2' => array(209, 238, 238), 
    'lightcyan3' => array(180, 205, 205), 
    'lightcyan4' => array(122, 139, 139), 
    'paleturquoise1' => array(187, 255, 255), 
    'paleturquoise2' => array(174, 238, 238), 
    'paleturquoise3' => array(150, 205, 205), 
    'paleturquoise4' => array(102, 139, 139), 
    'cadetblue1' => array(152, 245, 255), 
    'cadetblue2' => array(142, 229, 238), 
    'cadetblue3' => array(122, 197, 205), 
    'cadetblue4' => array(83, 134, 139), 
    'turquoise1' => array(0, 245, 255), 
    'turquoise2' => array(0, 229, 238), 
    'turquoise3' => array(0, 197, 205), 
    'turquoise4' => array(0, 134, 139), 
    'cyan1' => array(0, 255, 255), 
    'cyan2' => array(0, 238, 238), 
    'cyan3' => array(0, 205, 205), 
    'cyan4' => array(0, 139, 139), 
    'darkslategray1' => array(151, 255, 255), 
    'darkslategray2' => array(141, 238, 238), 
    'darkslategray3' => array(121, 205, 205), 
    'darkslategray4' => array(82, 139, 139), 
    'aquamarine1' => array(127, 255, 212), 
    'aquamarine2' => array(118, 238, 198), 
    'aquamarine3' => array(102, 205, 170), 
    'aquamarine4' => array(69, 139, 116), 
    'darkseagreen1' => array(193, 255, 193), 
    'darkseagreen2' => array(180, 238, 180), 
    'darkseagreen3' => array(155, 205, 155), 
    'darkseagreen4' => array(105, 139, 105), 
    'seagreen1' => array(84, 255, 159), 
    'seagreen2' => array(78, 238, 148), 
    'seagreen3' => array(67, 205, 128), 
    'seagreen4' => array(46, 139, 87), 
    'palegreen1' => array(154, 255, 154), 
    'palegreen2' => array(144, 238, 144), 
    'palegreen3' => array(124, 205, 124), 
    'palegreen4' => array(84, 139, 84), 
    'springgreen1' => array(0, 255, 127), 
    'springgreen2' => array(0, 238, 118), 
    'springgreen3' => array(0, 205, 102), 
    'springgreen4' => array(0, 139, 69), 
    'chartreuse1' => array(127, 255, 0), 
    'chartreuse2' => array(118, 238, 0), 
    'chartreuse3' => array(102, 205, 0), 
    'chartreuse4' => array(69, 139, 0), 
    'olivedrab1' => array(192, 255, 62), 
    'olivedrab2' => array(179, 238, 58), 
    'olivedrab3' => array(154, 205, 50), 
    'olivedrab4' => array(105, 139, 34), 
    'darkolivegreen1' => array(202, 255, 112), 
    'darkolivegreen2' => array(188, 238, 104), 
    'darkolivegreen3' => array(162, 205, 90), 
    'darkolivegreen4' => array(110, 139, 61), 
    'khaki1' => array(255, 246, 143), 
    'khaki2' => array(238, 230, 133), 
    'khaki3' => array(205, 198, 115), 
    'khaki4' => array(139, 134, 78), 
    'lightgoldenrod1' => array(255, 236, 139), 
    'lightgoldenrod2' => array(238, 220, 130), 
    'lightgoldenrod3' => array(205, 190, 112), 
    'lightgoldenrod4' => array(139, 129, 76), 
    'yellow1' => array(255, 255, 0), 
    'yellow2' => array(238, 238, 0), 
    'yellow3' => array(205, 205, 0), 
    'yellow4' => array(139, 139, 0), 
    'gold1' => array(255, 215, 0), 
    'gold2' => array(238, 201, 0), 
    'gold3' => array(205, 173, 0), 
    'gold4' => array(139, 117, 0), 
    'goldenrod1' => array(255, 193, 37), 
    'goldenrod2' => array(238, 180, 34), 
    'goldenrod3' => array(205, 155, 29), 
    'goldenrod4' => array(139, 105, 20), 
    'darkgoldenrod1' => array(255, 185, 15), 
    'darkgoldenrod2' => array(238, 173, 14), 
    'darkgoldenrod3' => array(205, 149, 12), 
    'darkgoldenrod4' => array(139, 101, 8), 
    'rosybrown1' => array(255, 193, 193), 
    'rosybrown2' => array(238, 180, 180), 
    'rosybrown3' => array(205, 155, 155), 
    'rosybrown4' => array(139, 105, 105), 
    'indianred1' => array(255, 106, 106), 
    'indianred2' => array(238, 99, 99), 
    'indianred3' => array(205, 85, 85), 
    'indianred4' => array(139, 58, 58), 
    'sienna1' => array(255, 130, 71), 
    'sienna2' => array(238, 121, 66), 
    'sienna3' => array(205, 104, 57), 
    'sienna4' => array(139, 71, 38), 
    'burlywood1' => array(255, 211, 155), 
    'burlywood2' => array(238, 197, 145), 
    'burlywood3' => array(205, 170, 125), 
    'burlywood4' => array(139, 115, 85), 
    'wheat1' => array(255, 231, 186), 
    'wheat2' => array(238, 216, 174), 
    'wheat3' => array(205, 186, 150), 
    'wheat4' => array(139, 126, 102), 
    'tan1' => array(255, 165, 79), 
    'tan2' => array(238, 154, 73), 
    'tan3' => array(205, 133, 63), 
    'tan4' => array(139, 90, 43), 
    'chocolate1' => array(255, 127, 36), 
    'chocolate2' => array(238, 118, 33), 
    'chocolate3' => array(205, 102, 29), 
    'chocolate4' => array(139, 69, 19), 
    'firebrick1' => array(255, 48, 48), 
    'firebrick2' => array(238, 44, 44), 
    'firebrick3' => array(205, 38, 38), 
    'firebrick4' => array(139, 26, 26), 
    'brown1' => array(255, 64, 64), 
    'brown2' => array(238, 59, 59), 
    'brown3' => array(205, 51, 51), 
    'brown4' => array(139, 35, 35), 
    'salmon1' => array(255, 140, 105), 
    'salmon2' => array(238, 130, 98), 
    'salmon3' => array(205, 112, 84), 
    'salmon4' => array(139, 76, 57), 
    'lightsalmon1' => array(255, 160, 122), 
    'lightsalmon2' => array(238, 149, 114), 
    'lightsalmon3' => array(205, 129, 98), 
    'lightsalmon4' => array(139, 87, 66), 
    'orange1' => array(255, 165, 0), 
    'orange2' => array(238, 154, 0), 
    'orange3' => array(205, 133, 0), 
    'orange4' => array(139, 90, 0), 
    'darkorange1' => array(255, 127, 0), 
    'darkorange2' => array(238, 118, 0), 
    'darkorange3' => array(205, 102, 0), 
    'darkorange4' => array(139, 69, 0), 
    'coral1' => array(255, 114, 86), 
    'coral2' => array(238, 106, 80), 
    'coral3' => array(205, 91, 69), 
    'coral4' => array(139, 62, 47), 
    'tomato1' => array(255, 99, 71), 
    'tomato2' => array(238, 92, 66), 
    'tomato3' => array(205, 79, 57), 
    'tomato4' => array(139, 54, 38), 
    'orangered1' => array(255, 69, 0), 
    'orangered2' => array(238, 64, 0), 
    'orangered3' => array(205, 55, 0), 
    'orangered4' => array(139, 37, 0), 
    'deeppink1' => array(255, 20, 147), 
    'deeppink2' => array(238, 18, 137), 
    'deeppink3' => array(205, 16, 118), 
    'deeppink4' => array(139, 10, 80), 
    'hotpink1' => array(255, 110, 180), 
    'hotpink2' => array(238, 106, 167), 
    'hotpink3' => array(205, 96, 144), 
    'hotpink4' => array(139, 58, 98), 
    'pink1' => array(255, 181, 197), 
    'pink2' => array(238, 169, 184), 
    'pink3' => array(205, 145, 158), 
    'pink4' => array(139, 99, 108), 
    'lightpink1' => array(255, 174, 185), 
    'lightpink2' => array(238, 162, 173), 
    'lightpink3' => array(205, 140, 149), 
    'lightpink4' => array(139, 95, 101), 
    'palevioletred1' => array(255, 130, 171), 
    'palevioletred2' => array(238, 121, 159), 
    'palevioletred3' => array(205, 104, 137), 
    'palevioletred4' => array(139, 71, 93), 
    'maroon1' => array(255, 52, 179), 
    'maroon2' => array(238, 48, 167), 
    'maroon3' => array(205, 41, 144), 
    'maroon4' => array(139, 28, 98), 
    'violetred1' => array(255, 62, 150), 
    'violetred2' => array(238, 58, 140), 
    'violetred3' => array(205, 50, 120), 
    'violetred4' => array(139, 34, 82), 
    'magenta1' => array(255, 0, 255), 
    'magenta2' => array(238, 0, 238), 
    'magenta3' => array(205, 0, 205), 
    'magenta4' => array(139, 0, 139), 
    'mediumred' => array(140, 34, 34),          
    'orchid1' => array(255, 131, 250), 
    'orchid2' => array(238, 122, 233), 
    'orchid3' => array(205, 105, 201), 
    'orchid4' => array(139, 71, 137), 
    'plum1' => array(255, 187, 255), 
    'plum2' => array(238, 174, 238), 
    'plum3' => array(205, 150, 205), 
    'plum4' => array(139, 102, 139), 
    'mediumorchid1' => array(224, 102, 255), 
    'mediumorchid2' => array(209, 95, 238), 
    'mediumorchid3' => array(180, 82, 205), 
    'mediumorchid4' => array(122, 55, 139), 
    'darkorchid1' => array(191, 62, 255), 
    'darkorchid2' => array(178, 58, 238), 
    'darkorchid3' => array(154, 50, 205), 
    'darkorchid4' => array(104, 34, 139), 
    'purple1' => array(155, 48, 255), 
    'purple2' => array(145, 44, 238), 
    'purple3' => array(125, 38, 205), 
    'purple4' => array(85, 26, 139), 
    'mediumpurple1' => array(171, 130, 255), 
    'mediumpurple2' => array(159, 121, 238), 
    'mediumpurple3' => array(137, 104, 205), 
    'mediumpurple4' => array(93, 71, 139), 
    'thistle1' => array(255, 225, 255), 
    'thistle2' => array(238, 210, 238), 
    'thistle3' => array(205, 181, 205), 
    'thistle4' => array(139, 123, 139), 
    'gray1' => array(10, 10, 10), 
    'gray2' => array(40, 40, 30), 
    'gray3' => array(70, 70, 70), 
    'gray4' => array(100, 100, 100), 
    'gray5' => array(130, 130, 130), 
    'gray6' => array(160, 160, 160), 
    'gray7' => array(190, 190, 190), 
    'gray8' => array(210, 210, 210), 
    'gray9' => array(240, 240, 240), 
    'darkgray' => array(100, 100, 100), 
    'darkblue' => array(0, 0, 139), 
    'darkcyan' => array(0, 139, 139), 
    'darkmagenta' => array(139, 0, 139), 
    'darkred' => array(139, 0, 0), 
    'silver' => array(192,  192,  192), 
    'eggplant' => array(144, 176, 168), 
    'lightgreen' => array(144, 238, 144)
);

--- NEW FILE: svg.php ---
<?php

require_once dirname(__FILE__) . '/../Image.php';
require_once 'XML/SVG.php';

/**
 * This class implements the Horde_Image:: API for SVG.
 *
 * $Horde: framework/Image/Image/svg.php,v 1.31 2004/05/07 20:51:36 chuck Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Image
 */
class Horde_Image_svg extends Horde_Image {

    var $_svg;

    /**
     * Capabilites of this driver.
     * @var array $_capabilities
     */
    var $_capabilities = array('canvas');

    function Horde_Image_svg($params)
    {
        parent::Horde_Image($params);
        $this->_svg = &new XML_SVG_Document(array('width' => $this->_width,
                                                  'height' => $this->_height));
    }

    function getContentType()
    {
        return 'image/svg+xml';
    }

    function getLink($url, $title = '')
    {
    }

    function display()
    {
        $this->_svg->printElement();
    }

    /**
     * Return the raw data for this image.
     *
     * @return string  The raw image data.
     */
    function raw()
    {
        return $this->_svg->bufferObject();
    }

    function getFont($font)
    {
        return $font;
    }

    function _createSymbol($s, $id)
    {
        $s->setParam('id', $id);
        $defs = &new XML_SVG_Defs();
        $defs->addChild($s);
        $this->_svg->addChild($defs);
    }

    function _createDropShadow($id = 'dropShadow')
    {
        $defs = &new XML_SVG_Defs();
        $filter = &new XML_SVG_Filter(array('id' => $id));
        $filter->addPrimitive('GaussianBlur', array('in' => 'SourceAlpha',
                                                    'stdDeviation' => 2,
                                                    'result' => 'blur'));
        $filter->addPrimitive('Offset', array('in' => 'blur',
                                              'dx' => 4,
                                              'dy' => 4,
                                              'result' => 'offsetBlur'));
        $merge = &new XML_SVG_FilterPrimitive('Merge');
        $merge->addMergeNode('offsetBlur');
        $merge->addMergeNode('SourceGraphic');

        $filter->addChild($merge);
        $defs->addChild($filter);
        $this->_svg->addChild($defs);
    }

    /**
     * Draws a text string on the image in a specified location, with
     * the specified style information.
     *
     * @param string  $text       The text to draw.
     * @param integer $x          The left x coordinate of the start of the text string.
     * @param integer $y          The top y coordinate of the start of the text string.
     * @param string  $font       The font identifier you want to use for the text.
     * @param string  $color      The color that you want the text displayed in.
     * @param integer $direction  An integer that specifies the orientation of the text.
     */
    function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0)
    {
        $height = 12;
        $style = 'font-family:' . $font . ';font-height:' . $height . 'px;fill:' . $this->getHexColor($color) . ';text-anchor:start;';
        $transform = 'rotate(' . $direction . ',' . $x . ',' . $y . ')';
        $this->_svg->addChild(new XML_SVG_Text(array('text' => $string,
                                                     'x' => (int)$x,
                                                     'y' => (int)$y + $height,
                                                     'transform' => $transform,
                                                     'style' => $style)));
    }

    /**
     * Draw a circle.
     *
     * @param integer $x      The x coordinate of the centre.
     * @param integer $y      The y coordinate of the centre.
     * @param integer $r      The radius of the circle.
     * @param string  $color  The line color of the circle.
     * @param string  $fill   (optional) The color to fill the circle.
     */
    function circle($x, $y, $r, $color, $fill = null)
    {
        if (!empty($fill)) {
            $style = 'fill:' . $this->getHexColor($fill) . '; ';
        } else {
            $style = 'fill:none;';
        }
        $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';

        $this->_svg->addChild(new XML_SVG_Circle(array('cx' => $x,
                                                       'cy' => $y,
                                                       'r' => $r,
                                                       'style' => $style)));
    }

    /**
     * Draw a polygon based on a set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the polygon with.
     * @param string  $fill      (optional) The color to fill the polygon.
     */
    function polygon($verts, $color, $fill = null)
    {
        if (!empty($fill)) {
            $style = 'fill:' . $this->getHexColor($fill) . '; ';
        } else {
            $style = 'fill:none;';
        }
        $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';

        $points = '';
        foreach ($verts as $v) {
            $points .= $v['x'] . ',' . $v['y'] . ' ';
        }
        $points = trim($points);

        $this->_svg->addChild(new XML_SVG_Polygon(array('points' => $points,
                                                        'style' => $style)));
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param string  $color   The line color of the rectangle.
     * @param string  $fill    (optional) The color to fill the rectangle.
     */
    function rectangle($x, $y, $width, $height, $color, $fill = null)
    {
        if (!empty($fill)) {
            $style = 'fill:' . $this->getHexColor($fill) . '; ';
        } else {
            $style = 'fill:none;';
        }
        $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';

        $this->_svg->addChild(new XML_SVG_Rect(array('x' => $x,
                                                     'y' => $y,
                                                     'width' => $width,
                                                     'height' => $height,
                                                     'style' => $style)));
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param integer $round   The width of the corner rounding.
     * @param string  $color   The line color of the rectangle.
     * @param string  $fill    The color to fill the rectangle.
     */
    function roundedRectangle($x, $y, $width, $height, $round, $color, $fill)
    {
        if (!empty($fill)) {
            $style = 'fill:' . $this->getHexColor($fill) . '; ';
        } else {
            $style = 'fill:none;';
        }
        $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';

        $this->_svg->addChild(new XML_SVG_Rect(array('x' => $x,
                                                     'y' => $y,
                                                     'rx' => $round,
                                                     'ry' => $round,
                                                     'width' => $width,
                                                     'height' => $height,
                                                     'style' => $style)));
    }

    /**
     * Draw a line.
     *
     * @param integer $x0     The x coordinate of the start.
     * @param integer $y0     The y coordinate of the start.
     * @param integer $x1     The x coordinate of the end.
     * @param integer $y1     The y coordinate of the end.
     * @param string  $color  (optional) The line color.
     * @param string  $width  (optional) The width of the line.
     */
    function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
    {
        $style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . (int)$width;
        $this->_svg->addChild(new XML_SVG_Line(array('x1' => $x1,
                                                     'y1' => $y1,
                                                     'x2' => $x2,
                                                     'y2' => $y2,
                                                     'style' => $style)));
    }

    /**
     * Draw a dashed line.
     *
     * @param integer $x0           The x coordinate of the start.
     * @param integer $y0           The y coordinate of the start.
     * @param integer $x1           The x coordinate of the end.
     * @param integer $y1           The y coordinate of the end.
     * @param string  $color        (optional) The line color.
     * @param string  $width        (optional) The width of the line.
     * @param integer $dash_length  The length of a dash on the dashed line
     * @param integer $dash_space   The length of a space in the dashed line
     */
    function dashedLine($x1, $y1, $x2, $y2, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
    {
        $style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . (int)$width . '; stroke-dasharray:' . $dash_length . ',' . $dash_space . ';';
        $this->_svg->addChild(new XML_SVG_Line(array('x1' => $x1,
                                                     'y1' => $y1,
                                                     'x2' => $x2,
                                                     'y2' => $y2,
                                                     'style' => $style)));
    }

    /**
     * Draw a polyline (a non-closed, non-filled polygon) based on a
     * set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the line with.
     * @param string  $width     (optional) The width of the line.
     */
    function polyline($verts, $color, $width = 1)
    {
        $style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . $width . ';fill:none;';

        // Calculate the path entry.
        $path = '';

        $first = true;
        foreach ($verts as $vert) {
            if ($first) {
                $first = false;
                $path .= 'M ' . $vert['x'] . ',' . $vert['y'];
            } else {
                $path .= ' L ' . $vert['x'] . ',' . $vert['y'];
            }
        }

        $this->_svg->addChild(new XML_SVG_Path(array('d' => $path,
                                                     'style' => $style)));
    }

    /**
     * Draw an arc.
     *
     * @param integer $x      The x coordinate of the centre.
     * @param integer $y      The y coordinate of the centre.
     * @param integer $r      The radius of the arc.
     * @param integer $start  The start angle of the arc.
     * @param integer $end    The end angle of the arc.
     * @param string  $color  The line color of the arc.
     * @param string  $fill   The fill color of the arc (defaults to none).
     */
    function arc($x, $y, $r, $start, $end, $color = 'black', $fill = null)
    {
        if (!empty($fill)) {
            $style = 'fill:' . $this->getHexColor($fill) . '; ';
        } else {
            $style = 'fill:none;';
        }
        $style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';

        $mid = round(($start + $end) / 2);

        // Calculate the path entry.
        $path = '';

        // If filled, draw the outline.
        if (!empty($fill)) {
            // Start at the center of the ellipse the arc is on.
            $path .= "M $x,$y ";

            // Draw out to ellipse edge.
            list($arcX, $arcY) = $this->_circlePoint($start, $r * 2);
            $path .= 'L ' . round($x + $arcX) . ',' .
                round($y + $arcY) . ' ';
        }

        // Draw arcs.
        list($arcX, $arcY) = $this->_circlePoint($mid, $r * 2);
        $path .= "A $r,$r 0 0 1 " .
            round($x + $arcX) . ',' .
            round($y + $arcY) . ' ';

        list($arcX, $arcY) = $this->_circlePoint($end, $r * 2);
        $path .= "A $r,$r 0 0 1 " .
            round($x + $arcX) . ',' .
            round($y + $arcY) . ' ';

        // If filled, close the outline.
        if (!empty($fill)) {
            $path .= 'Z';
        }

        $path = trim($path);

        $this->_svg->addChild(new XML_SVG_Path(array('d' => $path,
                                                     'style' => $style)));
    }

}

--- NEW FILE: swf.php ---
<?php

require_once dirname(__FILE__) . '/../Image.php';

/**
 * This class implements the Horde_Image:: API for SWF, using the PHP
 * Ming extension.
 *
 * $Horde: framework/Image/Image/swf.php,v 1.24 2004/05/05 19:12:44 chuck Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Image
 */
class Horde_Image_swf extends Horde_Image {

    /**
     * Capabilites of this driver.
     * @var array $_capabilities
     */
    var $_capabilities = array('canvas');

    /**
     * SWF root movie.
     * @var resource $_movie
     */
    var $_movie;

    function Horde_Image_swf($params)
    {
        parent::Horde_Image($params);

        $this->_movie = &new SWFMovie();
        $this->_movie->setDimension($this->_width, $this->_height);

        // FIXME: honor the 'background' parameter here.
        $this->_movie->setBackground(0xff, 0xff, 0xff);
        $this->_movie->setRate(30);
    }

    function getContentType()
    {
        return 'application/x-shockwave-flash';
    }

    /**
     * Display the movie.
     */
    function display()
    {
        $this->headers();
        $this->_movie->output();
    }

    /**
     * Return the raw data for this image.
     *
     * @return string  The raw image data.
     */
    function raw()
    {
        ob_start();
        $this->_movie->output();
        $data = ob_get_contents();
        ob_end_clean();

        return $data;
    }

    /**
     * Creates a color that can be accessed in this object. When a
     * color is set, the rgba values are returned in an array.
     *
     * @param string $name  The name of the color.
     *
     * @return array  The red, green, blue, alpha values of the color.
     */
    function allocateColor($name)
    {
        list($r, $g, $b) = $this->getRGB($name);
        return array('red' => $r,
                     'green' => $g,
                     'blue' => $b,
                     'alpha' => 255);
    }

    function getFont($font)
    {
        switch ($font) {
        case 'sans-serif':
            return '_sans';

        case 'serif':
            return '_serif';

        case 'monospace':
            return '_typewriter';

        default:
            return $font;
        }
    }

    /**
     * Draws a text string on the image in a specified location, with
     * the specified style information.
     *
     * @param string  $text       The text to draw.
     * @param integer $x          The left x coordinate of the start of the text string.
     * @param integer $y          The top y coordinate of the start of the text string.
     * @param string  $font       The font identifier you want to use for the text.
     * @param string  $color      The color that you want the text displayed in.
     * @param integer $direction  An integer that specifies the orientation of the text.
     */
    function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0)
    {
        $color = $this->allocateColor($color);

        if (substr(PHP_OS, 0, 3) == 'WIN') {
            $text = &new SWFTextField(SWFTEXTFIELD_NOEDIT);
        } else {
            $text = &new SWFText();
        }
        $text->setColor($color['red'], $color['green'], $color['blue'], $color['alpha']);
        $text->addString($string);
        $text->setFont(new SWFFont($this->getFont($font)));

        $t = $this->_movie->add($text);
        $t->moveTo($x, $y);
        $t->rotate($direction);

        return $t;
    }

    /**
     * Draw a circle.
     *
     * @param integer $x      The x co-ordinate of the centre.
     * @param integer $y      The y co-ordinate of the centre.
     * @param integer $r      The radius of the circle.
     * @param string  $color  The line color of the circle.
     * @param string  $fill   (optional) The color to fill the circle.
     */
    function circle($x, $y, $r, $color, $fill = 'none')
    {
        $s = &new SWFShape();
        $color = $this->allocateColor($color);
        $s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);

        if ($fill != 'none') {
            $fillColor = $this->allocateColor($fill);
            $f = $s->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
            $s->setRightFill($f);
        }

        $a = $r * 0.414213562; // = tan(22.5 deg)
        $b = $r * 0.707106781; // = sqrt(2)/2 = sin(45 deg)

        $s->movePenTo($x + $r, $y);

        $s->drawCurveTo($x + $r, $y - $a, $x + $b, $y - $b);
        $s->drawCurveTo($x + $a, $y - $r, $x, $y - $r);
        $s->drawCurveTo($x - $a, $y - $r, $x - $b, $y - $b);
        $s->drawCurveTo($x - $r, $y - $a, $x - $r, $y);
        $s->drawCurveTo($x - $r, $y + $a, $x - $b, $y + $b);
        $s->drawCurveTo($x - $a, $y + $r, $x, $y + $r);
        $s->drawCurveTo($x + $a, $y + $r, $x + $b, $y + $b);
        $s->drawCurveTo($x + $r, $y + $a, $x + $r, $y);

        return $this->_movie->add($s);
    }

    /**
     * Draw a polygon based on a set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the polygon with.
     * @param string  $fill      (optional) The color to fill the polygon.
     */
    function polygon($verts, $color, $fill = 'none')
    {
        $color = $this->allocateColor($color);

        if (is_array($color) && is_array($verts) && (sizeof($verts) > 2)) {
            $shape = &new SWFShape();
            $shape->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);

            if ($fill != 'none') {
                $fillColor = $this->allocateColor($fill);
                $f = $shape->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
                $shape->setRightFill($f);
            }

            $first_done = false;
            foreach ($verts as $value) {
                if (!$first_done) {
                    $shape->movePenTo($value['x'], $value['y']);
                    $first_done = true;
                    $first_x = $value['x'];
                    $first_y = $value['y'];
                }
                $shape->drawLineTo($value['x'], $value['y']);
            }
            $shape->drawLineTo($first_x, $first_y);

            return $this->_movie->add($shape);
        } else {
            // If the color is an array and the vertices is a an array
            // of more than 2 points.
            return false;
        }
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param string  $color   The line color of the rectangle.
     * @param string  $fill    (optional) The color to fill the rectangle.
     */
    function rectangle($x, $y, $width, $height, $color, $fill = 'none')
    {
        $verts[0] = array('x' => $x, 'y' => $y);
        $verts[1] = array('x' => $x + $width, 'y' => $y);
        $verts[2] = array('x' => $x + $width, 'y' => $y + $height);
        $verts[3] = array('x' => $x, 'y' => $y + $height);

        return $this->polygon($verts, $color, $fill);
    }

    /**
     * Draw a rectangle.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param integer $round   The width of the corner rounding.
     * @param string  $color   (optional) The line color of the rectangle. Defaults to black.
     * @param string  $fill    (optional) The color to fill the rectangle. Defaults to none.
     */
    function roundedRectangle($x, $y, $width, $height, $round, $color = 'black', $fill = 'none')
    {
        if ($round <= 0) {
            // Optimize out any calls with no corner rounding.
            return $this->rectangle($x, $y, $width, $height, $color, $fill);
        }

        $s = &new SWFShape();
        $color = $this->allocateColor($color);
        $s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);

        if ($fill != 'none') {
            $fillColor = $this->allocateColor($fill);
            $f = $s->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
            $s->setRightFill($f);
        }

        // Set corner points to avoid lots of redundant math.
        $x1 = $x + $round;
        $y1 = $y + $round;

        $x2 = $x + $width - $round;
        $y2 = $y + $round;

        $x3 = $x + $width - $round;
        $y3 = $y + $height - $round;

        $x4 = $x + $round;
        $y4 = $y + $height - $round;

        // Start in the upper left.
        $p1 = $this->_arcPoints($round, 180, 225);
        $p2 = $this->_arcPoints($round, 225, 270);

        // Start at the lower left corner of the top left curve.
        $s->movePenTo($x1 + $p1['x1'], $y1 + $p1['y1']);

        // Draw the upper left corner.
        $s->drawCurveTo($x1 + $p1['x3'], $y1 + $p1['y3'], $x1 + $p1['x2'], $y1 + $p1['y2']);
        $s->drawCurveTo($x1 + $p2['x3'], $y1 + $p2['y3'], $x1 + $p2['x2'], $y1 + $p2['y2']);

        // Calculate the upper right points.
        $p3 = $this->_arcPoints($round, 270, 315);
        $p4 = $this->_arcPoints($round, 315, 360);

        // Connect the top left and right curves.
        $s->drawLineTo($x2 + $p3['x1'], $y2 + $p3['y1']);

        // Draw the upper right corner.
        $s->drawCurveTo($x2 + $p3['x3'], $y2 + $p3['y3'], $x2 + $p3['x2'], $y2 + $p3['y2']);
        $s->drawCurveTo($x2 + $p4['x3'], $y2 + $p4['y3'], $x2 + $p4['x2'], $y2 + $p4['y2']);

        // Calculate the lower right points.
        $p5 = $this->_arcPoints($round, 0, 45);
        $p6 = $this->_arcPoints($round, 45, 90);

        // Connect the top right and lower right curves.
        $s->drawLineTo($x3 + $p5['x1'], $y3 + $p5['y1']);

        // Draw the lower right corner.
        $s->drawCurveTo($x3 + $p5['x3'], $y3 + $p5['y3'], $x3 + $p5['x2'], $y3 + $p5['y2']);
        $s->drawCurveTo($x3 + $p6['x3'], $y3 + $p6['y3'], $x3 + $p6['x2'], $y3 + $p6['y2']);

        // Calculate the lower left points.
        $p7 = $this->_arcPoints($round, 90, 135);
        $p8 = $this->_arcPoints($round, 135, 180);

        // Connect the bottom right and bottom left curves.
        $s->drawLineTo($x4 + $p7['x1'], $y4 + $p7['y1']);

        // Draw the lower left corner.
        $s->drawCurveTo($x4 + $p7['x3'], $y4 + $p7['y3'], $x4 + $p7['x2'], $y4 + $p7['y2']);
        $s->drawCurveTo($x4 + $p8['x3'], $y4 + $p8['y3'], $x4 + $p8['x2'], $y4 + $p8['y2']);

        // Close the shape.
        $s->drawLineTo($x1 + $p1['x1'], $y1 + $p1['y1']);

        return $this->_movie->add($s);
    }

    /**
     * Draw a line.
     *
     * @param integer $x0     The x co-ordinate of the start.
     * @param integer $y0     The y co-ordinate of the start.
     * @param integer $x1     The x co-ordinate of the end.
     * @param integer $y1     The y co-ordinate of the end.
     * @param string  $color  (optional) The line color.
     * @param string  $width  (optional) The width of the line.
     */
    function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
    {
        $color = $this->allocateColor($color);

        if (is_array($color)) {
            $shape = &new SWFShape();
            $shape->setLine($width, $color['red'], $color['green'], $color['blue'], $color['alpha']);
            $shape->movePenTo($x1, $y1);
            $shape->drawLineTo($x2, $y2);

            return $this->_movie->add($shape);
        } else {
            return false;
        }
    }

    /**
     * Draw a dashed line.
     *
     * @param integer $x0           The x co-ordinate of the start.
     * @param integer $y0           The y co-ordinate of the start.
     * @param integer $x1           The x co-ordinate of the end.
     * @param integer $y1           The y co-ordinate of the end.
     * @param string  $color        (optional) The line color.
     * @param string  $width        (optional) The width of the line.
     * @param integer $dash_length  The length of a dash on the dashed line
     * @param integer $dash_space   The length of a space in the dashed line
     */
    function dashedLine($x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
    {
        // Get the length of the line in pixels.
        $line_length = max(ceil(sqrt(pow(($x1 - $x0), 2) + pow(($y1 - $y0), 2))), 2);

        $cosTheta = ($x1 - $x0) / $line_length;
        $sinTheta = ($y1 - $y0) / $line_length;
        $lastx = $x0;
        $lasty = $y0;

        // Draw the dashed line.
        for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
            $x = ($dash_length * $cosTheta) + $lastx;
            $y = ($dash_length * $sinTheta) + $lasty;

            $this->line($lastx, $lasty, $x, $y, $color);

            $lastx = $x + ($dash_space * $cosTheta);
            $lasty = $y + ($dash_space * $sinTheta);
        }
    }

    /**
     * Draw a polyline (a non-closed, non-filled polygon) based on a
     * set of vertices.
     *
     * @param array   $vertices  An array of x and y labeled arrays
     *                           (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
     * @param string  $color     The color you want to draw the line with.
     * @param string  $width     (optional) The width of the line.
     */
    function polyline($verts, $color, $width = 1)
    {
        $color = $this->allocateColor($color);

        $shape = &new SWFShape();
        $shape->setLine($width, $color['red'], $color['green'], $color['blue'], $color['alpha']);

        $first_done = false;
        foreach ($verts as $value) {
            if (!$first_done) {
                $shape->movePenTo($value['x'], $value['y']);
                $first_done = true;
            }
            $shape->drawLineTo($value['x'], $value['y']);
        }

        return $this->_movie->add($shape);
    }

    /**
     * Draw an arc.
     *
     * @param integer $x      The x co-ordinate of the centre.
     * @param integer $y      The y co-ordinate of the centre.
     * @param integer $r      The radius of the arc.
     * @param integer $start  The start angle of the arc.
     * @param integer $end    The end angle of the arc.
     * @param string  $color  (optional) The line color of the arc (defaults to black).
     * @param string  $fill   (optional) The fill color of the arc (defaults to none).
     */
    function arc($x, $y, $r, $start, $end, $color = 'black', $fill = 'none')
    {
        $s = &new SWFShape();
        $color = $this->allocateColor($color);
        $s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);

        if ($fill != 'none') {
            $fillColor = $this->allocateColor($fill);
            $f = $s->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
            $s->setRightFill($f);
        }

        if ($end - $start <= 45) {
            $pts = $this->_arcPoints($r, $start, $end);
            $s->movePenTo($x, $y);
            $s->drawLineTo($pts['x1'] + $x, $pts['y1'] + $y);
            $s->drawCurveTo($pts['x3'] + $x, $pts['y3'] + $y, $pts['x2'] + $x, $pts['y2'] + $y);
            $s->drawLineTo($x, $y);
        } else {
            $sections = ceil(($end - $start) / 45);
            for ($i = 0; $i < $sections; $i++) {
                $pts = $this->_arcPoints($r, $start + ($i * 45), ($start + (($i + 1) * 45) > $end)
                                         ? $end
                                         : ($start + (($i + 1) * 45)));

                // If we are on the first section, move the pen to the
                // centre and draw out to the edge.
                if ($i == 0 && $fill != 'none') {
                    $s->movePenTo($x, $y);
                    $s->drawLineTo($pts['x1'] + $x, $pts['y1'] + $y);
                } else {
                    $s->movePenTo($pts['x1'] + $x, $pts['y1'] + $y);
                }

                // Draw the arc.
                $s->drawCurveTo($pts['x3'] + $x, $pts['y3'] + $y, $pts['x2'] + $x, $pts['y2'] + $y);
            }

            if ($fill != 'none') {
                // Draw a line from the edge back to the centre to close
                // off the segment.
                $s->drawLineTo($x, $y);
            }
        }

        return $this->_movie->add($s);
    }

    /**
     * Draw a rectangle filled with a gradient from $color1 to
     * $color2.
     *
     * @param integer $x       The left x-coordinate of the rectangle.
     * @param integer $y       The top y-coordinate of the rectangle.
     * @param integer $width   The width of the rectangle.
     * @param integer $height  The height of the rectangle.
     * @param string  $color   The outline color of the rectangle.
     * @param string  $fill1   The name of the start color for the gradient.
     * @param string  $fill2   The name of the end color for the gradient.
     */
    function gradientRectangle($x, $y, $width, $height, $color = 'black', $fill1 = 'black', $fill2 = 'white')
    {
        $s = &new SWFShape();

        if ($color != 'none') {
            $color = $this->allocateColor($color);
            $s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);
        }

        $fill1 = $this->allocateColor($fill1);
        $fill2 = $this->allocateColor($fill2);
        $gradient = &new SWFGradient();
        $gradient->addEntry(0.0, $fill1['red'], $fill1['green'], $fill1['blue'], $fill1['alpha']);
        $gradient->addEntry(1.0, $fill2['red'], $fill2['green'], $fill2['blue'], $fill2['alpha']);

        $f = $s->addFill($gradient, SWFFILL_LINEAR_GRADIENT);
        $f->scaleTo($width / $this->_width);
        $f->moveTo($x, $y);
        $s->setRightFill($f);

        $verts[0] = array('x' => $x, 'y' => $y);
        $verts[1] = array('x' => $x + $width, 'y' => $y);
        $verts[2] = array('x' => $x + $width, 'y' => $y + $height);
        $verts[3] = array('x' => $x, 'y' => $y + $height);

        $first_done = false;
        foreach ($verts as $vert) {
            if (!$first_done) {
                $s->movePenTo($vert['x'], $vert['y']);
                $first_done = true;
                $first_x = $vert['x'];
                $first_y = $vert['y'];
            }
            $s->drawLineTo($vert['x'], $vert['y']);
        }
        $s->drawLineTo($first_x, $first_y);

        return $this->_movie->add($s);
    }

}





More information about the commits mailing list