<?php
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Urs Gehrig <urs@circle.ch>                                  |
// +----------------------------------------------------------------------+
//
// $Id: image2svg.php 302251 2010-08-15 06:06:54Z clockwerx $

/**
* XML_image2svg - Image to SVG conversion
*
* The class converts images, such as of the format JPEG, PNG
* and GIF to a standalone SVG representation. The image is being
* encoded by the PHP native encode_base64() function. You can use it
* to get back a complete SVG file, which is based on a predefinded,
* easy adaptable template file, or you can take the encoded
* file as a return value, using the get() method. Due to the
* encoding by base64, the SVG files will increase approx. 30% in
* size compared to the conventional image.
*
* NOTE:
*   Since PHP 4.0.5 GetImageSize() support has been added.
*
* EXAMPLE:
*   $i = &new XML_image2svg("test.jpg");
*   $i->tplFile = "tpl.image.svg";
*   $i->show();
*
*   The class allows also commandline conversion of images:
*   C:\>php-cli -f Test_image2svg.php > test.svg
*
* REFERENCES:
*   http://www.ietf.org/rfc/rfc2397.txt, The "data" URL scheme
*   http://www.ietf.org/rfc/rfc2045.txt, Multipurpose Internet Mail Extensions
*   http://www.w3.org/Graphics/SVG/, Scalable Vector Graphics (SVG)
*/

/**
* Image to svg conversion.
*
*
* @version  $Id: image2svg.php 302251 2010-08-15 06:06:54Z clockwerx $
* @package  XML
* @author   Urs Gehrig <urs@circle.ch>
*/
require_once("PEAR.php");
require_once("HTML/Template/IT.php");

class XML_image2svg extends PEAR
{
    /**
    * TODO: Modify the quality of the output image. Needs GD support.
    *
    * @var imageQuality
    */
    var $imageQuality = 0.8;

    /**
    * Image file pointer.
    *
    */
    var $fp = null;

    /**
    * Storage representation of the image string
    *
    * @var  string
    */
    var $buffer = "";

    /**
    * Image information
    *
    * @var  array
    */
    var $par = array(0, 0, 0, "");

    /**
    * Constructs a new XML_image2svg object.
    *
    * @param string image filename
    * @access public
    * @author Urs Gehrig <urs@circle.ch>
    */
    function XML_image2svg($fileName = "")
    {
        $this->PEAR();
        $this->fileName = $fileName;
        $this->tpl      = new HTML_Template_IT(".");
        $this->tplFile  = "";

        $this->_getImage();
    }

    /**
    * Read the image file binary-safe
    *
    * @access private
    * @return mixed bool on success or an error object otherwise
    * @author Urs Gehrig <urs@circle.ch>
    */
    function _getImage()
    {
        if(file_exists($this->fileName)) {
            $fp = fopen ($this->fileName, "rb");
            $this->buffer = fread($fp, filesize ($this->fileName));

            if (!$fp) {
                return $this->errorHandler("FAILED_OPENING_FILE", __FILE__, __LINE__);
            }

            $this->fp = $fp;
            fclose ($fp);

            return $this->_getImageParamteres();
        }
        return $this->errorHandler("FILE_NOT_FOUND", __FILE__, __LINE__);
    }

    /**
    * Get the specific image parameters
    *
    * @access private
    * @return mixed bool on success or an error object on fail
    * @author Urs Gehrig <urs@circle.ch>
    */
    function _getImageParamteres()
    {
        if (is_resource($this->fp)) {
            $this->par = @getimagesize($this->fileName);
            switch ($this->par[2]) {
            case 1:
                $this->imagetype = 'image/gif';
                break;
            case 2:
                $this->imagetype = 'image/jpg';
                break;
            case 3:
                $this->imagetype = 'image/png';
                break;
            default:
                return $this->errorHandler("MIME_ERROR", __FILE__, __LINE__);
            }
            return true;
        }
        return $this->errorHandler("READ_ERROR", __FILE__, __LINE__);
    }

    /**
    * Convert the image to an SVG file. Having specified a template file
    * allows direct inclusion of the image into a more sophisticated SVG file;
    * while not giving a template file, a most slim standalone SVG string will
    * be returned.
    *
    * @access private
    * @return mixed a string on success or an error object otherwise
    * @author Urs Gehrig <urs@circle.ch>
    */
    function _convertToSvg()
    {
        if(file_exists($this->tplFile)) {

            if(strlen($this->tplFile)) {
                $this->tpl->loadTemplatefile($this->tplFile, true, true);
                $content = array(
                    "img_width"         =>  $this->par[0],
                    "img_height"        =>  $this->par[1],
                    "data_url_scheme"   =>  sprintf("data:%s;base64,%s",
                                                $this->imagetype,
                                                $this->encodeImage()
                                            )
                );
                $this->tpl->setVariable($content);

                return $this->tpl->get();

            } else {
                $s .= sprintf("<?xml version=\"1.0\" ?>");
                $s .= sprintf("<svg %s viewBox=\"0 0 %s %s\" xml:space=\"preserve\">",
                            $this->par[3],
                            $this->par[0],
                            $this->par[1]
                );
                $s .= sprintf("<g><image %s xlink:href=\"data:%s;base64,%s\"  /></g>",
                            $this->par[3],
                            $this->imagetype,
                            $this->encodeImage()
                );
                $s .= sprintf("</svg>");

                return $s;
            }
        } else {
            return $this->errorHandler("TEMPLATE_FILE_MISSING", __FILE__, __LINE__);
        }
    }

    /**
    * Encode image by base 64
    *
    * @access public
    * @return string Returns encoded image as string
    * @author Urs Gehrig <urs@circle.ch>
    */
    function encodeImage()
    {
        return base64_encode($this->buffer);
    }

    /**
    * Return the SVG output
    *
    * @access public
    * @return mixed PEAR_Error object or string
    * @author Urs Gehrig <urs@circle.ch>
    */
    function get()
    {
        if (is_resource($this->fp)) {
            return $this->_convertToSvg();
        }
        return $this->errorHandler("CONVERSION", __FILE__, __LINE__);
    }

    /**
    * Display the SVG output
    *
    * @access public
    * @return mixed PEAR_Error object or bool on success
    * @author Urs Gehrig <urs@circle.ch>
    */
    function show()
    {
        if ($res = $this->get()) {
            header("Content-type: image/svg+xml");
            echo $res;
            return TRUE;
        }
        return $this->errorHandler("UNKNOWN", __FILE__, __LINE__);
    }

    /**
    * Handling the error messages
    *
    * @param string error message
    * @param string file where the error occured
    * @param string linenumber where the error occured
    * @access public
    * @return object PEAR_Error object
    * @author Urs Gehrig <urs@circle.ch>
    */
    function errorHandler($err="UNKNOWN", $file = __FILE__, $line = __LINE__)
    {
        $this->error_codes = array(
            "FILE_NOT_FOUND"        => "The image file could not be found.",
            "FAILED_OPENING_FILE"   => "The image file could not be opened.",
            "READ_ERROR"            => "The image file could not be read.",
            "MIME_ERROR"            => "Unsupported mimetype.",
            "TEMPLATE_FILE_MISSING" => "The SVG template file is missing.",
            "CONVERSION"            => "Error on returning the conversion result.",
            "UNKNOWN"               => "Unknown error."
        );

        return $this->raiseError(sprintf("%s [%s on line %d].",
                    $this->error_codes[$err],
                    $file,
                    $line),
                    null,
                    PEAR_ERROR_DIE
               );
    }

    /**
    * Class destructor
    *
    * @access private
    * @author Urs Gehrig <urs@circle.ch>
    */
    function _XML_image2svg()
    {
        $this->_PEAR();
    }
}
?>