diff --git a/build.xml b/build.xml
index 81f5962..d3027b5 100644
--- a/build.xml
+++ b/build.xml
@@ -27,12 +27,19 @@
+ * 'Me/myclass1.php',
+ * 'erMyClass2' => 'Me/myclass2.php',
+ * );
+ * ?>
+ *
+ *
+ * "your_autoload.php":
+ *
+ * 'You/yourclass1.php',
+ * 'erYourClass2' => 'You/yourclass2.php',
+ * );
+ * ?>
+ *
+ *
+ * The directory structure for the external repository is then:
+ *
+ * ./repos/autoloads/my_autoload.php
+ * ./repos/autoloads/you_autoload.php
+ * ./repos/Me/myclass1.php
+ * ./repos/Me/myclass2.php
+ * ./repos/You/yourclass1.php
+ * ./repos/You/yourclass2.php
+ *
+ *
+ * To use this repository with the autoload mechanism you have to use the
+ * following code:
+ *
+ *
+ *
+ *
+ * @throws ezcBaseFileNotFoundException if $autoloadDirPath or $basePath do not exist.
+ * @param string $basePath
+ * @param string $autoloadDirPath
+ * @param string $prefix
+ */
+ public static function addClassRepository( $basePath, $autoloadDirPath = null, $prefix = null )
+ {
+ // check if base path exists
+ if ( !is_dir( $basePath ) )
+ {
+ throw new ezcBaseFileNotFoundException( $basePath, 'base directory' );
+ }
+
+ // calculate autoload path if it wasn't given
+ if ( is_null( $autoloadDirPath ) )
+ {
+ $autoloadDirPath = $basePath . '/autoload';
+ }
+
+ // check if autoload dir exists
+ if ( !is_dir( $autoloadDirPath ) )
+ {
+ throw new ezcBaseFileNotFoundException( $autoloadDirPath, 'autoload directory' );
+ }
+
+ // add info to $repositoryDirs
+ if ( $prefix === null )
+ {
+ $array = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath );
+
+ // add info to the list of extra dirs
+ ezcBase::$repositoryDirs[] = $array;
+ }
+ else
+ {
+ if ( array_key_exists( $prefix, ezcBase::$repositoryDirs ) )
+ {
+ throw new ezcBaseDoubleClassRepositoryPrefixException( $prefix, $basePath, $autoloadDirPath );
+ }
+
+ // add info to the list of extra dirs, and use the prefix to identify the new repository.
+ ezcBase::$repositoryDirs[$prefix] = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath );
+ }
+ }
+
+ /**
+ * Returns the base path of the eZ Components installation
+ *
+ * This method returns the base path, including a trailing directory
+ * separator.
+ *
+ * @return string
+ */
+ public static function getInstallationPath()
+ {
+ self::setPackageDir();
+
+ $path = realpath( self::$packageDir );
+ if ( substr( $path, -1 ) !== DIRECTORY_SEPARATOR )
+ {
+ $path .= DIRECTORY_SEPARATOR;
+ }
+ return $path;
+ }
+
+ /**
+ * Sets the development mode to the one specified.
+ *
+ * @param int $runMode
+ */
+ public static function setRunMode( $runMode )
+ {
+ if ( !in_array( $runMode, array( ezcBase::MODE_PRODUCTION, ezcBase::MODE_DEVELOPMENT ) ) )
+ {
+ throw new ezcBaseValueException( 'runMode', $runMode, 'ezcBase::MODE_PRODUCTION or ezcBase::MODE_DEVELOPMENT' );
+ }
+
+ self::$runMode = $runMode;
+ }
+
+ /**
+ * Returns the current development mode.
+ *
+ * @return int
+ */
+ public static function getRunMode()
+ {
+ return self::$runMode;
+ }
+
+ /**
+ * Returns true when we are in development mode.
+ *
+ * @return bool
+ */
+ public static function inDevMode()
+ {
+ return self::$runMode == ezcBase::MODE_DEVELOPMENT;
+ }
+
+ /**
+ * Returns the installation method
+ *
+ * Possible return values are 'custom', 'devel', 'tarball' and 'pear'. Only
+ * 'tarball' and 'pear' are returned for user-installed versions.
+ *
+ * @return string
+ */
+ public static function getInstallMethod()
+ {
+ return self::$libraryMode;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/exceptions/autoload.php b/src/TUnit/external/ezc/Base/exceptions/autoload.php
new file mode 100644
index 0000000..fba1649
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/autoload.php
@@ -0,0 +1,38 @@
+autoloadPath );
+ }
+ parent::__construct( "Could not find a class to file mapping for '{$className}'. Searched for ". implode( ', ', $files ) . " in: " . implode( ', ', $paths ) );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/exceptions/double_class_repository_prefix.php b/src/TUnit/external/ezc/Base/exceptions/double_class_repository_prefix.php
new file mode 100644
index 0000000..1c33df1
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/double_class_repository_prefix.php
@@ -0,0 +1,34 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/exception.php b/src/TUnit/external/ezc/Base/exceptions/exception.php
new file mode 100644
index 0000000..c36164f
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/exception.php
@@ -0,0 +1,43 @@
+originalMessage = $message;
+
+ if ( php_sapi_name() == 'cli' )
+ {
+ parent::__construct( $message );
+ }
+ else
+ {
+ parent::__construct( htmlspecialchars( $message ) );
+ }
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/exceptions/extension_not_found.php b/src/TUnit/external/ezc/Base/exceptions/extension_not_found.php
new file mode 100644
index 0000000..4e6ff88
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/extension_not_found.php
@@ -0,0 +1,38 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/file_exception.php b/src/TUnit/external/ezc/Base/exceptions/file_exception.php
new file mode 100644
index 0000000..945b3d5
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/file_exception.php
@@ -0,0 +1,25 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/file_io.php b/src/TUnit/external/ezc/Base/exceptions/file_io.php
new file mode 100644
index 0000000..3d635a5
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/file_io.php
@@ -0,0 +1,50 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/file_not_found.php b/src/TUnit/external/ezc/Base/exceptions/file_not_found.php
new file mode 100644
index 0000000..9b65afc
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/file_not_found.php
@@ -0,0 +1,43 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/file_permission.php b/src/TUnit/external/ezc/Base/exceptions/file_permission.php
new file mode 100644
index 0000000..1bbd243
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/file_permission.php
@@ -0,0 +1,63 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/functionality_not_supported.php b/src/TUnit/external/ezc/Base/exceptions/functionality_not_supported.php
new file mode 100644
index 0000000..3fa617c
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/functionality_not_supported.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/init_callback_configured.php b/src/TUnit/external/ezc/Base/exceptions/init_callback_configured.php
new file mode 100644
index 0000000..95f158f
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/init_callback_configured.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/invalid_callback_class.php b/src/TUnit/external/ezc/Base/exceptions/invalid_callback_class.php
new file mode 100644
index 0000000..80c7095
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/invalid_callback_class.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/invalid_parent_class.php b/src/TUnit/external/ezc/Base/exceptions/invalid_parent_class.php
new file mode 100644
index 0000000..3ab5346
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/invalid_parent_class.php
@@ -0,0 +1,29 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/property_not_found.php b/src/TUnit/external/ezc/Base/exceptions/property_not_found.php
new file mode 100644
index 0000000..d4d549f
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/property_not_found.php
@@ -0,0 +1,30 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/property_permission.php b/src/TUnit/external/ezc/Base/exceptions/property_permission.php
new file mode 100644
index 0000000..1ee4961
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/property_permission.php
@@ -0,0 +1,42 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/setting_not_found.php b/src/TUnit/external/ezc/Base/exceptions/setting_not_found.php
new file mode 100644
index 0000000..df1ebcf
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/setting_not_found.php
@@ -0,0 +1,29 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/setting_value.php b/src/TUnit/external/ezc/Base/exceptions/setting_value.php
new file mode 100644
index 0000000..d436f37
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/setting_value.php
@@ -0,0 +1,42 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/value.php b/src/TUnit/external/ezc/Base/exceptions/value.php
new file mode 100644
index 0000000..fe8498a
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/value.php
@@ -0,0 +1,43 @@
+
diff --git a/src/TUnit/external/ezc/Base/exceptions/whatever.php b/src/TUnit/external/ezc/Base/exceptions/whatever.php
new file mode 100644
index 0000000..75568b5
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/exceptions/whatever.php
@@ -0,0 +1,40 @@
+
diff --git a/src/TUnit/external/ezc/Base/ezc_bootstrap.php b/src/TUnit/external/ezc/Base/ezc_bootstrap.php
new file mode 100644
index 0000000..5d8b1b0
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/ezc_bootstrap.php
@@ -0,0 +1,40 @@
+
diff --git a/src/TUnit/external/ezc/Base/features.php b/src/TUnit/external/ezc/Base/features.php
new file mode 100644
index 0000000..1047565
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/features.php
@@ -0,0 +1,365 @@
+
+ *
+ *
+ *
+ * @package Base
+ * @version 1.7
+ */
+class ezcBaseFeatures
+{
+ /**
+ * Used to store the path of the ImageMagick convert utility.
+ *
+ * It is initialized in the {@link getImageConvertExecutable()} function.
+ *
+ * @var string
+ */
+ private static $imageConvert = null;
+
+ /**
+ * Used to store the path of the ImageMagick identify utility.
+ *
+ * It is initialized in the {@link getImageIdentifyExecutable()} function.
+ *
+ * @var string
+ */
+ private static $imageIdentify = null;
+
+ /**
+ * Used to store the operating system.
+ *
+ * It is initialized in the {@link os()} function.
+ *
+ * @var string
+ */
+ private static $os = null;
+
+ /**
+ * Determines if hardlinks are supported.
+ *
+ * @return bool
+ */
+ public static function supportsLink()
+ {
+ return function_exists( 'link' );
+ }
+
+ /**
+ * Determines if symlinks are supported.
+ *
+ * @return bool
+ */
+ public static function supportsSymLink()
+ {
+ return function_exists( 'symlink' );
+ }
+
+ /**
+ * Determines if posix uids are supported.
+ *
+ * @return bool
+ */
+ public static function supportsUserId()
+ {
+ return function_exists( 'posix_getpwuid' );
+ }
+
+ /**
+ * Determines if the ImageMagick convert utility is installed.
+ *
+ * @return bool
+ */
+ public static function hasImageConvert()
+ {
+ return !is_null( self::getImageConvertExecutable() );
+ }
+
+ /**
+ * Returns the path to the ImageMagick convert utility.
+ *
+ * On Linux, Unix,... it will return something like: /usr/bin/convert
+ * On Windows it will return something like: C:\Windows\System32\convert.exe
+ *
+ * @return string
+ */
+ public static function getImageConvertExecutable()
+ {
+ if ( !is_null( self::$imageConvert ) )
+ {
+ return self::$imageConvert;
+ }
+ return ( self::$imageConvert = self::findExecutableInPath( 'convert' ) );
+ }
+
+ /**
+ * Determines if the ImageMagick identify utility is installed.
+ *
+ * @return bool
+ */
+ public static function hasImageIdentify()
+ {
+ return !is_null( self::getImageIdentifyExecutable() );
+ }
+
+ /**
+ * Returns the path to the ImageMagick identify utility.
+ *
+ * On Linux, Unix,... it will return something like: /usr/bin/identify
+ * On Windows it will return something like: C:\Windows\System32\identify.exe
+ *
+ * @return string
+ */
+ public static function getImageIdentifyExecutable()
+ {
+ if ( !is_null( self::$imageIdentify ) )
+ {
+ return self::$imageIdentify;
+ }
+ return ( self::$imageIdentify = self::findExecutableInPath( 'identify' ) );
+ }
+
+ /**
+ * Determines if the specified extension is loaded.
+ *
+ * If $version is specified, the specified extension will be tested also
+ * against the version of the loaded extension.
+ *
+ * Examples:
+ *
+ * hasExtensionSupport( 'gzip' );
+ *
+ * will return true if gzip extension is loaded.
+ *
+ *
+ * hasExtensionSupport( 'pdo_mysql', '1.0.2' );
+ *
+ * will return true if pdo_mysql extension is loaded and its version is at least 1.0.2.
+ *
+ * @param string $extension
+ * @param string $version
+ * @return bool
+ */
+ public static function hasExtensionSupport( $extension, $version = null )
+ {
+ if ( is_null( $version ) )
+ {
+ return extension_loaded( $extension );
+ }
+ return extension_loaded( $extension ) && version_compare( phpversion( $extension ), $version, ">=" ) ;
+ }
+
+ /**
+ * Determines if the specified function is available.
+ *
+ * Examples:
+ *
+ * ezcBaseFeatures::hasFunction( 'imagepstext' );
+ *
+ * will return true if support for Type 1 fonts is available with your GD
+ * extension.
+ *
+ * @param string $functionName
+ * @return bool
+ */
+ public static function hasFunction( $functionName )
+ {
+ return function_exists( $functionName );
+ }
+
+ /**
+ * Returns if a given class exists.
+ * Checks for a given class name and returns if this class exists or not.
+ * Catches the ezcBaseAutoloadException and returns false, if it was thrown.
+ *
+ * @param string $className The class to check for.
+ * @param bool $autoload True to use __autoload(), otherwise false.
+ * @return bool True if the class exists. Otherwise false.
+ */
+ public static function classExists( $className, $autoload = true )
+ {
+ try
+ {
+ if ( class_exists( $className, $autoload ) )
+ {
+ return true;
+ }
+ return false;
+ }
+ catch ( ezcBaseAutoloadException $e )
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the operating system on which PHP is running.
+ *
+ * This method returns a sanitized form of the OS name, example
+ * return values are "Windows", "Mac", "Linux" and "FreeBSD". In
+ * all other cases it returns the value of the internal PHP constant
+ * PHP_OS.
+ *
+ * @return string
+ */
+ public static function os()
+ {
+ if ( is_null( self::$os ) )
+ {
+ $uname = php_uname( 's' );
+ if ( substr( $uname, 0, 7 ) == 'Windows' )
+ {
+ self::$os = 'Windows';
+ }
+ elseif ( substr( $uname, 0, 3 ) == 'Mac' )
+ {
+ self::$os = 'Mac';
+ }
+ elseif ( strtolower( $uname ) == 'linux' )
+ {
+ self::$os = 'Linux';
+ }
+ elseif ( strtolower( substr( $uname, 0, 7 ) ) == 'freebsd' )
+ {
+ self::$os = 'FreeBSD';
+ }
+ else
+ {
+ self::$os = PHP_OS;
+ }
+ }
+ return self::$os;
+ }
+
+ /**
+ * Returns the path of the specified executable, if it can be found in the system's path.
+ *
+ * It scans the PATH enviroment variable based on the OS to find the
+ * $fileName. For Windows, the path is with \, not /. If $fileName is not
+ * found, it returns null.
+ *
+ * @todo consider using getenv( 'PATH' ) instead of $_ENV['PATH']
+ * (but that won't work under IIS)
+ *
+ * @param string $fileName
+ * @return string
+ */
+ public static function findExecutableInPath( $fileName )
+ {
+ if ( array_key_exists( 'PATH', $_ENV ) )
+ {
+ $envPath = trim( $_ENV['PATH'] );
+ }
+ else if ( ( $envPath = getenv( 'PATH' ) ) !== false )
+ {
+ $envPath = trim( $envPath );
+ }
+ if ( is_string( $envPath ) && strlen( trim( $envPath ) ) == 0 )
+ {
+ $envPath = false;
+ }
+
+ switch ( self::os() )
+ {
+ case 'Unix':
+ case 'FreeBSD':
+ case 'Mac':
+ case 'MacOS':
+ case 'Darwin':
+ case 'Linux':
+ case 'SunOS':
+ if ( $envPath )
+ {
+ $dirs = explode( ':', $envPath );
+ foreach ( $dirs as $dir )
+ {
+ // The @-operator is used here mainly to avoid
+ // open_basedir warnings. If open_basedir (or any other
+ // circumstance) prevents the desired file from being
+ // accessed, it is fine for file_exists() to return
+ // false, since it is useless for use then, anyway.
+ if ( file_exists( "{$dir}/{$fileName}" ) )
+ {
+ return "{$dir}/{$fileName}";
+ }
+ }
+ }
+ // The @-operator is used here mainly to avoid open_basedir
+ // warnings. If open_basedir (or any other circumstance)
+ // prevents the desired file from being accessed, it is fine
+ // for file_exists() to return false, since it is useless for
+ // use then, anyway.
+ elseif ( @file_exists( "./{$fileName}" ) )
+ {
+ return $fileName;
+ }
+ break;
+ case 'Windows':
+ if ( $envPath )
+ {
+ $dirs = explode( ';', $envPath );
+ foreach ( $dirs as $dir )
+ {
+ // The @-operator is used here mainly to avoid
+ // open_basedir warnings. If open_basedir (or any other
+ // circumstance) prevents the desired file from being
+ // accessed, it is fine for file_exists() to return
+ // false, since it is useless for use then, anyway.
+ if ( @file_exists( "{$dir}\\{$fileName}.exe" ) )
+ {
+ return "{$dir}\\{$fileName}.exe";
+ }
+ }
+ }
+ // The @-operator is used here mainly to avoid open_basedir
+ // warnings. If open_basedir (or any other circumstance)
+ // prevents the desired file from being accessed, it is fine
+ // for file_exists() to return false, since it is useless for
+ // use then, anyway.
+ elseif ( @file_exists( "{$fileName}.exe" ) )
+ {
+ return "{$fileName}.exe";
+ }
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Reset the cached information.
+ *
+ * @return void
+ * @access private
+ * @ignore
+ */
+ public static function reset()
+ {
+ self::$imageIdentify = null;
+ self::$imageConvert = null;
+ self::$os = null;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/file.php b/src/TUnit/external/ezc/Base/file.php
new file mode 100644
index 0000000..4f11aeb
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/file.php
@@ -0,0 +1,495 @@
+
+ *
+ *
+ *
+ * @package Base
+ * @version 1.7
+ * @mainclass
+ */
+class ezcBaseFile
+{
+ /**
+ * This is the callback used by findRecursive to collect data.
+ *
+ * This callback method works together with walkRecursive() and is called
+ * for every file/and or directory. The $context is a callback specific
+ * container in which data can be stored and shared between the different
+ * calls to the callback function. The walkRecursive() function also passes
+ * in the full absolute directory in $sourceDir, the filename in $fileName
+ * and file information (such as size, modes, types) as an array as
+ * returned by PHP's stat() in the $fileInfo parameter.
+ *
+ * @param ezcBaseFileFindContext $context
+ * @param string $sourceDir
+ * @param string $fileName
+ * @param array(stat) $fileInfo
+ */
+ static protected function findRecursiveCallback( ezcBaseFileFindContext $context, $sourceDir, $fileName, $fileInfo )
+ {
+ // ignore if we have a directory
+ if ( $fileInfo['mode'] & 0x4000 )
+ {
+ return;
+ }
+
+ // update the statistics
+ $context->elements[] = $sourceDir . DIRECTORY_SEPARATOR . $fileName;
+ $context->count++;
+ $context->size += $fileInfo['size'];
+ }
+
+ /**
+ * Walks files and directories recursively on a file system
+ *
+ * This method walks over a directory and calls a callback from every file
+ * and directory it finds. You can use $includeFilters to include only
+ * specific files, and $excludeFilters to exclude certain files from being
+ * returned. The function will always go into subdirectories even if the
+ * entry would not have passed the filters.
+ *
+ * The callback is passed in the $callback parameter, and the
+ * $callbackContext will be send to the callback function/method as
+ * parameter so that you can store data in there that persists with all the
+ * calls and recursive calls to this method. It's up to the callback method
+ * to do something useful with this. The callback function's parameters are
+ * in order:
+ *
+ *
+ *
+ *
+ *
+ * The class 'cfgConfigurationManager' is required to implement the
+ * ezcBaseConfigurationInitializer interface, which defines only one method:
+ * configureObject(). An example on how to implement such a class could be:
+ *
+ *
+ * init( 'ezcConfigurationIniReader', 'settings', array( 'useComments' => true ) );
+ * }
+ * }
+ * ?>
+ *
+ *
+ * Of course the implementation of this callback class is up to the application
+ * developer that uses the component (in this example the Configuration
+ * component's class ezcConfigurationManager).
+ *
+ * @package Base
+ * @version 1.7
+ */
+class ezcBaseInit
+{
+ /**
+ * Contains the callback where the identifier is the key of the array, and the classname to callback to the value.
+ *
+ * @var array(string=>string)
+ */
+ static private $callbackMap = array();
+
+ /**
+ * Adds the classname $callbackClassname as callback for the identifier $identifier.
+ *
+ * @param string $identifier
+ * @param string $callbackClassname
+ */
+ public static function setCallback( $identifier, $callbackClassname )
+ {
+ if ( array_key_exists( $identifier, self::$callbackMap ) )
+ {
+ throw new ezcBaseInitCallbackConfiguredException( $identifier, self::$callbackMap[$identifier] );
+ }
+ else
+ {
+ // Check if the passed classname actually exists
+ if ( !ezcBaseFeatures::classExists( $callbackClassname, true ) )
+ {
+ throw new ezcBaseInitInvalidCallbackClassException( $callbackClassname );
+ }
+
+ // Check if the passed classname actually implements the interface.
+ if ( !in_array( 'ezcBaseConfigurationInitializer', class_implements( $callbackClassname ) ) )
+ {
+ throw new ezcBaseInitInvalidCallbackClassException( $callbackClassname );
+ }
+
+ self::$callbackMap[$identifier] = $callbackClassname;
+ }
+ }
+
+ /**
+ * Uses the configured callback belonging to $identifier to configure the $object.
+ *
+ * The method will return the return value of the callback method, or null
+ * in case there was no callback set for the specified $identifier.
+ *
+ * @param string $identifier
+ * @param object $object
+ * @return mixed
+ */
+ public static function fetchConfig( $identifier, $object )
+ {
+ if ( isset( self::$callbackMap[$identifier] ) )
+ {
+ $callbackClassname = self::$callbackMap[$identifier];
+ return call_user_func( array( $callbackClassname, 'configureObject' ), $object );
+ }
+ return null;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/interfaces/configuration_initializer.php b/src/TUnit/external/ezc/Base/interfaces/configuration_initializer.php
new file mode 100644
index 0000000..0388027
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/interfaces/configuration_initializer.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Base/interfaces/persistable.php b/src/TUnit/external/ezc/Base/interfaces/persistable.php
new file mode 100644
index 0000000..3da6ce8
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/interfaces/persistable.php
@@ -0,0 +1,40 @@
+mixed)
+ */
+ public function getState();
+
+ /**
+ * Accepts an array containing data for one or more of the class' properties.
+ *
+ * @param array $properties
+ */
+ public function setState( array $properties );
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/metadata.php b/src/TUnit/external/ezc/Base/metadata.php
new file mode 100644
index 0000000..3962649
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/metadata.php
@@ -0,0 +1,120 @@
+reader = new ezcBaseMetaDataTarballReader;
+ break;
+ case 'pear':
+ $this->reader = new ezcBaseMetaDataPearReader;
+ break;
+ default:
+ throw new ezcBaseMetaDataReaderException( "Unknown install method '$installMethod'." );
+ break;
+ }
+ }
+
+ /**
+ * Returns the version string for the installed eZ Components bundle.
+ *
+ * A version string such as "2008.2.2" is returned.
+ *
+ * @return string
+ */
+ public function getBundleVersion()
+ {
+ return $this->reader->getBundleVersion();
+ }
+
+ /**
+ * Returns a PHP version string that describes the required PHP version for
+ * this installed eZ Components bundle.
+ *
+ * @return string
+ */
+ public function getRequiredPhpVersion()
+ {
+ return $this->reader->getRequiredPhpVersion();
+ }
+
+ /**
+ * Returns whether $componentName is installed
+ *
+ * If installed with PEAR, it checks the PEAR registry whether the
+ * component is there. In case the tarball installation method is used, it
+ * will return true for every component that exists (because all of them
+ * are then available).
+ *
+ * @return bool
+ */
+ public function isComponentInstalled( $componentName )
+ {
+ return $this->reader->isComponentInstalled( $componentName );
+ }
+
+ /**
+ * Returns the version string of the available $componentName or false when
+ * the component is not installed.
+ *
+ * @return string
+ */
+ public function getComponentVersion( $componentName )
+ {
+ return $this->reader->getComponentVersion( $componentName );
+ }
+
+ /**
+ * Returns a list of components that $componentName depends on.
+ *
+ * If $componentName is left empty, all installed components are returned.
+ *
+ * The returned array has as keys the component names, and as values the
+ * version of the components.
+ *
+ * @return array(string=>string).
+ */
+ public function getComponentDependencies( $componentName = null )
+ {
+ if ( $componentName === null )
+ {
+ return $this->reader->getComponentDependencies();
+ }
+ else
+ {
+ return $this->reader->getComponentDependencies( $componentName );
+ }
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/metadata/pear.php b/src/TUnit/external/ezc/Base/metadata/pear.php
new file mode 100644
index 0000000..6c789d2
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/metadata/pear.php
@@ -0,0 +1,129 @@
+registry = new PEAR_Registry;
+ }
+
+ /**
+ * Returns the version string for the installed eZ Components bundle.
+ *
+ * A version string such as "2008.2.2" is returned.
+ *
+ * @return string
+ */
+ public function getBundleVersion()
+ {
+ @$packageInfo = $this->registry->packageInfo( 'ezcomponents', null, 'components.ez.no' );
+ return $packageInfo['version']['release'];
+ }
+
+ /**
+ * Returns a PHP version string that describes the required PHP version for
+ * this installed eZ Components bundle.
+ *
+ * @return string
+ */
+ public function getRequiredPhpVersion()
+ {
+ @$packageInfo = $this->registry->packageInfo( 'ezcomponents', null, 'components.ez.no' );
+ if ( array_key_exists( 'required', $packageInfo['dependencies'] ) )
+ {
+ return $packageInfo['dependencies']['required']['php']['min'];
+ }
+ return $packageInfo['dependencies']['php']['min'];
+ }
+
+ /**
+ * Returns whether $componentName is installed
+ *
+ * Checks the PEAR registry whether the component is there.
+ *
+ * @return bool
+ */
+ public function isComponentInstalled( $componentName )
+ {
+ @$packageInfo = $this->registry->packageInfo( $componentName, null, 'components.ez.no' );
+ return is_array( $packageInfo );
+ }
+
+ /**
+ * Returns the version string of the available $componentName or false when
+ * the component is not installed.
+ *
+ * @return string
+ */
+ public function getComponentVersion( $componentName )
+ {
+ @$packageInfo = $this->registry->packageInfo( $componentName, null, 'components.ez.no' );
+ $release = $packageInfo['version']['release'];
+ return $release === null ? false : $release;
+ }
+
+ /**
+ * Returns a list of components that $componentName depends on.
+ *
+ * If $componentName is left empty, all installed components are returned.
+ *
+ * The returned array has as keys the component names, and as values the
+ * version of the components.
+ *
+ * @return array(string=>string).
+ */
+ public function getComponentDependencies( $componentName = 'ezcomponents' )
+ {
+ @$packageInfo = $this->registry->packageInfo( $componentName, 'dependencies', 'components.ez.no' );
+ if ( isset( $packageInfo['required']['package'] ) )
+ {
+ $deps = array();
+ if ( isset( $packageInfo['required']['package']['name'] ) )
+ {
+ $deps[$packageInfo['required']['package']['name']] = $packageInfo['required']['package']['min'];
+ }
+ else
+ {
+ foreach ( $packageInfo['required']['package'] as $package )
+ {
+ $deps[$package['name']] = $package['min'];
+ }
+ }
+ return $deps;
+ }
+ return array();
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/metadata/tarball.php b/src/TUnit/external/ezc/Base/metadata/tarball.php
new file mode 100644
index 0000000..5f82042
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/metadata/tarball.php
@@ -0,0 +1,153 @@
+xml = simplexml_load_file( $filename );
+ }
+
+ /**
+ * Returns the version string for the installed eZ Components bundle.
+ *
+ * A version string such as "2008.2.2" is returned.
+ *
+ * @return string
+ */
+ public function getBundleVersion()
+ {
+ return (string) $this->xml->version;
+ }
+
+ /**
+ * Returns a PHP version string that describes the required PHP version for
+ * this installed eZ Components bundle.
+ *
+ * @return string
+ */
+ public function getRequiredPhpVersion()
+ {
+ return (string) $this->xml->deps->php;
+ }
+
+ /**
+ * Returns whether $componentName is installed
+ *
+ * Returns true for every component that exists (because all of them are
+ * then available).
+ *
+ * @return bool
+ */
+ public function isComponentInstalled( $componentName )
+ {
+ $root = $this->xml->deps->packages->package;
+
+ foreach ( $root as $package )
+ {
+ if ( (string) $package['name'] == $componentName )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the version string of the available $componentName or false when
+ * the component is not installed.
+ *
+ * @return string
+ */
+ public function getComponentVersion( $componentName )
+ {
+ $root = $this->xml->deps->packages->package;
+
+ foreach ( $root as $package )
+ {
+ if ( (string) $package['name'] == $componentName )
+ {
+ return (string) $package['version'];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a list of components that $componentName depends on.
+ *
+ * If $componentName is left empty, all installed components are returned.
+ *
+ * The returned array has as keys the component names, and as values the
+ * version of the components. It returns null of the $componentName
+ * is not found.
+ *
+ * @return array(string=>string).
+ */
+ public function getComponentDependencies( $componentName = null )
+ {
+ $baseVersion = false;
+ $root = $this->xml->deps->packages;
+ $found = $componentName === null ? true : false;
+
+ // in case $componentName != null, we loop through all the components
+ // in the file, and figure out the new root that we can list dependency
+ // packages from.
+ foreach ( $root->package as $package )
+ {
+ if ( (string) $package['name'] == 'Base' )
+ {
+ $baseVersion = $package['version'];
+ }
+ if ( !$found && (string) $package['name'] == $componentName )
+ {
+ $root = $package->deps;
+ $found = true;
+ }
+ }
+
+ if ( !$found )
+ {
+ return null;
+ }
+
+ // We always add the Base dependency even though it's not in the dependency file.
+ $deps = array();
+ $deps['Base'] = (string) $baseVersion;
+
+ if ( !isset( $root->package ) )
+ {
+ return $deps;
+ }
+ foreach ( $root->package as $package )
+ {
+ $deps[(string) $package['name']] = (string) $package['version'];
+ }
+ return $deps;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/options.php b/src/TUnit/external/ezc/Base/options.php
new file mode 100644
index 0000000..79cc03c
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/options.php
@@ -0,0 +1,174 @@
+mixed)
+ */
+ protected $properties;
+
+ /**
+ * Construct a new options object.
+ * Options are constructed from an option array by default. The constructor
+ * automatically passes the given options to the __set() method to set them
+ * in the class.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If trying to access a non existent property.
+ * @throws ezcBaseValueException
+ * If the value for a property is out of range.
+ * @param array(string=>mixed) $options The initial options to set.
+ */
+ public function __construct( array $options = array() )
+ {
+ foreach ( $options as $option => $value )
+ {
+ $this->__set( $option, $value );
+ }
+ }
+
+ /**
+ * Merge an array into the actual options object.
+ * This method merges an array of new options into the actual options object.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If trying to access a non existent property.
+ * @throws ezcBaseValueException
+ * If the value for a property is out of range.
+ * @param array(string=>mixed) $newOptions The new options.
+ */
+ public function merge( array $newOptions )
+ {
+ foreach ( $newOptions as $key => $value )
+ {
+ $this->__set( $key, $value );
+ }
+ }
+
+ /**
+ * Property get access.
+ * Simply returns a given option.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @param string $propertyName The name of the option to get.
+ * @return mixed The option value.
+ * @ignore
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if the given property does not exist.
+ * @throws ezcBasePropertyPermissionException
+ * if the property to be set is a write-only property.
+ */
+ public function __get( $propertyName )
+ {
+ if ( $this->__isset( $propertyName ) === true )
+ {
+ return $this->properties[$propertyName];
+ }
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+
+ /**
+ * Sets an option.
+ * This method is called when an option is set.
+ *
+ * @param string $propertyName The name of the option to set.
+ * @param mixed $propertyValue The option value.
+ * @ignore
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if the given property does not exist.
+ * @throws ezcBaseValueException
+ * if the value to be assigned to a property is invalid.
+ * @throws ezcBasePropertyPermissionException
+ * if the property to be set is a read-only property.
+ */
+ abstract public function __set( $propertyName, $propertyValue );
+
+ /**
+ * Returns if a option exists.
+ *
+ * @param string $propertyName Option name to check for.
+ * @return bool Whether the option exists.
+ * @ignore
+ */
+ public function __isset( $propertyName )
+ {
+ return array_key_exists( $propertyName, $this->properties );
+ }
+
+ /**
+ * Returns if an option exists.
+ * Allows isset() using ArrayAccess.
+ *
+ * @param string $propertyName The name of the option to get.
+ * @return bool Whether the option exists.
+ */
+ public function offsetExists( $propertyName )
+ {
+ return $this->__isset( $propertyName );
+ }
+
+ /**
+ * Returns an option value.
+ * Get an option value by ArrayAccess.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If $propertyName is not a key in the $properties array.
+ * @param string $propertyName The name of the option to get.
+ * @return mixed The option value.
+ */
+ public function offsetGet( $propertyName )
+ {
+ return $this->__get( $propertyName );
+ }
+
+ /**
+ * Set an option.
+ * Sets an option using ArrayAccess.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If $propertyName is not a key in the $properties array.
+ * @throws ezcBaseValueException
+ * If the value for a property is out of range.
+ * @param string $propertyName The name of the option to set.
+ * @param mixed $propertyValue The value for the option.
+ */
+ public function offsetSet( $propertyName, $propertyValue )
+ {
+ $this->__set( $propertyName, $propertyValue );
+ }
+
+ /**
+ * Unset an option.
+ * Unsets an option using ArrayAccess.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If $propertyName is not a key in the $properties array.
+ * @throws ezcBaseValueException
+ * If a the value for a property is out of range.
+ * @param string $propertyName The name of the option to unset.
+ */
+ public function offsetUnset( $propertyName )
+ {
+ $this->__set( $propertyName, null );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/options/autoload.php b/src/TUnit/external/ezc/Base/options/autoload.php
new file mode 100644
index 0000000..09b758f
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/options/autoload.php
@@ -0,0 +1,75 @@
+mixed) $options
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->preload = false;
+ $this->debug = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Sets the option $name to $value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * if the property $name is not defined
+ * @throws ezcBaseValueException
+ * if $value is not correct for the property $name
+ * @param string $name
+ * @param mixed $value
+ * @ignore
+ */
+ public function __set( $name, $value )
+ {
+ switch ( $name )
+ {
+ case 'debug':
+ case 'preload':
+ if ( !is_bool( $value ) )
+ {
+ throw new ezcBaseValueException( $name, $value, 'bool' );
+ }
+ $this->properties[$name] = $value;
+ break;
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $name );
+ }
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/struct.php b/src/TUnit/external/ezc/Base/struct.php
new file mode 100644
index 0000000..3c43f20
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/struct.php
@@ -0,0 +1,42 @@
+
diff --git a/src/TUnit/external/ezc/Base/structs/file_find_context.php b/src/TUnit/external/ezc/Base/structs/file_find_context.php
new file mode 100644
index 0000000..1ce0cf1
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/structs/file_find_context.php
@@ -0,0 +1,72 @@
+elements = $elements;
+ $this->count = $count;
+ $this->size = $size;
+ }
+
+ /**
+ * Returns a new instance of this class with the data specified by $array.
+ *
+ * $array contains all the data members of this class in the form:
+ * array('member_name'=>value).
+ *
+ * __set_state makes this class exportable with var_export.
+ * var_export() generates code, that calls this method when it
+ * is parsed with PHP.
+ *
+ * @param array(string=>mixed) $array
+ * @return ezcBaseFileFindContext
+ */
+ static public function __set_state( array $array )
+ {
+ return new ezcBaseFileFindContext( $array['elements'], $array['count'], $array['size'] );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Base/structs/repository_directory.php b/src/TUnit/external/ezc/Base/structs/repository_directory.php
new file mode 100644
index 0000000..3c7b193
--- /dev/null
+++ b/src/TUnit/external/ezc/Base/structs/repository_directory.php
@@ -0,0 +1,83 @@
+type = $type;
+ $this->basePath = $basePath;
+ $this->autoloadPath = $autoloadPath;
+ }
+
+ /**
+ * Returns a new instance of this class with the data specified by $array.
+ *
+ * $array contains all the data members of this class in the form:
+ * array('member_name'=>value).
+ *
+ * __set_state makes this class exportable with var_export.
+ * var_export() generates code, that calls this method when it
+ * is parsed with PHP.
+ *
+ * @param array(string=>mixed) $array
+ * @return ezcBaseRepositoryDirectory
+ */
+ static public function __set_state( array $array )
+ {
+ return new ezcBaseRepositoryDirectory( $array['type'], $array['basePath'], $array['autoloadPath'] );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/axis/container.php b/src/TUnit/external/ezc/Graph/axis/container.php
new file mode 100644
index 0000000..827250c
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/axis/container.php
@@ -0,0 +1,221 @@
+chart = $chart;
+ }
+
+ /**
+ * Returns if the given offset exists.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key Identifier of dataset.
+ * @return bool True when the offset exists, otherwise false.
+ */
+ public function offsetExists( $key )
+ {
+ return isset( $this->data[$key] );
+ }
+
+ /**
+ * Returns the element with the given offset.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key Identifier of dataset.
+ * @return ezcGraphChartElementAxis
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If no dataset with identifier exists
+ */
+ public function offsetGet( $key )
+ {
+ if ( !isset( $this->data[$key] ) )
+ {
+ throw new ezcBasePropertyNotFoundException( $key );
+ }
+
+ return $this->data[$key];
+ }
+
+ /**
+ * Set the element with the given offset.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key
+ * @param ezcGraphChartElementAxis $value
+ * @return void
+ *
+ * @throws ezcBaseValueException
+ * If supplied value is not an ezcGraphChartElementAxis
+ */
+ public function offsetSet( $key, $value )
+ {
+ if ( !$value instanceof ezcGraphChartElementAxis )
+ {
+ throw new ezcBaseValueException( $key, $value, 'ezcGraphChartElementAxis' );
+ }
+
+ if ( $key === null )
+ {
+ $key = count( $this->data );
+ }
+
+ // Add axis and configure it with current font and palette
+ $this->data[$key] = $value;
+ $value->font = $this->chart->options->font;
+ $value->setFromPalette( $this->chart->palette );
+
+ return $value;
+ }
+
+ /**
+ * Unset the element with the given offset.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function offsetUnset( $key )
+ {
+ if ( !isset( $this->data[$key] ) )
+ {
+ throw new ezcBasePropertyNotFoundException( $key );
+ }
+
+ unset( $this->data[$key] );
+ }
+
+ /**
+ * Returns the currently selected dataset.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return ezcGraphChartElementAxis The currently selected dataset.
+ */
+ public function current()
+ {
+ return current( $this->data );
+ }
+
+ /**
+ * Returns the next dataset and selects it or false on the last dataset.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return mixed ezcGraphChartElementAxis if the next dataset exists, or false.
+ */
+ public function next()
+ {
+ return next( $this->data );
+ }
+
+ /**
+ * Returns the key of the currently selected dataset.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return int The key of the currently selected dataset.
+ */
+ public function key()
+ {
+ return key( $this->data );
+ }
+
+ /**
+ * Returns if the current dataset is valid.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return bool If the current dataset is valid
+ */
+ public function valid()
+ {
+ return ( current( $this->data ) !== false );
+ }
+
+ /**
+ * Selects the very first dataset and returns it.
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return ezcGraphChartElementAxis The very first dataset.
+ */
+ public function rewind()
+ {
+ return reset( $this->data );
+ }
+
+ /**
+ * Returns the number of datasets in the row.
+ *
+ * This method is part of the Countable interface to allow the usage of
+ * PHP's count() function to check how many datasets exist.
+ *
+ * @return int Number of datasets.
+ */
+ public function count()
+ {
+ return count( $this->data );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/axis/date.php b/src/TUnit/external/ezc/Graph/axis/date.php
new file mode 100644
index 0000000..94bb6a0
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/axis/date.php
@@ -0,0 +1,633 @@
+
+ * $graph = new ezcGraphLineChart();
+ * $graph->options->fillLines = 210;
+ * $graph->title = 'Concurrent requests';
+ * $graph->legend = false;
+ *
+ * $graph->xAxis = new ezcGraphChartElementDateAxis();
+ *
+ * // Add data
+ * $graph->data['Machine 1'] = new ezcGraphArrayDataSet( array(
+ * '8:00' => 3241,
+ * '8:13' => 934,
+ * '8:24' => 1201,
+ * '8:27' => 1752,
+ * '8:51' => 123,
+ * ) );
+ * $graph->data['Machine 2'] = new ezcGraphArrayDataSet( array(
+ * '8:05' => 623,
+ * '8:12' => 2103,
+ * '8:33' => 543,
+ * '8:43' => 2034,
+ * '8:59' => 3410,
+ * ) );
+ *
+ * $graph->data['Machine 1']->symbol = ezcGraph::BULLET;
+ * $graph->data['Machine 2']->symbol = ezcGraph::BULLET;
+ *
+ * $graph->render( 400, 150, 'tutorial_axis_datetime.svg' );
+ *
+ *
+ * @property float $startDate
+ * Starting date used to display on axis.
+ * @property float $endDate
+ * End date used to display on axis.
+ * @property float $interval
+ * Time interval between steps on axis.
+ * @property string $dateFormat
+ * Format of date string
+ * Like http://php.net/date
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphChartElementDateAxis extends ezcGraphChartElementAxis
+{
+
+ const MONTH = 2629800;
+
+ const YEAR = 31536000;
+
+ const DECADE = 315360000;
+
+ /**
+ * Minimum inserted date
+ *
+ * @var int
+ */
+ protected $minValue = false;
+
+ /**
+ * Maximum inserted date
+ *
+ * @var int
+ */
+ protected $maxValue = false;
+
+ /**
+ * Nice time intervals to used if there is no user defined interval
+ *
+ * @var array
+ */
+ protected $predefinedIntervals = array(
+ // Second
+ 1 => 'H:i.s',
+ // Ten seconds
+ 10 => 'H:i.s',
+ // Thirty seconds
+ 30 => 'H:i.s',
+ // Minute
+ 60 => 'H:i',
+ // Ten minutes
+ 600 => 'H:i',
+ // Half an hour
+ 1800 => 'H:i',
+ // Hour
+ 3600 => 'H:i',
+ // Four hours
+ 14400 => 'H:i',
+ // Six hours
+ 21600 => 'H:i',
+ // Half a day
+ 43200 => 'd.m a',
+ // Day
+ 86400 => 'd.m',
+ // Week
+ 604800 => 'W',
+ // Month
+ self::MONTH => 'M y',
+ // Year
+ self::YEAR => 'Y',
+ // Decade
+ self::DECADE => 'Y',
+ );
+
+ /**
+ * Constant used for calculation of automatic definition of major scaling
+ * steps
+ */
+ const MAJOR_COUNT = 10;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['startDate'] = false;
+ $this->properties['endDate'] = false;
+ $this->properties['interval'] = false;
+ $this->properties['dateFormat'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'startDate':
+ $this->properties['startDate'] = (int) $propertyValue;
+ break;
+ case 'endDate':
+ $this->properties['endDate'] = (int) $propertyValue;
+ break;
+ case 'interval':
+ $this->properties['interval'] = (int) $propertyValue;
+ $this->properties['initialized'] = true;
+ break;
+ case 'dateFormat':
+ $this->properties['dateFormat'] = (string) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Ensure proper timestamp
+ *
+ * Takes a mixed value from datasets, like timestamps, or strings
+ * describing some time and converts it to a timestamp.
+ *
+ * @param mixed $value
+ * @return int
+ */
+ protected static function ensureTimestamp( $value )
+ {
+ if ( is_numeric( $value ) )
+ {
+ $timestamp = (int) $value;
+ }
+ elseif ( ( $timestamp = strtotime( $value ) ) === false )
+ {
+ throw new ezcGraphErrorParsingDateException( $value );
+ }
+
+ return $timestamp;
+ }
+
+ /**
+ * Add data for this axis
+ *
+ * @param array $values Value which will be displayed on this axis
+ * @return void
+ */
+ public function addData( array $values )
+ {
+ foreach ( $values as $nr => $value )
+ {
+ $value = self::ensureTimestamp( $value );
+
+ if ( $this->minValue === false ||
+ $value < $this->minValue )
+ {
+ $this->minValue = $value;
+ }
+
+ if ( $this->maxValue === false ||
+ $value > $this->maxValue )
+ {
+ $this->maxValue = $value;
+ }
+ }
+
+ $this->properties['initialized'] = true;
+ }
+
+ /**
+ * Calculate nice time interval
+ *
+ * Use the best fitting time interval defined in class property array
+ * predefinedIntervals.
+ *
+ * @param int $min Start time
+ * @param int $max End time
+ * @return void
+ */
+ protected function calculateInterval( $min, $max )
+ {
+ $diff = $max - $min;
+
+ foreach ( $this->predefinedIntervals as $interval => $format )
+ {
+ if ( ( $diff / $interval ) <= self::MAJOR_COUNT )
+ {
+ break;
+ }
+ }
+
+ if ( ( $this->properties['startDate'] !== false ) &&
+ ( $this->properties['endDate'] !== false ) )
+ {
+ // Use interval between defined borders
+ if ( ( $diff % $interval ) > 0 )
+ {
+ // Stil use predefined date format from old interval if not set
+ if ( $this->properties['dateFormat'] === false )
+ {
+ $this->properties['dateFormat'] = $this->predefinedIntervals[$interval];
+ }
+
+ $count = ceil( $diff / $interval );
+ $interval = round( $diff / $count, 0 );
+ }
+ }
+
+ $this->properties['interval'] = $interval;
+ }
+
+ /**
+ * Calculate lower nice date
+ *
+ * Calculates a date which is earlier or equal to the given date, and is
+ * divisible by the given interval.
+ *
+ * @param int $min Date
+ * @param int $interval Interval
+ * @return int Earlier date
+ */
+ protected function calculateLowerNiceDate( $min, $interval )
+ {
+ switch ( $interval )
+ {
+ case self::MONTH:
+ // Special handling for months - not covered by the default
+ // algorithm
+ return mktime(
+ 1,
+ 0,
+ 0,
+ (int) date( 'm', $min ),
+ 1,
+ (int) date( 'Y', $min )
+ );
+ default:
+ $dateSteps = array( 60, 60, 24, 7, 52 );
+
+ $date = array(
+ (int) date( 's', $min ),
+ (int) date( 'i', $min ),
+ (int) date( 'H', $min ),
+ (int) date( 'd', $min ),
+ (int) date( 'm', $min ),
+ (int) date( 'Y', $min ),
+ );
+
+ $element = 0;
+ while ( ( $step = array_shift( $dateSteps ) ) &&
+ ( $interval > $step ) )
+ {
+ $interval /= $step;
+ $date[$element++] = (int) ( $element > 2 );
+ }
+
+ $date[$element] -= $date[$element] % $interval;
+
+ return mktime(
+ $date[2],
+ $date[1],
+ $date[0],
+ $date[4],
+ $date[3],
+ $date[5]
+ );
+ }
+ }
+
+ /**
+ * Calculate start date
+ *
+ * Use calculateLowerNiceDate to get a date earlier or equal date then the
+ * minimum date to use it as the start date for the axis depending on the
+ * selected interval.
+ *
+ * @param mixed $min Minimum date
+ * @param mixed $max Maximum date
+ * @return void
+ */
+ public function calculateMinimum( $min, $max )
+ {
+ if ( $this->properties['endDate'] === false )
+ {
+ $this->properties['startDate'] = $this->calculateLowerNiceDate( $min, $this->interval );
+ }
+ else
+ {
+ $this->properties['startDate'] = $this->properties['endDate'];
+
+ while ( $this->properties['startDate'] > $min )
+ {
+ switch ( $this->interval )
+ {
+ case self::MONTH:
+ $this->properties['startDate'] = strtotime( '-1 month', $this->properties['startDate'] );
+ break;
+ case self::YEAR:
+ $this->properties['startDate'] = strtotime( '-1 year', $this->properties['startDate'] );
+ break;
+ case self::DECADE:
+ $this->properties['startDate'] = strtotime( '-10 years', $this->properties['startDate'] );
+ break;
+ default:
+ $this->properties['startDate'] -= $this->interval;
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculate end date
+ *
+ * Use calculateLowerNiceDate to get a date later or equal date then the
+ * maximum date to use it as the end date for the axis depending on the
+ * selected interval.
+ *
+ * @param mixed $min Minimum date
+ * @param mixed $max Maximum date
+ * @return void
+ */
+ public function calculateMaximum( $min, $max )
+ {
+ $this->properties['endDate'] = $this->properties['startDate'];
+
+ while ( $this->properties['endDate'] < $max )
+ {
+ switch ( $this->interval )
+ {
+ case self::MONTH:
+ $this->properties['endDate'] = strtotime( '+1 month', $this->properties['endDate'] );
+ break;
+ case self::YEAR:
+ $this->properties['endDate'] = strtotime( '+1 year', $this->properties['endDate'] );
+ break;
+ case self::DECADE:
+ $this->properties['endDate'] = strtotime( '+10 years', $this->properties['endDate'] );
+ break;
+ default:
+ $this->properties['endDate'] += $this->interval;
+ }
+ }
+ }
+
+ /**
+ * Calculate axis bounding values on base of the assigned values
+ *
+ * @return void
+ */
+ public function calculateAxisBoundings()
+ {
+ // Prevent division by zero, when min == max
+ if ( $this->minValue == $this->maxValue )
+ {
+ if ( $this->minValue == 0 )
+ {
+ $this->maxValue = 1;
+ }
+ else
+ {
+ $this->minValue -= ( $this->minValue * .1 );
+ $this->maxValue += ( $this->maxValue * .1 );
+ }
+ }
+
+ // Use custom minimum and maximum if available
+ if ( $this->properties['startDate'] !== false )
+ {
+ $this->minValue = $this->properties['startDate'];
+ }
+
+ if ( $this->properties['endDate'] !== false )
+ {
+ $this->maxValue = $this->properties['endDate'];
+ }
+
+ // Calculate "nice" values for scaling parameters
+ if ( $this->properties['interval'] === false )
+ {
+ $this->calculateInterval( $this->minValue, $this->maxValue );
+ }
+
+ if ( $this->properties['dateFormat'] === false && isset( $this->predefinedIntervals[$this->interval] ) )
+ {
+ $this->properties['dateFormat'] = $this->predefinedIntervals[$this->interval];
+ }
+
+ if ( $this->properties['startDate'] === false )
+ {
+ $this->calculateMinimum( $this->minValue, $this->maxValue );
+ }
+
+ if ( $this->properties['endDate'] === false )
+ {
+ $this->calculateMaximum( $this->minValue, $this->maxValue );
+ }
+ }
+
+ /**
+ * Get coordinate for a dedicated value on the chart
+ *
+ * @param float $value Value to determine position for
+ * @return float Position on chart
+ */
+ public function getCoordinate( $value )
+ {
+ // Force typecast, because ( false < -100 ) results in (bool) true
+ $intValue = ( $value === false ? false : self::ensureTimestamp( $value ) );
+
+ if ( ( $value === false ) &&
+ ( ( $intValue < $this->startDate ) || ( $intValue > $this->endDate ) ) )
+ {
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return 0.;
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1.;
+ }
+ }
+ else
+ {
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate );
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1 - ( $intValue - $this->startDate ) / ( $this->endDate - $this->startDate );
+ }
+ }
+ }
+
+ /**
+ * Return count of minor steps
+ *
+ * @return integer Count of minor steps
+ */
+ public function getMinorStepCount()
+ {
+ return false;
+ }
+
+ /**
+ * Return count of major steps
+ *
+ * @return integer Count of major steps
+ */
+ public function getMajorStepCount()
+ {
+ return (int) ceil( ( $this->properties['endDate'] - $this->startDate ) / $this->interval );
+ }
+
+ /**
+ * Get label for a dedicated step on the axis
+ *
+ * @param integer $step Number of step
+ * @return string label
+ */
+ public function getLabel( $step )
+ {
+ return $this->getLabelFromTimestamp( $this->startDate + ( $step * $this->interval ), $step );
+ }
+
+ /**
+ * Get label for timestamp
+ *
+ * @param int $time
+ * @param int $step
+ * @return string
+ */
+ protected function getLabelFromTimestamp( $time, $step )
+ {
+ if ( $this->properties['labelCallback'] !== null )
+ {
+ return call_user_func_array(
+ $this->properties['labelCallback'],
+ array(
+ date( $this->properties['dateFormat'], $time ),
+ $step,
+ )
+ );
+ }
+ else
+ {
+ return date( $this->properties['dateFormat'], $time );
+ }
+ }
+
+ /**
+ * Return array of steps on this axis
+ *
+ * @return array( ezcGraphAxisStep )
+ */
+ public function getSteps()
+ {
+ $steps = array();
+
+ $start = $this->properties['startDate'];
+ $end = $this->properties['endDate'];
+ $distance = $end - $start;
+
+ $step = 0;
+ for ( $time = $start; $time <= $end; )
+ {
+ $steps[] = new ezcGraphAxisStep(
+ ( $time - $start ) / $distance,
+ $this->interval / $distance,
+ $this->getLabelFromTimestamp( $time, $step++ ),
+ array(),
+ $step === 1,
+ $time >= $end
+ );
+
+ switch ( $this->interval )
+ {
+ case self::MONTH:
+ $time = strtotime( '+1 month', $time );
+ break;
+ case self::YEAR:
+ $time = strtotime( '+1 year', $time );
+ break;
+ case self::DECADE:
+ $time = strtotime( '+10 years', $time );
+ break;
+ default:
+ $time += $this->interval;
+ break;
+ }
+ }
+
+ return $steps;
+ }
+
+ /**
+ * Is zero step
+ *
+ * Returns true if the given step is the one on the initial axis position
+ *
+ * @param int $step Number of step
+ * @return bool Status If given step is initial axis position
+ */
+ public function isZeroStep( $step )
+ {
+ return ( $step == 0 );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/axis/labeled.php b/src/TUnit/external/ezc/Graph/axis/labeled.php
new file mode 100644
index 0000000..23f1c64
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/axis/labeled.php
@@ -0,0 +1,520 @@
+
+ * $graph = new ezcGraphLineChart();
+ * $graph->options->fillLines = 210;
+ * $graph->options->font->maxFontSize = 10;
+ * $graph->title = 'Error level colors';
+ * $graph->legend = false;
+ *
+ * $graph->yAxis = new ezcGraphChartElementLabeledAxis();
+ * $graph->yAxis->axisLabelRenderer->showZeroValue = true;
+ *
+ * $graph->yAxis->label = 'Color';
+ * $graph->xAxis->label = 'Error level';
+ *
+ * // Add data
+ * $graph->data['colors'] = new ezcGraphArrayDataSet(
+ * array(
+ * 'info' => 'blue',
+ * 'notice' => 'green',
+ * 'warning' => 'orange',
+ * 'error' => 'red',
+ * 'fatal' => 'red',
+ * )
+ * );
+ *
+ * $graph->render( 400, 150, 'tutorial_axis_labeled.svg' );
+ *
+ *
+ * @property float $labelCount
+ * Define count of displayed labels on the axis
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphChartElementLabeledAxis extends ezcGraphChartElementAxis
+{
+ /**
+ * Array with labeles for data
+ *
+ * @var array
+ */
+ protected $labels = array();
+
+ /**
+ * Labels indexed by their name as key for faster lookups
+ *
+ * @var array
+ */
+ protected $labelsIndexed = array();
+
+ /**
+ * Reduced amount of labels which will be displayed in the chart
+ *
+ * @var array
+ */
+ protected $displayedLabels = array();
+
+ /**
+ * Maximum count of labels which can be displayed on one axis
+ * @todo Perhaps base this on the chart size
+ */
+ const MAX_LABEL_COUNT = 10;
+
+ /**
+ * Precalculated steps on the axis
+ *
+ * @var array(ezcGraphAxisStep)
+ */
+ protected $steps;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['labelCount'] = null;
+
+ $this->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer();
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'labelCount':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' );
+ }
+
+ $this->properties['labelCount'] = (int) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Increase the keys of all elements in the array up from the start key, to
+ * insert an additional element at the correct position.
+ *
+ * @param array $array Array
+ * @param int $startKey Key to increase keys from
+ * @return array Updated array
+ */
+ protected function increaseKeys( array $array, $startKey )
+ {
+ foreach ( $array as $key => $value )
+ {
+ if ( $key === $startKey )
+ {
+ // Recursive check, if next key should be increased, too
+ if ( isset ( $array[$key + 1] ) )
+ {
+ $array = $this->increaseKeys( $array, $key + 1 );
+ }
+
+ // Increase key
+ $array[$key + 1] = $array[$key];
+ unset( $array[$key] );
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Provide initial set of labels
+ *
+ * This method may be used to provide an ordered set of labels, containing
+ * labels, which are not available in the datasets or to provide a label
+ * order different to the one in the given dataset.
+ *
+ * @param array $labels
+ * @return void
+ */
+ public function provideLabels( array $labels )
+ {
+ $this->addData( $labels );
+ }
+
+ /**
+ * Add data for this axis
+ *
+ * @param array $values Value which will be displayed on this axis
+ * @return void
+ */
+ public function addData( array $values )
+ {
+ $position = 0;
+ foreach ( $values as $label )
+ {
+ $label = (string) $label;
+
+ if ( !in_array( $label, $this->labels, true ) )
+ {
+ if ( isset( $this->labels[$position] ) )
+ {
+ $this->labels = $this->increaseKeys( $this->labels, $position );
+ $this->labels[$position++] = $label;
+ }
+ else
+ {
+ $this->labels[$position++] = $label;
+ }
+ }
+ else
+ {
+ $position = array_search( $label, $this->labels, true ) + 1;
+ }
+ }
+ ksort( $this->labels );
+ $this->labelsIndexed = array_flip( $this->labels );
+
+ $this->properties['initialized'] = true;
+ }
+
+ /**
+ * Calculate axis bounding values on base of the assigned values
+ *
+ * @abstract
+ * @access public
+ * @return void
+ */
+ public function calculateAxisBoundings()
+ {
+ $this->steps = array();
+
+ // Apply label format callback function
+ if ( $this->properties['labelCallback'] !== null )
+ {
+ foreach ( $this->labels as $nr => $label )
+ {
+ $this->labels[$nr] = call_user_func_array(
+ $this->properties['labelCallback'],
+ array(
+ $label,
+ $nr
+ )
+ );
+ }
+ }
+
+ $labelCount = count( $this->labels ) - 1;
+
+ if ( $labelCount === 0 )
+ {
+ // Create single only step
+ $this->steps = array(
+ new ezcGraphAxisStep(
+ 0,
+ 1,
+ reset( $this->labels ),
+ array(),
+ true,
+ true
+ ),
+ );
+
+ return true;
+ }
+
+ if ( $this->properties['labelCount'] === null )
+ {
+ if ( $labelCount <= self::MAX_LABEL_COUNT )
+ {
+ $stepSize = 1 / $labelCount;
+
+ foreach ( $this->labels as $nr => $label )
+ {
+ $this->steps[] = new ezcGraphAxisStep(
+ $stepSize * $nr,
+ $stepSize,
+ $label,
+ array(),
+ $nr === 0,
+ $nr === $labelCount
+ );
+ }
+
+ // @TODO: This line is deprecated and only build for
+ // deprecated getLabel()
+ $this->displayedLabels = $this->labels;
+
+ return true;
+ }
+
+ for ( $div = self::MAX_LABEL_COUNT; $div > 1; --$div )
+ {
+ if ( ( $labelCount % $div ) === 0 )
+ {
+ // @TODO: This part is deprecated and only build for
+ // deprecated getLabel()
+ $step = $labelCount / $div;
+
+ foreach ( $this->labels as $nr => $label )
+ {
+ if ( ( $nr % $step ) === 0 )
+ {
+ $this->displayedLabels[] = $label;
+ }
+ }
+ // End of deprecated part
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ $div = false;
+ }
+
+ // Build up step array
+ if ( $div > 2 )
+ {
+ $step = $labelCount / $div;
+ $stepSize = 1 / $div;
+ $minorStepSize = $stepSize / $step;
+
+ foreach ( $this->labels as $nr => $label )
+ {
+ if ( ( $nr % $step ) === 0 )
+ {
+ $mainstep = new ezcGraphAxisStep(
+ $stepSize * ( $nr / $step ),
+ $stepSize,
+ $label,
+ array(),
+ $nr === 0,
+ $nr === $labelCount
+ );
+
+ $this->steps[] = $mainstep;
+ }
+ else
+ {
+ $mainstep->childs[] = new ezcGraphAxisStep(
+ $mainstep->position + $minorStepSize * ( $nr % $step ),
+ $minorStepSize
+ );
+ }
+ }
+ }
+ else
+ {
+ if ( $this->properties['labelCount'] === null )
+ {
+ $floatStep = $labelCount / ( self::MAX_LABEL_COUNT - 1 );
+ }
+ else
+ {
+ $floatStep = $labelCount / min( $labelCount, $this->properties['labelCount'] - 1 );
+ }
+
+ $position = 0;
+ $minorStepSize = 1 / $labelCount;
+
+ foreach ( $this->labels as $nr => $label )
+ {
+ if ( $nr >= $position )
+ {
+ $position += $floatStep;
+
+ // Add as major step
+ $mainstep = new ezcGraphAxisStep(
+ $minorStepSize * $nr,
+ ceil( $position - $nr ) * $minorStepSize,
+ $label,
+ array(),
+ $nr === 0,
+ $nr === $labelCount
+ );
+
+ // @TODO: This line is deprecated and only build for
+ // deprecated getLabel()
+ $this->displayedLabels[] = $label;
+
+ $this->steps[] = $mainstep;
+ }
+ else
+ {
+ $mainstep->childs[] = new ezcGraphAxisStep(
+ $minorStepSize * $nr,
+ $minorStepSize
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Return array of steps on this axis
+ *
+ * @return array( ezcGraphAxisStep )
+ */
+ public function getSteps()
+ {
+ return $this->steps;
+ }
+
+ /**
+ * Get coordinate for a dedicated value on the chart
+ *
+ * @param string $value Value to determine position for
+ * @return float Position on chart
+ */
+ public function getCoordinate( $value )
+ {
+ if ( ( $value === false ) ||
+ ( $value === null ) ||
+ ( !isset( $this->labelsIndexed[$value] ) ) )
+ {
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return 0.;
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1.;
+ }
+ }
+ else
+ {
+ $key = $this->labelsIndexed[$value];
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ if ( count( $this->labels ) > 1 )
+ {
+ return (float) $key / ( count ( $this->labels ) - 1 );
+ }
+ else
+ {
+ return 0;
+ }
+ case ezcGraph::BOTTOM:
+ case ezcGraph::RIGHT:
+ if ( count( $this->labels ) > 1 )
+ {
+ return (float) 1 - $key / ( count ( $this->labels ) - 1 );
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ }
+
+ /**
+ * Return count of minor steps
+ *
+ * @return integer Count of minor steps
+ */
+ public function getMinorStepCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Return count of major steps
+ *
+ * @return integer Count of major steps
+ */
+ public function getMajorStepCount()
+ {
+ return max( count( $this->displayedLabels ) - 1, 1 );
+ }
+
+ /**
+ * Get label for a dedicated step on the axis
+ *
+ * @param integer $step Number of step
+ * @return string label
+ */
+ public function getLabel( $step )
+ {
+ if ( isset( $this->displayedLabels[$step] ) )
+ {
+ return $this->displayedLabels[$step];
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Is zero step
+ *
+ * Returns true if the given step is the one on the initial axis position
+ *
+ * @param int $step Number of step
+ * @return bool Status If given step is initial axis position
+ */
+ public function isZeroStep( $step )
+ {
+ return !$step;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/axis/logarithmic.php b/src/TUnit/external/ezc/Graph/axis/logarithmic.php
new file mode 100644
index 0000000..53c82ef
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/axis/logarithmic.php
@@ -0,0 +1,341 @@
+
+ * $graph = new ezcGraphLineChart();
+ * $graph->title = 'The power of x';
+ * $graph->legend->position = ezcGraph::BOTTOM;
+ *
+ * $graph->xAxis = new ezcGraphChartElementNumericAxis();
+ * $graph->yAxis = new ezcGraphChartElementLogarithmicalAxis();
+ *
+ * $graph->data['x^2'] = new ezcGraphNumericDataSet(
+ * -10, 10,
+ * create_function( '$x', 'return pow( $x, 2 ) + 1;' )
+ * );
+ *
+ * $graph->data['x^4'] = new ezcGraphNumericDataSet(
+ * -10, 10,
+ * create_function( '$x', 'return pow( $x, 4 ) + 1;' )
+ * );
+ *
+ * $graph->data['x^6'] = new ezcGraphNumericDataSet(
+ * -10, 10,
+ * create_function( '$x', 'return pow( $x, 6 ) + 1;' )
+ * );
+ *
+ * $graph->render( 400, 250, 'tutorial_axis_logarithmic.svg' );
+ *
+ *
+ * @property float $base
+ * Base for logarithmical scaling.
+ * @property string $logarithmicalFormatString
+ * Sprintf formatstring for the axis labels where
+ * $1 is the base and
+ * $2 is the exponent.
+ * @property-read float $minValue
+ * Minimum Value to display on this axis.
+ * @property-read float $maxValue
+ * Maximum value to display on this axis.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphChartElementLogarithmicalAxis extends ezcGraphChartElementAxis
+{
+
+ /**
+ * Constant used for calculation of automatic definition of major scaling
+ * steps
+ */
+ const MAX_STEPS = 9;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['min'] = null;
+ $this->properties['max'] = null;
+ $this->properties['base'] = 10;
+ $this->properties['logarithmicalFormatString'] = '%1$d^%2$d';
+ $this->properties['minValue'] = null;
+ $this->properties['maxValue'] = null;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'min':
+ case 'max':
+ if ( !is_numeric( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ $this->properties['initialized'] = true;
+ break;
+ case 'base':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+ case 'logarithmicalFormatString':
+ $this->properties['logarithmicalFormatString'] = (string) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Add data for this axis
+ *
+ * @param array $values Value which will be displayed on this axis
+ * @return void
+ */
+ public function addData( array $values )
+ {
+ foreach ( $values as $value )
+ {
+ if ( $this->properties['minValue'] === null ||
+ $value < $this->properties['minValue'] )
+ {
+ $this->properties['minValue'] = $value;
+ }
+
+ if ( $this->properties['maxValue'] === null ||
+ $value > $this->properties['maxValue'] )
+ {
+ $this->properties['maxValue'] = $value;
+ }
+ }
+
+ $this->properties['initialized'] = true;
+ }
+
+ /**
+ * Calculate axis bounding values on base of the assigned values
+ *
+ * @abstract
+ * @access public
+ * @return void
+ */
+ public function calculateAxisBoundings()
+ {
+ // Prevent division by zero, when min == max
+ if ( $this->properties['minValue'] == $this->properties['maxValue'] )
+ {
+ if ( $this->properties['minValue'] == 0 )
+ {
+ $this->properties['maxValue'] = 1;
+ }
+ else
+ {
+ $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 );
+ $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 );
+ }
+ }
+
+ if ( $this->properties['minValue'] <= 0 )
+ {
+ throw new ezcGraphOutOfLogithmicalBoundingsException( $this->properties['minValue'] );
+ }
+
+ // Use custom minimum and maximum if available
+ if ( $this->properties['min'] !== null )
+ {
+ $this->properties['minValue'] = pow( $this->properties['base'], $this->properties['min'] );
+ }
+
+ if ( $this->properties['max'] !== null )
+ {
+ $this->properties['maxValue'] = pow( $this->properties['base'], $this->properties['max'] );
+ }
+
+ // Calculate "nice" values for scaling parameters
+ if ( $this->properties['min'] === null )
+ {
+ $this->properties['min'] = floor( log( $this->properties['minValue'], $this->properties['base'] ) );
+ }
+
+ if ( $this->properties['max'] === null )
+ {
+ $this->properties['max'] = ceil( log( $this->properties['maxValue'], $this->properties['base'] ) );
+ }
+
+ $this->properties['minorStep'] = 1;
+ if ( ( $modifier = ( ( $this->properties['max'] - $this->properties['min'] ) / self::MAX_STEPS ) ) > 1 )
+ {
+ $this->properties['majorStep'] = $modifier = ceil( $modifier );
+ $this->properties['min'] = floor( $this->properties['min'] / $modifier ) * $modifier;
+ $this->properties['max'] = floor( $this->properties['max'] / $modifier ) * $modifier;
+ }
+ else
+ {
+ $this->properties['majorStep'] = 1;
+ }
+ }
+
+ /**
+ * Get coordinate for a dedicated value on the chart
+ *
+ * @param float $value Value to determine position for
+ * @return float Position on chart
+ */
+ public function getCoordinate( $value )
+ {
+ // Force typecast, because ( false < -100 ) results in (bool) true
+ $floatValue = (float) $value;
+
+ if ( $value === false )
+ {
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return 0.;
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1.;
+ }
+ }
+ else
+ {
+ $position = ( log( $value, $this->properties['base'] ) - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] );
+
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return $position;
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1 - $position;
+ }
+ }
+ }
+
+ /**
+ * Return count of minor steps
+ *
+ * @return integer Count of minor steps
+ */
+ public function getMinorStepCount()
+ {
+ return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] );
+ }
+
+ /**
+ * Return count of major steps
+ *
+ * @return integer Count of major steps
+ */
+ public function getMajorStepCount()
+ {
+ return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] );
+ }
+
+ /**
+ * Get label for a dedicated step on the axis
+ *
+ * @param integer $step Number of step
+ * @return string label
+ */
+ public function getLabel( $step )
+ {
+ if ( $this->properties['labelCallback'] !== null )
+ {
+ return call_user_func_array(
+ $this->properties['labelCallback'],
+ array(
+ sprintf(
+ $this->properties['logarithmicalFormatString'],
+ $this->properties['base'],
+ $this->properties['min'] + ( $step * $this->properties['majorStep'] )
+ ),
+ $step,
+ )
+ );
+ }
+ else
+ {
+ return sprintf(
+ $this->properties['logarithmicalFormatString'],
+ $this->properties['base'],
+ $this->properties['min'] + ( $step * $this->properties['majorStep'] )
+ );
+ }
+ }
+
+ /**
+ * Is zero step
+ *
+ * Returns true if the given step is the one on the initial axis position
+ *
+ * @param int $step Number of step
+ * @return bool Status If given step is initial axis position
+ */
+ public function isZeroStep( $step )
+ {
+ return ( $step == 0 );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/axis/numeric.php b/src/TUnit/external/ezc/Graph/axis/numeric.php
new file mode 100644
index 0000000..ce38b45
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/axis/numeric.php
@@ -0,0 +1,501 @@
+
+ * $graph = new ezcGraphLineChart();
+ * $graph->title = 'Some random data';
+ * $graph->legend = false;
+ *
+ * $graph->xAxis = new ezcGraphChartElementNumericAxis();
+ * // The y axis is numeric by default.
+ *
+ * $graph->xAxis->min = -15;
+ * $graph->xAxis->max = 15;
+ * $graph->xAxis->majorStep = 5;
+ *
+ * $data = array(
+ * array(),
+ * array()
+ * );
+ * for ( $i = -10; $i <= 10; $i++ )
+ * {
+ * $data[0][$i] = mt_rand( -23, 59 );
+ * $data[1][$i] = mt_rand( -23, 59 );
+ * }
+ *
+ * // Add data
+ * $graph->data['random blue'] = new ezcGraphArrayDataSet( $data[0] );
+ * $graph->data['random green'] = new ezcGraphArrayDataSet( $data[1] );
+ *
+ * $graph->render( 400, 150, 'tutorial_axis_numeric.svg' );
+ *
+ *
+ * @property float $min
+ * Minimum value of displayed scale on axis.
+ * @property float $max
+ * Maximum value of displayed scale on axis.
+ * @property mixed $majorStep
+ * Labeled major steps displayed on the axis.
+ * @property mixed $minorStep
+ * Non labeled minor steps on the axis.
+ * @property-read float $minValue
+ * Minimum Value to display on this axis.
+ * @property-read float $maxValue
+ * Maximum value to display on this axis.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphChartElementNumericAxis extends ezcGraphChartElementAxis
+{
+
+ /**
+ * Constant used for calculation of automatic definition of major scaling
+ * steps
+ */
+ const MIN_MAJOR_COUNT = 5;
+
+ /**
+ * Constant used for automatic calculation of minor steps from given major
+ * steps
+ */
+ const MIN_MINOR_COUNT = 8;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['min'] = null;
+ $this->properties['max'] = null;
+ $this->properties['minValue'] = null;
+ $this->properties['maxValue'] = null;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'min':
+ if ( !is_numeric( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' );
+ }
+
+ $this->properties['min'] = (float) $propertyValue;
+ $this->properties['initialized'] = true;
+ break;
+ case 'max':
+ if ( !is_numeric( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' );
+ }
+
+ $this->properties['max'] = (float) $propertyValue;
+ $this->properties['initialized'] = true;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Returns a "nice" number for a given floating point number.
+ *
+ * Nice numbers are steps on a scale which are easily recognized by humans
+ * like 0.5, 25, 1000 etc.
+ *
+ * @param float $float Number to be altered
+ * @return float Nice number
+ */
+ protected function getNiceNumber( $float )
+ {
+ // Get absolute value and save sign
+ $abs = abs( $float );
+ $sign = $float / $abs;
+
+ // Normalize number to a range between 1 and 10
+ $log = (int) round( log10( $abs ), 0 );
+ $abs /= pow( 10, $log );
+
+
+ // find next nice number
+ if ( $abs > 5 )
+ {
+ $abs = 10.;
+ }
+ elseif ( $abs > 2.5 )
+ {
+ $abs = 5.;
+ }
+ elseif ( $abs > 1 )
+ {
+ $abs = 2.5;
+ }
+ else
+ {
+ $abs = 1;
+ }
+
+ // unnormalize number to original values
+ return $abs * pow( 10, $log ) * $sign;
+ }
+
+ /**
+ * Calculate minimum value for displayed axe basing on real minimum and
+ * major step size
+ *
+ * @param float $min Real data minimum
+ * @param float $max Real data maximum
+ * @return void
+ */
+ protected function calculateMinimum( $min, $max )
+ {
+ if ( $this->properties['max'] === null )
+ {
+ $this->properties['min'] = floor( $min / $this->properties['majorStep'] ) * $this->properties['majorStep'];
+ }
+ else
+ {
+ $calculatedMin = $this->properties['max'];
+
+ do {
+ $calculatedMin -= $this->properties['majorStep'];
+ } while ( $calculatedMin > $min );
+
+ $this->properties['min'] = $calculatedMin;
+ }
+ }
+
+ /**
+ * Calculate maximum value for displayed axe basing on real maximum and
+ * major step size
+ *
+ * @param float $min Real data minimum
+ * @param float $max Real data maximum
+ * @return void
+ */
+ protected function calculateMaximum( $min, $max )
+ {
+ $calculatedMax = $this->properties['min'];
+
+ do {
+ $calculatedMax += $this->properties['majorStep'];
+ } while ( $calculatedMax < $max );
+
+ $this->properties['max'] = $calculatedMax;
+ }
+
+ /**
+ * Calculate size of minor steps based on the size of the major step size
+ *
+ * @param float $min Real data minimum
+ * @param float $max Real data maximum
+ * @return void
+ */
+ protected function calculateMinorStep( $min, $max )
+ {
+ $stepSize = $this->properties['majorStep'] / self::MIN_MINOR_COUNT;
+ $this->properties['minorStep'] = $this->getNiceNumber( $stepSize );
+ }
+
+ /**
+ * Calculate size of major step based on the span to be displayed and the
+ * defined MIN_MAJOR_COUNT constant.
+ *
+ * @param float $min Real data minimum
+ * @param float $max Real data maximum
+ * @return void
+ */
+ protected function calculateMajorStep( $min, $max )
+ {
+ $span = $max - $min;
+ $stepSize = $span / self::MIN_MAJOR_COUNT;
+ $this->properties['majorStep'] = $this->getNiceNumber( $stepSize );
+ }
+
+ /**
+ * Add data for this axis
+ *
+ * @param array $values Value which will be displayed on this axis
+ * @return void
+ */
+ public function addData( array $values )
+ {
+ foreach ( $values as $value )
+ {
+ if ( $this->properties['minValue'] === null ||
+ $value < $this->properties['minValue'] )
+ {
+ $this->properties['minValue'] = $value;
+ }
+
+ if ( $this->properties['maxValue'] === null ||
+ $value > $this->properties['maxValue'] )
+ {
+ $this->properties['maxValue'] = $value;
+ }
+ }
+
+ $this->properties['initialized'] = true;
+ }
+
+ /**
+ * Calculate axis bounding values on base of the assigned values
+ *
+ * @abstract
+ * @access public
+ * @return void
+ */
+ public function calculateAxisBoundings()
+ {
+ // Prevent division by zero, when min == max
+ if ( $this->properties['minValue'] == $this->properties['maxValue'] )
+ {
+ if ( $this->properties['minValue'] == 0 )
+ {
+ $this->properties['maxValue'] = 1;
+ }
+ else
+ {
+ if ( $this->properties['majorStep'] !== null )
+ {
+ $this->properties['minValue'] -= $this->properties['majorStep'];
+ $this->properties['maxValue'] += $this->properties['majorStep'];
+ }
+ else
+ {
+ $this->properties['minValue'] -= ( $this->properties['minValue'] * .1 );
+ $this->properties['maxValue'] += ( $this->properties['maxValue'] * .1 );
+ }
+ }
+ }
+
+ // Use custom minimum and maximum if available
+ if ( $this->properties['min'] !== null )
+ {
+ $this->properties['minValue'] = $this->properties['min'];
+ }
+
+ if ( $this->properties['max'] !== null )
+ {
+ $this->properties['maxValue'] = $this->properties['max'];
+ }
+
+ // If min and max values are forced, we may not be able to find a
+ // "nice" number for the steps. Try to find such a nice step size, or
+ // fall back to a step size, which is just the span divided by 5.
+ if ( ( $this->properties['min'] !== null ) &&
+ ( $this->properties['max'] !== null ) &&
+ ( $this->properties['majorStep'] === null ) )
+ {
+ $diff = $this->properties['max'] - $this->properties['min'];
+ $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] );
+ $stepInvariance = $diff / $this->properties['majorStep'];
+ if ( ( $stepInvariance - floor( $stepInvariance ) ) > .0000001 )
+ {
+ // For too big step invariances calculate the step size just
+ // from the given difference between min and max value.
+ $this->properties['majorStep'] = ( $this->properties['max'] - $this->properties['min'] ) / self::MIN_MAJOR_COUNT;
+ $this->properties['minorStep'] = $this->properties['majorStep'] / self::MIN_MAJOR_COUNT;
+ }
+ }
+
+ // Calculate "nice" values for scaling parameters
+ if ( $this->properties['majorStep'] === null )
+ {
+ $this->calculateMajorStep( $this->properties['minValue'], $this->properties['maxValue'] );
+ }
+
+ if ( $this->properties['minorStep'] === null )
+ {
+ $this->calculateMinorStep( $this->properties['minValue'], $this->properties['maxValue'] );
+ }
+
+ if ( $this->properties['min'] === null )
+ {
+ $this->calculateMinimum( $this->properties['minValue'], $this->properties['maxValue'] );
+ }
+
+ if ( $this->properties['max'] === null )
+ {
+ $this->calculateMaximum( $this->properties['minValue'], $this->properties['maxValue'] );
+ }
+
+ // Check that the major step size matches up with the min and max
+ // values on the axis.
+ $quotient = ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'];
+ $quotient = abs( $quotient - floor( $quotient ) );
+ if ( ( $quotient >= .00001 ) &&
+ ( abs( $quotient - 1 ) >= .00001 ) )
+ {
+ throw new ezcGraphInvalidStepSizeException( "The difference between minimum and maximum value is not a multiplier of the major step size." );
+ }
+
+ // Check that the minor step size matches up with major step size on
+ // the axis.
+ $quotient = $this->properties['majorStep'] / $this->properties['minorStep'];
+ $quotient = abs( $quotient - floor( $quotient ) );
+ if ( ( $quotient >= .00001 ) &&
+ ( abs( $quotient - 1 ) >= .00001 ) )
+ {
+ throw new ezcGraphInvalidStepSizeException( "The major step size value is not a multiplier of the minor step size." );
+ }
+ }
+
+ /**
+ * Get coordinate for a dedicated value on the chart
+ *
+ * @param float $value Value to determine position for
+ * @return float Position on chart
+ */
+ public function getCoordinate( $value )
+ {
+ // Force typecast, because ( false < -100 ) results in (bool) true
+ $floatValue = (float) $value;
+
+ if ( ( $value === false ) &&
+ ( ( $floatValue < $this->properties['min'] ) || ( $floatValue > $this->properties['max'] ) ) )
+ {
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return 0.;
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1.;
+ }
+ }
+ else
+ {
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ case ezcGraph::TOP:
+ return ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] );
+ case ezcGraph::RIGHT:
+ case ezcGraph::BOTTOM:
+ return 1 - ( $value - $this->properties['min'] ) / ( $this->properties['max'] - $this->properties['min'] );
+ }
+ }
+ }
+
+ /**
+ * Return count of minor steps
+ *
+ * @return integer Count of minor steps
+ */
+ public function getMinorStepCount()
+ {
+ return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['minorStep'] );
+ }
+
+ /**
+ * Return count of major steps
+ *
+ * @return integer Count of major steps
+ */
+ public function getMajorStepCount()
+ {
+ return (int) ( ( $this->properties['max'] - $this->properties['min'] ) / $this->properties['majorStep'] );
+ }
+
+ /**
+ * Get label for a dedicated step on the axis
+ *
+ * @param integer $step Number of step
+ * @return string label
+ */
+ public function getLabel( $step )
+ {
+ if ( $this->properties['labelCallback'] !== null )
+ {
+ return call_user_func_array(
+ $this->properties['labelCallback'],
+ array(
+ $this->properties['min'] + ( $step * $this->properties['majorStep'] ),
+ $step,
+ )
+ );
+ }
+ else
+ {
+ return $this->properties['min'] + ( $step * $this->properties['majorStep'] );
+ }
+ }
+
+ /**
+ * Is zero step
+ *
+ * Returns true if the given step is the one on the initial axis position
+ *
+ * @param int $step Number of step
+ * @return bool Status If given step is initial axis position
+ */
+ public function isZeroStep( $step )
+ {
+ return ( $this->getLabel( $step ) == 0 );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/charts/bar.php b/src/TUnit/external/ezc/Graph/charts/bar.php
new file mode 100644
index 0000000..2806b21
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/charts/bar.php
@@ -0,0 +1,94 @@
+
+ * // Create a new line chart
+ * $chart = new ezcGraphBarChart();
+ *
+ * // Add data to line chart
+ * $chart->data['sample dataset'] = new ezcGraphArrayDataSet(
+ * array(
+ * '100' => 1.2,
+ * '200' => 43.2,
+ * '300' => -34.14,
+ * '350' => 65,
+ * '400' => 123,
+ * )
+ * );
+ *
+ * // Render chart with default 2d renderer and default SVG driver
+ * $chart->render( 500, 200, 'bar_chart.svg' );
+ *
+ *
+ * Each chart consists of several chart elements which represents logical
+ * parts of the chart and can be formatted independently. The bar chart
+ * consists of:
+ * - title ( {@link ezcGraphChartElementText} )
+ * - legend ( {@link ezcGraphChartElementLegend} )
+ * - background ( {@link ezcGraphChartElementBackground} )
+ * - xAxis ( {@link ezcGraphChartElementLabeledAxis} )
+ * - yAxis ( {@link ezcGraphChartElementNumericAxis} )
+ *
+ * The type of the axis may be changed and all elements can be configured by
+ * accessing them as properties of the chart:
+ *
+ *
+ * $chart->legend->position = ezcGraph::RIGHT;
+ *
+ *
+ * The chart itself also offers several options to configure the appearance. As
+ * bar charts extend line charts the the extended configure options are
+ * available in {@link ezcGraphLineChartOptions} extending the
+ * {@link ezcGraphChartOptions}.
+ *
+ * @property ezcGraphLineChartOptions $options
+ * Chart options class
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphBarChart extends ezcGraphLineChart
+{
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ parent::__construct();
+
+ $this->elements['xAxis']->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer();
+ }
+
+ /**
+ * Returns the default display type of the current chart type.
+ *
+ * @return int Display type
+ */
+ public function getDefaultDisplayType()
+ {
+ return ezcGraph::BAR;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/charts/line.php b/src/TUnit/external/ezc/Graph/charts/line.php
new file mode 100644
index 0000000..e95b36e
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/charts/line.php
@@ -0,0 +1,715 @@
+
+ * // Create a new line chart
+ * $chart = new ezcGraphLineChart();
+ *
+ * // Add data to line chart
+ * $chart->data['sample dataset'] = new ezcGraphArrayDataSet(
+ * array(
+ * '100' => 1.2,
+ * '200' => 43.2,
+ * '300' => -34.14,
+ * '350' => 65,
+ * '400' => 123,
+ * )
+ * );
+ *
+ * // Render chart with default 2d renderer and default SVG driver
+ * $chart->render( 500, 200, 'line_chart.svg' );
+ *
+ *
+ * Each chart consists of several chart elements which represents logical
+ * parts of the chart and can be formatted independently. The line chart
+ * consists of:
+ * - title ( {@link ezcGraphChartElementText} )
+ * - legend ( {@link ezcGraphChartElementLegend} )
+ * - background ( {@link ezcGraphChartElementBackground} )
+ * - xAxis ( {@link ezcGraphChartElementLabeledAxis} )
+ * - yAxis ( {@link ezcGraphChartElementNumericAxis} )
+ *
+ * The type of the axis may be changed and all elements can be configured by
+ * accessing them as properties of the chart:
+ *
+ *
+ * $chart->legend->position = ezcGraph::RIGHT;
+ *
+ *
+ * The chart itself also offers several options to configure the appearance.
+ * The extended configure options are available in
+ * {@link ezcGraphLineChartOptions} extending the {@link ezcGraphChartOptions}.
+ *
+ * @property ezcGraphLineChartOptions $options
+ * Chart options class
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphLineChart extends ezcGraphChart
+{
+ /**
+ * Array with additional axis for the chart
+ *
+ * @var ezcGraphAxisContainer
+ */
+ protected $additionalAxis;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->additionalAxis = new ezcGraphAxisContainer( $this );
+
+ $this->options = new ezcGraphLineChartOptions( $options );
+ $this->options->highlightFont = $this->options->font;
+
+ parent::__construct();
+
+ $this->addElement( 'xAxis', new ezcGraphChartElementLabeledAxis() );
+ $this->elements['xAxis']->position = ezcGraph::LEFT;
+
+ $this->addElement( 'yAxis', new ezcGraphChartElementNumericAxis() );
+ $this->elements['yAxis']->position = ezcGraph::BOTTOM;
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'additionalAxis':
+ return $this->additionalAxis;
+ }
+
+ return parent::__get( $propertyName );
+ }
+
+ /**
+ * Options write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param mixed $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return mixed
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName ) {
+ case 'xAxis':
+ if ( $propertyValue instanceof ezcGraphChartElementAxis )
+ {
+ $this->addElement( 'xAxis', $propertyValue );
+ $this->elements['xAxis']->position = ezcGraph::LEFT;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' );
+ }
+ break;
+ case 'yAxis':
+ if ( $propertyValue instanceof ezcGraphChartElementAxis )
+ {
+ $this->addElement( 'yAxis', $propertyValue );
+ $this->elements['yAxis']->position = ezcGraph::BOTTOM;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' );
+ }
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Set colors and border for this element
+ *
+ * @param ezcGraphPalette $palette Palette
+ * @return void
+ */
+ public function setFromPalette( ezcGraphPalette $palette )
+ {
+ foreach ( $this->additionalAxis as $element )
+ {
+ $element->setFromPalette( $palette );
+ }
+
+ parent::setFromPalette( $palette );
+ }
+
+ /**
+ * Render the assigned data
+ *
+ * Will renderer all charts data in the remaining boundings after drawing
+ * all other chart elements. The data will be rendered depending on the
+ * settings in the dataset.
+ *
+ * @param ezcGraphRenderer $renderer Renderer
+ * @param ezcGraphBoundings $boundings Remaining boundings
+ * @return void
+ */
+ protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphBoundings $innerBoundings )
+ {
+ // Use inner boundings for drawning chart data
+ $boundings = $innerBoundings;
+
+ $yAxisNullPosition = $this->elements['yAxis']->getCoordinate( false );
+
+ // Initialize counters
+ $nr = array();
+ $count = array();
+
+ foreach ( $this->data as $data )
+ {
+ if ( !isset( $nr[$data->displayType->default] ) )
+ {
+ $nr[$data->displayType->default] = 0;
+ $count[$data->displayType->default] = 0;
+ }
+
+ $nr[$data->displayType->default]++;
+ $count[$data->displayType->default]++;
+ }
+
+ $checkedRegularSteps = false;
+
+ // Display data
+ foreach ( $this->data as $datasetName => $data )
+ {
+ --$nr[$data->displayType->default];
+
+ // Check which axis should be used
+ $xAxis = ( $data->xAxis->default ? $data->xAxis->default: $this->elements['xAxis'] );
+ $yAxis = ( $data->yAxis->default ? $data->yAxis->default: $this->elements['yAxis'] );
+
+ // Determine fill color for dataset
+ if ( $this->options->fillLines !== false )
+ {
+ $fillColor = clone $data->color->default;
+ $fillColor->alpha = (int) round( ( 255 - $fillColor->alpha ) * ( $this->options->fillLines / 255 ) );
+ }
+ else
+ {
+ $fillColor = null;
+ }
+
+ // Ensure regular steps on axis when used with bar charts and
+ // precalculate some values use to render bar charts
+ //
+ // Called only once and only when bars should be rendered
+ if ( ( $checkedRegularSteps === false ) &&
+ ( $data->displayType->default === ezcGraph::BAR ) )
+ {
+ $steps = $xAxis->getSteps();
+
+ $stepWidth = null;
+ foreach ( $steps as $step )
+ {
+ if ( $stepWidth === null )
+ {
+ $stepWidth = $step->width;
+ }
+ elseif ( $step->width !== $stepWidth )
+ {
+ throw new ezcGraphUnregularStepsException();
+ }
+ }
+
+ $step = reset( $steps );
+ if ( count( $step->childs ) )
+ {
+ // Keep this for BC reasons
+ $barCount = ( $xAxis->getMajorStepCount() + 1 ) * ( $xAxis->getMinorStepCount() - 1 );
+ $stepWidth = 1 / $barCount;
+ }
+
+ $checkedRegularSteps = true;
+ $width = $xAxis->axisLabelRenderer->modifyChartDataPosition(
+ $yAxis->axisLabelRenderer->modifyChartDataPosition(
+ new ezcGraphCoordinate(
+ ( $boundings->x1 - $boundings->x0 ) * $stepWidth,
+ 0
+ )
+ )
+ )->x;
+ }
+
+ // Draw lines for dataset
+ $lastPoint = false;
+ foreach ( $data as $key => $value )
+ {
+ // Calculate point in chart
+ $point = $xAxis->axisLabelRenderer->modifyChartDataPosition(
+ $yAxis->axisLabelRenderer->modifyChartDataPosition(
+ new ezcGraphCoordinate(
+ $xAxis->getCoordinate( $key ),
+ $yAxis->getCoordinate( $value )
+ )
+ )
+ );
+
+ // Render depending on display type of dataset
+ switch ( true )
+ {
+ case $data->displayType->default === ezcGraph::LINE:
+ $renderer->drawDataLine(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $data->color->default,
+ ( $lastPoint === false ? $point : $lastPoint ),
+ $point,
+ $nr[$data->displayType->default],
+ $count[$data->displayType->default],
+ $data->symbol[$key],
+ $data->color[$key],
+ $fillColor,
+ $yAxisNullPosition,
+ ( $data->lineThickness->default ? $data->lineThickness->default : $this->options->lineThickness )
+ );
+
+ // Render highlight string if requested
+ if ( $data->highlight[$key] )
+ {
+ $renderer->drawDataHighlightText(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $point,
+ $yAxisNullPosition,
+ $nr[$data->displayType->default],
+ $count[$data->displayType->default],
+ $this->options->highlightFont,
+ ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ),
+ $this->options->highlightSize + $this->options->highlightFont->padding * 2,
+ ( $this->options->highlightLines ? $data->color[$key] : null ),
+ ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ),
+ ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ),
+ 0.,
+ ezcGraph::LINE
+ );
+ }
+ break;
+ case ( $data->displayType->default === ezcGraph::BAR ) &&
+ $this->options->stackBars :
+ // Check if a bar has already been stacked
+ if ( !isset( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] ) )
+ {
+ $start = new ezcGraphCoordinate(
+ $point->x,
+ $yAxisNullPosition
+ );
+
+ $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] = $value;
+ }
+ else
+ {
+ $start = $xAxis->axisLabelRenderer->modifyChartDataPosition(
+ $yAxis->axisLabelRenderer->modifyChartDataPosition(
+ new ezcGraphCoordinate(
+ $xAxis->getCoordinate( $key ),
+ $yAxis->getCoordinate( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] )
+ )
+ )
+ );
+
+ $point = $xAxis->axisLabelRenderer->modifyChartDataPosition(
+ $yAxis->axisLabelRenderer->modifyChartDataPosition(
+ new ezcGraphCoordinate(
+ $xAxis->getCoordinate( $key ),
+ $yAxis->getCoordinate( $stackedValue[(int) ( $point->x * 10000 )][(int) $value > 0] += $value )
+ )
+ )
+ );
+ }
+
+ // Force one symbol for each stacked bar
+ if ( !isset( $stackedSymbol[(int) ( $point->x * 10000 )] ) )
+ {
+ $stackedSymbol[(int) ( $point->x * 10000 )] = $data->symbol[$key];
+ }
+
+ // Store stacked value for next iteration
+ $side = ( $point->y == 0 ? 1 : $point->y / abs( $point->y ) );
+ $stacked[(int) ( $point->x * 10000 )][$side] = $point;
+
+ $renderer->drawStackedBar(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $data->color->default,
+ $start,
+ $point,
+ $width,
+ $stackedSymbol[(int) ( $point->x * 10000 )],
+ $yAxisNullPosition
+ );
+
+ // Render highlight string if requested
+ if ( $data->highlight[$key] )
+ {
+ $renderer->drawDataHighlightText(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $point,
+ $yAxisNullPosition,
+ $nr[$data->displayType->default],
+ $count[$data->displayType->default],
+ $this->options->highlightFont,
+ ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ),
+ $this->options->highlightSize + $this->options->highlightFont->padding * 2,
+ ( $this->options->highlightLines ? $data->color[$key] : null ),
+ ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ),
+ ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ),
+ 0.,
+ ezcGraph::LINE
+ );
+ }
+ break;
+ case $data->displayType->default === ezcGraph::BAR:
+ $renderer->drawBar(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $data->color[$key],
+ $point,
+ $width,
+ $nr[$data->displayType->default],
+ $count[$data->displayType->default],
+ $data->symbol[$key],
+ $yAxisNullPosition
+ );
+
+ // Render highlight string if requested
+ if ( $data->highlight[$key] )
+ {
+ $renderer->drawDataHighlightText(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $point,
+ $yAxisNullPosition,
+ $nr[$data->displayType->default],
+ $count[$data->displayType->default],
+ $this->options->highlightFont,
+ ( $data->highlightValue[$key] ? $data->highlightValue[$key] : $value ),
+ $this->options->highlightSize + $this->options->highlightFont->padding * 2,
+ ( $this->options->highlightLines ? $data->color[$key] : null ),
+ ( $this->options->highlightXOffset ? $this->options->highlightXOffset : 0 ),
+ ( $this->options->highlightYOffset ? $this->options->highlightYOffset : 0 ),
+ $width,
+ $data->displayType->default
+ );
+ }
+ break;
+ default:
+ throw new ezcGraphInvalidDisplayTypeException( $data->displayType->default );
+ break;
+ }
+
+ // Store last point, used to connect lines in line chart.
+ $lastPoint = $point;
+ }
+ }
+ }
+
+ /**
+ * Returns the default display type of the current chart type.
+ *
+ * @return int Display type
+ */
+ public function getDefaultDisplayType()
+ {
+ return ezcGraph::LINE;
+ }
+
+ /**
+ * Check if renderer supports features requested by some special chart
+ * options.
+ *
+ * @throws ezcBaseValueException
+ * If some feature is not supported
+ *
+ * @return void
+ */
+ protected function checkRenderer()
+ {
+ // When stacked bars are enabled, check if renderer supports them
+ if ( $this->options->stackBars )
+ {
+ if ( !$this->renderer instanceof ezcGraphStackedBarsRenderer )
+ {
+ throw new ezcBaseValueException( 'renderer', $this->renderer, 'ezcGraphStackedBarsRenderer' );
+ }
+ }
+ }
+
+ /**
+ * Aggregate and calculate value boundings on axis.
+ *
+ * @return void
+ */
+ protected function setAxisValues()
+ {
+ // Virtual data set build for agrregated values sums for bar charts
+ $virtualBarSumDataSet = array( array(), array() );
+
+ // Calculate axis scaling and labeling
+ foreach ( $this->data as $dataset )
+ {
+ $nr = 0;
+ $labels = array();
+ $values = array();
+ foreach ( $dataset as $label => $value )
+ {
+ $labels[] = $label;
+ $values[] = $value;
+
+ // Build sum of all bars
+ if ( $this->options->stackBars &&
+ ( $dataset->displayType->default === ezcGraph::BAR ) )
+ {
+ if ( !isset( $virtualBarSumDataSet[(int) $value >= 0][$nr] ) )
+ {
+ $virtualBarSumDataSet[(int) $value >= 0][$nr++] = $value;
+ }
+ else
+ {
+ $virtualBarSumDataSet[(int) $value >= 0][$nr++] += $value;
+ }
+ }
+ }
+
+ // Check if data has been associated with another custom axis, use
+ // default axis otherwise.
+ if ( $dataset->xAxis->default )
+ {
+ $dataset->xAxis->default->addData( $labels );
+ }
+ else
+ {
+ $this->elements['xAxis']->addData( $labels );
+ }
+
+ if ( $dataset->yAxis->default )
+ {
+ $dataset->yAxis->default->addData( $values );
+ }
+ else
+ {
+ $this->elements['yAxis']->addData( $values );
+ }
+ }
+
+ // Also use stacked bar values as base for y axis value span
+ // calculation
+ if ( $this->options->stackBars )
+ {
+ $this->elements['yAxis']->addData( $virtualBarSumDataSet[0] );
+ $this->elements['yAxis']->addData( $virtualBarSumDataSet[1] );
+ }
+
+ // There should always be something assigned to the main x and y axis.
+ if ( !$this->elements['xAxis']->initialized ||
+ !$this->elements['yAxis']->initialized )
+ {
+ throw new ezcGraphNoDataException();
+ }
+
+ // Calculate boundings from assigned data
+ $this->elements['xAxis']->calculateAxisBoundings();
+ $this->elements['yAxis']->calculateAxisBoundings();
+ }
+
+ /**
+ * Renders the basic elements of this chart type
+ *
+ * @param int $width
+ * @param int $height
+ * @return void
+ */
+ protected function renderElements( $width, $height )
+ {
+ if ( !count( $this->data ) )
+ {
+ throw new ezcGraphNoDataException();
+ }
+
+ // Check if renderer supports requested features
+ $this->checkRenderer();
+
+ // Set values form datasets on axis to calculate correct spans
+ $this->setAxisValues();
+
+ // Generate legend
+ $this->elements['legend']->generateFromDataSets( $this->data );
+
+ // Get boundings from parameters
+ $this->options->width = $width;
+ $this->options->height = $height;
+
+ // Set image properties in driver
+ $this->driver->options->width = $width;
+ $this->driver->options->height = $height;
+
+ // Render subelements
+ $boundings = new ezcGraphBoundings();
+ $boundings->x1 = $this->options->width;
+ $boundings->y1 = $this->options->height;
+
+ $boundings = $this->elements['xAxis']->axisLabelRenderer->modifyChartBoundings(
+ $this->elements['yAxis']->axisLabelRenderer->modifyChartBoundings(
+ $boundings, new ezcGraphCoordinate( 1, 0 )
+ ), new ezcGraphCoordinate( -1, 0 )
+ );
+
+ // Render subelements
+ foreach ( $this->elements as $name => $element )
+ {
+ // Skip element, if it should not get rendered
+ if ( ( $this->renderElement[$name] === false ) ||
+ ( $name === 'xAxis' ) ||
+ ( $name === 'yAxis' ) )
+ {
+ continue;
+ }
+
+ $this->driver->options->font = $element->font;
+ $boundings = $element->render( $this->renderer, $boundings );
+ }
+
+ // Set relative positions of axis in chart depending on the "null"
+ // value on the other axis.
+ $this->elements['xAxis']->nullPosition = $this->elements['yAxis']->getCoordinate( false );
+ $this->elements['yAxis']->nullPosition = $this->elements['xAxis']->getCoordinate( false );
+
+ // Calculate inner data boundings of chart
+ $innerBoundings = new ezcGraphBoundings(
+ $boundings->x0 + $boundings->width *
+ ( ( ( $this->elements['yAxis']->outerAxisSpace === null ) ||
+ ( $this->elements['xAxis']->position === ezcGraph::LEFT ) ) ?
+ $this->elements['yAxis']->axisSpace :
+ $this->elements['yAxis']->outerAxisSpace ),
+ $boundings->y0 + $boundings->height *
+ ( ( ( $this->elements['xAxis']->outerAxisSpace === null ) ||
+ ( $this->elements['yAxis']->position === ezcGraph::TOP ) ) ?
+ $this->elements['xAxis']->axisSpace :
+ $this->elements['yAxis']->outerAxisSpace ),
+ $boundings->x1 - $boundings->width *
+ ( ( ( $this->elements['yAxis']->outerAxisSpace === null ) ||
+ ( $this->elements['xAxis']->position === ezcGraph::RIGHT ) ) ?
+ $this->elements['yAxis']->axisSpace :
+ $this->elements['yAxis']->outerAxisSpace ),
+ $boundings->y1 - $boundings->height *
+ ( ( ( $this->elements['xAxis']->outerAxisSpace === null ) ||
+ ( $this->elements['yAxis']->position === ezcGraph::BOTTOM ) ) ?
+ $this->elements['xAxis']->axisSpace :
+ $this->elements['yAxis']->outerAxisSpace )
+ );
+
+ // Render axis
+ $this->driver->options->font = $this->elements['yAxis']->font;
+ $boundings = $this->elements['xAxis']->render( $this->renderer, $boundings, $innerBoundings );
+ $boundings = $this->elements['yAxis']->render( $this->renderer, $boundings, $innerBoundings );
+
+ // Render additional axis
+ foreach ( $this->additionalAxis as $element )
+ {
+ if ( $element->initialized )
+ {
+ // Calculate all required step sizes if values has been
+ // assigned to axis.
+ $element->calculateAxisBoundings();
+ }
+ else
+ {
+ // Do not render any axis labels, if no values were assigned
+ // and no step sizes were defined.
+ $element->axisLabelRenderer = new ezcGraphAxisNoLabelRenderer();
+ }
+
+ $this->driver->options->font = $element->font;
+ $element->nullPosition = $element->chartPosition;
+ $boundings = $element->render( $this->renderer, $boundings, $innerBoundings );
+ }
+
+ // Render graph
+ $this->renderData( $this->renderer, $boundings, $innerBoundings );
+ }
+
+ /**
+ * Render the line chart
+ *
+ * Renders the chart into a file or stream. The width and height are
+ * needed to specify the dimensions of the resulting image. For direct
+ * output use 'php://stdout' as output file.
+ *
+ * @param int $width Image width
+ * @param int $height Image height
+ * @param string $file Output file
+ * @apichange
+ * @return void
+ */
+ public function render( $width, $height, $file = null )
+ {
+ $this->renderElements( $width, $height );
+
+ if ( !empty( $file ) )
+ {
+ $this->renderer->render( $file );
+ }
+
+ $this->renderedFile = $file;
+ }
+
+ /**
+ * Renders this chart to direct output
+ *
+ * Does the same as ezcGraphChart::render(), but renders directly to
+ * output and not into a file.
+ *
+ * @param int $width
+ * @param int $height
+ * @apichange
+ * @return void
+ */
+ public function renderToOutput( $width, $height )
+ {
+ // @TODO: merge this function with render an deprecate ommit of third
+ // argument in render() when API break is possible
+ $this->renderElements( $width, $height );
+ $this->renderer->render( null );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/charts/odometer.php b/src/TUnit/external/ezc/Graph/charts/odometer.php
new file mode 100644
index 0000000..4db240b
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/charts/odometer.php
@@ -0,0 +1,296 @@
+
+ * $graph = new ezcGraphOdometerChart();
+ * $graph->title = 'Custom odometer';
+ *
+ * $graph->data['data'] = new ezcGraphArrayDataSet(
+ * array( 87 )
+ * );
+ *
+ * // Set the marker color
+ * $graph->data['data']->color[0] = '#A0000055';
+ *
+ * // Set colors for the background gradient
+ * $graph->options->startColor = '#2E3436';
+ * $graph->options->endColor = '#EEEEEC';
+ *
+ * // Define a border for the odometer
+ * $graph->options->borderWidth = 2;
+ * $graph->options->borderColor = '#BABDB6';
+ *
+ * // Set marker width
+ * $graph->options->markerWidth = 5;
+ *
+ * // Set space, which the odometer may consume
+ * $graph->options->odometerHeight = .7;
+ *
+ * // Set axis span and label
+ * $graph->axis->min = 0;
+ * $graph->axis->max = 100;
+ * $graph->axis->label = 'Coverage ';
+ *
+ * $graph->render( 400, 150, 'custom_odometer_chart.svg' );
+ *
+ *
+ * Each chart consists of several chart elements which represents logical parts
+ * of the chart and can be formatted independently. The odometer chart consists
+ * of:
+ * - title ( {@link ezcGraphChartElementText} )
+ * - background ( {@link ezcGraphChartElementBackground} )
+ *
+ * All elements can be configured by accessing them as properties of the chart:
+ *
+ *
+ * $chart->title->position = ezcGraph::BOTTOM;
+ *
+ *
+ * The chart itself also offers several options to configure the appearance.
+ * The extended configure options are available in
+ * {@link ezcGraphOdometerChartOptions} extending the {@link
+ * ezcGraphChartOptions}.
+ *
+ * @property ezcGraphOdometerChartOptions $options
+ * Chart options class
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphOdometerChart extends ezcGraphChart
+{
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->options = new ezcGraphOdometerChartOptions( $options );
+
+ parent::__construct( $options );
+
+ $this->data = new ezcGraphChartSingleDataContainer( $this );
+
+ $this->addElement( 'axis', new ezcGraphChartElementNumericAxis());
+ $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer();
+ $this->elements['axis']->axisLabelRenderer->showZeroValue = true;
+ $this->elements['axis']->position = ezcGraph::LEFT;
+ $this->elements['axis']->axisSpace = .05;
+ }
+
+ /**
+ * Property write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param string $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName ) {
+ case 'axis':
+ if ( $propertyValue instanceof ezcGraphChartElementAxis )
+ {
+ $this->addElement( 'axis', $propertyValue );
+ $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer();
+ $this->elements['axis']->axisLabelRenderer->showZeroValue = true;
+ $this->elements['axis']->position = ezcGraph::LEFT;
+ $this->elements['axis']->axisSpace = .05;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' );
+ }
+ break;
+ case 'renderer':
+ if ( $propertyValue instanceof ezcGraphOdometerRenderer )
+ {
+ parent::__set( $propertyName, $propertyValue );
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphOdometerRenderer' );
+ }
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Render the assigned data
+ *
+ * Will renderer all charts data in the remaining boundings after drawing
+ * all other chart elements. The data will be rendered depending on the
+ * settings in the dataset.
+ *
+ * @param ezcGraphRenderer $renderer Renderer
+ * @param ezcGraphBoundings $boundings Remaining boundings
+ * @return void
+ */
+ protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings )
+ {
+ // Draw the odometer data
+ $dataset = $this->data->rewind();
+
+ foreach ( $dataset as $key => $value )
+ {
+ $renderer->drawOdometerMarker(
+ $boundings,
+ $this->elements['axis']->axisLabelRenderer->modifyChartDataPosition(
+ new ezcGraphCoordinate(
+ $this->elements['axis']->getCoordinate( $value ),
+ 0
+ )
+ ),
+ $dataset->symbol[$key],
+ $dataset->color[$key],
+ $this->options->markerWidth
+ );
+ }
+ }
+
+ /**
+ * Returns the default display type of the current chart type.
+ *
+ * @return int Display type
+ */
+ public function getDefaultDisplayType()
+ {
+ return ezcGraph::ODOMETER;
+ }
+
+ /**
+ * Renders the basic elements of this chart type
+ *
+ * @param int $width
+ * @param int $height
+ * @return void
+ */
+ protected function renderElements( $width, $height )
+ {
+ if ( !count( $this->data ) )
+ {
+ throw new ezcGraphNoDataException();
+ }
+
+ // Set image properties in driver
+ $this->driver->options->width = $width;
+ $this->driver->options->height = $height;
+
+ // no legend
+ $this->renderElement['legend'] = false;
+
+ // Get boundings from parameters
+ $this->options->width = $width;
+ $this->options->height = $height;
+
+ $boundings = new ezcGraphBoundings();
+ $boundings->x1 = $this->options->width;
+ $boundings->y1 = $this->options->height;
+
+ // Get values out the single used dataset to calculate axis boundings
+ $values = array();
+ foreach ( $this->data->rewind() as $value )
+ {
+ $values[] = $value;
+ }
+
+ // Set values for Axis
+ $this->elements['axis']->addData( $values );
+ $this->elements['axis']->nullPosition = 0.5 + $this->options->odometerHeight / 2;
+ $this->elements['axis']->calculateAxisBoundings();
+
+ // Render subelements exept axis, which will be drawn together with the
+ // odometer bar
+ foreach ( $this->elements as $name => $element )
+ {
+ // Skip element, if it should not get rendered
+ if ( $this->renderElement[$name] === false ||
+ $name === 'axis' )
+ {
+ continue;
+ }
+
+ $this->driver->options->font = $element->font;
+ $boundings = $element->render( $this->renderer, $boundings );
+ }
+
+ // Draw basic odometer
+ $this->driver->options->font = $this->elements['axis']->font;
+ $boundings = $this->renderer->drawOdometer(
+ $boundings,
+ $this->elements['axis'],
+ $this->options
+ );
+
+ // Render graph
+ $this->renderData( $this->renderer, $boundings );
+ }
+
+ /**
+ * Render the pie chart
+ *
+ * Renders the chart into a file or stream. The width and height are
+ * needed to specify the dimensions of the resulting image. For direct
+ * output use 'php://stdout' as output file.
+ *
+ * @param int $width Image width
+ * @param int $height Image height
+ * @param string $file Output file
+ * @apichange
+ * @return void
+ */
+ public function render( $width, $height, $file = null )
+ {
+ $this->renderElements( $width, $height );
+
+ if ( !empty( $file ) )
+ {
+ $this->renderer->render( $file );
+ }
+
+ $this->renderedFile = $file;
+ }
+
+ /**
+ * Renders this chart to direct output
+ *
+ * Does the same as ezcGraphChart::render(), but renders directly to
+ * output and not into a file.
+ *
+ * @param int $width
+ * @param int $height
+ * @apichange
+ * @return void
+ */
+ public function renderToOutput( $width, $height )
+ {
+ // @TODO: merge this function with render an deprecate ommit of third
+ // argument in render() when API break is possible
+ $this->renderElements( $width, $height );
+ $this->renderer->render( null );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/charts/pie.php b/src/TUnit/external/ezc/Graph/charts/pie.php
new file mode 100644
index 0000000..ac189ec
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/charts/pie.php
@@ -0,0 +1,308 @@
+
+ * // Create a new pie chart
+ * $chart = new ezcGraphPieChart();
+ *
+ * // Add data to line chart
+ * $chart->data['sample dataset'] = new ezcGraphArrayDataSet(
+ * array(
+ * 'one' => 1.2,
+ * 'two' => 43.2,
+ * 'three' => -34.14,
+ * 'four' => 65,
+ * 'five' => 123,
+ * )
+ * );
+ *
+ * // Render chart with default 2d renderer and default SVG driver
+ * $chart->render( 500, 200, 'pie_chart.svg' );
+ *
+ *
+ * Each chart consists of several chart elements which represents logical
+ * parts of the chart and can be formatted independently. The pie chart
+ * consists of:
+ * - title ( {@link ezcGraphChartElementText} )
+ * - legend ( {@link ezcGraphChartElementLegend} )
+ * - background ( {@link ezcGraphChartElementBackground} )
+ *
+ * All elements can be configured by accessing them as properties of the chart:
+ *
+ *
+ * $chart->legend->position = ezcGraph::RIGHT;
+ *
+ *
+ * The chart itself also offers several options to configure the appearance.
+ * The extended configure options are available in
+ * {@link ezcGraphPieChartOptions} extending the {@link ezcGraphChartOptions}.
+ *
+ * @property ezcGraphPieChartOptions $options
+ * Chart options class
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphPieChart extends ezcGraphChart
+{
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->options = new ezcGraphPieChartOptions( $options );
+
+ parent::__construct( $options );
+
+ $this->data = new ezcGraphChartSingleDataContainer( $this );
+ }
+
+ /**
+ * Render the assigned data
+ *
+ * Will renderer all charts data in the remaining boundings after drawing
+ * all other chart elements. The data will be rendered depending on the
+ * settings in the dataset.
+ *
+ * @param ezcGraphRenderer $renderer Renderer
+ * @param ezcGraphBoundings $boundings Remaining boundings
+ * @return void
+ */
+ protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings )
+ {
+ // Only draw the first (and only) dataset
+ $dataset = $this->data->rewind();
+ $datasetName = $this->data->key();
+
+ $this->driver->options->font = $this->options->font;
+
+ // Calculate sum of all values to be able to calculate percentage
+ $sum = 0;
+ foreach ( $dataset as $name => $value )
+ {
+ if ( $value < 0 )
+ {
+ throw new ezcGraphInvalidDataException( "Values >= 0 required, '$name' => '$value'." );
+ }
+
+ $sum += $value;
+ }
+ if ( $this->options->sum !== false )
+ {
+ $sum = max( $sum, $this->options->sum );
+ }
+
+ if ( $sum <= 0 )
+ {
+ throw new ezcGraphInvalidDataException( "Pie charts require a value sum > 0, your value: '$sum'." );
+ }
+
+ $angle = 0;
+ foreach ( $dataset as $label => $value )
+ {
+ // Skip rendering values which equals 0
+ if ( $value <= 0 )
+ {
+ continue;
+ }
+
+ switch ( $dataset->displayType->default )
+ {
+ case ezcGraph::PIE:
+ $displayLabel = ( $this->options->labelCallback !== null
+ ? call_user_func( $this->options->labelCallback, $label, $value, $value / $sum )
+ : sprintf( $this->options->label, $label, $value, $value / $sum * 100 ) );
+
+ $renderer->drawPieSegment(
+ $boundings,
+ new ezcGraphContext( $datasetName, $label, $dataset->url[$label] ),
+ $dataset->color[$label],
+ $angle,
+ $angle += $value / $sum * 360,
+ $displayLabel,
+ $dataset->highlight[$label]
+ );
+ break;
+ default:
+ throw new ezcGraphInvalidDisplayTypeException( $dataset->displayType->default );
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns the default display type of the current chart type.
+ *
+ * @return int Display type
+ */
+ public function getDefaultDisplayType()
+ {
+ return ezcGraph::PIE;
+ }
+
+ /**
+ * Apply tresh hold
+ *
+ * Iterates over the dataset and applies the configured tresh hold to
+ * the datasets data.
+ *
+ * @return void
+ */
+ protected function applyThreshold()
+ {
+ if ( $this->options->percentThreshold || $this->options->absoluteThreshold )
+ {
+ $dataset = $this->data->rewind();
+
+ $sum = 0;
+ foreach ( $dataset as $value )
+ {
+ $sum += $value;
+ }
+ if ( $this->options->sum !== false )
+ {
+ $sum = max( $sum, $this->options->sum );
+ }
+
+ $unset = array();
+ foreach ( $dataset as $label => $value )
+ {
+ if ( $label === $this->options->summarizeCaption )
+ {
+ continue;
+ }
+
+ if ( ( $value <= $this->options->absoluteThreshold ) ||
+ ( ( $value / $sum ) <= $this->options->percentThreshold ) )
+ {
+ if ( !isset( $dataset[$this->options->summarizeCaption] ) )
+ {
+ $dataset[$this->options->summarizeCaption] = $value;
+ }
+ else
+ {
+ $dataset[$this->options->summarizeCaption] += $value;
+ }
+
+ $unset[] = $label;
+ }
+ }
+
+ foreach ( $unset as $label )
+ {
+ unset( $dataset[$label] );
+ }
+ }
+ }
+
+ /**
+ * Renders the basic elements of this chart type
+ *
+ * @param int $width
+ * @param int $height
+ * @return void
+ */
+ protected function renderElements( $width, $height )
+ {
+ if ( !count( $this->data ) )
+ {
+ throw new ezcGraphNoDataException();
+ }
+
+ // Set image properties in driver
+ $this->driver->options->width = $width;
+ $this->driver->options->height = $height;
+
+ // Apply tresh hold
+ $this->applyThreshold();
+
+ // Generate legend
+ $this->elements['legend']->generateFromDataSet( $this->data->rewind() );
+
+ // Get boundings from parameters
+ $this->options->width = $width;
+ $this->options->height = $height;
+
+ $boundings = new ezcGraphBoundings();
+ $boundings->x1 = $this->options->width;
+ $boundings->y1 = $this->options->height;
+
+ // Render subelements
+ foreach ( $this->elements as $name => $element )
+ {
+ // Skip element, if it should not get rendered
+ if ( $this->renderElement[$name] === false )
+ {
+ continue;
+ }
+
+ $this->driver->options->font = $element->font;
+ $boundings = $element->render( $this->renderer, $boundings );
+ }
+
+ // Render graph
+ $this->renderData( $this->renderer, $boundings );
+ }
+
+ /**
+ * Render the pie chart
+ *
+ * Renders the chart into a file or stream. The width and height are
+ * needed to specify the dimensions of the resulting image. For direct
+ * output use 'php://stdout' as output file.
+ *
+ * @param int $width Image width
+ * @param int $height Image height
+ * @param string $file Output file
+ * @apichange
+ * @return void
+ */
+ public function render( $width, $height, $file = null )
+ {
+ $this->renderElements( $width, $height );
+
+ if ( !empty( $file ) )
+ {
+ $this->renderer->render( $file );
+ }
+
+ $this->renderedFile = $file;
+ }
+
+ /**
+ * Renders this chart to direct output
+ *
+ * Does the same as ezcGraphChart::render(), but renders directly to
+ * output and not into a file.
+ *
+ * @param int $width
+ * @param int $height
+ * @apichange
+ * @return void
+ */
+ public function renderToOutput( $width, $height )
+ {
+ // @TODO: merge this function with render an deprecate ommit of third
+ // argument in render() when API break is possible
+ $this->renderElements( $width, $height );
+ $this->renderer->render( null );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/charts/radar.php b/src/TUnit/external/ezc/Graph/charts/radar.php
new file mode 100644
index 0000000..adc0378
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/charts/radar.php
@@ -0,0 +1,457 @@
+
+ * // Create a new radar chart
+ * $chart = new ezcGraphRadarChart();
+ *
+ * // Add data to line chart
+ * $chart->data['sample dataset'] = new ezcGraphArrayDataSet(
+ * array(
+ * '100' => 1.2,
+ * '200' => 43.2,
+ * '300' => -34.14,
+ * '350' => 65,
+ * '400' => 123,
+ * )
+ * );
+ *
+ * // Render chart with default 2d renderer and default SVG driver
+ * $chart->render( 500, 200, 'radar_chart.svg' );
+ *
+ *
+ * Each chart consists of several chart elements which represents logical
+ * parts of the chart and can be formatted independently. The line chart
+ * consists of:
+ * - title ( {@link ezcGraphChartElementText} )
+ * - legend ( {@link ezcGraphChartElementLegend} )
+ * - background ( {@link ezcGraphChartElementBackground} )
+ * - axis ( {@link ezcGraphChartElementNumericAxis} )
+ * - ratation axis ( {@link ezcGraphChartElementLabeledAxis} )
+ *
+ * The type of the axis may be changed and all elements can be configured by
+ * accessing them as properties of the chart:
+ *
+ * The chart itself also offers several options to configure the appearance.
+ * The extended configure options are available in
+ * {@link ezcGraphRadarChartOptions} extending the
+ * {@link ezcGraphChartOptions}.
+ *
+ *
+ * $chart->legend->position = ezcGraph::RIGHT;
+ *
+ *
+ * @property ezcGraphRadarChartOptions $options
+ * Chart options class
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphRadarChart extends ezcGraphChart
+{
+ /**
+ * Store major grid color for child axis.
+ *
+ * @var ezcGraphColor
+ */
+ protected $childAxisColor;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->options = new ezcGraphRadarChartOptions( $options );
+ $this->options->highlightFont = $this->options->font;
+
+ parent::__construct();
+
+ $this->elements['rotationAxis'] = new ezcGraphChartElementLabeledAxis();
+
+ $this->addElement( 'axis', new ezcGraphChartElementNumericAxis() );
+ $this->elements['axis']->position = ezcGraph::BOTTOM;
+ $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer();
+ $this->elements['axis']->axisLabelRenderer->outerStep = true;
+
+ $this->addElement( 'rotationAxis', new ezcGraphChartElementLabeledAxis() );
+
+ // Do not render axis with default method, because we need an axis for
+ // each label in dataset
+ $this->renderElement['axis'] = false;
+ $this->renderElement['rotationAxis'] = false;
+ }
+
+ /**
+ * Set colors and border fro this element
+ *
+ * @param ezcGraphPalette $palette Palette
+ * @return void
+ */
+ public function setFromPalette( ezcGraphPalette $palette )
+ {
+ $this->childAxisColor = $palette->majorGridColor;
+
+ parent::setFromPalette( $palette );
+ }
+
+ /**
+ * Property write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param string $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName ) {
+ case 'axis':
+ if ( $propertyValue instanceof ezcGraphChartElementAxis )
+ {
+ $this->addElement( 'axis', $propertyValue );
+ $this->elements['axis']->position = ezcGraph::BOTTOM;
+ $this->elements['axis']->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer();
+ $this->renderElement['axis'] = false;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' );
+ }
+ break;
+ case 'rotationAxis':
+ if ( $propertyValue instanceof ezcGraphChartElementAxis )
+ {
+ $this->addElement( 'rotationAxis', $propertyValue );
+ $this->renderElement['rotationAxis'] = false;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphChartElementAxis' );
+ }
+ break;
+ case 'renderer':
+ if ( $propertyValue instanceof ezcGraphRadarRenderer )
+ {
+ parent::__set( $propertyName, $propertyValue );
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphRadarRenderer' );
+ }
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Draws a single rotated axis
+ *
+ * Sets the axis label position depending on the axis rotation.
+ *
+ * @param ezcGraphChartElementAxis $axis
+ * @param ezcGraphBoundings $boundings
+ * @param ezcGraphCoordinate $center
+ * @param float $position
+ * @param float $lastPosition
+ * @return void
+ */
+ protected function drawRotatedAxis( ezcGraphChartElementAxis $axis, ezcGraphBoundings $boundings, ezcGraphCoordinate $center, $position, $lastPosition = null )
+ {
+ // Set axis position depending on angle for better axis label
+ // positioning
+ $angle = $position * 2 * M_PI;
+ switch ( (int) ( ( $position + .125 ) * 4 ) )
+ {
+ case 0:
+ case 4:
+ $axis->position = ezcGraph::BOTTOM;
+ break;
+ case 1:
+ $axis->position = ezcGraph::LEFT;
+ break;
+ case 2:
+ $axis->position = ezcGraph::TOP;
+ break;
+ case 3:
+ $axis->position = ezcGraph::RIGHT;
+ break;
+ }
+
+ // Set last step to correctly draw grid
+ if ( $axis->axisLabelRenderer instanceof ezcGraphAxisRadarLabelRenderer )
+ {
+ $axis->axisLabelRenderer->lastStep = $lastPosition;
+ }
+
+ // Do not draw axis label for last step
+ if ( abs( $position - 1 ) <= .001 )
+ {
+ $axis->label = null;
+ }
+
+ $this->renderer->drawAxis(
+ $boundings,
+ clone $center,
+ $dest = new ezcGraphCoordinate(
+ $center->x + sin( $angle ) * ( $boundings->width / 2 ),
+ $center->y - cos( $angle ) * ( $boundings->height / 2 )
+ ),
+ clone $axis,
+ clone $axis->axisLabelRenderer
+ );
+ }
+
+ /**
+ * Render the assigned data
+ *
+ * Will renderer all charts data in the remaining boundings after drawing
+ * all other chart elements. The data will be rendered depending on the
+ * settings in the dataset.
+ *
+ * @param ezcGraphRenderer $renderer Renderer
+ * @param ezcGraphBoundings $boundings Remaining boundings
+ * @return void
+ */
+ protected function renderData( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings )
+ {
+ // Apply axis space
+ $xAxisSpace = ( $boundings->x1 - $boundings->x0 ) * $this->axis->axisSpace;
+ $yAxisSpace = ( $boundings->y1 - $boundings->y0 ) * $this->axis->axisSpace;
+
+ $center = new ezcGraphCoordinate(
+ ( $boundings->width / 2 ),
+ ( $boundings->height / 2 )
+ );
+
+ // We do not differentiate between display types in radar charts.
+ $nr = $count = count( $this->data );
+
+ // Draw axis at major steps of virtual axis
+ $steps = $this->elements['rotationAxis']->getSteps();
+ $lastStepPosition = null;
+ $axisColor = $this->elements['axis']->border;
+ foreach ( $steps as $step )
+ {
+ $this->elements['axis']->label = $step->label;
+ $this->drawRotatedAxis( $this->elements['axis'], $boundings, $center, $step->position, $lastStepPosition );
+ $lastStepPosition = $step->position;
+
+ if ( count( $step->childs ) )
+ {
+ foreach ( $step->childs as $childStep )
+ {
+ $this->elements['axis']->label = null;
+ $this->elements['axis']->border = $this->childAxisColor;
+
+ $this->drawRotatedAxis( $this->elements['axis'], $boundings, $center, $childStep->position, $lastStepPosition );
+ $lastStepPosition = $childStep->position;
+ }
+ }
+
+ $this->elements['axis']->border = $axisColor;
+ }
+
+ // Display data
+ $this->elements['axis']->position = ezcGraph::TOP;
+ foreach ( $this->data as $datasetName => $data )
+ {
+ --$nr;
+ // Determine fill color for dataset
+ if ( $this->options->fillLines !== false )
+ {
+ $fillColor = clone $data->color->default;
+ $fillColor->alpha = (int) round( ( 255 - $fillColor->alpha ) * ( $this->options->fillLines / 255 ) );
+ }
+ else
+ {
+ $fillColor = null;
+ }
+
+ // Draw lines for dataset
+ $lastPoint = false;
+ foreach ( $data as $key => $value )
+ {
+ $point = new ezcGraphCoordinate(
+ $this->elements['rotationAxis']->getCoordinate( $key ),
+ $this->elements['axis']->getCoordinate( $value )
+ );
+
+ /* Transformation required for 3d like renderers ...
+ * which axis should transform here?
+ $point = $this->elements['xAxis']->axisLabelRenderer->modifyChartDataPosition(
+ $this->elements['yAxis']->axisLabelRenderer->modifyChartDataPosition(
+ new ezcGraphCoordinate(
+ $this->elements['xAxis']->getCoordinate( $key ),
+ $this->elements['yAxis']->getCoordinate( $value )
+ )
+ )
+ );
+ // */
+
+ $renderer->drawRadarDataLine(
+ $boundings,
+ new ezcGraphContext( $datasetName, $key, $data->url[$key] ),
+ $data->color->default,
+ clone $center,
+ ( $lastPoint === false ? $point : $lastPoint ),
+ $point,
+ $nr,
+ $count,
+ $data->symbol[$key],
+ $data->color[$key],
+ $fillColor,
+ $this->options->lineThickness
+ );
+
+ $lastPoint = $point;
+ }
+ }
+ }
+
+ /**
+ * Returns the default display type of the current chart type.
+ *
+ * @return int Display type
+ */
+ public function getDefaultDisplayType()
+ {
+ return ezcGraph::LINE;
+ }
+
+ /**
+ * Renders the basic elements of this chart type
+ *
+ * @param int $width
+ * @param int $height
+ * @return void
+ */
+ protected function renderElements( $width, $height )
+ {
+ if ( !count( $this->data ) )
+ {
+ throw new ezcGraphNoDataException();
+ }
+
+ // Set image properties in driver
+ $this->driver->options->width = $width;
+ $this->driver->options->height = $height;
+
+ // Calculate axis scaling and labeling
+ foreach ( $this->data as $dataset )
+ {
+ $labels = array();
+ $values = array();
+ foreach ( $dataset as $label => $value )
+ {
+ $labels[] = $label;
+ $values[] = $value;
+ }
+
+ $this->elements['axis']->addData( $values );
+ $this->elements['rotationAxis']->addData( $labels );
+ }
+
+ $this->elements['axis']->calculateAxisBoundings();
+ $this->elements['rotationAxis']->calculateAxisBoundings();
+
+ // Generate legend
+ $this->elements['legend']->generateFromDataSets( $this->data );
+
+ // Get boundings from parameters
+ $this->options->width = $width;
+ $this->options->height = $height;
+
+ // Render subelements
+ $boundings = new ezcGraphBoundings();
+ $boundings->x1 = $this->options->width;
+ $boundings->y1 = $this->options->height;
+
+ // Render subelements
+ foreach ( $this->elements as $name => $element )
+ {
+ // Skip element, if it should not get rendered
+ if ( $this->renderElement[$name] === false )
+ {
+ continue;
+ }
+
+ $this->driver->options->font = $element->font;
+ $boundings = $element->render( $this->renderer, $boundings );
+ }
+
+ // Render graph
+ $this->renderData( $this->renderer, $boundings );
+ }
+
+ /**
+ * Render the line chart
+ *
+ * Renders the chart into a file or stream. The width and height are
+ * needed to specify the dimensions of the resulting image. For direct
+ * output use 'php://stdout' as output file.
+ *
+ * @param int $width Image width
+ * @param int $height Image height
+ * @param string $file Output file
+ * @apichange
+ * @return void
+ */
+ public function render( $width, $height, $file = null )
+ {
+ $this->renderElements( $width, $height );
+
+ if ( !empty( $file ) )
+ {
+ $this->renderer->render( $file );
+ }
+
+ $this->renderedFile = $file;
+ }
+
+ /**
+ * Renders this chart to direct output
+ *
+ * Does the same as ezcGraphChart::render(), but renders directly to
+ * output and not into a file.
+ *
+ * @param int $width
+ * @param int $height
+ * @apichange
+ * @return void
+ */
+ public function renderToOutput( $width, $height )
+ {
+ // @TODO: merge this function with render an deprecate ommit of third
+ // argument in render() when API break is possible
+ $this->renderElements( $width, $height );
+ $this->renderer->render( null );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/colors/color.php b/src/TUnit/external/ezc/Graph/colors/color.php
new file mode 100644
index 0000000..d310cca
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/colors/color.php
@@ -0,0 +1,290 @@
+properties['red'] = 0;
+ $this->properties['green'] = 0;
+ $this->properties['blue'] = 0;
+ $this->properties['alpha'] = 0;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'red':
+ case 'green':
+ case 'blue':
+ case 'alpha':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 255 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 255' );
+ }
+
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+
+ /**
+ * Creates an ezcGraphColor object from a hexadecimal color representation
+ *
+ * @param mixed $string Hexadecimal color representation
+ * @return ezcGraphColor
+ */
+ static public function fromHex( $string )
+ {
+ // Remove trailing #
+ if ( $string[0] === '#' )
+ {
+ $string = substr( $string, 1 );
+ }
+
+ // Iterate over chunks and convert to integer
+ $color = new ezcGraphColor();
+ $keys = array( 'red', 'green', 'blue', 'alpha' );
+ foreach ( str_split( $string, 2) as $nr => $hexValue )
+ {
+ if ( isset( $keys[$nr] ) )
+ {
+ $key = $keys[$nr];
+ $color->$key = hexdec( $hexValue ) % 256;
+ }
+ }
+
+ // Set missing values to zero
+ for ( ++$nr; $nr < count( $keys ); ++$nr )
+ {
+ $key = $keys[$nr];
+ $color->$key = 0;
+ }
+
+ return $color;
+ }
+
+ /**
+ * Creates an ezcGraphColor object from an array of integers
+ *
+ * @param array $array Array of integer color values
+ * @return ezcGraphColor
+ */
+ static public function fromIntegerArray( array $array )
+ {
+ // Iterate over array elements
+ $color = new ezcGraphColor();
+ $keys = array( 'red', 'green', 'blue', 'alpha' );
+ $nr = 0;
+ foreach ( $array as $colorValue )
+ {
+ if ( isset( $keys[$nr] ) )
+ {
+ $key = $keys[$nr++];
+ $color->$key = ( (int) $colorValue ) % 256;
+ }
+ }
+
+ // Set missing values to zero
+ for ( $nr; $nr < count( $keys ); ++$nr )
+ {
+ $key = $keys[$nr];
+ $color->$key = 0;
+ }
+
+ return $color;
+ }
+
+ /**
+ * Creates an ezcGraphColor object from an array of floats
+ *
+ * @param array $array Array of float color values
+ * @return ezcGraphColor
+ */
+ static public function fromFloatArray( array $array )
+ {
+ // Iterate over array elements
+ $color = new ezcGraphColor();
+ $keys = array( 'red', 'green', 'blue', 'alpha' );
+ $nr = 0;
+ foreach ( $array as $colorValue )
+ {
+ if ( isset( $keys[$nr] ) )
+ {
+ $key = $keys[$nr++];
+ $color->$key = ( (float) $colorValue * 255 ) % 256;
+ }
+ }
+
+ // Set missing values to zero
+ for ( $nr; $nr < count( $keys ); ++$nr )
+ {
+ $key = $keys[$nr];
+ $color->$key = 0;
+ }
+
+ return $color;
+ }
+
+ /**
+ * Tries to parse provided color value
+ *
+ * This method can be used to create a color struct from arbritrary color
+ * representations. The following values are accepted
+ *
+ * - Hexadecimal color definitions, like known from HTML, CSS and SVG
+ *
+ * Color definitions like #FF0000, with and and without number sign,
+ * where each pair of bytes is interpreted as a color value for the
+ * channels RGB(A). These color values may contain up to 4 values, where
+ * the last value is considered as the alpha channel.
+ *
+ * - Array of integers
+ *
+ * If an array of integers is provided as input teh value in each channel
+ * may be in the span [0 - 255] and is assigned to the color channels
+ * RGB(A). Up to four values are used from the array.
+ *
+ * - Array of floats
+ *
+ * If an array of floats is provided as input teh value in each channel
+ * may be in the span [0 - 1] and is assigned to the color channels
+ * RGB(A). Up to four values are used from the array.
+ *
+ * @param mixed $color Some kind of color definition
+ * @return ezcGraphColor
+ */
+ static public function create( $color )
+ {
+ if ( $color instanceof ezcGraphColor )
+ {
+ return $color;
+ }
+ elseif ( is_string( $color ) )
+ {
+ return ezcGraphColor::fromHex( $color );
+ }
+ elseif ( is_array( $color ) )
+ {
+ $testElement = reset( $color );
+ if ( is_int( $testElement ) )
+ {
+ return ezcGraphColor::fromIntegerArray( $color );
+ }
+ else
+ {
+ return ezcGraphColor::fromFloatArray( $color );
+ }
+ }
+ else
+ {
+ throw new ezcGraphUnknownColorDefinitionException( $color );
+ }
+ }
+
+ /**
+ * Returns a copy of the current color made more transparent by the given
+ * factor
+ *
+ * @param mixed $value Percent to make color mor transparent
+ * @return ezcGraphColor New color
+ */
+ public function transparent( $value )
+ {
+ $color = clone $this;
+
+ $color->alpha = 255 - (int) round( ( 255 - $this->alpha ) * ( 1 - $value ) );
+
+ return $color;
+ }
+
+ /**
+ * Inverts and returns a copy of the current color
+ *
+ * @return ezcGraphColor New Color
+ */
+ public function invert()
+ {
+ $color = new ezcGraphColor();
+
+ $color->red = 255 - $this->red;
+ $color->green = 255 - $this->green;
+ $color->blue = 255 - $this->blue;
+ $color->alpha = $this->alpha;
+
+ return $color;
+ }
+
+ /**
+ * Returns a copy of the current color darkened by the given factor
+ *
+ * @param float $value Percent to darken the color
+ * @return ezcGraphColor New color
+ */
+ public function darken( $value )
+ {
+ $color = clone $this;
+
+ $value = 1 - $value;
+ $color->red = min( 255, max( 0, (int) round( $this->red * $value ) ) );
+ $color->green = min( 255, max( 0, (int) round( $this->green * $value ) ) );
+ $color->blue = min( 255, max( 0, (int) round( $this->blue * $value ) ) );
+
+ return $color;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/colors/linear_gradient.php b/src/TUnit/external/ezc/Graph/colors/linear_gradient.php
new file mode 100644
index 0000000..5b51797
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/colors/linear_gradient.php
@@ -0,0 +1,147 @@
+properties['startColor'] = $startColor;
+ $this->properties['endColor'] = $endColor;
+ $this->properties['startPoint'] = $startPoint;
+ $this->properties['endPoint'] = $endPoint;
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'startPoint':
+ if ( !$propertyValue instanceof ezcGraphCoordinate )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' );
+ }
+ else
+ {
+ $this->properties['startPoint'] = $propertyValue;
+ }
+ break;
+ case 'endPoint':
+ if ( !$propertyValue instanceof ezcGraphCoordinate )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' );
+ }
+ else
+ {
+ $this->properties['endPoint'] = $propertyValue;
+ }
+ break;
+ case 'startColor':
+ $this->properties['startColor'] = ezcGraphColor::create( $propertyValue );
+ break;
+ case 'endColor':
+ $this->properties['endColor'] = ezcGraphColor::create( $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'red':
+ case 'green':
+ case 'blue':
+ case 'alpha':
+ // Fallback to native color
+ return $this->properties['startColor']->$propertyName;
+ default:
+ if ( isset( $this->properties[$propertyName] ) )
+ {
+ return $this->properties[$propertyName];
+ }
+ else
+ {
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+ }
+
+ /**
+ * Returns a unique string representation for the gradient.
+ *
+ * @access public
+ * @return void
+ */
+ public function __toString()
+ {
+ return sprintf( 'LinearGradient_%d_%d_%d_%d_%02x%02x%02x%02x_%02x%02x%02x%02x',
+ $this->properties['startPoint']->x,
+ $this->properties['startPoint']->y,
+ $this->properties['endPoint']->x,
+ $this->properties['endPoint']->y,
+ $this->properties['startColor']->red,
+ $this->properties['startColor']->green,
+ $this->properties['startColor']->blue,
+ $this->properties['startColor']->alpha,
+ $this->properties['endColor']->red,
+ $this->properties['endColor']->green,
+ $this->properties['endColor']->blue,
+ $this->properties['endColor']->alpha
+ );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/colors/radial_gradient.php b/src/TUnit/external/ezc/Graph/colors/radial_gradient.php
new file mode 100644
index 0000000..2d046cb
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/colors/radial_gradient.php
@@ -0,0 +1,173 @@
+properties['center'] = $center;
+ $this->properties['width'] = (float) $width;
+ $this->properties['height'] = (float) $height;
+ $this->properties['offset'] = 0;
+ $this->properties['startColor'] = $startColor;
+ $this->properties['endColor'] = $endColor;
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'center':
+ if ( !$propertyValue instanceof ezcGraphCoordinate )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' );
+ }
+ else
+ {
+ $this->properties['center'] = $propertyValue;
+ }
+ break;
+ case 'width':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['width'] = (float) $propertyValue;
+ break;
+ case 'height':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['height'] = (float) $propertyValue;
+ break;
+ case 'offset':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['offset'] = $propertyValue;
+ break;
+ case 'startColor':
+ $this->properties['startColor'] = ezcGraphColor::create( $propertyValue );
+ break;
+ case 'endColor':
+ $this->properties['endColor'] = ezcGraphColor::create( $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'red':
+ case 'green':
+ case 'blue':
+ case 'alpha':
+ // Fallback to native color
+ return $this->properties['startColor']->$propertyName;
+ default:
+ if ( isset( $this->properties[$propertyName] ) )
+ {
+ return $this->properties[$propertyName];
+ }
+ else
+ {
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+ }
+
+ /**
+ * Returns a unique string representation for the gradient.
+ *
+ * @access public
+ * @return void
+ */
+ public function __toString()
+ {
+ return sprintf( 'RadialGradient_%d_%d_%d_%d_%.2f_%02x%02x%02x%02x_%02x%02x%02x%02x',
+ $this->properties['center']->x,
+ $this->properties['center']->y,
+ $this->properties['width'],
+ $this->properties['height'],
+ $this->properties['offset'],
+ $this->properties['startColor']->red,
+ $this->properties['startColor']->green,
+ $this->properties['startColor']->blue,
+ $this->properties['startColor']->alpha,
+ $this->properties['endColor']->red,
+ $this->properties['endColor']->green,
+ $this->properties['endColor']->blue,
+ $this->properties['endColor']->alpha
+ );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/data_container/base.php b/src/TUnit/external/ezc/Graph/data_container/base.php
new file mode 100644
index 0000000..dfe87b1
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/data_container/base.php
@@ -0,0 +1,225 @@
+chart = $chart;
+ }
+
+ /**
+ * Adds a dataset to the charts data
+ *
+ * @param string $name Name of dataset
+ * @param ezcGraphDataSet $dataSet
+ * @param mixed $values Values to create dataset with
+ * @throws ezcGraphTooManyDataSetExceptions
+ * If too many datasets are created
+ * @return ezcGraphDataSet
+ */
+ protected function addDataSet( $name, ezcGraphDataSet $dataSet )
+ {
+ $this->data[$name] = $dataSet;
+
+ $this->data[$name]->label = $name;
+ $this->data[$name]->palette = $this->chart->palette;
+ $this->data[$name]->displayType = $this->chart->getDefaultDisplayType();
+ }
+
+ /**
+ * Returns if the given offset exists.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key Identifier of dataset.
+ * @return bool True when the offset exists, otherwise false.
+ */
+ public function offsetExists( $key )
+ {
+ return isset( $this->data[$key] );
+ }
+
+ /**
+ * Returns the element with the given offset.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key Identifier of dataset.
+ * @return ezcGraphDataSet
+ *
+ * @throws ezcGraphNoSuchDataSetException
+ * If no dataset with identifier exists
+ */
+ public function offsetGet( $key )
+ {
+ if ( !isset( $this->data[$key] ) )
+ {
+ throw new ezcGraphNoSuchDataSetException( $key );
+ }
+
+ return $this->data[$key];
+ }
+
+ /**
+ * Set the element with the given offset.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key
+ * @param ezcGraphDataSet $value
+ * @return void
+ *
+ * @throws ezcBaseValueException
+ * If supplied value is not an ezcGraphDataSet
+ */
+ public function offsetSet( $key, $value )
+ {
+ if ( !$value instanceof ezcGraphDataSet )
+ {
+ throw new ezcBaseValueException( $key, $value, 'ezcGraphDataSet' );
+ }
+
+ return $this->addDataSet( $key, $value );
+ }
+
+ /**
+ * Unset the element with the given offset.
+ *
+ * This method is part of the ArrayAccess interface to allow access to the
+ * data of this object as if it was an array.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function offsetUnset( $key )
+ {
+ if ( !isset( $this->data[$key] ) )
+ {
+ throw new ezcGraphNoSuchDataSetException( $key );
+ }
+
+ unset( $this->data[$key] );
+ }
+
+ /**
+ * Returns the currently selected dataset.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return ezcGraphDataSet The currently selected dataset.
+ */
+ public function current()
+ {
+ return current( $this->data );
+ }
+
+ /**
+ * Returns the next dataset and selects it or false on the last dataset.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return mixed ezcGraphDataSet if the next dataset exists, or false.
+ */
+ public function next()
+ {
+ return next( $this->data );
+ }
+
+ /**
+ * Returns the key of the currently selected dataset.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return int The key of the currently selected dataset.
+ */
+ public function key()
+ {
+ return key( $this->data );
+ }
+
+ /**
+ * Returns if the current dataset is valid.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return bool If the current dataset is valid
+ */
+ public function valid()
+ {
+ return ( current( $this->data ) !== false );
+ }
+
+ /**
+ * Selects the very first dataset and returns it.
+ * This method is part of the Iterator interface to allow access to the
+ * datasets of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return ezcGraphDataSet The very first dataset.
+ */
+ public function rewind()
+ {
+ return reset( $this->data );
+ }
+
+ /**
+ * Returns the number of datasets in the row.
+ *
+ * This method is part of the Countable interface to allow the usage of
+ * PHP's count() function to check how many datasets exist.
+ *
+ * @return int Number of datasets.
+ */
+ public function count()
+ {
+ return count( $this->data );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/data_container/single.php b/src/TUnit/external/ezc/Graph/data_container/single.php
new file mode 100644
index 0000000..d24b375
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/data_container/single.php
@@ -0,0 +1,51 @@
+data ) >= 1 &&
+ !isset( $this->data[$name] ) )
+ {
+ throw new ezcGraphTooManyDataSetsExceptions( $name );
+ }
+ else
+ {
+ parent::addDataSet( $name, $dataSet );
+
+ // Resette palette color counter
+ $this->chart->palette->resetColorCounter();
+
+ // Colorize each data element
+ foreach ( $this->data[$name] as $label => $value )
+ {
+ $this->data[$name]->color[$label] = $this->chart->palette->dataSetColor;
+ }
+ }
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/datasets/array.php b/src/TUnit/external/ezc/Graph/datasets/array.php
new file mode 100644
index 0000000..56cc186
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/array.php
@@ -0,0 +1,71 @@
+createFromArray( $data );
+ parent::__construct();
+ }
+
+ /**
+ * setData
+ *
+ * Can handle data provided through an array or iterator.
+ *
+ * @param array|Iterator $data
+ * @access public
+ * @return void
+ */
+ protected function createFromArray( $data = array() )
+ {
+ if ( !is_array( $data ) &&
+ !( $data instanceof Traversable ) )
+ {
+ throw new ezcGraphInvalidArrayDataSourceException( $data );
+ }
+
+ $this->data = array();
+ foreach ( $data as $key => $value )
+ {
+ $this->data[$key] = $value;
+ }
+
+ if ( !count( $this->data ) )
+ {
+ throw new ezcGraphInvalidDataException( 'Data sets should contain some values.' );
+ }
+ }
+
+ /**
+ * Returns the number of elements in this dataset
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count( $this->data );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/datasets/average.php b/src/TUnit/external/ezc/Graph/datasets/average.php
new file mode 100644
index 0000000..89f7c67
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/average.php
@@ -0,0 +1,359 @@
+mixed)
+ */
+ protected $properties;
+
+ /**
+ * Constructor
+ *
+ * @param ezcGraphDataSet $dataset Dataset to interpolate
+ * @param int $order Maximum order of interpolating polynom
+ * @return void
+ * @ignore
+ */
+ public function __construct( ezcGraphDataSet $dataset, $order = 3 )
+ {
+ parent::__construct();
+
+ $this->properties['resolution'] = 100;
+ $this->properties['polynomOrder'] = (int) $order;
+
+ $this->source = $dataset;
+ }
+
+ /**
+ * Options write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param mixed $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return mixed
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName ) {
+ case 'polynomOrder':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 0' );
+ }
+
+ $this->properties['polynomOrder'] = (int) $propertyValue;
+ $this->polynom = false;
+ break;
+ case 'resolution':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' );
+ }
+
+ $this->properties['resolution'] = (int) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Property get access.
+ * Simply returns a given option.
+ *
+ * @param string $propertyName The name of the option to get.
+ * @return mixed The option value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ */
+ public function __get( $propertyName )
+ {
+ if ( array_key_exists( $propertyName, $this->properties ) )
+ {
+ return $this->properties[$propertyName];
+ }
+ return parent::__get( $propertyName );
+ }
+
+ /**
+ * Build the polynom based on the given points.
+ *
+ * @return void
+ */
+ protected function buildPolynom()
+ {
+ $points = array();
+
+ foreach ( $this->source as $key => $value )
+ {
+ if ( !is_numeric( $key ) )
+ {
+ throw new ezcGraphDatasetAverageInvalidKeysException();
+ }
+
+ if ( ( $this->min === false ) || ( $this->min > $key ) )
+ {
+ $this->min = (float) $key;
+ }
+
+ if ( ( $this->max === false ) || ( $this->max < $key ) )
+ {
+ $this->max = (float) $key;
+ }
+
+ $points[] = new ezcGraphCoordinate( (float) $key, (float) $value );
+ }
+
+ // Build transposed and normal Matrix out of coordiantes
+ $a = new ezcGraphMatrix( count( $points ), $this->polynomOrder + 1 );
+ $b = new ezcGraphMatrix( count( $points ), 1 );
+
+ for ( $i = 0; $i <= $this->properties['polynomOrder']; ++$i )
+ {
+ foreach ( $points as $nr => $point )
+ {
+ $a->set( $nr, $i, pow( $point->x, $i ) );
+ $b->set( $nr, 0, $point->y );
+ }
+ }
+
+ $at = clone $a;
+ $at->transpose();
+
+ $left = $at->multiply( $a );
+ $right = $at->multiply( $b );
+
+ $this->polynom = $left->solveNonlinearEquatation( $right );
+ }
+
+ /**
+ * Returns a polynom of the defined order witch matches the datapoints
+ * using the least squares algorithm.
+ *
+ * @return ezcGraphPolynom Polynom
+ */
+ public function getPolynom()
+ {
+ if ( $this->polynom === false )
+ {
+ $this->buildPolynom();
+ }
+
+ return $this->polynom;
+ }
+
+ /**
+ * Get the x coordinate for the current position
+ *
+ * @param int $position Position
+ * @return float x coordinate
+ */
+ protected function getKey()
+ {
+ $polynom = $this->getPolynom();
+ return $this->min +
+ ( $this->max - $this->min ) / $this->resolution * $this->position;
+ }
+
+ /**
+ * Returns true if the given datapoint exists
+ * Allows isset() using ArrayAccess.
+ *
+ * @param string $key The key of the datapoint to get.
+ * @return bool Wether the key exists.
+ */
+ public function offsetExists( $key )
+ {
+ $polynom = $this->getPolynom();
+ return ( ( $key >= $this->min ) && ( $key <= $this->max ) );
+ }
+
+ /**
+ * Returns the value for the given datapoint
+ * Get an datapoint value by ArrayAccess.
+ *
+ * @param string $key The key of the datapoint to get.
+ * @return float The datapoint value.
+ */
+ public function offsetGet( $key )
+ {
+ $polynom = $this->getPolynom();
+ return $polynom->evaluate( $key );
+ }
+
+ /**
+ * Throws a ezcBasePropertyPermissionException because single datapoints
+ * cannot be set in average datasets.
+ *
+ * @param string $key The kex of a datapoint to set.
+ * @param float $value The value for the datapoint.
+ * @throws ezcBasePropertyPermissionException
+ * Always, because access is readonly.
+ * @return void
+ */
+ public function offsetSet( $key, $value )
+ {
+ throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ );
+ }
+
+ /**
+ * Returns the currently selected datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return string The currently selected datapoint.
+ */
+ final public function current()
+ {
+ $polynom = $this->getPolynom();
+ return $polynom->evaluate( $this->getKey() );
+ }
+
+ /**
+ * Returns the next datapoint and selects it or false on the last datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return float datapoint if it exists, or false.
+ */
+ final public function next()
+ {
+ if ( ++$this->position >= $this->resolution )
+ {
+ return false;
+ }
+ else
+ {
+ return $this->current();
+ }
+ }
+
+ /**
+ * Returns the key of the currently selected datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return string The key of the currently selected datapoint.
+ */
+ final public function key()
+ {
+ return (string) $this->getKey();
+ }
+
+ /**
+ * Returns if the current datapoint is valid.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return bool If the current datapoint is valid
+ */
+ final public function valid()
+ {
+ $polynom = $this->getPolynom();
+
+ if ( $this->min >= $this->max )
+ {
+ return false;
+ }
+
+ return ( ( $this->getKey() >= $this->min ) && ( $this->getKey() <= $this->max ) );
+ }
+
+ /**
+ * Selects the very first datapoint and returns it.
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return float The very first datapoint.
+ */
+ final public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * Returns the number of elements in this dataset
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->resolution;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/datasets/base.php b/src/TUnit/external/ezc/Graph/datasets/base.php
new file mode 100644
index 0000000..385f677
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/base.php
@@ -0,0 +1,302 @@
+properties['label'] = new ezcGraphDataSetStringProperty( $this );
+ $this->properties['color'] = new ezcGraphDataSetColorProperty( $this );
+ $this->properties['symbol'] = new ezcGraphDataSetIntProperty( $this );
+ $this->properties['lineThickness'] = new ezcGraphDataSetIntProperty( $this );
+ $this->properties['highlight'] = new ezcGraphDataSetBooleanProperty( $this );
+ $this->properties['highlightValue'] = new ezcGraphDataSetStringProperty( $this );
+ $this->properties['displayType'] = new ezcGraphDataSetIntProperty( $this );
+ $this->properties['url'] = new ezcGraphDataSetStringProperty( $this );
+
+ $this->properties['xAxis'] = new ezcGraphDataSetAxisProperty( $this );
+ $this->properties['yAxis'] = new ezcGraphDataSetAxisProperty( $this );
+
+ $this->properties['highlight']->default = false;
+ }
+
+ /**
+ * Options write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param mixed $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'hilight':
+ $propertyName = 'highlight';
+ case 'label':
+ case 'url':
+ case 'color':
+ case 'symbol':
+ case 'lineThickness':
+ case 'highlight':
+ case 'highlightValue':
+ case 'displayType':
+ case 'xAxis':
+ case 'yAxis':
+ $this->properties[$propertyName]->default = $propertyValue;
+ break;
+
+ case 'palette':
+ $this->palette = $propertyValue;
+ $this->color->default = $this->palette->dataSetColor;
+ $this->symbol->default = $this->palette->dataSetSymbol;
+ break;
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+
+ /**
+ * Property get access.
+ * Simply returns a given option.
+ *
+ * @param string $propertyName The name of the option to get.
+ * @return mixed The option value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ */
+ public function __get( $propertyName )
+ {
+ if ( array_key_exists( $propertyName, $this->properties ) )
+ {
+ return $this->properties[$propertyName];
+ }
+ else
+ {
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+
+ /**
+ * Returns true if the given datapoint exists
+ * Allows isset() using ArrayAccess.
+ *
+ * @param string $key The key of the datapoint to get.
+ * @return bool Wether the key exists.
+ */
+ public function offsetExists( $key )
+ {
+ return isset( $this->data[$key] );
+ }
+
+ /**
+ * Returns the value for the given datapoint
+ * Get an datapoint value by ArrayAccess.
+ *
+ * @param string $key The key of the datapoint to get.
+ * @return float The datapoint value.
+ */
+ public function offsetGet( $key )
+ {
+ return $this->data[$key];
+ }
+
+ /**
+ * Sets the value for a datapoint.
+ * Sets an datapoint using ArrayAccess.
+ *
+ * @param string $key The kex of a datapoint to set.
+ * @param float $value The value for the datapoint.
+ * @return void
+ */
+ public function offsetSet( $key, $value )
+ {
+ $this->data[$key] = (float) $value;
+ }
+
+ /**
+ * Unset an option.
+ * Unsets an option using ArrayAccess.
+ *
+ * @param string $key The options to unset.
+ * @return void
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @throws ezcBaseValueException
+ * If a the value for a property is out of range.
+ */
+ public function offsetUnset( $key )
+ {
+ unset( $this->data[$key] );
+ }
+
+ /**
+ * Returns the currently selected datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return string The currently selected datapoint.
+ */
+ public function current()
+ {
+ if ( !isset( $this->current ) )
+ {
+ $this->keys = array_keys( $this->data );
+ $this->current = 0;
+ }
+
+ return $this->data[$this->keys[$this->current]];
+ }
+
+ /**
+ * Returns the next datapoint and selects it or false on the last datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return float datapoint if it exists, or false.
+ */
+ public function next()
+ {
+ if ( ++$this->current >= count( $this->keys ) )
+ {
+ return false;
+ }
+ else
+ {
+ return $this->data[$this->keys[$this->current]];
+ }
+ }
+
+ /**
+ * Returns the key of the currently selected datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return string The key of the currently selected datapoint.
+ */
+ public function key()
+ {
+ return $this->keys[$this->current];
+ }
+
+ /**
+ * Returns if the current datapoint is valid.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return bool If the current datapoint is valid
+ */
+ public function valid()
+ {
+ return isset( $this->keys[$this->current] );
+ }
+
+ /**
+ * Selects the very first datapoint and returns it.
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return float The very first datapoint.
+ */
+ public function rewind()
+ {
+ $this->keys = array_keys( $this->data );
+ $this->current = 0;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/datasets/numeric.php b/src/TUnit/external/ezc/Graph/datasets/numeric.php
new file mode 100644
index 0000000..11590c3
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/numeric.php
@@ -0,0 +1,287 @@
+mixed)
+ */
+ protected $properties;
+
+ /**
+ * Constructor
+ *
+ * @param float $start Start value for x axis values of function
+ * @param float $end End value for x axis values of function
+ * @param callback $callback Callback function
+ * @return void
+ * @ignore
+ */
+ public function __construct( $start = null, $end = null, $callback = null )
+ {
+ parent::__construct();
+
+ $this->properties['start'] = null;
+ $this->properties['end'] = null;
+ $this->properties['callback'] = null;
+
+ if ( $start !== null )
+ {
+ $this->start = $start;
+ }
+
+ if ( $end !== null )
+ {
+ $this->end = $end;
+ }
+
+ if ( $callback !== null )
+ {
+ $this->callback = $callback;
+ }
+
+ $this->properties['resolution'] = 100;
+ }
+
+ /**
+ * Options write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param mixed $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return mixed
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName ) {
+ case 'resolution':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int > 1' );
+ }
+
+ $this->properties['resolution'] = (int) $propertyValue;
+ break;
+ case 'start':
+ case 'end':
+ if ( !is_numeric( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+ case 'callback':
+ if ( !is_callable( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback' );
+ }
+
+ $this->properties[$propertyName] = $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Property get access.
+ * Simply returns a given option.
+ *
+ * @param string $propertyName The name of the option to get.
+ * @return mixed The option value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ */
+ public function __get( $propertyName )
+ {
+ if ( array_key_exists( $propertyName, $this->properties ) )
+ {
+ return $this->properties[$propertyName];
+ }
+ return parent::__get( $propertyName );
+ }
+
+ /**
+ * Get the x coordinate for the current position
+ *
+ * @param int $position Position
+ * @return float x coordinate
+ */
+ protected function getKey()
+ {
+ return $this->start +
+ ( $this->end - $this->start ) / $this->resolution * $this->position;
+ }
+
+ /**
+ * Returns true if the given datapoint exists
+ * Allows isset() using ArrayAccess.
+ *
+ * @param string $key The key of the datapoint to get.
+ * @return bool Wether the key exists.
+ */
+ public function offsetExists( $key )
+ {
+ return ( ( $key >= $this->start ) && ( $key <= $this->end ) );
+ }
+
+ /**
+ * Returns the value for the given datapoint
+ * Get an datapoint value by ArrayAccess.
+ *
+ * @param string $key The key of the datapoint to get.
+ * @return float The datapoint value.
+ */
+ public function offsetGet( $key )
+ {
+ return call_user_func( $this->callback, $key );
+ }
+
+ /**
+ * Throws a ezcBasePropertyPermissionException because single datapoints
+ * cannot be set in average datasets.
+ *
+ * @param string $key The kex of a datapoint to set.
+ * @param float $value The value for the datapoint.
+ * @throws ezcBasePropertyPermissionException
+ * Always, because access is readonly.
+ * @return void
+ */
+ public function offsetSet( $key, $value )
+ {
+ throw new ezcBasePropertyPermissionException( $key, ezcBasePropertyPermissionException::READ );
+ }
+
+ /**
+ * Returns the currently selected datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return string The currently selected datapoint.
+ */
+ final public function current()
+ {
+ return call_user_func( $this->callback, $this->getKey() );
+ }
+
+ /**
+ * Returns the next datapoint and selects it or false on the last datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return float datapoint if it exists, or false.
+ */
+ final public function next()
+ {
+ if ( $this->start === $this->end )
+ {
+ throw new ezcGraphDatasetAverageInvalidKeysException();
+ }
+
+ if ( ++$this->position >= $this->resolution )
+ {
+ return false;
+ }
+ else
+ {
+ return $this->current();
+ }
+ }
+
+ /**
+ * Returns the key of the currently selected datapoint.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return string The key of the currently selected datapoint.
+ */
+ final public function key()
+ {
+ return (string) $this->getKey();
+ }
+
+ /**
+ * Returns if the current datapoint is valid.
+ *
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return bool If the current datapoint is valid
+ */
+ final public function valid()
+ {
+ return ( ( $this->getKey() >= $this->start ) && ( $this->getKey() <= $this->end ) );
+ }
+
+ /**
+ * Selects the very first datapoint and returns it.
+ * This method is part of the Iterator interface to allow access to the
+ * datapoints of this row by iterating over it like an array (e.g. using
+ * foreach).
+ *
+ * @return float The very first datapoint.
+ */
+ final public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * Returns the number of elements in this dataset
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->resolution + 1;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/datasets/property/axis.php b/src/TUnit/external/ezc/Graph/datasets/property/axis.php
new file mode 100644
index 0000000..bcb1c00
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/property/axis.php
@@ -0,0 +1,62 @@
+
diff --git a/src/TUnit/external/ezc/Graph/datasets/property/boolean.php b/src/TUnit/external/ezc/Graph/datasets/property/boolean.php
new file mode 100644
index 0000000..ab4b9e8
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/property/boolean.php
@@ -0,0 +1,37 @@
+
diff --git a/src/TUnit/external/ezc/Graph/datasets/property/color.php b/src/TUnit/external/ezc/Graph/datasets/property/color.php
new file mode 100644
index 0000000..2bea337
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/property/color.php
@@ -0,0 +1,37 @@
+
diff --git a/src/TUnit/external/ezc/Graph/datasets/property/integer.php b/src/TUnit/external/ezc/Graph/datasets/property/integer.php
new file mode 100644
index 0000000..3eda50f
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/property/integer.php
@@ -0,0 +1,37 @@
+
diff --git a/src/TUnit/external/ezc/Graph/datasets/property/string.php b/src/TUnit/external/ezc/Graph/datasets/property/string.php
new file mode 100644
index 0000000..69a0b13
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/datasets/property/string.php
@@ -0,0 +1,37 @@
+
diff --git a/src/TUnit/external/ezc/Graph/driver/cairo.php b/src/TUnit/external/ezc/Graph/driver/cairo.php
new file mode 100644
index 0000000..7a90dfc
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/driver/cairo.php
@@ -0,0 +1,1010 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->background->color = '#FFFFFFFF';
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->renderer = new ezcGraphRenderer3d();
+ * $graph->renderer->options->pieChartShadowSize = 10;
+ * $graph->renderer->options->pieChartGleam = .5;
+ * $graph->renderer->options->dataBorder = false;
+ * $graph->renderer->options->pieChartHeight = 16;
+ * $graph->renderer->options->legendSymbolGleam = .5;
+ *
+ * // Use cairo driver
+ * $graph->driver = new ezcGraphCairoDriver();
+ *
+ * $graph->render( 400, 200, 'tutorial_driver_cairo.png' );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphCairoDriver extends ezcGraphDriver
+{
+ /**
+ * Surface for cairo
+ *
+ * @var resource
+ */
+ protected $surface;
+
+ /**
+ * Current cairo context.
+ *
+ * @var resource
+ */
+ protected $context;
+
+ /**
+ * List of strings to draw
+ * array ( array(
+ * 'text' => array( 'strings' ),
+ * 'options' => ezcGraphFontOptions,
+ * )
+ *
+ * @var array
+ */
+ protected $strings = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'cairo_wrapper' );
+ $this->options = new ezcGraphCairoDriverOptions( $options );
+ }
+
+ /**
+ * Initilize cairo surface
+ *
+ * Initilize cairo surface from values provided in the options object, if
+ * is has not been already initlized.
+ *
+ * @return void
+ */
+ protected function initiliazeSurface()
+ {
+ // Immediatly exit, if surface already exists
+ if ( $this->surface !== null )
+ {
+ return;
+ }
+
+ $this->surface = cairo_image_surface_create(
+ CAIRO_FORMAT_ARGB32,
+ $this->options->width,
+ $this->options->height
+ );
+
+ $this->context = cairo_create( $this->surface );
+ cairo_set_line_width( $this->context, 1 );
+ }
+
+ /**
+ * Get SVG style definition
+ *
+ * Returns a string with SVG style definitions created from color,
+ * fillstatus and line thickness.
+ *
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness.
+ * @return string Formatstring
+ */
+ protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. )
+ {
+ switch ( true )
+ {
+ case $color instanceof ezcGraphLinearGradient:
+ $pattern = cairo_pattern_create_linear(
+ $color->startPoint->x, $color->startPoint->y,
+ $color->endPoint->x, $color->endPoint->y
+ );
+
+ cairo_pattern_add_color_stop_rgba (
+ $pattern,
+ 0,
+ $color->startColor->red / 255,
+ $color->startColor->green / 255,
+ $color->startColor->blue / 255,
+ 1 - $color->startColor->alpha / 255
+ );
+
+ cairo_pattern_add_color_stop_rgba (
+ $pattern,
+ 1,
+ $color->endColor->red / 255,
+ $color->endColor->green / 255,
+ $color->endColor->blue / 255,
+ 1 - $color->endColor->alpha / 255
+ );
+
+ cairo_set_source( $this->context, $pattern );
+ cairo_fill( $this->context );
+ break;
+
+ case $color instanceof ezcGraphRadialGradient:
+ $pattern = cairo_pattern_create_radial(
+ 0, 0, 0,
+ 0, 0, 1
+ );
+
+ cairo_pattern_add_color_stop_rgba (
+ $pattern,
+ 0,
+ $color->startColor->red / 255,
+ $color->startColor->green / 255,
+ $color->startColor->blue / 255,
+ 1 - $color->startColor->alpha / 255
+ );
+
+ cairo_pattern_add_color_stop_rgba (
+ $pattern,
+ 1,
+ $color->endColor->red / 255,
+ $color->endColor->green / 255,
+ $color->endColor->blue / 255,
+ 1 - $color->endColor->alpha / 255
+ );
+
+ // Scale pattern, and move it to the correct position
+ $matrix = cairo_matrix_multiply(
+ $move = cairo_matrix_create_translate( -$color->center->x, -$color->center->y ),
+ $scale = cairo_matrix_create_scale( 1 / $color->width, 1 / $color->height )
+ );
+ cairo_pattern_set_matrix( $pattern, $matrix );
+
+ cairo_set_source( $this->context, $pattern );
+ cairo_fill( $this->context );
+ break;
+ default:
+ cairo_set_source_rgba(
+ $this->context,
+ $color->red / 255,
+ $color->green / 255,
+ $color->blue / 255,
+ 1 - $color->alpha / 255
+ );
+ break;
+ }
+
+ // Set line width
+ cairo_set_line_width( $this->context, $thickness );
+
+ // Set requested fill state for context
+ if ( $filled )
+ {
+ cairo_fill_preserve( $this->context );
+ }
+ }
+
+ /**
+ * Draws a single polygon.
+ *
+ * @param array $points Point array
+ * @param ezcGraphColor $color Polygon color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
+ {
+ $this->initiliazeSurface();
+
+ $path = cairo_new_path( $this->context );
+
+ $lastPoint = end( $points );
+ cairo_move_to( $this->context, $lastPoint->x, $lastPoint->y );
+
+ foreach ( $points as $point )
+ {
+ cairo_line_to( $this->context, $point->x, $point->y );
+ }
+
+ cairo_close_path( $this->context );
+
+ $this->getStyle( $color, $filled, $thickness );
+ cairo_stroke( $this->context );
+
+ return $points;
+ }
+
+ /**
+ * Draws a line
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Line color
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
+ {
+ $this->initiliazeSurface();
+
+ $path = cairo_new_path( $this->context );
+
+ cairo_move_to( $this->context, $start->x, $start->y );
+ cairo_line_to( $this->context, $end->x, $end->y );
+
+ $this->getStyle( $color, false, $thickness );
+ cairo_stroke( $this->context );
+
+ return array( $start, $end );
+ }
+
+ /**
+ * Returns boundings of text depending on the available font extension
+ *
+ * @param float $size Textsize
+ * @param ezcGraphFontOptions $font Font
+ * @param string $text Text
+ * @return ezcGraphBoundings Boundings of text
+ */
+ protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
+ {
+ cairo_select_font_face( $this->context, $font->name, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL );
+ cairo_set_font_size( $this->context, $size );
+ $extents = cairo_text_extents( $this->context, $text );
+
+ return new ezcGraphBoundings(
+ 0,
+ 0,
+ $extents['width'],
+ $extents['height']
+ );
+ }
+
+ /**
+ * Writes text in a box of desired size
+ *
+ * @param string $string Text
+ * @param ezcGraphCoordinate $position Top left position
+ * @param float $width Width of text box
+ * @param float $height Height of text box
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
+ {
+ $this->initiliazeSurface();
+
+ $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 );
+
+ $width -= $padding * 2;
+ $height -= $padding * 2;
+ $textPosition = new ezcGraphCoordinate(
+ $position->x + $padding,
+ $position->y + $padding
+ );
+
+ // Try to get a font size for the text to fit into the box
+ $maxSize = min( $height, $this->options->font->maxFontSize );
+ $result = false;
+ for ( $size = $maxSize; $size >= $this->options->font->minFontSize; )
+ {
+ $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size );
+ if ( is_array( $result ) )
+ {
+ break;
+ }
+ $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) );
+ }
+
+ if ( !is_array( $result ) )
+ {
+ if ( ( $height >= $this->options->font->minFontSize ) &&
+ ( $this->options->autoShortenString ) )
+ {
+ $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize );
+ }
+ else
+ {
+ throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height );
+ }
+ }
+
+ $this->options->font->minimalUsedFont = $size;
+ $this->strings[] = array(
+ 'text' => $result,
+ 'position' => $textPosition,
+ 'width' => $width,
+ 'height' => $height,
+ 'align' => $align,
+ 'font' => $this->options->font,
+ 'rotation' => $rotation,
+ );
+
+ return array(
+ clone $position,
+ new ezcGraphCoordinate( $position->x + $width, $position->y ),
+ new ezcGraphCoordinate( $position->x + $width, $position->y + $height ),
+ new ezcGraphCoordinate( $position->x, $position->y + $height ),
+ );
+ }
+
+ /**
+ * Render text depending of font type and available font extensions
+ *
+ * @param string $id
+ * @param string $text
+ * @param string $font
+ * @param ezcGraphColor $color
+ * @param ezcGraphCoordinate $position
+ * @param float $size
+ * @param float $rotation
+ * @return void
+ */
+ protected function renderText( $text, $font, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null )
+ {
+ cairo_select_font_face( $this->context, $font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL );
+ cairo_set_font_size( $this->context, $size );
+
+ // Store current state of context
+ cairo_save( $this->context );
+ cairo_move_to( $this->context, 0, 0 );
+
+ if ( $rotation !== null )
+ {
+ // Move to the center
+ cairo_translate( $this->context,
+ $rotation->getCenter()->x,
+ $rotation->getCenter()->y
+ );
+ // Rotate around text center
+ cairo_rotate( $this->context,
+ deg2rad( $rotation->getRotation() )
+ );
+ // Center the text
+ cairo_translate( $this->context,
+ $position->x - $rotation->getCenter()->x,
+ $position->y - $rotation->getCenter()->y - $size * .15
+ );
+ } else {
+ cairo_translate( $this->context,
+ $position->x,
+ $position->y - $size * .15
+ );
+ }
+
+ cairo_new_path( $this->context );
+ $this->getStyle( $color, true );
+ cairo_show_text( $this->context, $text );
+ cairo_stroke( $this->context );
+
+ // Restore state of context
+ cairo_restore( $this->context );
+ }
+
+ /**
+ * Draw all collected texts
+ *
+ * The texts are collected and their maximum possible font size is
+ * calculated. This function finally draws the texts on the image, this
+ * delayed drawing has two reasons:
+ *
+ * 1) This way the text strings are always on top of the image, what
+ * results in better readable texts
+ * 2) The maximum possible font size can be calculated for a set of texts
+ * with the same font configuration. Strings belonging to one chart
+ * element normally have the same font configuration, so that all texts
+ * belonging to one element will have the same font size.
+ *
+ * @access protected
+ * @return void
+ */
+ protected function drawAllTexts()
+ {
+ $this->initiliazeSurface();
+
+ foreach ( $this->strings as $text )
+ {
+ $size = $text['font']->minimalUsedFont;
+
+ $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing;
+
+ // Calculate y offset for vertical alignement
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::BOTTOM ):
+ $yOffset = $text['height'] - $completeHeight;
+ break;
+ case ( $text['align'] & ezcGraph::MIDDLE ):
+ $yOffset = ( $text['height'] - $completeHeight ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::TOP ):
+ default:
+ $yOffset = 0;
+ break;
+ }
+
+ $padding = $text['font']->padding + $text['font']->borderWidth / 2;
+ if ( $this->options->font->minimizeBorder === true )
+ {
+ // Calculate maximum width of text rows
+ $width = false;
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ $boundings = $this->getTextBoundings( $size, $text['font'], $string );
+ if ( ( $width === false) || ( $boundings->width > $width ) )
+ {
+ $width = $boundings->width;
+ }
+ }
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $xOffset = ( $text['width'] - $width ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $xOffset = $text['width'] - $width;
+ break;
+ case ( $text['align'] & ezcGraph::LEFT ):
+ default:
+ $xOffset = 0;
+ break;
+ }
+
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding + $xOffset,
+ $text['position']->y - $padding + $yOffset
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $xOffset + $width,
+ $text['position']->y - $padding + $yOffset
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $xOffset + $width,
+ $text['position']->y + $padding * 2 + $yOffset + $completeHeight
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding + $xOffset,
+ $text['position']->y + $padding * 2 + $yOffset + $completeHeight
+ ),
+ );
+ }
+ else
+ {
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding,
+ $text['position']->y - $padding
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $text['width'],
+ $text['position']->y - $padding
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $text['width'],
+ $text['position']->y + $padding * 2 + $text['height']
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding,
+ $text['position']->y + $padding * 2 + $text['height']
+ ),
+ );
+ }
+
+ if ( $text['rotation'] !== null )
+ {
+ foreach ( $borderPolygonArray as $nr => $point )
+ {
+ $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point );
+ }
+ }
+
+ if ( $text['font']->background !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->background,
+ true
+ );
+ }
+
+ if ( $text['font']->border !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->border,
+ false,
+ $text['font']->borderWidth
+ );
+ }
+
+ // Render text with evaluated font size
+ $completeString = '';
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ $completeString .= $string;
+ $boundings = $this->getTextBoundings( $size, $text['font'], $string );
+ $text['position']->y += $size;
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x,
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( $text['width'] - $boundings->width ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ }
+
+ // Optionally draw text shadow
+ if ( $text['font']->textShadow === true )
+ {
+ $this->renderText(
+ $string,
+ $text['font']->name,
+ $text['font']->textShadowColor,
+ new ezcGraphCoordinate(
+ $position->x + $text['font']->textShadowOffset,
+ $position->y + $text['font']->textShadowOffset
+ ),
+ $size,
+ $text['rotation']
+ );
+ }
+
+ // Finally draw text
+ $this->renderText(
+ $string,
+ $text['font']->name,
+ $text['font']->color,
+ $position,
+ $size,
+ $text['rotation']
+ );
+
+ $text['position']->y += $size * $this->options->lineSpacing;
+ }
+ }
+ }
+
+ /**
+ * Draws a sector of cirlce
+ *
+ * @param ezcGraphCoordinate $center Center of circle
+ * @param mixed $width Width
+ * @param mixed $height Height
+ * @param mixed $startAngle Start angle of circle sector
+ * @param mixed $endAngle End angle of circle sector
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled;
+ * @return void
+ */
+ public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ $this->initiliazeSurface();
+
+ // Normalize angles
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ cairo_save( $this->context );
+
+ // Draw circular arc path
+ $path = cairo_new_path( $this->context );
+ cairo_translate( $this->context,
+ $center->x,
+ $center->y
+ );
+ cairo_scale( $this->context,
+ 1, $height / $width
+ );
+
+ cairo_move_to( $this->context, 0, 0 );
+ cairo_arc( $this->context,
+ 0., 0.,
+ $width / 2,
+ deg2rad( $startAngle ),
+ deg2rad( $endAngle )
+ );
+ cairo_line_to( $this->context, 0, 0 );
+
+ cairo_restore( $this->context );
+ $this->getStyle( $color, $filled );
+ cairo_stroke( $this->context );
+
+ // Create polygon array to return
+ $polygonArray = array( $center );
+ for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
+ );
+
+ return $polygonArray;
+ }
+
+ /**
+ * Draws a circular arc consisting of several minor steps on the bounding
+ * lines.
+ *
+ * @param ezcGraphCoordinate $center
+ * @param mixed $width
+ * @param mixed $height
+ * @param mixed $size
+ * @param mixed $startAngle
+ * @param mixed $endAngle
+ * @param ezcGraphColor $color
+ * @param bool $filled
+ * @return string Element id
+ */
+ protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled )
+ {
+ for (
+ $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle );
+ $tmpAngle <= $endAngle;
+ $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) )
+ {
+ $path = cairo_new_path( $this->context );
+ cairo_move_to( $this->context,
+ $center->x + cos( deg2rad( $startAngle ) ) * $width / 2,
+ $center->y + sin( deg2rad( $startAngle ) ) * $height / 2
+ );
+
+ // @TODO: Use cairo_curve_to()
+ for(
+ $angle = $startAngle;
+ $angle <= $tmpAngle;
+ $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) )
+ {
+ cairo_line_to( $this->context,
+ $center->x + cos( deg2rad( $angle ) ) * $width / 2,
+ $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size
+ );
+
+ if ( $angle === $tmpAngle )
+ {
+ break;
+ }
+ }
+
+ for(
+ $angle = $tmpAngle;
+ $angle >= $startAngle;
+ $angle = max( $angle - $this->options->circleResolution, $startAngle ) )
+ {
+ cairo_line_to( $this->context,
+ $center->x + cos( deg2rad( $angle ) ) * $width / 2,
+ $center->y + sin( deg2rad( $angle ) ) * $height / 2
+ );
+
+ if ( $angle === $startAngle )
+ {
+ break;
+ }
+ }
+
+ cairo_close_path( $this->context );
+ $this->getStyle( $color, $filled );
+ cairo_stroke( $this->context );
+
+ $startAngle = $tmpAngle;
+ if ( $tmpAngle === $endAngle )
+ {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Draws a circular arc
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param integer $width Width of ellipse
+ * @param integer $height Height of ellipse
+ * @param integer $size Height of border
+ * @param float $startAngle Starting angle of circle sector
+ * @param float $endAngle Ending angle of circle sector
+ * @param ezcGraphColor $color Color of Border
+ * @param bool $filled
+ * @return void
+ */
+ public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ $this->initiliazeSurface();
+
+ // Normalize angles
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled );
+
+ if ( ( $this->options->shadeCircularArc !== false ) &&
+ $filled )
+ {
+ $gradient = new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $center->x - $width,
+ $center->y
+ ),
+ new ezcGraphCoordinate(
+ $center->x + $width,
+ $center->y
+ ),
+ ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ),
+ ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 )
+ );
+
+ $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled );
+ }
+
+ // Create polygon array to return
+ $polygonArray = array();
+ for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
+ );
+
+ for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size,
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size,
+ $center->y +
+ ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 )
+ );
+
+ return $polygonArray;
+ }
+
+ /**
+ * Draw circle
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param mixed $width Width of ellipse
+ * @param mixed $height height of ellipse
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
+ {
+ $this->initiliazeSurface();
+
+ cairo_save( $this->context );
+
+ // Draw circular arc path
+ $path = cairo_new_path( $this->context );
+ cairo_translate( $this->context,
+ $center->x,
+ $center->y
+ );
+ cairo_scale( $this->context,
+ 1, $height / $width
+ );
+
+ cairo_arc( $this->context,
+ 0., 0.,
+ $width / 2,
+ 0, 2 * M_PI
+ );
+
+ cairo_restore( $this->context );
+ $this->getStyle( $color, $filled );
+ cairo_stroke( $this->context );
+
+ // Create polygon array to return
+ $polygonArray = array();
+ for ( $angle = 0; $angle < ( 2 * M_PI ); $angle += deg2rad( $this->options->imageMapResolution ) )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( $angle ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( $angle ) * $height ) / 2 )
+ );
+ }
+
+ return $polygonArray;
+ }
+
+ /**
+ * Draw an image
+ *
+ * The image will be inlined in the SVG document using data URL scheme. For
+ * this the mime type and base64 encoded file content will be merged to
+ * URL.
+ *
+ * @param mixed $file Image file
+ * @param ezcGraphCoordinate $position Top left position
+ * @param mixed $width Width of image in destination image
+ * @param mixed $height Height of image in destination image
+ * @return void
+ */
+ public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
+ {
+ $this->initiliazeSurface();
+
+ // Ensure given bitmap is a PNG image
+ $data = getimagesize( $file );
+ if ( $data[2] !== IMAGETYPE_PNG )
+ {
+ throw new Exception( 'Cairo only has support for PNGs.' );
+ }
+
+ // Create new surface from given bitmap
+ $imageSurface = cairo_image_surface_create_from_png( $file );
+
+ // Create pattern from source image to be able to transform it
+ $pattern = cairo_pattern_create_for_surface( $imageSurface );
+
+ // Scale pattern to defined dimensions and move it to its destination position
+ $matrix = cairo_matrix_multiply(
+ $move = cairo_matrix_create_translate( -$position->x, -$position->y ),
+ $scale = cairo_matrix_create_scale( $data[0] / $width, $data[1] / $height )
+ );
+ cairo_pattern_set_matrix( $pattern, $matrix );
+
+ // Merge surfaces
+ cairo_set_source( $this->context, $pattern );
+ cairo_rectangle( $this->context, $position->x, $position->y, $width, $height );
+ cairo_fill( $this->context );
+ }
+
+ /**
+ * Return mime type for current image format
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return 'image/png';
+ }
+
+ /**
+ * Render image directly to output
+ *
+ * The method renders the image directly to the standard output. You
+ * normally do not want to use this function, because it makes it harder
+ * to proper cache the generated graphs.
+ *
+ * @return void
+ */
+ public function renderToOutput()
+ {
+ $this->drawAllTexts();
+
+ header( 'Content-Type: ' . $this->getMimeType() );
+
+ // Write to tmp file, echo and remove tmp file again.
+ $fileName = tempnam( '/tmp', 'ezc' );
+
+ // cairo_surface_write_to_png( $this->surface, $file );
+ cairo_surface_write_to_png( $this->surface, $fileName );
+ $contents = file_get_contents( $fileName );
+ unlink( $fileName );
+
+ // Directly echo contents
+ echo $contents;
+ }
+
+ /**
+ * Finally save image
+ *
+ * @param string $file Destination filename
+ * @return void
+ */
+ public function render( $file )
+ {
+ $this->drawAllTexts();
+ cairo_surface_write_to_png( $this->surface, $file );
+ }
+
+ /**
+ * Get resource of rendered result
+ *
+ * Return the resource of the rendered result. You should not use this
+ * method before you called either renderToOutput() or render(), as the
+ * image may not be completely rendered until then.
+ *
+ * This method returns an array, containing the surface and the context in
+ * a structure like:
+ *
+ * array(
+ * 'surface' => resource,
+ * 'context' => resource,
+ * )
+ *
+ *
+ * @return array
+ */
+ public function getResource()
+ {
+ return array(
+ 'surface' => $this->surface,
+ 'context' => $this->context,
+ );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/driver/flash.php b/src/TUnit/external/ezc/Graph/driver/flash.php
new file mode 100644
index 0000000..9adde87
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/driver/flash.php
@@ -0,0 +1,972 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->driver = new ezcGraphFlashDriver();
+ * $graph->options->font = 'tutorial_font.fdb';
+ *
+ * $graph->driver->options->compression = 7;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->render( 400, 200, 'tutorial_driver_flash.swf' );
+ *
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphFlashDriver extends ezcGraphDriver
+{
+ /**
+ * Flash movie
+ *
+ * @var SWFMovie
+ */
+ protected $movie;
+
+ /**
+ * Unique element id
+ *
+ * @var int
+ */
+ protected $id = 1;
+
+ /**
+ * Array with strings to draw later
+ *
+ * @var array
+ */
+ protected $strings = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'ming' );
+ $this->options = new ezcGraphFlashDriverOptions( $options );
+ }
+
+ /**
+ * Returns unique movie object as a parent canvas for all swf objects.
+ *
+ * @return SWFMovie
+ */
+ public function getDocument()
+ {
+ if ( $this->movie === null )
+ {
+ ming_setscale( 1.0 );
+ $this->movie = new SWFMovie();
+ $this->movie->setDimension( $this->modifyCoordinate( $this->options->width ), $this->modifyCoordinate( $this->options->height ) );
+ $this->movie->setRate( 1 );
+ $this->movie->setBackground( 255, 255, 255 );
+ }
+
+ return $this->movie;
+ }
+
+ /**
+ * Set the fill and line properties for a SWWFShape according to the
+ * given parameters.
+ *
+ * @param SWFShape $shape
+ * @param ezcGraphColor $color
+ * @param mixed $thickness
+ * @param mixed $filled
+ * @return void
+ */
+ protected function setShapeColor( SWFShape $shape, ezcGraphColor $color, $thickness, $filled )
+ {
+ if ( $filled )
+ {
+ switch ( true )
+ {
+ case ( $color instanceof ezcGraphLinearGradient ):
+ $gradient = new SWFGradient();
+ $gradient->addEntry(
+ 0,
+ $color->startColor->red,
+ $color->startColor->green,
+ $color->startColor->blue,
+ 255 - $color->startColor->alpha
+ );
+ $gradient->addEntry(
+ 1,
+ $color->endColor->red,
+ $color->endColor->green,
+ $color->endColor->blue,
+ 255 - $color->endColor->alpha
+ );
+
+ $fill = $shape->addFill( $gradient, SWFFILL_LINEAR_GRADIENT );
+
+ // Calculate desired length of gradient
+ $length = sqrt(
+ pow( $color->endPoint->x - $color->startPoint->x, 2 ) +
+ pow( $color->endPoint->y - $color->startPoint->y, 2 )
+ );
+
+ $fill->scaleTo( $this->modifyCoordinate( $length ) / 32768 , $this->modifyCoordinate( $length ) / 32768 );
+ $fill->rotateTo(
+ rad2deg( asin(
+ ( $color->endPoint->x - $color->startPoint->x ) / $length
+ ) + 180 )
+ );
+ $fill->moveTo(
+ $this->modifyCoordinate(
+ ( $color->startPoint->x + $color->endPoint->x ) / 2
+ ),
+ $this->modifyCoordinate(
+ ( $color->startPoint->y + $color->endPoint->y ) / 2
+ )
+ );
+
+ $shape->setLeftFill( $fill );
+ break;
+ case ( $color instanceof ezcGraphRadialGradient ):
+ $gradient = new SWFGradient();
+ $gradient->addEntry(
+ 0,
+ $color->startColor->red,
+ $color->startColor->green,
+ $color->startColor->blue,
+ 255 - $color->startColor->alpha
+ );
+ $gradient->addEntry(
+ 1,
+ $color->endColor->red,
+ $color->endColor->green,
+ $color->endColor->blue,
+ 255 - $color->endColor->alpha
+ );
+
+ $fill = $shape->addFill( $gradient, SWFFILL_RADIAL_GRADIENT );
+
+ $fill->scaleTo( $this->modifyCoordinate( $color->width ) / 32768, $this->modifyCoordinate( $color->height ) / 32768 );
+ $fill->moveTo( $this->modifyCoordinate( $color->center->x ), $this->modifyCoordinate( $color->center->y ) );
+
+ $shape->setLeftFill( $fill );
+ break;
+ default:
+ $fill = $shape->addFill( $color->red, $color->green, $color->blue, 255 - $color->alpha );
+ $shape->setLeftFill( $fill );
+ break;
+ }
+ }
+ else
+ {
+ $shape->setLine( $this->modifyCoordinate( $thickness ), $color->red, $color->green, $color->blue, 255 - $color->alpha );
+ }
+ }
+
+ /**
+ * Modifies a coordinate value, as flash usally uses twips instead of
+ * pixels for a higher solution, as it only accepts integer values.
+ *
+ * @param float $pointValue
+ * @return float
+ */
+ protected function modifyCoordinate( $pointValue )
+ {
+ return $pointValue * 10;
+ }
+
+ /**
+ * Demodifies a coordinate value, as flash usally uses twips instead of
+ * pixels for a higher solution, as it only accepts integer values.
+ *
+ * @param float $pointValue
+ * @return float
+ */
+ protected function deModifyCoordinate( $pointValue )
+ {
+ return $pointValue / 10;
+ }
+
+ /**
+ * Draws a single polygon.
+ *
+ * @param array $points Point array
+ * @param ezcGraphColor $color Polygon color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
+ {
+ $movie = $this->getDocument();
+
+ if ( !$filled )
+ {
+ // The middle of the border is on the outline of a polygon in ming,
+ // fix that:
+ try
+ {
+ $points = $this->reducePolygonSize( $points, $thickness / 2 );
+ }
+ catch ( ezcGraphReducementFailedException $e )
+ {
+ return false;
+ }
+ }
+
+ $shape = new SWFShape();
+
+ $this->setShapeColor( $shape, $color, $thickness, $filled );
+
+ $lastPoint = end( $points );
+ $shape->movePenTo( $this->modifyCoordinate( $lastPoint->x ), $this->modifyCoordinate( $lastPoint->y ) );
+
+ foreach ( $points as $point )
+ {
+ $shape->drawLineTo( $this->modifyCoordinate( $point->x ), $this->modifyCoordinate( $point->y ) );
+ }
+
+ $object = $movie->add( $shape );
+ $object->setName( $id = 'ezcGraphPolygon_' . $this->id++ );
+
+ return $id;
+ }
+
+ /**
+ * Draws a line
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Line color
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
+ {
+ $movie = $this->getDocument();
+
+ $shape = new SWFShape();
+
+ $this->setShapeColor( $shape, $color, $thickness, false );
+
+ $shape->movePenTo( $this->modifyCoordinate( $start->x ), $this->modifyCoordinate( $start->y ) );
+ $shape->drawLineTo( $this->modifyCoordinate( $end->x ), $this->modifyCoordinate( $end->y ) );
+
+ $object = $movie->add( $shape );
+ $object->setName( $id = 'ezcGraphLine_' . $this->id++ );
+
+ return $id;
+ }
+
+ /**
+ * Returns boundings of text depending on the available font extension
+ *
+ * @param float $size Textsize
+ * @param ezcGraphFontOptions $font Font
+ * @param string $text Text
+ * @return ezcGraphBoundings Boundings of text
+ */
+ protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
+ {
+ $t = new SWFText();
+ $t->setFont( new SWFFont( $font->path ) );
+ $t->setHeight( $size );
+
+ $boundings = new ezcGraphBoundings( 0, 0, $t->getWidth( $text ), $size );
+
+ return $boundings;
+ }
+
+ /**
+ * Writes text in a box of desired size
+ *
+ * @param string $string Text
+ * @param ezcGraphCoordinate $position Top left position
+ * @param float $width Width of text box
+ * @param float $height Height of text box
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
+ {
+ $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 );
+
+ $width = $this->modifyCoordinate( $width - $padding * 2 );
+ $height = $this->modifyCoordinate( $height - $padding * 2 );
+ $position = new ezcGraphCoordinate(
+ $this->modifyCoordinate( $position->x + $padding ),
+ $this->modifyCoordinate( $position->y + $padding )
+ );
+
+ // Try to get a font size for the text to fit into the box
+ $maxSize = $this->modifyCoordinate( min( $height, $this->options->font->maxFontSize ) );
+ $minSize = $this->modifyCoordinate( $this->options->font->minFontSize );
+ $result = false;
+ for ( $size = $maxSize; $size >= $minSize; )
+ {
+ $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size );
+ if ( is_array( $result ) )
+ {
+ break;
+ }
+ $size = $this->deModifyCoordinate( $size );
+ $size = $this->modifyCoordinate( floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize ) );
+ }
+
+ if ( !is_array( $result ) )
+ {
+ if ( ( $height >= $this->options->font->minFontSize ) &&
+ ( $this->options->autoShortenString ) )
+ {
+ $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->modifyCoordinate( $this->options->font->minFontSize ) );
+ }
+ else
+ {
+ throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height );
+ }
+ }
+
+
+ $this->options->font->minimalUsedFont = $this->deModifyCoordinate( $size );
+
+ $this->strings[] = array(
+ 'text' => $result,
+ 'id' => $id = 'ezcGraphTextBox_' . $this->id++,
+ 'position' => $position,
+ 'width' => $width,
+ 'height' => $height,
+ 'align' => $align,
+ 'font' => $this->options->font,
+ 'rotation' => $rotation,
+ );
+
+ return $id;
+ }
+
+ /**
+ * Render text depending of font type and available font extensions
+ *
+ * @param string $id
+ * @param string $text
+ * @param string $chars
+ * @param int $type
+ * @param string $path
+ * @param ezcGraphColor $color
+ * @param ezcGraphCoordinate $position
+ * @param float $size
+ * @param float $rotation
+ * @return void
+ */
+ protected function renderText( $id, $text, $chars, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null )
+ {
+ $movie = $this->getDocument();
+
+ $tb = new SWFTextField( SWFTEXTFIELD_NOEDIT );
+ $tb->setFont( new SWFFont( $path ) );
+ $tb->setHeight( $size );
+ $tb->setColor( $color->red, $color->green, $color->blue, 255 - $color->alpha );
+ $tb->addString( $text );
+ $tb->addChars( $chars );
+
+ $object = $movie->add( $tb );
+ $object->rotate(
+ ( $rotation !== null ? -$rotation->getRotation() : 0 )
+ );
+ $object->moveTo(
+ $position->x +
+ ( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 0, 2 ) ) ),
+ $position->y -
+ $size * ( 1 + $this->options->lineSpacing ) +
+ ( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 1, 2 ) ) )
+ );
+ $object->setName( $id );
+ }
+
+ /**
+ * Draw all collected texts
+ *
+ * The texts are collected and their maximum possible font size is
+ * calculated. This function finally draws the texts on the image, this
+ * delayed drawing has two reasons:
+ *
+ * 1) This way the text strings are always on top of the image, what
+ * results in better readable texts
+ * 2) The maximum possible font size can be calculated for a set of texts
+ * with the same font configuration. Strings belonging to one chart
+ * element normally have the same font configuration, so that all texts
+ * belonging to one element will have the same font size.
+ *
+ * @access protected
+ * @return void
+ */
+ protected function drawAllTexts()
+ {
+ // Iterate over all strings to collect used chars per font
+ $chars = array();
+ foreach ( $this->strings as $text )
+ {
+ $completeString = '';
+ foreach ( $text['text'] as $line )
+ {
+ $completeString .= implode( ' ', $line );
+ }
+
+ // Collect chars for each font
+ if ( !isset( $chars[$text['font']->path] ) )
+ {
+ $chars[$text['font']->path] = $completeString;
+ }
+ else
+ {
+ $chars[$text['font']->path] .= $completeString;
+ }
+ }
+
+ foreach ( $this->strings as $text )
+ {
+ $size = $this->modifyCoordinate( $text['font']->minimalUsedFont );
+
+ $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing;
+
+ // Calculate y offset for vertical alignement
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::BOTTOM ):
+ $yOffset = $text['height'] - $completeHeight;
+ break;
+ case ( $text['align'] & ezcGraph::MIDDLE ):
+ $yOffset = ( $text['height'] - $completeHeight ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::TOP ):
+ default:
+ $yOffset = 0;
+ break;
+ }
+
+ $padding = $text['font']->padding + $text['font']->borderWidth / 2;
+ if ( $this->options->font->minimizeBorder === true )
+ {
+ // Calculate maximum width of text rows
+ $width = false;
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ $boundings = $this->getTextBoundings( $size, $text['font'], $string );
+ if ( ( $width === false) || ( $boundings->width > $width ) )
+ {
+ $width = $boundings->width;
+ }
+ }
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $xOffset = 0;
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $xOffset = ( $text['width'] - $width ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $xOffset = $text['width'] - $width;
+ break;
+ }
+
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ),
+ $this->deModifyCoordinate( $text['position']->y - $padding + $yOffset )
+ ),
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ),
+ $this->deModifyCoordinate( $text['position']->y - $padding + $yOffset )
+ ),
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ),
+ $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight )
+ ),
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ),
+ $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight )
+ ),
+ );
+ }
+ else
+ {
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x - $padding ),
+ $this->deModifyCoordinate( $text['position']->y - $padding )
+ ),
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ),
+ $this->deModifyCoordinate( $text['position']->y - $padding )
+ ),
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ),
+ $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] )
+ ),
+ new ezcGraphCoordinate(
+ $this->deModifyCoordinate( $text['position']->x - $padding ),
+ $this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] )
+ ),
+ );
+ }
+
+ if ( $text['rotation'] !== null )
+ {
+ foreach ( $borderPolygonArray as $nr => $point )
+ {
+ $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point );
+ }
+ }
+
+ if ( $text['font']->background !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->background,
+ true
+ );
+ }
+
+ if ( $text['font']->border !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->border,
+ false,
+ $text['font']->borderWidth
+ );
+ }
+
+ // Render text with evaluated font size
+ $completeString = '';
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ $completeString .= $string;
+ $boundings = $this->getTextBoundings( $size, $text['font'], $string );
+ $text['position']->y += $size;
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x,
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( $text['width'] - $boundings->width ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ }
+
+ // Optionally draw text shadow
+ if ( $text['font']->textShadow === true )
+ {
+ $this->renderText(
+ $text['id'],
+ $string,
+ $chars[$text['font']->path],
+ $text['font']->type,
+ $text['font']->path,
+ $text['font']->textShadowColor,
+ new ezcGraphCoordinate(
+ $position->x + $this->modifyCoordinate( $text['font']->textShadowOffset ),
+ $position->y + $this->modifyCoordinate( $text['font']->textShadowOffset )
+ ),
+ $size,
+ $text['rotation']
+ );
+ }
+
+ // Finally draw text
+ $this->renderText(
+ $text['id'],
+ $string,
+ $chars[$text['font']->path],
+ $text['font']->type,
+ $text['font']->path,
+ $text['font']->color,
+ $position,
+ $size,
+ $text['rotation']
+ );
+
+ $text['position']->y += $size * $this->options->lineSpacing;
+ }
+ }
+ }
+
+ /**
+ * Draws a sector of cirlce
+ *
+ * @param ezcGraphCoordinate $center Center of circle
+ * @param mixed $width Width
+ * @param mixed $height Height
+ * @param mixed $startAngle Start angle of circle sector
+ * @param mixed $endAngle End angle of circle sector
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ $movie = $this->getDocument();
+
+ $shape = new SWFShape();
+ $this->setShapeColor( $shape, $color, 1, $filled );
+
+ if ( !$filled )
+ {
+ try
+ {
+ $reduced = $this->reduceEllipseSize( $center, $width, $height, $startAngle, $endAngle, .5 );
+ }
+ catch ( ezcGraphReducementFailedException $e )
+ {
+ return false;
+ }
+
+ $startAngle = $reduced['startAngle'];
+ $endAngle = $reduced['endAngle'];
+
+ $width -= 1;
+ $height -= 1;
+ }
+
+ $shape->movePenTo( $this->modifyCoordinate( $center->x ), $this->modifyCoordinate( $center->y ) );
+
+ // @TODO: User SWFShape::curveTo
+ for(
+ $angle = $startAngle;
+ $angle <= $endAngle;
+ $angle = min( $angle + $this->options->circleResolution, $endAngle ) )
+ {
+ $shape->drawLineTo(
+ $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
+ $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 )
+ );
+
+ if ( $angle === $endAngle )
+ {
+ break;
+ }
+ }
+
+ $shape->drawLineTo(
+ $this->modifyCoordinate( $center->x ),
+ $this->modifyCoordinate( $center->y )
+ );
+
+ $object = $movie->add( $shape );
+ $object->setName( $id = 'ezcGraphCircleSector_' . $this->id++ );
+
+ return $id;
+ }
+
+ /**
+ * Draws a circular arc consisting of several minor steps on the bounding
+ * lines.
+ *
+ * @param ezcGraphCoordinate $center
+ * @param mixed $width
+ * @param mixed $height
+ * @param mixed $size
+ * @param mixed $startAngle
+ * @param mixed $endAngle
+ * @param ezcGraphColor $color
+ * @param bool $filled
+ * @return string Element id
+ */
+ protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled )
+ {
+ $movie = $this->getDocument();
+ $id = 'ezcGraphCircularArc_' . $this->id++;
+
+ for (
+ $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle );
+ $tmpAngle <= $endAngle;
+ $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) )
+ {
+ $shape = new SWFShape();
+ $this->setShapeColor( $shape, $color, 1, $filled );
+
+ $shape->movePenTo(
+ $this->modifyCoordinate( $center->x + cos( deg2rad( $startAngle ) ) * $width / 2 ),
+ $this->modifyCoordinate( $center->y + sin( deg2rad( $startAngle ) ) * $height / 2 )
+ );
+
+ // @TODO: Use SWFShape::curveTo
+ for(
+ $angle = $startAngle;
+ $angle <= $tmpAngle;
+ $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) )
+ {
+ $shape->drawLineTo(
+ $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
+ $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size )
+ );
+
+ if ( $angle === $tmpAngle )
+ {
+ break;
+ }
+ }
+
+ for(
+ $angle = $tmpAngle;
+ $angle >= $startAngle;
+ $angle = max( $angle - $this->options->circleResolution, $startAngle ) )
+ {
+ $shape->drawLineTo(
+ $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
+ $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 )
+ );
+
+ if ( $angle === $startAngle )
+ {
+ break;
+ }
+ }
+
+ $object = $movie->add( $shape );
+ $object->setName( $id );
+
+ $startAngle = $tmpAngle;
+ if ( $tmpAngle === $endAngle )
+ {
+ break;
+ }
+ }
+
+ return $id;
+ }
+
+ /**
+ * Draws a circular arc
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param integer $width Width of ellipse
+ * @param integer $height Height of ellipse
+ * @param integer $size Height of border
+ * @param float $startAngle Starting angle of circle sector
+ * @param float $endAngle Ending angle of circle sector
+ * @param ezcGraphColor $color Color of Border
+ * @param bool $filled
+ * @return void
+ */
+ public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ $id = $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled );
+
+ if ( ( $this->options->shadeCircularArc !== false ) &&
+ $filled )
+ {
+ $gradient = new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $center->x - $width,
+ $center->y
+ ),
+ new ezcGraphCoordinate(
+ $center->x + $width,
+ $center->y
+ ),
+ ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ),
+ ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 )
+ );
+
+ $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled );
+ }
+
+ return $id;
+ }
+
+ /**
+ * Draw circle
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param mixed $width Width of ellipse
+ * @param mixed $height height of ellipse
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
+ {
+ $movie = $this->getDocument();
+
+ $shape = new SWFShape();
+ $this->setShapeColor( $shape, $color, 1, $filled );
+
+ // Reduce size
+ if ( !$filled )
+ {
+ $width -= 1;
+ $height -= 1;
+ }
+
+ $shape->movePenTo(
+ $this->modifyCoordinate( $center->x + $width / 2 ),
+ $this->modifyCoordinate( $center->y )
+ );
+
+ // @TODO: User SWFShape::curveTo
+ for ( $angle = $this->options->circleResolution; $angle < 360; $angle += $this->options->circleResolution )
+ {
+ $shape->drawLineTo(
+ $this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
+ $this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 )
+ );
+ }
+
+ $shape->drawLineTo(
+ $this->modifyCoordinate( $center->x + $width / 2 ),
+ $this->modifyCoordinate( $center->y )
+ );
+
+ $object = $movie->add( $shape );
+ $object->setName( $id = 'ezcGraphCircle_' . $this->id++ );
+
+ return $id;
+ }
+
+ /**
+ * Draw an image
+ *
+ * The image will be inlined in the SVG document using data URL scheme. For
+ * this the mime type and base64 encoded file content will be merged to
+ * URL.
+ *
+ * @param mixed $file Image file
+ * @param ezcGraphCoordinate $position Top left position
+ * @param float $width Width of image in destination image
+ * @param float $height Height of image in destination image
+ * @return void
+ */
+ public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
+ {
+ $movie = $this->getDocument();
+
+ $imageData = getimagesize( $file );
+ if ( ( $imageData[2] !== IMAGETYPE_JPEG ) && ( $imageData[2] !== IMAGETYPE_PNG ) )
+ {
+ throw new ezcGraphFlashBitmapTypeException( $imageData[2] );
+ }
+
+ // Try to create a new SWFBitmap object from provided file
+ $bitmap = new SWFBitmap( fopen( $file, 'rb' ) );
+
+ // Add the image to the movie
+ $object = $this->movie->add( $bitmap );
+
+ // Image size is calculated on the base of a tick size of 20, so
+ // that we need to transform this, to our tick size.
+ $factor = $this->modifyCoordinate( 1 ) / 20;
+ $object->scale( $factor, $factor );
+
+ // Scale by ratio of requested and original image size
+ $object->scale(
+ $width / $imageData[0],
+ $height / $imageData[1]
+ );
+
+ // Move object to the right position
+ $object->moveTo(
+ $this->modifyCoordinate( $position->x ),
+ $this->modifyCoordinate( $position->y )
+ );
+
+ // Create, set and return unique ID
+ $object->setName( $id = 'ezcGraphImage_'. $this->id++ );
+ return $id;
+ }
+
+ /**
+ * Return mime type for current image format
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return 'application/x-shockwave-flash';
+ }
+
+ /**
+ * Finally save image
+ *
+ * @param string $file Destination filename
+ * @return void
+ */
+ public function render( $file )
+ {
+ $this->drawAllTexts();
+ $movie = $this->getDocument();
+ $movie->save( $file, $this->options->compression );
+ }
+
+ /**
+ * Get resource of rendered result
+ *
+ * Return the resource of the rendered result. You should not use this
+ * method before you called either renderToOutput() or render(), as the
+ * image may not be completely rendered until then.
+ *
+ * @return SWFMovie
+ */
+ public function getResource()
+ {
+ return $this->movie;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/driver/gd.php b/src/TUnit/external/ezc/Graph/driver/gd.php
new file mode 100644
index 0000000..b1cca35
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/driver/gd.php
@@ -0,0 +1,1236 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzGreen();
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->driver = new ezcGraphGdDriver();
+ * $graph->options->font = 'tutorial_font.ttf';
+ *
+ * // Generate a Jpeg with lower quality. The default settings result in a image
+ * // with better quality.
+ * //
+ * // The reduction of the supersampling to 1 will result in no anti aliasing of
+ * // the image. JPEG is not the optimal format for grapics, PNG is far better for
+ * // this kind of images.
+ * $graph->driver->options->supersampling = 1;
+ * $graph->driver->options->jpegQuality = 100;
+ * $graph->driver->options->imageFormat = IMG_JPEG;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->render( 400, 200, 'tutorial_dirver_gd.jpg' );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphGdDriver extends ezcGraphDriver
+{
+
+ /**
+ * Image resource
+ *
+ * @var resource
+ */
+ protected $image;
+
+ /**
+ * Array with image files to draw
+ *
+ * @var array
+ */
+ protected $preProcessImages = array();
+
+ /**
+ * List of strings to draw
+ * array ( array(
+ * 'text' => array( 'strings' ),
+ * 'options' => ezcGraphFontOptions,
+ * )
+ *
+ * @var array
+ */
+ protected $strings = array();
+
+ /**
+ * Contains resources for already loaded ps fonts.
+ * array(
+ * path => resource
+ * )
+ *
+ * @var array
+ */
+ protected $psFontResources = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'gd' );
+ $this->options = new ezcGraphGdDriverOptions( $options );
+ }
+
+ /**
+ * Returns the image resource to draw on.
+ *
+ * If no resource exists the image will be created. The size of the
+ * returned image depends on the supersampling factor and the size of the
+ * chart.
+ *
+ * @return resource
+ */
+ protected function getImage()
+ {
+ if ( !isset( $this->image ) )
+ {
+ $this->image = imagecreatetruecolor(
+ $this->supersample( $this->options->width ),
+ $this->supersample( $this->options->height )
+ );
+
+ // Default to a transparent white background
+ $bgColor = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 );
+ imagealphablending( $this->image, true );
+ imagesavealpha( $this->image, true );
+ imagefill( $this->image, 1, 1, $bgColor );
+
+ imagesetthickness(
+ $this->image,
+ $this->options->supersampling
+ );
+ }
+
+ return $this->image;
+ }
+
+ /**
+ * Allocates a color
+ *
+ * This function tries to allocate the requested color. If the color
+ * already exists in the imaga it will be reused.
+ *
+ * @param ezcGraphColor $color
+ * @return int Color index
+ */
+ protected function allocate( ezcGraphColor $color )
+ {
+ $image = $this->getImage();
+
+ if ( $color->alpha > 0 )
+ {
+ $fetched = imagecolorexactalpha( $image, $color->red, $color->green, $color->blue, $color->alpha / 2 );
+ if ( $fetched < 0 )
+ {
+ $fetched = imagecolorallocatealpha( $image, $color->red, $color->green, $color->blue, $color->alpha / 2 );
+ }
+ return $fetched;
+ }
+ else
+ {
+ $fetched = imagecolorexact( $image, $color->red, $color->green, $color->blue );
+ if ( $fetched < 0 )
+ {
+ $fetched = imagecolorallocate( $image, $color->red, $color->green, $color->blue );
+ }
+ return $fetched;
+ }
+ }
+
+ /**
+ * Creates an image resource from an image file
+ *
+ * @param string $file Filename
+ * @return resource Image
+ */
+ protected function imageCreateFrom( $file )
+ {
+ $data = getimagesize( $file );
+
+ switch ( $data[2] )
+ {
+ case 1:
+ return array(
+ 'width' => $data[0],
+ 'height' => $data[1],
+ 'image' => imagecreatefromgif( $file )
+ );
+ case 2:
+ return array(
+ 'width' => $data[0],
+ 'height' => $data[1],
+ 'image' => imagecreatefromjpeg( $file )
+ );
+ case 3:
+ return array(
+ 'width' => $data[0],
+ 'height' => $data[1],
+ 'image' => imagecreatefrompng( $file )
+ );
+ default:
+ throw new ezcGraphGdDriverUnsupportedImageTypeException( $data[2] );
+ }
+ }
+
+ /**
+ * Supersamples a single coordinate value.
+ *
+ * Applies supersampling to a single coordinate value.
+ *
+ * @param float $value Coordinate value
+ * @return float Supersampled coordinate value
+ */
+ protected function supersample( $value )
+ {
+ $mod = (int) floor( $this->options->supersampling / 2 );
+ return $value * $this->options->supersampling - $mod;
+ }
+
+ /**
+ * Draws a single polygon.
+ *
+ * @param array $points Point array
+ * @param ezcGraphColor $color Polygon color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
+ {
+ $image = $this->getImage();
+
+ $drawColor = $this->allocate( $color );
+
+ // Create point array
+ $pointCount = count( $points );
+ $pointArray = array();
+ for ( $i = 0; $i < $pointCount; ++$i )
+ {
+ $pointArray[] = $this->supersample( $points[$i]->x );
+ $pointArray[] = $this->supersample( $points[$i]->y );
+ }
+
+ // Draw polygon
+ if ( $filled )
+ {
+ imagefilledpolygon( $image, $pointArray, $pointCount, $drawColor );
+ }
+ else
+ {
+ imagepolygon( $image, $pointArray, $pointCount, $drawColor );
+ }
+
+ return $points;
+ }
+
+ /**
+ * Draws a line
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Line color
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
+ {
+ $image = $this->getImage();
+
+ $drawColor = $this->allocate( $color );
+
+ imagesetthickness(
+ $this->image,
+ $this->options->supersampling * $thickness
+ );
+
+ imageline(
+ $image,
+ $this->supersample( $start->x ),
+ $this->supersample( $start->y ),
+ $this->supersample( $end->x ),
+ $this->supersample( $end->y ),
+ $drawColor
+ );
+
+ imagesetthickness(
+ $this->image,
+ $this->options->supersampling
+ );
+
+ return array();
+ }
+
+ /**
+ * Returns boundings of text depending on the available font extension
+ *
+ * @param float $size Textsize
+ * @param ezcGraphFontOptions $font Font
+ * @param string $text Text
+ * @return ezcGraphBoundings Boundings of text
+ */
+ protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
+ {
+ switch ( $font->type )
+ {
+ case ezcGraph::PS_FONT:
+ if ( !isset( $this->psFontResources[$font->path] ) )
+ {
+ $this->psFontResources[$font->path] = imagePsLoadFont( $font->path );
+ }
+
+ $boundings = imagePsBBox( $text, $this->psFontResources[$font->path], $size );
+ return new ezcGraphBoundings(
+ $boundings[0],
+ $boundings[1],
+ $boundings[2],
+ $boundings[3]
+ );
+ case ezcGraph::TTF_FONT:
+ switch ( true )
+ {
+ case ezcBaseFeatures::hasFunction( 'imageftbbox' ) && !$this->options->forceNativeTTF:
+ $boundings = imageFtBBox( $size, 0, $font->path, $text );
+ return new ezcGraphBoundings(
+ $boundings[0],
+ $boundings[1],
+ $boundings[4],
+ $boundings[5]
+ );
+ case ezcBaseFeatures::hasFunction( 'imagettfbbox' ):
+ $boundings = imageTtfBBox( $size, 0, $font->path, $text );
+ return new ezcGraphBoundings(
+ $boundings[0],
+ $boundings[1],
+ $boundings[4],
+ $boundings[5]
+ );
+ }
+ break;
+ }
+ }
+
+ /**
+ * Render text depending of font type and available font extensions
+ *
+ * @param resource $image Image resource
+ * @param string $text Text
+ * @param int $type Font type
+ * @param string $path Font path
+ * @param ezcGraphColor $color Font color
+ * @param ezcGraphCoordinate $position Position
+ * @param float $size Textsize
+ * @param ezcGraphRotation $rotation
+ *
+ * @return void
+ */
+ protected function renderText( $image, $text, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, ezcGraphRotation $rotation = null )
+ {
+ if ( $rotation !== null )
+ {
+ // Rotation is relative to top left point of text and not relative
+ // to the bounding coordinate system
+ $rotation = new ezcGraphRotation(
+ $rotation->getRotation(),
+ new ezcGraphCoordinate(
+ $rotation->getCenter()->x - $position->x,
+ $rotation->getCenter()->y - $position->y
+ )
+ );
+ }
+
+ switch ( $type )
+ {
+ case ezcGraph::PS_FONT:
+ imagePsText(
+ $image,
+ $text,
+ $this->psFontResources[$path],
+ $size,
+ $this->allocate( $color ),
+ 1,
+ $position->x +
+ ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ),
+ $position->y +
+ ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ),
+ 0,
+ 0,
+ ( $rotation === null ? 0 : -$rotation->getRotation() ),
+ 4
+ );
+ break;
+ case ezcGraph::TTF_FONT:
+ switch ( true )
+ {
+ case ezcBaseFeatures::hasFunction( 'imagefttext' ) && !$this->options->forceNativeTTF:
+ imageFtText(
+ $image,
+ $size,
+ ( $rotation === null ? 0 : -$rotation->getRotation() ),
+ $position->x +
+ ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ),
+ $position->y +
+ ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ),
+ $this->allocate( $color ),
+ $path,
+ $text
+ );
+ break;
+ case ezcBaseFeatures::hasFunction( 'imagettftext' ):
+ imageTtfText(
+ $image,
+ $size,
+ ( $rotation === null ? 0 : -$rotation->getRotation() ),
+ $position->x +
+ ( $rotation === null ? 0 : $rotation->get( 0, 2 ) ),
+ $position->y +
+ ( $rotation === null ? 0 : $rotation->get( 1, 2 ) ),
+ $this->allocate( $color ),
+ $path,
+ $text
+ );
+ break;
+ }
+ break;
+ }
+ }
+
+ /**
+ * Writes text in a box of desired size
+ *
+ * @param string $string Text
+ * @param ezcGraphCoordinate $position Top left position
+ * @param float $width Width of text box
+ * @param float $height Height of text box
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
+ {
+ $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 );
+
+ $width -= $padding * 2;
+ $height -= $padding * 2;
+ $position->x += $padding;
+ $position->y += $padding;
+
+ // Try to get a font size for the text to fit into the box
+ $maxSize = min( $height, $this->options->font->maxFontSize );
+ $result = false;
+ for ( $size = $maxSize; $size >= $this->options->font->minFontSize; --$size )
+ {
+ $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size );
+ if ( is_array( $result ) )
+ {
+ break;
+ }
+ $size = floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize );
+ }
+
+ if ( !is_array( $result ) )
+ {
+ if ( ( $height >= $this->options->font->minFontSize ) &&
+ ( $this->options->autoShortenString ) )
+ {
+ $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize );
+ }
+ else
+ {
+ throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height );
+ }
+ }
+
+ $this->options->font->minimalUsedFont = $size;
+
+ $this->strings[] = array(
+ 'text' => $result,
+ 'position' => $position,
+ 'width' => $width,
+ 'height' => $height,
+ 'align' => $align,
+ 'font' => $this->options->font,
+ 'rotation' => $rotation,
+ );
+
+ return array(
+ clone $position,
+ new ezcGraphCoordinate( $position->x + $width, $position->y ),
+ new ezcGraphCoordinate( $position->x + $width, $position->y + $height ),
+ new ezcGraphCoordinate( $position->x, $position->y + $height ),
+ );
+ }
+
+ /**
+ * Draw all collected texts
+ *
+ * The texts are collected and their maximum possible font size is
+ * calculated. This function finally draws the texts on the image, this
+ * delayed drawing has two reasons:
+ *
+ * 1) This way the text strings are always on top of the image, what
+ * results in better readable texts
+ * 2) The maximum possible font size can be calculated for a set of texts
+ * with the same font configuration. Strings belonging to one chart
+ * element normally have the same font configuration, so that all texts
+ * belonging to one element will have the same font size.
+ *
+ * @access protected
+ * @return void
+ */
+ protected function drawAllTexts()
+ {
+ $image = $this->getImage();
+
+ foreach ( $this->strings as $text )
+ {
+ $size = $text['font']->minimalUsedFont;
+
+ $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing;
+
+ // Calculate y offset for vertical alignement
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::BOTTOM ):
+ $yOffset = $text['height'] - $completeHeight;
+ break;
+ case ( $text['align'] & ezcGraph::MIDDLE ):
+ $yOffset = ( $text['height'] - $completeHeight ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::TOP ):
+ default:
+ $yOffset = 0;
+ break;
+ }
+
+ $padding = $text['font']->padding + $text['font']->borderWidth / 2;
+ if ( $this->options->font->minimizeBorder === true )
+ {
+ // Calculate maximum width of text rows
+ $width = false;
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ $boundings = $this->getTextBoundings( $size, $text['font'], $string );
+ if ( ( $width === false) || ( $boundings->width > $width ) )
+ {
+ $width = $boundings->width;
+ }
+ }
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $xOffset = 0;
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $xOffset = ( $text['width'] - $width ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $xOffset = $text['width'] - $width;
+ break;
+ }
+
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding + $xOffset,
+ $text['position']->y - $padding + $yOffset
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $xOffset + $width,
+ $text['position']->y - $padding + $yOffset
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $xOffset + $width,
+ $text['position']->y + $padding * 2 + $yOffset + $completeHeight
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding + $xOffset,
+ $text['position']->y + $padding * 2 + $yOffset + $completeHeight
+ ),
+ );
+ }
+ else
+ {
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding,
+ $text['position']->y - $padding
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $text['width'],
+ $text['position']->y - $padding
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $text['width'],
+ $text['position']->y + $padding * 2 + $text['height']
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding,
+ $text['position']->y + $padding * 2 + $text['height']
+ ),
+ );
+ }
+
+ if ( $text['rotation'] !== null )
+ {
+ foreach ( $borderPolygonArray as $nr => $point )
+ {
+ $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point );
+ }
+ }
+
+ if ( $text['font']->background !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->background,
+ true
+ );
+ }
+
+ if ( $text['font']->border !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->border,
+ false,
+ $text['font']->borderWidth
+ );
+ }
+
+ // Render text with evaluated font size
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ $boundings = $this->getTextBoundings( $size, $text['font'], $string );
+ $text['position']->y += $size;
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x,
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( $text['width'] - $boundings->width ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ }
+
+ // Calculate relative modification of rotation center point
+ if ( $text['rotation'] !== null )
+ {
+ $rotation = new ezcGraphRotation(
+ $text['rotation']->getRotation(),
+ new ezcGraphCoordinate(
+ $text['rotation']->getCenter()->x +
+ $position->x - $text['position']->x,
+ $text['rotation']->getCenter()->y +
+ $position->y - $text['position']->y
+ )
+ );
+ $rotation = $text['rotation'];
+ }
+ else
+ {
+ $rotation = null;
+ }
+
+ // Optionally draw text shadow
+ if ( $text['font']->textShadow === true )
+ {
+ $this->renderText(
+ $image,
+ $string,
+ $text['font']->type,
+ $text['font']->path,
+ $text['font']->textShadowColor,
+ new ezcGraphCoordinate(
+ $position->x + $text['font']->textShadowOffset,
+ $position->y + $text['font']->textShadowOffset
+ ),
+ $size,
+ $rotation
+ );
+ }
+
+ // Finally draw text
+ $this->renderText(
+ $image,
+ $string,
+ $text['font']->type,
+ $text['font']->path,
+ $text['font']->color,
+ $position,
+ $size,
+ $rotation
+ );
+
+ $text['position']->y += $size * $this->options->lineSpacing;
+ }
+ }
+ }
+
+ /**
+ * Draws a sector of cirlce
+ *
+ * @param ezcGraphCoordinate $center Center of circle
+ * @param mixed $width Width
+ * @param mixed $height Height
+ * @param mixed $startAngle Start angle of circle sector
+ * @param mixed $endAngle End angle of circle sector
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ $image = $this->getImage();
+ $drawColor = $this->allocate( $color );
+
+ // Normalize angles
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ if ( ( $endAngle - $startAngle ) > 359.99999 )
+ {
+ return $this->drawCircle( $center, $width, $height, $color, $filled );
+ }
+
+ // Because of bug #45552 in PHPs ext/GD we check for a minimal distance
+ // on the outer border of the circle sector, and skip the drawing if
+ // the distance is lower then 1.
+ //
+ // See also: http://bugs.php.net/45552
+ $startPoint = new ezcGraphVector(
+ $center->x +
+ ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 )
+ );
+ if ( $startPoint->sub( new ezcGraphVector(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
+ ) )->length() < 1 )
+ {
+ // Skip this circle sector
+ return array();
+ }
+
+ if ( $filled )
+ {
+ imagefilledarc(
+ $image,
+ $this->supersample( $center->x ),
+ $this->supersample( $center->y ),
+ $this->supersample( $width ),
+ $this->supersample( $height ),
+ $startAngle,
+ $endAngle,
+ $drawColor,
+ IMG_ARC_PIE
+ );
+ }
+ else
+ {
+ imagefilledarc(
+ $image,
+ $this->supersample( $center->x ),
+ $this->supersample( $center->y ),
+ $this->supersample( $width ),
+ $this->supersample( $height ),
+ $startAngle,
+ $endAngle,
+ $drawColor,
+ IMG_ARC_PIE | IMG_ARC_NOFILL | IMG_ARC_EDGED
+ );
+ }
+
+ // Create polygon array to return
+ $polygonArray = array( $center );
+ for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
+ );
+
+ return $polygonArray;
+ }
+
+ /**
+ * Draws a single element of a circular arc
+ *
+ * ext/gd itself does not support something like circular arcs, so that
+ * this functions draws rectangular polygons as a part of circular arcs
+ * to interpolate them. This way it is possible to apply a linear gradient
+ * to the circular arc, because we draw single steps anyway.
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param integer $width Width of ellipse
+ * @param integer $height Height of ellipse
+ * @param integer $size Height of border
+ * @param float $startAngle Starting angle of circle sector
+ * @param float $endAngle Ending angle of circle sector
+ * @param ezcGraphColor $color Color of Border
+ * @return void
+ */
+ protected function drawCircularArcStep( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color )
+ {
+ $this->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 )
+ ),
+ new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 ) + $size
+ ),
+ new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 ) + $size
+ ),
+ new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
+ ),
+ ),
+ $color->darken( $this->options->shadeCircularArc * ( 1 + cos ( deg2rad( $startAngle ) ) ) / 2 ),
+ true
+ );
+ }
+
+ /**
+ * Draws a circular arc
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param integer $width Width of ellipse
+ * @param integer $height Height of ellipse
+ * @param integer $size Height of border
+ * @param float $startAngle Starting angle of circle sector
+ * @param float $endAngle Ending angle of circle sector
+ * @param ezcGraphColor $color Color of Border
+ * @param bool $filled
+ * @return void
+ */
+ public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ $image = $this->getImage();
+ $drawColor = $this->allocate( $color );
+
+ // Normalize angles
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ if ( $filled === true )
+ {
+ $startIteration = ceil( $startAngle / $this->options->detail ) * $this->options->detail;
+ $endIteration = floor( $endAngle / $this->options->detail ) * $this->options->detail;
+
+ if ( $startAngle < $startIteration )
+ {
+ // Draw initial step
+ $this->drawCircularArcStep(
+ $center,
+ $width,
+ $height,
+ $size,
+ $startAngle,
+ $startIteration,
+ $color
+ );
+ }
+
+ // Draw all steps
+ for ( ; $startIteration < $endIteration; $startIteration += $this->options->detail )
+ {
+ $this->drawCircularArcStep(
+ $center,
+ $width,
+ $height,
+ $size,
+ $startIteration,
+ $startIteration + $this->options->detail,
+ $color
+ );
+ }
+
+ if ( $endIteration < $endAngle )
+ {
+ // Draw closing step
+ $this->drawCircularArcStep(
+ $center,
+ $width,
+ $height,
+ $size,
+ $endIteration,
+ $endAngle,
+ $color
+ );
+ }
+ }
+ else
+ {
+ imagefilledarc(
+ $image,
+ $this->supersample( $center->x ),
+ $this->supersample( $center->y ),
+ $this->supersample( $width ),
+ $this->supersample( $height ),
+ $startAngle,
+ $endAngle,
+ $drawColor,
+ IMG_ARC_PIE | IMG_ARC_NOFILL
+ );
+ }
+
+ // Create polygon array to return
+ $polygonArray = array();
+ for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
+ );
+
+ for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size,
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size,
+ $center->y +
+ ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 )
+ );
+
+ return $polygonArray;
+ }
+
+ /**
+ * Draw circle
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param mixed $width Width of ellipse
+ * @param mixed $height height of ellipse
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
+ {
+ $image = $this->getImage();
+
+ $drawColor = $this->allocate( $color );
+
+ if ( $filled )
+ {
+ imagefilledellipse(
+ $image,
+ $this->supersample( $center->x ),
+ $this->supersample( $center->y ),
+ $this->supersample( $width ),
+ $this->supersample( $height ),
+ $drawColor
+ );
+ }
+ else
+ {
+ imageellipse(
+ $image,
+ $this->supersample( $center->x ),
+ $this->supersample( $center->y ),
+ $this->supersample( $width ),
+ $this->supersample( $height ),
+ $drawColor
+ );
+ }
+
+ $polygonArray = array();
+ for ( $angle = 0; $angle < 360; $angle += $this->options->imageMapResolution )
+ {
+ $polygonArray[] = new ezcGraphCoordinate(
+ $center->x +
+ ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
+ $center->y +
+ ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
+ );
+ }
+
+ return $polygonArray;
+ }
+
+ /**
+ * Draw an image
+ *
+ * The actual drawing of the image is delayed, to not apply supersampling
+ * to the image. The image will normally be resized using the gd function
+ * imagecopyresampled, which provides nice antialiased scaling, so that
+ * additional supersampling would make the image look blurred. The delayed
+ * images will be pre-processed, so that they are draw in the back of
+ * everything else.
+ *
+ * @param mixed $file Image file
+ * @param ezcGraphCoordinate $position Top left position
+ * @param mixed $width Width of image in destination image
+ * @param mixed $height Height of image in destination image
+ * @return void
+ */
+ public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
+ {
+ $this->preProcessImages[] = array(
+ 'file' => $file,
+ 'position' => clone $position,
+ 'width' => $width,
+ 'height' => $height,
+ );
+
+ return array(
+ $position,
+ new ezcGraphCoordinate( $position->x + $width, $position->y ),
+ new ezcGraphCoordinate( $position->x + $width, $position->y + $height ),
+ new ezcGraphCoordinate( $position->x, $position->y + $height ),
+ );
+ }
+
+ /**
+ * Draw all images to image resource handler
+ *
+ * @param resource $image Image to draw on
+ * @return resource Updated image resource
+ */
+ protected function addImages( $image )
+ {
+ foreach ( $this->preProcessImages as $preImage )
+ {
+ $preImageData = $this->imageCreateFrom( $preImage['file'] );
+ call_user_func_array(
+ $this->options->resampleFunction,
+ array(
+ $image,
+ $preImageData['image'],
+ $preImage['position']->x, $preImage['position']->y,
+ 0, 0,
+ $preImage['width'], $preImage['height'],
+ $preImageData['width'], $preImageData['height'],
+ )
+ );
+ }
+
+ return $image;
+ }
+
+ /**
+ * Return mime type for current image format
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ switch ( $this->options->imageFormat )
+ {
+ case IMG_PNG:
+ return 'image/png';
+ case IMG_JPEG:
+ return 'image/jpeg';
+ }
+ }
+
+ /**
+ * Render image directly to output
+ *
+ * The method renders the image directly to the standard output. You
+ * normally do not want to use this function, because it makes it harder
+ * to proper cache the generated graphs.
+ *
+ * @return void
+ */
+ public function renderToOutput()
+ {
+ header( 'Content-Type: ' . $this->getMimeType() );
+ $this->render( null );
+ }
+
+ /**
+ * Finally save image
+ *
+ * @param string $file Destination filename
+ * @return void
+ */
+ public function render( $file )
+ {
+ $destination = imagecreatetruecolor( $this->options->width, $this->options->height );
+
+ // Default to a transparent white background
+ $bgColor = imagecolorallocatealpha( $destination, 255, 255, 255, 127 );
+ imagealphablending( $destination, true );
+ imagesavealpha( $destination, true );
+ imagefill( $destination, 1, 1, $bgColor );
+
+ // Apply background if one is defined
+ if ( $this->options->background !== false )
+ {
+ $background = $this->imageCreateFrom( $this->options->background );
+
+ call_user_func_array(
+ $this->options->resampleFunction,
+ array(
+ $destination,
+ $background['image'],
+ 0, 0,
+ 0, 0,
+ $this->options->width, $this->options->height,
+ $background['width'], $background['height'],
+ )
+ );
+ }
+
+ // Draw all images to exclude them from supersampling
+ $destination = $this->addImages( $destination );
+
+ // Finally merge with graph
+ $image = $this->getImage();
+ call_user_func_array(
+ $this->options->resampleFunction,
+ array(
+ $destination,
+ $image,
+ 0, 0,
+ 0, 0,
+ $this->options->width, $this->options->height,
+ $this->supersample( $this->options->width ), $this->supersample( $this->options->height )
+ )
+ );
+
+ $this->image = $destination;
+ imagedestroy( $image );
+
+ // Draw all texts
+ // Reset supersampling during text rendering
+ $supersampling = $this->options->supersampling;
+ $this->options->supersampling = 1;
+ $this->drawAllTexts();
+ $this->options->supersampling = $supersampling;
+
+ $image = $this->getImage();
+ switch ( $this->options->imageFormat )
+ {
+ case IMG_PNG:
+ if ( $file === null )
+ {
+ imagepng( $image );
+ }
+ else
+ {
+ imagepng( $image, $file );
+ }
+ break;
+ case IMG_JPEG:
+ imagejpeg( $image, $file, $this->options->jpegQuality );
+ break;
+ default:
+ throw new ezcGraphGdDriverUnsupportedImageTypeException( $this->options->imageFormat );
+ }
+ }
+
+ /**
+ * Get resource of rendered result
+ *
+ * Return the resource of the rendered result. You should not use this
+ * method before you called either renderToOutput() or render(), as the
+ * image may not be completely rendered until then.
+ *
+ * @return resource
+ */
+ public function getResource()
+ {
+ return $this->image;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/driver/svg.php b/src/TUnit/external/ezc/Graph/driver/svg.php
new file mode 100644
index 0000000..110e311
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/driver/svg.php
@@ -0,0 +1,1230 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->background->color = '#FFFFFFFF';
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->renderer = new ezcGraphRenderer3d();
+ * $graph->renderer->options->pieChartShadowSize = 10;
+ * $graph->renderer->options->pieChartGleam = .5;
+ * $graph->renderer->options->dataBorder = false;
+ * $graph->renderer->options->pieChartHeight = 16;
+ * $graph->renderer->options->legendSymbolGleam = .5;
+ *
+ * // SVG driver options
+ * $graph->driver->options->templateDocument = dirname( __FILE__ ) . '/template.svg';
+ * $graph->driver->options->graphOffset = new ezcGraphCoordinate( 25, 40 );
+ * $graph->driver->options->insertIntoGroup = 'ezcGraph';
+ *
+ * $graph->render( 400, 200, 'tutorial_driver_svg.svg' );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphSvgDriver extends ezcGraphDriver
+{
+
+ /**
+ * DOM tree of the svg document
+ *
+ * @var DOMDocument
+ */
+ protected $dom;
+
+ /**
+ * DOMElement containing all svg style definitions
+ *
+ * @var DOMElement
+ */
+ protected $defs;
+
+ /**
+ * DOMElement containing all svg objects
+ *
+ * @var DOMElement
+ */
+ protected $elements;
+
+ /**
+ * List of strings to draw
+ * array ( array(
+ * 'text' => array( 'strings' ),
+ * 'options' => ezcGraphFontOptions,
+ * )
+ *
+ * @var array
+ */
+ protected $strings = array();
+
+ /**
+ * List of already created gradients
+ *
+ * @var array
+ */
+ protected $drawnGradients = array();
+
+ /**
+ * Numeric unique element id
+ *
+ * @var int
+ */
+ protected $elementID = 0;
+
+ /**
+ * Font storage for SVG font glyphs and kernings.
+ *
+ * @var ezcGraphSvgFont
+ */
+ protected $font = null;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'dom' );
+ $this->options = new ezcGraphSvgDriverOptions( $options );
+ $this->font = new ezcGraphSvgFont();
+ }
+
+ /**
+ * Creates the DOM object to insert SVG nodes in.
+ *
+ * If the DOM document does not exists it will be created or loaded
+ * according to the settings.
+ *
+ * @return void
+ */
+ protected function createDocument()
+ {
+ if ( $this->dom === null )
+ {
+ // Create encoding based dom document
+ if ( $this->options->encoding !== null )
+ {
+ $this->dom = new DOMDocument( '1.0', $this->options->encoding );
+ }
+ else
+ {
+ $this->dom = new DOMDocument( '1.0' );
+ }
+
+ if ( $this->options->templateDocument !== false )
+ {
+ $this->dom->load( $this->options->templateDocument );
+
+ $this->defs = $this->dom->getElementsByTagName( 'defs' )->item( 0 );
+ $svg = $this->dom->getElementsByTagName( 'svg' )->item( 0 );
+ }
+ else
+ {
+ $svg = $this->dom->createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
+ $this->dom->appendChild( $svg );
+
+ $svg->setAttribute( 'width', $this->options->width );
+ $svg->setAttribute( 'height', $this->options->height );
+ $svg->setAttribute( 'version', '1.0' );
+ $svg->setAttribute( 'id', $this->options->idPrefix );
+
+ $this->defs = $this->dom->createElement( 'defs' );
+ $this->defs = $svg->appendChild( $this->defs );
+ }
+
+ if ( $this->options->insertIntoGroup !== false )
+ {
+ // getElementById only works for Documents validated against a certain
+ // schema, so that the use of XPath should be faster in most cases.
+ $xpath = new DomXPath( $this->dom );
+ $this->elements = $xpath->query( '//*[@id = \'' . $this->options->insertIntoGroup . '\']' )->item( 0 );
+ if ( !$this->elements )
+ {
+ throw new ezcGraphSvgDriverInvalidIdException( $this->options->insertIntoGroup );
+ }
+ }
+ else
+ {
+ $this->elements = $this->dom->createElement( 'g' );
+ $this->elements->setAttribute( 'id', $this->options->idPrefix . 'Chart' );
+ $this->elements->setAttribute( 'color-rendering', $this->options->colorRendering );
+ $this->elements->setAttribute( 'shape-rendering', $this->options->shapeRendering );
+ $this->elements->setAttribute( 'text-rendering', $this->options->textRendering );
+ $this->elements = $svg->appendChild( $this->elements );
+ }
+ }
+ }
+
+ /**
+ * Return gradient URL
+ *
+ * Creates the definitions needed for a gradient, if a proper gradient does
+ * not yet exists. In each case a URL referencing the correct gradient will
+ * be returned.
+ *
+ * @param ezcGraphColor $color Gradient
+ * @return string Gradient URL
+ */
+ protected function getGradientUrl( ezcGraphColor $color )
+ {
+ switch ( true )
+ {
+ case ( $color instanceof ezcGraphLinearGradient ):
+ if ( !in_array( $color->__toString(), $this->drawnGradients, true ) )
+ {
+ $gradient = $this->dom->createElement( 'linearGradient' );
+ $gradient->setAttribute( 'id', 'Definition_' . $color->__toString() );
+ $this->defs->appendChild( $gradient );
+
+ // Start of linear gradient
+ $stop = $this->dom->createElement( 'stop' );
+ $stop->setAttribute( 'offset', 0 );
+ $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;',
+ $color->startColor->red,
+ $color->startColor->green,
+ $color->startColor->blue,
+ 1 - ( $color->startColor->alpha / 255 )
+ )
+ );
+ $gradient->appendChild( $stop );
+
+ // End of linear gradient
+ $stop = $this->dom->createElement( 'stop' );
+ $stop->setAttribute( 'offset', 1 );
+ $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;',
+ $color->endColor->red,
+ $color->endColor->green,
+ $color->endColor->blue,
+ 1 - ( $color->endColor->alpha / 255 )
+ )
+ );
+ $gradient->appendChild( $stop );
+
+ $gradient = $this->dom->createElement( 'linearGradient' );
+ $gradient->setAttribute( 'id', $color->__toString() );
+ $gradient->setAttribute( 'x1', sprintf( '%.4F', $color->startPoint->x ) );
+ $gradient->setAttribute( 'y1', sprintf( '%.4F', $color->startPoint->y ) );
+ $gradient->setAttribute( 'x2', sprintf( '%.4F', $color->endPoint->x ) );
+ $gradient->setAttribute( 'y2', sprintf( '%.4F', $color->endPoint->y ) );
+ $gradient->setAttribute( 'gradientUnits', 'userSpaceOnUse' );
+ $gradient->setAttributeNS(
+ 'http://www.w3.org/1999/xlink',
+ 'xlink:href',
+ '#Definition_' . $color->__toString()
+ );
+ $this->defs->appendChild( $gradient );
+
+ $this->drawnGradients[] = $color->__toString();
+ }
+
+ return sprintf( 'url(#%s)',
+ $color->__toString()
+ );
+ case ( $color instanceof ezcGraphRadialGradient ):
+ if ( !in_array( $color->__toString(), $this->drawnGradients, true ) )
+ {
+ $gradient = $this->dom->createElement( 'linearGradient' );
+ $gradient->setAttribute( 'id', 'Definition_' . $color->__toString() );
+ $this->defs->appendChild( $gradient );
+
+ // Start of linear gradient
+ $stop = $this->dom->createElement( 'stop' );
+ $stop->setAttribute( 'offset', 0 );
+ $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;',
+ $color->startColor->red,
+ $color->startColor->green,
+ $color->startColor->blue,
+ 1 - ( $color->startColor->alpha / 255 )
+ )
+ );
+ $gradient->appendChild( $stop );
+
+ // End of linear gradient
+ $stop = $this->dom->createElement( 'stop' );
+ $stop->setAttribute( 'offset', 1 );
+ $stop->setAttribute( 'style', sprintf( 'stop-color: #%02x%02x%02x; stop-opacity: %.2F;',
+ $color->endColor->red,
+ $color->endColor->green,
+ $color->endColor->blue,
+ 1 - ( $color->endColor->alpha / 255 )
+ )
+ );
+ $gradient->appendChild( $stop );
+
+ $gradient = $this->dom->createElement( 'radialGradient' );
+ $gradient->setAttribute( 'id', $color->__toString() );
+ $gradient->setAttribute( 'cx', sprintf( '%.4F', $color->center->x ) );
+ $gradient->setAttribute( 'cy', sprintf( '%.4F', $color->center->y ) );
+ $gradient->setAttribute( 'fx', sprintf( '%.4F', $color->center->x ) );
+ $gradient->setAttribute( 'fy', sprintf( '%.4F', $color->center->y ) );
+ $gradient->setAttribute( 'r', max( $color->height, $color->width ) );
+ $gradient->setAttribute( 'gradientUnits', 'userSpaceOnUse' );
+ $gradient->setAttributeNS(
+ 'http://www.w3.org/1999/xlink',
+ 'xlink:href',
+ '#Definition_' . $color->__toString()
+ );
+ $this->defs->appendChild( $gradient );
+
+ $this->drawnGradients[] = $color->__toString();
+ }
+
+ return sprintf( 'url(#%s)',
+ $color->__toString()
+ );
+ default:
+ return false;
+ }
+
+ }
+
+ /**
+ * Get SVG style definition
+ *
+ * Returns a string with SVG style definitions created from color,
+ * fillstatus and line thickness.
+ *
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness.
+ * @return string Formatstring
+ */
+ protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. )
+ {
+ if ( $filled )
+ {
+ if ( $url = $this->getGradientUrl( $color ) )
+ {
+ return sprintf( 'fill: %s; stroke: none;', $url );
+ }
+ else
+ {
+ return sprintf( 'fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;',
+ $color->red,
+ $color->green,
+ $color->blue,
+ 1 - ( $color->alpha / 255 )
+ );
+ }
+ }
+ else
+ {
+ if ( $url = $this->getGradientUrl( $color ) )
+ {
+ return sprintf( 'fill: none; stroke: %s;', $url );
+ }
+ else
+ {
+ return sprintf( 'fill: none; stroke: #%02x%02x%02x; stroke-width: %d; stroke-opacity: %.2F; stroke-linecap: %s; stroke-linejoin: %s;',
+ $color->red,
+ $color->green,
+ $color->blue,
+ $thickness,
+ 1 - ( $color->alpha / 255 ),
+ $this->options->strokeLineCap,
+ $this->options->strokeLineJoin
+ );
+ }
+ }
+ }
+
+ /**
+ * Draws a single polygon.
+ *
+ * @param array $points Point array
+ * @param ezcGraphColor $color Polygon color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
+ {
+ $this->createDocument();
+
+ if ( !$filled )
+ {
+ // The middle of the border is on the outline of a polygon in SVG,
+ // fix that:
+ try
+ {
+ $points = $this->reducePolygonSize( $points, $thickness / 2 );
+ }
+ catch ( ezcGraphReducementFailedException $e )
+ {
+ return false;
+ }
+ }
+
+ $lastPoint = end( $points );
+ $pointString = sprintf( ' M %.4F,%.4F',
+ $lastPoint->x + $this->options->graphOffset->x,
+ $lastPoint->y + $this->options->graphOffset->y
+ );
+
+ foreach ( $points as $point )
+ {
+ $pointString .= sprintf( ' L %.4F,%.4F',
+ $point->x + $this->options->graphOffset->x,
+ $point->y + $this->options->graphOffset->y
+ );
+ }
+ $pointString .= ' z ';
+
+ $path = $this->dom->createElement( 'path' );
+ $path->setAttribute( 'd', $pointString );
+
+ $path->setAttribute(
+ 'style',
+ $this->getStyle( $color, $filled, $thickness )
+ );
+ $path->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Polygon_' . ++$this->elementID ) );
+ $this->elements->appendChild( $path );
+
+ return $id;
+ }
+
+ /**
+ * Draws a line
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Line color
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
+ {
+ $this->createDocument();
+
+ $pointString = sprintf( ' M %.4F,%.4F L %.4F,%.4F',
+ $start->x + $this->options->graphOffset->x,
+ $start->y + $this->options->graphOffset->y,
+ $end->x + $this->options->graphOffset->x,
+ $end->y + $this->options->graphOffset->y
+ );
+
+ $path = $this->dom->createElement( 'path' );
+ $path->setAttribute( 'd', $pointString );
+ $path->setAttribute(
+ 'style',
+ $this->getStyle( $color, false, $thickness )
+ );
+
+ $path->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Line_' . ++$this->elementID ) );
+ $this->elements->appendChild( $path );
+
+ return $id;
+ }
+
+ /**
+ * Returns boundings of text depending on the available font extension
+ *
+ * @param float $size Textsize
+ * @param ezcGraphFontOptions $font Font
+ * @param string $text Text
+ * @return ezcGraphBoundings Boundings of text
+ */
+ protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
+ {
+ if ( $font->type === ezcGraph::SVG_FONT )
+ {
+ return new ezcGraphBoundings(
+ 0,
+ 0,
+ $this->font->calculateStringWidth( $font->path, $text ) * $size,
+ $size
+ );
+ }
+ else
+ {
+ // If we didn't get a SVG font, continue guessing the font width.
+ return new ezcGraphBoundings(
+ 0,
+ 0,
+ $this->getTextWidth( $text, $size ),
+ $size
+ );
+ }
+ }
+
+ /**
+ * Writes text in a box of desired size
+ *
+ * @param string $string Text
+ * @param ezcGraphCoordinate $position Top left position
+ * @param float $width Width of text box
+ * @param float $height Height of text box
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
+ {
+ $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 );
+
+ $width -= $padding * 2;
+ $height -= $padding * 2;
+ $textPosition = new ezcGraphCoordinate(
+ $position->x + $padding,
+ $position->y + $padding
+ );
+
+ // Try to get a font size for the text to fit into the box
+ $maxSize = min( $height, $this->options->font->maxFontSize );
+ $result = false;
+ for ( $size = $maxSize; $size >= $this->options->font->minFontSize; )
+ {
+ $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size );
+ if ( is_array( $result ) )
+ {
+ break;
+ }
+ $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) );
+ }
+
+ if ( !is_array( $result ) )
+ {
+ if ( ( $height >= $this->options->font->minFontSize ) &&
+ ( $this->options->autoShortenString ) )
+ {
+ $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize );
+ }
+ else
+ {
+ throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height );
+ }
+ }
+
+ $this->options->font->minimalUsedFont = $size;
+ $this->strings[] = array(
+ 'text' => $result,
+ 'id' => $id = ( $this->options->idPrefix . 'TextBox_' . ++$this->elementID ),
+ 'position' => $textPosition,
+ 'width' => $width,
+ 'height' => $height,
+ 'align' => $align,
+ 'font' => $this->options->font,
+ 'rotation' => $rotation,
+ );
+
+ return $id;
+ }
+
+ /**
+ * Guess text width for string
+ *
+ * The is no way to know the font or fontsize used by the SVG renderer to
+ * render the string. We assume some character width defined in the SVG
+ * driver options, tu guess the length of a string. We discern between
+ * numeric an non numeric strings, because we often use only numeric
+ * strings to display chart data and numbers tend to be a bit wider then
+ * characters.
+ *
+ * @param mixed $string
+ * @param mixed $size
+ * @access protected
+ * @return void
+ */
+ protected function getTextWidth( $string, $size )
+ {
+ switch ( strtolower( $this->options->encoding ) )
+ {
+ case '':
+ case 'utf-8':
+ case 'utf-16':
+ $string = utf8_decode( $string );
+ break;
+ }
+
+ if ( is_numeric( $string ) )
+ {
+ return $size * strlen( $string ) * $this->options->assumedNumericCharacterWidth;
+ }
+ else
+ {
+ return $size * strlen( $string ) * $this->options->assumedTextCharacterWidth;
+ }
+ }
+
+ /**
+ * Encodes non-utf-8 strings
+ *
+ * Transforms non-utf-8 strings to their hex entities, because ext/DOM
+ * fails here with conversion errors.
+ *
+ * @param string $string
+ * @return string
+ */
+ protected function encode( $string )
+ {
+ $string = htmlspecialchars( $string );
+
+ switch ( strtolower( $this->options->encoding ) )
+ {
+ case '':
+ case 'utf-8':
+ case 'utf-16':
+ return $string;
+ default:
+ // Manual escaping of non ANSII characters, because ext/DOM fails here
+ return preg_replace_callback(
+ '/[\\x80-\\xFF]/',
+ create_function(
+ '$char',
+ 'return sprintf( \'%02x;\', ord( $char[0] ) );'
+ ),
+ $string
+ );
+ }
+ }
+
+ /**
+ * Draw all collected texts
+ *
+ * The texts are collected and their maximum possible font size is
+ * calculated. This function finally draws the texts on the image, this
+ * delayed drawing has two reasons:
+ *
+ * 1) This way the text strings are always on top of the image, what
+ * results in better readable texts
+ * 2) The maximum possible font size can be calculated for a set of texts
+ * with the same font configuration. Strings belonging to one chart
+ * element normally have the same font configuration, so that all texts
+ * belonging to one element will have the same font size.
+ *
+ * @access protected
+ * @return void
+ */
+ protected function drawAllTexts()
+ {
+ $elementsRoot = $this->elements;
+
+ foreach ( $this->strings as $text )
+ {
+ // Add all text elements into one group
+ $group = $this->dom->createElement( 'g' );
+ $group->setAttribute( 'id', $text['id'] );
+
+ if ( $text['rotation'] !== null )
+ {
+ $group->setAttribute( 'transform', sprintf( 'rotate( %.2F %.4F %.4F )',
+ $text['rotation']->getRotation(),
+ $text['rotation']->getCenter()->x,
+ $text['rotation']->getCenter()->y
+ ) );
+ }
+
+ $group = $elementsRoot->appendChild( $group );
+
+ $size = $text['font']->minimalUsedFont;
+ $font = $text['font']->name;
+
+ $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing;
+
+ // Calculate y offset for vertical alignement
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::BOTTOM ):
+ $yOffset = $text['height'] - $completeHeight;
+ break;
+ case ( $text['align'] & ezcGraph::MIDDLE ):
+ $yOffset = ( $text['height'] - $completeHeight ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::TOP ):
+ default:
+ $yOffset = 0;
+ break;
+ }
+
+ $padding = $text['font']->padding + $text['font']->borderWidth / 2;
+ if ( $this->options->font->minimizeBorder === true )
+ {
+ // Calculate maximum width of text rows
+ $width = false;
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+ if ( ( $strWidth = $this->getTextBoundings( $size, $text['font'], $string )->width ) > $width )
+ {
+ $width = $strWidth;
+ }
+ }
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $xOffset = 0;
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $xOffset = ( $text['width'] - $width ) / 2;
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $xOffset = $text['width'] - $width;
+ break;
+ }
+
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding + $xOffset,
+ $text['position']->y - $padding + $yOffset
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $xOffset + $width,
+ $text['position']->y - $padding + $yOffset
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $xOffset + $width,
+ $text['position']->y + $padding * 2 + $yOffset + $completeHeight
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding + $xOffset,
+ $text['position']->y + $padding * 2 + $yOffset + $completeHeight
+ ),
+ );
+ }
+ else
+ {
+ $borderPolygonArray = array(
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding,
+ $text['position']->y - $padding
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $text['width'],
+ $text['position']->y - $padding
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x + $padding * 2 + $text['width'],
+ $text['position']->y + $padding * 2 + $text['height']
+ ),
+ new ezcGraphCoordinate(
+ $text['position']->x - $padding,
+ $text['position']->y + $padding * 2 + $text['height']
+ ),
+ );
+ }
+
+ // Set elements root temporary to local text group to ensure
+ // background and border beeing elements of text group
+ $this->elements = $group;
+ if ( $text['font']->background !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->background,
+ true
+ );
+ }
+ else
+ {
+ // Always draw full tranparent background polygon as fallback,
+ // to be able to click on complete font space, not only on
+ // the text
+ $this->drawPolygon(
+ $borderPolygonArray,
+ ezcGraphColor::fromHex( '#FFFFFFFF' ),
+ true
+ );
+ }
+
+ if ( $text['font']->border !== false )
+ {
+ $this->drawPolygon(
+ $borderPolygonArray,
+ $text['font']->border,
+ false,
+ $text['font']->borderWidth
+ );
+ }
+ $this->elements = $elementsRoot;
+
+ // Bottom line for SVG fonts is lifted a bit
+ $text['position']->y += $size * .85;
+
+ // Render text with evaluated font size
+ foreach ( $text['text'] as $line )
+ {
+ $string = implode( ' ', $line );
+
+ switch ( true )
+ {
+ case ( $text['align'] & ezcGraph::LEFT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x,
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::RIGHT ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( $text['width'] - $this->getTextBoundings( $size, $text['font'], $string )->width ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ case ( $text['align'] & ezcGraph::CENTER ):
+ $position = new ezcGraphCoordinate(
+ $text['position']->x + ( ( $text['width'] - $this->getTextBoundings( $size, $text['font'], $string )->width ) / 2 ),
+ $text['position']->y + $yOffset
+ );
+ break;
+ }
+
+ // Optionally draw text shadow
+ if ( $text['font']->textShadow === true )
+ {
+ $textNode = $this->dom->createElement( 'text', $this->encode( $string ) );
+ $textNode->setAttribute( 'id', $text['id'] . '_shadow' );
+ $textNode->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x + $text['font']->textShadowOffset ) );
+ $textNode->setAttribute( 'text-length', sprintf( '%.4Fpx', $this->getTextBoundings( $size, $text['font'], $string )->width ) );
+ $textNode->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y + $text['font']->textShadowOffset ) );
+ $textNode->setAttribute(
+ 'style',
+ sprintf(
+ 'font-size: %dpx; font-family: \'%s\'; fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;',
+ $size,
+ $text['font']->name,
+ $text['font']->textShadowColor->red,
+ $text['font']->textShadowColor->green,
+ $text['font']->textShadowColor->blue,
+ 1 - ( $text['font']->textShadowColor->alpha / 255 )
+ )
+ );
+ $group->appendChild( $textNode );
+ }
+
+ // Finally draw text
+ $textNode = $this->dom->createElement( 'text', $this->encode( $string ) );
+ $textNode->setAttribute( 'id', $text['id'] . '_text' );
+ $textNode->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x ) );
+ $textNode->setAttribute( 'text-length', sprintf( '%.4Fpx', $this->getTextBoundings( $size, $text['font'], $string )->width ) );
+ $textNode->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y ) );
+ $textNode->setAttribute(
+ 'style',
+ sprintf(
+ 'font-size: %dpx; font-family: \'%s\'; fill: #%02x%02x%02x; fill-opacity: %.2F; stroke: none;',
+ $size,
+ $text['font']->name,
+ $text['font']->color->red,
+ $text['font']->color->green,
+ $text['font']->color->blue,
+ 1 - ( $text['font']->color->alpha / 255 )
+ )
+ );
+ $group->appendChild( $textNode );
+
+ $text['position']->y += $size + $size * $this->options->lineSpacing;
+ }
+ }
+ }
+
+ /**
+ * Draws a sector of cirlce
+ *
+ * @param ezcGraphCoordinate $center Center of circle
+ * @param mixed $width Width
+ * @param mixed $height Height
+ * @param mixed $startAngle Start angle of circle sector
+ * @param mixed $endAngle End angle of circle sector
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled;
+ * @return void
+ */
+ public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ $this->createDocument();
+
+ // Normalize angles
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ if ( ( $endAngle - $startAngle ) >= 360 )
+ {
+ return $this->drawCircle( $center, $width, $height, $color, $filled );
+ }
+
+ // We need the radius
+ $width /= 2;
+ $height /= 2;
+
+ // Apply offset to copy of center coordinate
+ $center = clone $center;
+ $center->x += $this->options->graphOffset->x;
+ $center->y += $this->options->graphOffset->y;
+
+ if ( $filled )
+ {
+ $Xstart = $center->x + $width * cos( -deg2rad( $startAngle ) );
+ $Ystart = $center->y + $height * sin( deg2rad( $startAngle ) );
+ $Xend = $center->x + $width * cos( ( -deg2rad( $endAngle ) ) );
+ $Yend = $center->y + $height * sin( ( deg2rad( $endAngle ) ) );
+
+ $arc = $this->dom->createElement( 'path' );
+ $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F L %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F z',
+ // Middle
+ $center->x, $center->y,
+ // Startpoint
+ $Xstart, $Ystart,
+ // Radius
+ $width, $height,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Endpoint
+ $Xend, $Yend
+ )
+ );
+
+ $arc->setAttribute(
+ 'style',
+ $this->getStyle( $color, $filled, 1 )
+ );
+ $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircleSector_' . ++$this->elementID ) );
+ $this->elements->appendChild( $arc );
+ return $id;
+ }
+ else
+ {
+ try
+ {
+ $reduced = $this->reduceEllipseSize( $center, $width * 2, $height * 2, $startAngle, $endAngle, .5 );
+ }
+ catch ( ezcGraphReducementFailedException $e )
+ {
+ return false;
+ }
+
+ $arc = $this->dom->createElement( 'path' );
+ $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F L %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F z',
+ // Middle
+ $reduced['center']->x, $reduced['center']->y,
+ // Startpoint
+ $reduced['start']->x, $reduced['start']->y,
+ // Radius
+ $width - .5, $height - .5,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Endpoint
+ $reduced['end']->x, $reduced['end']->y
+ )
+ );
+
+ $arc->setAttribute(
+ 'style',
+ $this->getStyle( $color, $filled, 1 )
+ );
+
+ $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircleSector_' . ++$this->elementID ) );
+ $this->elements->appendChild( $arc );
+
+ return $id;
+ }
+ }
+
+ /**
+ * Draws a circular arc
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param integer $width Width of ellipse
+ * @param integer $height Height of ellipse
+ * @param integer $size Height of border
+ * @param float $startAngle Starting angle of circle sector
+ * @param float $endAngle Ending angle of circle sector
+ * @param ezcGraphColor $color Color of Border
+ * @param bool $filled
+ * @return void
+ */
+ public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
+ {
+ $this->createDocument();
+
+ // Normalize angles
+ if ( $startAngle > $endAngle )
+ {
+ $tmp = $startAngle;
+ $startAngle = $endAngle;
+ $endAngle = $tmp;
+ }
+
+ if ( ( $endAngle - $startAngle > 180 ) ||
+ ( ( $startAngle % 180 != 0) && ( $endAngle % 180 != 0) && ( ( $startAngle % 360 > 180 ) XOR ( $endAngle % 360 > 180 ) ) ) )
+ {
+ // Border crosses he 180 degrees border
+ $intersection = floor( $endAngle / 180 ) * 180;
+ while ( $intersection >= $endAngle )
+ {
+ $intersection -= 180;
+ }
+
+ $this->drawCircularArc( $center, $width, $height, $size, $startAngle, $intersection, $color, $filled );
+ $this->drawCircularArc( $center, $width, $height, $size, $intersection, $endAngle, $color, $filled );
+ return;
+ }
+
+ // We need the radius
+ $width /= 2;
+ $height /= 2;
+
+ $Xstart = $center->x + $this->options->graphOffset->x + $width * cos( -deg2rad( $startAngle ) );
+ $Ystart = $center->y + $this->options->graphOffset->y + $height * sin( deg2rad( $startAngle ) );
+ $Xend = $center->x + $this->options->graphOffset->x + $width * cos( ( -deg2rad( $endAngle ) ) );
+ $Yend = $center->y + $this->options->graphOffset->y + $height * sin( ( deg2rad( $endAngle ) ) );
+
+ if ( $filled === true )
+ {
+ $arc = $this->dom->createElement( 'path' );
+ $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,0 %.2F,%.2F L %.2F,%.2F A %.2F,%2F 0 %d,1 %.2F,%.2F z',
+ // Endpoint low
+ $Xend, $Yend + $size,
+ // Radius
+ $width, $height,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Startpoint low
+ $Xstart, $Ystart + $size,
+ // Startpoint
+ $Xstart, $Ystart,
+ // Radius
+ $width, $height,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Endpoint
+ $Xend, $Yend
+ )
+ );
+ }
+ else
+ {
+ $arc = $this->dom->createElement( 'path' );
+ $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,1 %.2F,%.2F',
+ // Startpoint
+ $Xstart, $Ystart,
+ // Radius
+ $width, $height,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Endpoint
+ $Xend, $Yend
+ )
+ );
+ }
+
+ $arc->setAttribute(
+ 'style',
+ $this->getStyle( $color, $filled )
+ );
+
+ $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircularArc_' . ++$this->elementID ) );
+ $this->elements->appendChild( $arc );
+
+ if ( ( $this->options->shadeCircularArc !== false ) &&
+ $filled )
+ {
+ $gradient = new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $center->x - $width,
+ $center->y
+ ),
+ new ezcGraphCoordinate(
+ $center->x + $width,
+ $center->y
+ ),
+ ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ),
+ ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc )
+ );
+
+ $arc = $this->dom->createElement( 'path' );
+ $arc->setAttribute( 'd', sprintf( 'M %.2F,%.2F A %.2F,%.2F 0 %d,0 %.2F,%.2F L %.2F,%.2F A %.2F,%2F 0 %d,1 %.2F,%.2F z',
+ // Endpoint low
+ $Xend, $Yend + $size,
+ // Radius
+ $width, $height,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Startpoint low
+ $Xstart, $Ystart + $size,
+ // Startpoint
+ $Xstart, $Ystart,
+ // Radius
+ $width, $height,
+ // SVG-Stuff
+ ( $endAngle - $startAngle ) > 180,
+ // Endpoint
+ $Xend, $Yend
+ )
+ );
+
+ $arc->setAttribute(
+ 'style',
+ $this->getStyle( $gradient, $filled )
+ );
+ $arc->setAttribute( 'id', $id = ( $this->options->idPrefix . 'CircularArc_' . ++$this->elementID ) );
+
+ $this->elements->appendChild( $arc );
+ }
+
+ return $id;
+ }
+
+ /**
+ * Draw circle
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param mixed $width Width of ellipse
+ * @param mixed $height height of ellipse
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
+ {
+ $this->createDocument();
+
+ $ellipse = $this->dom->createElement( 'ellipse' );
+ $ellipse->setAttribute( 'cx', sprintf( '%.4F', $center->x + $this->options->graphOffset->x ) );
+ $ellipse->setAttribute( 'cy', sprintf( '%.4F', $center->y + $this->options->graphOffset->y ) );
+ $ellipse->setAttribute( 'rx', sprintf( '%.4F', $width / 2 - ( $filled ? 0 : .5 ) ) );
+ $ellipse->setAttribute( 'ry', sprintf( '%.4F', $height / 2 - ( $filled ? 0 : .5 ) ) );
+
+ $ellipse->setAttribute(
+ 'style',
+ $this->getStyle( $color, $filled, 1 )
+ );
+
+ $ellipse->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Circle_' . ++$this->elementID ) );
+ $this->elements->appendChild( $ellipse );
+
+ return $id;
+ }
+
+ /**
+ * Draw an image
+ *
+ * The image will be inlined in the SVG document using data URL scheme. For
+ * this the mime type and base64 encoded file content will be merged to
+ * URL.
+ *
+ * @param mixed $file Image file
+ * @param ezcGraphCoordinate $position Top left position
+ * @param mixed $width Width of image in destination image
+ * @param mixed $height Height of image in destination image
+ * @return void
+ */
+ public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
+ {
+ $this->createDocument();
+
+ $data = getimagesize( $file );
+ $image = $this->dom->createElement( 'image' );
+
+ $image->setAttribute( 'x', sprintf( '%.4F', $position->x + $this->options->graphOffset->x ) );
+ $image->setAttribute( 'y', sprintf( '%.4F', $position->y + $this->options->graphOffset->y ) );
+ $image->setAttribute( 'width', sprintf( '%.4Fpx', $width ) );
+ $image->setAttribute( 'height', sprintf( '%.4Fpx', $height ) );
+ $image->setAttributeNS(
+ 'http://www.w3.org/1999/xlink',
+ 'xlink:href',
+ sprintf( 'data:%s;base64,%s',
+ $data['mime'],
+ base64_encode( file_get_contents( $file ) )
+ )
+ );
+
+ $this->elements->appendChild( $image );
+ $image->setAttribute( 'id', $id = ( $this->options->idPrefix . 'Image_' . ++$this->elementID ) );
+
+ return $id;
+ }
+
+ /**
+ * Return mime type for current image format
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return 'image/svg+xml';
+ }
+
+ /**
+ * Render image directly to output
+ *
+ * The method renders the image directly to the standard output. You
+ * normally do not want to use this function, because it makes it harder
+ * to proper cache the generated graphs.
+ *
+ * @return void
+ */
+ public function renderToOutput()
+ {
+ $this->createDocument();
+ $this->drawAllTexts();
+
+ header( 'Content-Type: ' . $this->getMimeType() );
+ echo $this->dom->saveXML();
+ }
+
+ /**
+ * Finally save image
+ *
+ * @param string $file Destination filename
+ * @return void
+ */
+ public function render( $file )
+ {
+ $this->createDocument();
+ $this->drawAllTexts();
+
+ // Embed used glyphs
+ $this->font->addFontToDocument( $this->dom );
+ $this->dom->save( $file );
+ }
+
+ /**
+ * Get resource of rendered result
+ *
+ * Return the resource of the rendered result. You should not use this
+ * method before you called either renderToOutput() or render(), as the
+ * image may not be completely rendered until then.
+ *
+ * @return DOMDocument
+ */
+ public function getResource()
+ {
+ return $this->dom;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/driver/svg_font.php b/src/TUnit/external/ezc/Graph/driver/svg_font.php
new file mode 100644
index 0000000..7858ccd
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/driver/svg_font.php
@@ -0,0 +1,298 @@
+`.
+ *
+ * Usage:
+ *
+ * $font = new ezcGraphSvgFont();
+ * var_dump(
+ * $font->calculateStringWidth( '../tests/data/font.svg', 'Just a test string.' ),
+ * $font->calculateStringWidth( '../tests/data/font2.svg', 'Just a test string.' )
+ * );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphSvgFont
+{
+ /**
+ * Units per EM
+ *
+ * @var float
+ */
+ protected $unitsPerEm;
+
+ /**
+ * Used glyphs
+ *
+ * @var array
+ */
+ protected $usedGlyphs = array();
+
+ /**
+ * Cache for glyph size to save XPath lookups.
+ *
+ * @var array
+ */
+ protected $glyphCache = array();
+
+ /**
+ * Used kernings
+ *
+ * @var array
+ */
+ protected $usedKerns = array();
+
+ /**
+ * Path to font
+ *
+ * @var string
+ */
+ protected $fonts = array();
+
+ /**
+ * Initialize SVG font
+ *
+ * Loads the SVG font $filename. This should be the path to the file
+ * generated by ttf2svg.
+ *
+ * Returns the (normlized) name of the initilized font.
+ *
+ * @param string $fontPath
+ * @return string
+ */
+ protected function initializeFont( $fontPath )
+ {
+ if ( isset( $this->fonts[$fontPath] ) )
+ {
+ return $fontPath;
+ }
+
+ // Check for existance of font file
+ if ( !is_file( $fontPath ) || !is_readable( $fontPath ) )
+ {
+ throw new ezcBaseFileNotFoundException( $fontPath );
+ }
+
+ $this->fonts[$fontPath] = simplexml_load_file( $fontPath )->defs->font;
+
+ // SimpleXML requires us to register a namespace for XPath to work
+ $this->fonts[$fontPath]->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
+
+ // Extract the number of units per Em
+ $this->unitsPerEm[$fontPath] = (int) $this->fonts[$fontPath]->{'font-face'}['units-per-em'];
+
+ return $fontPath;
+ }
+
+ /**
+ * Get name of font
+ *
+ * Get the name of the given font, by extracting its font family from the
+ * SVG font file.
+ *
+ * @param string $fontPath
+ * @return string
+ */
+ public static function getFontName( $fontPath )
+ {
+ $font = simplexml_load_file( $fontPath )->defs->font;
+
+ // SimpleXML requires us to register a namespace for XPath to work
+ $font->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
+
+ // Extract the font family name
+ return (string) $font->{'font-face'}['font-family'];
+ }
+
+ /**
+ * XPath has no standard means of escaping ' and ", with the only solution
+ * being to delimit your string with the opposite type of quote. ( And if
+ * your string contains both concat( ) it ).
+ *
+ * This method will correctly delimit $char with the appropriate quote type
+ * so that it can be used in an XPath expression.
+ *
+ * @param string $char
+ * @return string
+ */
+ protected static function xpathEscape( $char )
+ {
+ return "'" . str_replace(
+ array( '\'', '\\' ),
+ array( '\\\'', '\\\\' ),
+ $char ) . "'";
+ }
+
+ /**
+ * Returns the
+ * $graph = new ezcGraphPieChart();
+ * $graph->data['example'] = new ezcGraphArrayDataSet( array(
+ * 'Foo' => 23,
+ * 'Bar' => 42,
+ * ) );
+ *
+ * // Format the legend element
+ * $graph->legend->background = '#FFFFFF80';
+ *
+ * // Place at the bottom of the chart, with a height of 5% of the remaining
+ * // chart space.
+ * $graph->legend->position = ezcGraph::BOTTOM;
+ * $graph->legend->landscapeSize = .05;
+ *
+ * $graph->render( 400, 250, 'legend.svg' );
+ *
+ *
+ * @property float $portraitSize
+ * Size of a portrait style legend in percent of the size of the
+ * complete chart.
+ * @property float $landscapeSize
+ * Size of a landscape style legend in percent of the size of the
+ * complete chart.
+ * @property int $symbolSize
+ * Standard size of symbols and text in legends.
+ * @property float $minimumSymbolSize
+ * Scale symbol size up to to percent of complete legends size for
+ * very big legends.
+ * @property int $spacing
+ * Space between labels elements in pixel.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphChartElementLegend extends ezcGraphChartElement
+{
+
+ /**
+ * Contains data which should be shown in the legend
+ * array(
+ * array(
+ * 'label' => (string) 'Label of data element',
+ * 'color' => (ezcGraphColor) $color,
+ * 'symbol' => (integer) ezcGraph::DIAMOND,
+ * ),
+ * ...
+ * )
+ *
+ * @var array
+ */
+ protected $labels;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['portraitSize'] = .2;
+ $this->properties['landscapeSize'] = .1;
+ $this->properties['symbolSize'] = 14;
+ $this->properties['padding'] = 1;
+ $this->properties['minimumSymbolSize'] = .05;
+ $this->properties['spacing'] = 2;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'padding':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['padding'] = (int) $propertyValue;
+ break;
+ case 'symbolSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['symbolSize'] = (int) $propertyValue;
+ break;
+ case 'landscapeSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' );
+ }
+
+ $this->properties['landscapeSize'] = (float) $propertyValue;
+ break;
+ case 'portraitSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' );
+ }
+
+ $this->properties['portraitSize'] = (float) $propertyValue;
+ break;
+ case 'minimumSymbolSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 1' );
+ }
+
+ $this->properties['minimumSymbolSize'] = (float) $propertyValue;
+ break;
+ case 'spacing':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['spacing'] = (int) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'labels':
+ return $this->labels;
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+
+ /**
+ * Generate legend from several datasets with on entry per dataset
+ *
+ * @param ezcGraphChartDataContainer $datasets
+ * @return void
+ */
+ public function generateFromDataSets( ezcGraphChartDataContainer $datasets )
+ {
+ $this->labels = array();
+ foreach ( $datasets as $dataset )
+ {
+ $this->labels[] = array(
+ 'label' => $dataset->label->default,
+ 'url' => $dataset->url->default,
+ 'color' => $dataset->color->default,
+ 'symbol' => ( $dataset->symbol->default === null ?
+ ezcGraph::NO_SYMBOL :
+ $dataset->symbol->default ),
+ );
+ }
+ }
+
+ /**
+ * Generate legend from single dataset with on entry per data element
+ *
+ * @param ezcGraphDataSet $dataset
+ * @return void
+ */
+ public function generateFromDataSet( ezcGraphDataSet $dataset )
+ {
+ $this->labels = array();
+ foreach ( $dataset as $label => $data )
+ {
+ $this->labels[] = array(
+ 'label' => $label,
+ 'url' => $dataset->url[$label],
+ 'color' => $dataset->color[$label],
+ 'symbol' => ( $dataset->symbol[$label] === null ?
+ ezcGraph::NO_SYMBOL :
+ $dataset->symbol[$label] ),
+ );
+ }
+ }
+
+ /**
+ * Calculated boundings needed for the legend.
+ *
+ * Uses the position and the configured horizontal or vertical size of a
+ * legend to calculate the boundings for the legend.
+ *
+ * @param ezcGraphBoundings $boundings Avalable boundings
+ * @return ezcGraphBoundings Remaining boundings
+ */
+ protected function calculateBoundings( ezcGraphBoundings $boundings )
+ {
+ $this->properties['boundings'] = clone $boundings;
+
+ switch ( $this->position )
+ {
+ case ezcGraph::LEFT:
+ $size = ( $boundings->width ) * $this->portraitSize;
+
+ $boundings->x0 += $size;
+ $this->boundings->x1 = $boundings->x0;
+ break;
+ case ezcGraph::RIGHT:
+ $size = ( $boundings->width ) * $this->portraitSize;
+
+ $boundings->x1 -= $size;
+ $this->boundings->x0 = $boundings->x1;
+ break;
+ case ezcGraph::TOP:
+ $size = ( $boundings->height ) * $this->landscapeSize;
+
+ $boundings->y0 += $size;
+ $this->boundings->y1 = $boundings->y0;
+ break;
+ case ezcGraph::BOTTOM:
+ $size = ( $boundings->height ) * $this->landscapeSize;
+
+ $boundings->y1 -= $size;
+ $this->boundings->y0 = $boundings->y1;
+ break;
+ }
+
+ return $boundings;
+ }
+
+ /**
+ * Render a legend
+ *
+ * @param ezcGraphRenderer $renderer Renderer
+ * @param ezcGraphBoundings $boundings Boundings for the axis
+ * @return ezcGraphBoundings Remaining boundings
+ */
+ public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings )
+ {
+ $boundings = $this->calculateBoundings( $boundings );
+
+ if ( $this->position === ezcGraph::LEFT || $this->position === ezcGraph::RIGHT )
+ {
+ $type = ezcGraph::VERTICAL;
+ }
+ else
+ {
+ $type = ezcGraph::HORIZONTAL;
+ }
+
+ // Render standard elements
+ $this->properties['boundings'] = $renderer->drawBox(
+ $this->properties['boundings'],
+ $this->properties['background'],
+ $this->properties['border'],
+ $this->properties['borderWidth'],
+ $this->properties['margin'],
+ $this->properties['padding'],
+ $this->properties['title'],
+ $this->getTitleSize( $this->properties['boundings'], $type )
+ );
+
+ // Render legend
+ $renderer->drawLegend(
+ $this->boundings,
+ $this,
+ $type
+ );
+
+ return $boundings;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/element/text.php b/src/TUnit/external/ezc/Graph/element/text.php
new file mode 100644
index 0000000..c67f7d7
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/element/text.php
@@ -0,0 +1,150 @@
+
+ * $chart = new ezcGraphPieChart();
+ * $chart->data['example'] = new ezcGraphArrayDataSet( array(
+ * 'Foo' => 23,
+ * 'Bar' => 42,
+ * ) );
+ *
+ * $chart->title = 'Some pie chart';
+ *
+ * // Use at maximum 5% of the chart height for the title.
+ * $chart->title->maxHeight = .05;
+ *
+ * $graph->render( 400, 250, 'title.svg' );
+ *
+ *
+ * @property float $maxHeight
+ * Maximum percent of bounding used to display the text.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphChartElementText extends ezcGraphChartElement
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['maxHeight'] = .1;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'maxHeight':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['maxHeight'] = (float) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+
+ /**
+ * Render the text
+ *
+ * @param ezcGraphRenderer $renderer Renderer
+ * @param ezcGraphBoundings $boundings Boundings for the axis
+ * @return ezcGraphBoundings Remaining boundings
+ */
+ public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings )
+ {
+ $height = (int) min(
+ round( $this->properties['maxHeight'] * ( $boundings->y1 - $boundings->y0 ) ),
+ $this->properties['font']->maxFontSize + $this->padding * 2 + $this->margin * 2
+ );
+
+ switch ( $this->properties['position'] )
+ {
+ case ezcGraph::TOP:
+ $textBoundings = new ezcGraphBoundings(
+ $boundings->x0,
+ $boundings->y0,
+ $boundings->x1,
+ $boundings->y0 + $height
+ );
+ $boundings->y0 += $height + $this->properties['margin'];
+ break;
+ case ezcGraph::BOTTOM:
+ $textBoundings = new ezcGraphBoundings(
+ $boundings->x0,
+ $boundings->y1 - $height,
+ $boundings->x1,
+ $boundings->y1
+ );
+ $boundings->y1 -= $height + $this->properties['margin'];
+ break;
+ }
+
+ $textBoundings = $renderer->drawBox(
+ $textBoundings,
+ $this->properties['background'],
+ $this->properties['border'],
+ $this->properties['borderWidth'],
+ $this->properties['margin'],
+ $this->properties['padding']
+ );
+
+ $renderer->drawText(
+ $textBoundings,
+ $this->properties['title'],
+ ezcGraph::CENTER | ezcGraph::MIDDLE
+ );
+
+ return $boundings;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/exceptions/date_parsing.php b/src/TUnit/external/ezc/Graph/exceptions/date_parsing.php
new file mode 100644
index 0000000..940ef0f
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/date_parsing.php
@@ -0,0 +1,33 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/exception.php b/src/TUnit/external/ezc/Graph/exceptions/exception.php
new file mode 100644
index 0000000..337148a
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/exception.php
@@ -0,0 +1,20 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/flash_bitmap_type.php b/src/TUnit/external/ezc/Graph/exceptions/flash_bitmap_type.php
new file mode 100644
index 0000000..628ff7d
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/flash_bitmap_type.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/font_rendering.php b/src/TUnit/external/ezc/Graph/exceptions/font_rendering.php
new file mode 100644
index 0000000..c19ff00
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/font_rendering.php
@@ -0,0 +1,40 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/font_type.php b/src/TUnit/external/ezc/Graph/exceptions/font_type.php
new file mode 100644
index 0000000..c8ca941
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/font_type.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/incompatible_driver.php b/src/TUnit/external/ezc/Graph/exceptions/incompatible_driver.php
new file mode 100644
index 0000000..efd585f
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/incompatible_driver.php
@@ -0,0 +1,34 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_assignement.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_assignement.php
new file mode 100644
index 0000000..fd3e509
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_assignement.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_data.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_data.php
new file mode 100644
index 0000000..0c35755
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_data.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_data_source.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_data_source.php
new file mode 100644
index 0000000..b2c6d48
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_data_source.php
@@ -0,0 +1,33 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_dimensions.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_dimensions.php
new file mode 100644
index 0000000..73414e4
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_dimensions.php
@@ -0,0 +1,35 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_display_type.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_display_type.php
new file mode 100644
index 0000000..568ed9a
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_display_type.php
@@ -0,0 +1,46 @@
+ 'Pie',
+ ezcGraph::LINE => 'Line',
+ ezcGraph::BAR => 'Bar',
+ );
+
+ if ( isset( $chartTypeNames[$type] ) )
+ {
+ $chartTypeName = $chartTypeNames[$type];
+ }
+ else
+ {
+ $chartTypeName = 'Unknown';
+ }
+
+ parent::__construct( "Invalid data set display type '$type' ('$chartTypeName') for current chart." );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_id.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_id.php
new file mode 100644
index 0000000..f59ad52
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_id.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_image_file.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_image_file.php
new file mode 100644
index 0000000..c1982fa
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_image_file.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_keys.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_keys.php
new file mode 100644
index 0000000..f4630f8
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_keys.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/invalid_step_size.php b/src/TUnit/external/ezc/Graph/exceptions/invalid_step_size.php
new file mode 100644
index 0000000..1c902b6
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/invalid_step_size.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/no_data.php b/src/TUnit/external/ezc/Graph/exceptions/no_data.php
new file mode 100644
index 0000000..9cc74c4
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/no_data.php
@@ -0,0 +1,30 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/no_such_data.php b/src/TUnit/external/ezc/Graph/exceptions/no_such_data.php
new file mode 100644
index 0000000..fc70e80
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/no_such_data.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/no_such_dataset.php b/src/TUnit/external/ezc/Graph/exceptions/no_such_dataset.php
new file mode 100644
index 0000000..9ecb5ac
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/no_such_dataset.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/no_such_element.php b/src/TUnit/external/ezc/Graph/exceptions/no_such_element.php
new file mode 100644
index 0000000..34106e1
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/no_such_element.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/not_rendered.php b/src/TUnit/external/ezc/Graph/exceptions/not_rendered.php
new file mode 100644
index 0000000..e3a8e4d
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/not_rendered.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/out_of_boundings.php b/src/TUnit/external/ezc/Graph/exceptions/out_of_boundings.php
new file mode 100644
index 0000000..e18f16c
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/out_of_boundings.php
@@ -0,0 +1,35 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/out_of_logarithmical_boundings.php b/src/TUnit/external/ezc/Graph/exceptions/out_of_logarithmical_boundings.php
new file mode 100644
index 0000000..a9b46de
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/out_of_logarithmical_boundings.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/reducement_failed.php b/src/TUnit/external/ezc/Graph/exceptions/reducement_failed.php
new file mode 100644
index 0000000..4049148
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/reducement_failed.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/too_many_datasets.php b/src/TUnit/external/ezc/Graph/exceptions/too_many_datasets.php
new file mode 100644
index 0000000..9829511
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/too_many_datasets.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/unknown_color_definition.php b/src/TUnit/external/ezc/Graph/exceptions/unknown_color_definition.php
new file mode 100644
index 0000000..46ad0b9
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/unknown_color_definition.php
@@ -0,0 +1,32 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/unregular_steps.php b/src/TUnit/external/ezc/Graph/exceptions/unregular_steps.php
new file mode 100644
index 0000000..27c7f4c
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/unregular_steps.php
@@ -0,0 +1,31 @@
+
diff --git a/src/TUnit/external/ezc/Graph/exceptions/unsupported_image_type.php b/src/TUnit/external/ezc/Graph/exceptions/unsupported_image_type.php
new file mode 100644
index 0000000..cb734e9
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/exceptions/unsupported_image_type.php
@@ -0,0 +1,61 @@
+ 'GIF',
+ 2 => 'Jpeg',
+ 3 => 'PNG',
+ 4 => 'SWF',
+ 5 => 'PSD',
+ 6 => 'BMP',
+ 7 => 'TIFF (intel)',
+ 8 => 'TIFF (motorola)',
+ 9 => 'JPC',
+ 10 => 'JP2',
+ 11 => 'JPX',
+ 12 => 'JB2',
+ 13 => 'SWC',
+ 14 => 'IFF',
+ 15 => 'WBMP',
+ 16 => 'XBM',
+
+ );
+
+ if ( isset( $typeName[$type] ) )
+ {
+ $type = $typeName[$type];
+ }
+ else
+ {
+ $type = 'Unknown';
+ }
+
+ parent::__construct( "Unsupported image format '{$type}'." );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/graph.php b/src/TUnit/external/ezc/Graph/graph.php
new file mode 100644
index 0000000..4c191bf
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/graph.php
@@ -0,0 +1,147 @@
+
diff --git a/src/TUnit/external/ezc/Graph/interfaces/axis_label_renderer.php b/src/TUnit/external/ezc/Graph/interfaces/axis_label_renderer.php
new file mode 100644
index 0000000..3bb2442
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/axis_label_renderer.php
@@ -0,0 +1,557 @@
+properties['majorStepCount'] = false;
+ $this->properties['minorStepCount'] = false;
+ $this->properties['majorStepSize'] = 3;
+ $this->properties['minorStepSize'] = 1;
+ $this->properties['innerStep'] = true;
+ $this->properties['outerStep'] = false;
+ $this->properties['outerGrid'] = false;
+ $this->properties['showLabels'] = true;
+ $this->properties['labelPadding'] = 2;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'driver':
+ if ( $propertyValue instanceof ezcGraphDriver )
+ {
+ $this->properties['driver'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcGraphInvalidDriverException( $propertyValue );
+ }
+ break;
+ case 'majorStepCount':
+ if ( ( $propertyValue !== false ) &&
+ !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['majorStepCount'] = (int) $propertyValue;
+ break;
+ case 'minorStepCount':
+ if ( ( $propertyValue !== false ) &&
+ !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['minorStepCount'] = (int) $propertyValue;
+ break;
+ case 'majorStepSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['majorStepSize'] = (int) $propertyValue;
+ break;
+ case 'minorStepSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['minorStepSize'] = (int) $propertyValue;
+ break;
+ case 'innerStep':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['innerStep'] = (bool) $propertyValue;
+ break;
+ case 'outerStep':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['outerStep'] = (bool) $propertyValue;
+ break;
+ case 'outerGrid':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['outerGrid'] = (bool) $propertyValue;
+ break;
+ case 'showLabels':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['showLabels'] = (bool) $propertyValue;
+ break;
+ case 'labelPadding':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['labelPadding'] = (int) $propertyValue;
+ break;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+
+ /**
+ * Checks for the cutting point of two lines.
+ *
+ * The lines are given by a start position and the direction of the line,
+ * both as instances of {@link ezcGraphCoordinate}. If no cutting point
+ * could be calculated, because the lines are parallel the function will
+ * return false. Otherwise the factor returned can be used to calculate the
+ * cutting point using the following equatation:
+ * point = $aStart + $factor * $aDir;
+ *
+ * We return the factor instead of the resulting point because it can be
+ * easily determined from the factor if the cutting point is in "behind"
+ * the line starting point, or if the distance to the cutting point is
+ * bigger then the direction vector is long ( $factor > 1 ).
+ *
+ * @param ezcGraphCoordinate $aStart
+ * @param ezcGraphCoordinate $aDir
+ * @param ezcGraphCoordinate $bStart
+ * @param ezcGraphCoordinate $bDir
+ * @return mixed
+ */
+ public function determineLineCuttingPoint( ezcGraphCoordinate $aStart, ezcGraphCoordinate $aDir, ezcGraphCoordinate $bStart, ezcGraphCoordinate $bDir )
+ {
+ // Check if lines are parallel
+ if ( ( ( abs( $aDir->x ) < .000001 ) && ( abs( $bDir->x ) < .000001 ) ) ||
+ ( ( abs( $aDir->y ) < .000001 ) && ( abs( $bDir->y ) < .000001 ) ) ||
+ ( ( abs( $aDir->x * $bDir->x * $aDir->y * $bDir->y ) > .000001 ) &&
+ ( abs( ( $aDir->x / $aDir->y ) - ( $bDir->x / $bDir->y ) ) < .000001 )
+ )
+ )
+ {
+ return false;
+ }
+
+ // Use ? : to prevent division by zero
+ $denominator =
+ ( abs( $aDir->y ) > .000001 ? $bDir->y / $aDir->y : .0 ) -
+ ( abs( $aDir->x ) > .000001 ? $bDir->x / $aDir->x : .0 );
+
+ // Solve equatation
+ if ( abs( $denominator ) < .000001 )
+ {
+ return - (
+ ( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) -
+ ( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) -
+ ( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) +
+ ( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 )
+ );
+ }
+ else
+ {
+ return - (
+ ( abs( $aDir->y ) > .000001 ? $bStart->y / $aDir->y : .0 ) -
+ ( abs( $aDir->y ) > .000001 ? $aStart->y / $aDir->y : .0 ) -
+ ( abs( $aDir->x ) > .000001 ? $bStart->x / $aDir->x : .0 ) +
+ ( abs( $aDir->x ) > .000001 ? $aStart->x / $aDir->x : .0 )
+ ) / $denominator;
+ }
+ }
+
+ /**
+ * Draw single step on a axis
+ *
+ * Draws a step on a axis at the current position
+ *
+ * @param ezcGraphRenderer $renderer Renderer to draw the step with
+ * @param ezcGraphCoordinate $position Position of step
+ * @param ezcGraphCoordinate $direction Direction of axis
+ * @param int $axisPosition Position of axis
+ * @param int $size Step size
+ * @param ezcGraphColor $color Color of axis
+ * @return void
+ */
+ public function drawStep( ezcGraphRenderer $renderer, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, $axisPosition, $size, ezcGraphColor $color )
+ {
+ if ( ! ( $this->innerStep || $this->outerStep ) )
+ {
+ return false;
+ }
+
+ $drawStep = false;
+ if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) ||
+ ( ( $axisPosition === ezcGraph::BOTTOM ) && $this->outerStep ) ||
+ ( ( $axisPosition === ezcGraph::TOP ) && $this->innerStep ) ||
+ ( ( $axisPosition === ezcGraph::RIGHT ) && $this->outerStep ) ||
+ ( ( $axisPosition === ezcGraph::LEFT ) && $this->innerStep ) )
+ {
+ // Turn direction vector to left by 90 degrees and multiply
+ // with major step size
+ $stepStart = new ezcGraphCoordinate(
+ $position->x + $direction->y * $size,
+ $position->y - $direction->x * $size
+ );
+ $drawStep = true;
+ }
+ else
+ {
+ $stepStart = $position;
+ }
+
+ if ( ( ( $axisPosition === ezcGraph::CENTER ) && $this->innerStep ) ||
+ ( ( $axisPosition === ezcGraph::BOTTOM ) && $this->innerStep ) ||
+ ( ( $axisPosition === ezcGraph::TOP ) && $this->outerStep ) ||
+ ( ( $axisPosition === ezcGraph::RIGHT ) && $this->innerStep ) ||
+ ( ( $axisPosition === ezcGraph::LEFT ) && $this->outerStep ) )
+ {
+ // Turn direction vector to right by 90 degrees and multiply
+ // with major step size
+ $stepEnd = new ezcGraphCoordinate(
+ $position->x - $direction->y * $size,
+ $position->y + $direction->x * $size
+ );
+ $drawStep = true;
+ }
+ else
+ {
+ $stepEnd = $position;
+ }
+
+ if ( $drawStep )
+ {
+ $renderer->drawStepLine(
+ $stepStart,
+ $stepEnd,
+ $color
+ );
+ }
+ }
+
+ /**
+ * Draw non-rectangular grid lines grid
+ *
+ * Draws a grid line at the current position, for non-rectangular axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer to draw the grid with
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $position Position of step
+ * @param ezcGraphCoordinate $direction Direction of axis
+ * @param ezcGraphColor $color Color of axis
+ * @return void
+ */
+ protected function drawNonRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color )
+ {
+ // Direction of grid line is direction of axis turned right by 90
+ // degrees
+ $gridDirection = new ezcGraphCoordinate(
+ $direction->y,
+ - $direction->x
+ );
+
+ $cuttingPoints = array();
+ foreach ( array( // Bounding lines
+ array(
+ 'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ 'dir' => new ezcGraphCoordinate( 0, $boundings->y1 - $boundings->y0 )
+ ),
+ array(
+ 'start' => new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ 'dir' => new ezcGraphCoordinate( $boundings->x1 - $boundings->x0, 0 )
+ ),
+ array(
+ 'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ 'dir' => new ezcGraphCoordinate( 0, $boundings->y0 - $boundings->y1 )
+ ),
+ array(
+ 'start' => new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ 'dir' => new ezcGraphCoordinate( $boundings->x0 - $boundings->x1, 0 )
+ ),
+ ) as $boundingLine )
+ {
+ // Test for cutting points with bounding lines, where cutting
+ // position is between 0 and 1, which means, that the line is hit
+ // on the bounding box rectangle. Use these points as a start and
+ // ending point for the grid lines. There should *always* be
+ // exactly two points returned.
+ $cuttingPosition = $this->determineLineCuttingPoint(
+ $boundingLine['start'],
+ $boundingLine['dir'],
+ $position,
+ $gridDirection
+ );
+
+ if ( $cuttingPosition === false )
+ {
+ continue;
+ }
+
+ $cuttingPosition = abs( $cuttingPosition );
+
+ if ( ( $cuttingPosition >= 0 ) &&
+ ( $cuttingPosition <= 1 ) )
+ {
+ $cuttingPoints[] = new ezcGraphCoordinate(
+ $boundingLine['start']->x + $cuttingPosition * $boundingLine['dir']->x,
+ $boundingLine['start']->y + $cuttingPosition * $boundingLine['dir']->y
+ );
+ }
+ }
+
+ if ( count( $cuttingPoints ) < 2 )
+ {
+ // This should not happpen
+ return false;
+ }
+
+ // Finally draw grid line
+ $renderer->drawGridLine(
+ $cuttingPoints[0],
+ $cuttingPoints[1],
+ $color
+ );
+ }
+
+ /**
+ * Draw rectangular grid
+ *
+ * Draws a grid line at the current position for rectangular directed axis.
+ *
+ * Method special for rectangularly directed axis to minimize the floating
+ * point calculation inaccuracies. Those are not necessary for rectangles,
+ * while for non-rectangular directed axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer to draw the grid with
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $position Position of step
+ * @param ezcGraphCoordinate $direction Direction of axis
+ * @param ezcGraphColor $color Color of axis
+ * @return void
+ */
+ protected function drawRectangularGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color )
+ {
+ if ( abs( $direction->x ) < .00001 )
+ {
+ $renderer->drawGridLine(
+ new ezcGraphCoordinate(
+ $boundings->x0,
+ $position->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x1,
+ $position->y
+ ),
+ $color
+ );
+ }
+ else
+ {
+ $renderer->drawGridLine(
+ new ezcGraphCoordinate(
+ $position->x,
+ $boundings->y0
+ ),
+ new ezcGraphCoordinate(
+ $position->x,
+ $boundings->y1
+ ),
+ $color
+ );
+ }
+ }
+
+ /**
+ * Draw grid
+ *
+ * Draws a grid line at the current position
+ *
+ * @param ezcGraphRenderer $renderer Renderer to draw the grid with
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $position Position of step
+ * @param ezcGraphCoordinate $direction Direction of axis
+ * @param ezcGraphColor $color Color of axis
+ * @return void
+ */
+ protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color )
+ {
+ // Check if the axis direction is rectangular
+ if ( ( abs( $direction->x ) < .00001 ) ||
+ ( abs( $direction->y ) < .00001 ) )
+ {
+ return $this->drawRectangularGrid( $renderer, $boundings, $position, $direction, $color );
+ }
+ else
+ {
+ return $this->drawNonRectangularGrid( $renderer, $boundings, $position, $direction, $color );
+ }
+ }
+
+ /**
+ * Modify chart boundings
+ *
+ * Optionally modify boundings of chart data
+ *
+ * @param ezcGraphBoundings $boundings Current boundings of chart
+ * @param ezcGraphCoordinate $direction Direction of the current axis
+ * @return ezcGraphBoundings Modified boundings
+ */
+ public function modifyChartBoundings( ezcGraphBoundings $boundings, ezcGraphCoordinate $direction )
+ {
+ return $boundings;
+ }
+
+ /**
+ * Modify chart data position
+ *
+ * Optionally additionally modify the coodinate of a data point
+ *
+ * @param ezcGraphCoordinate $coordinate Data point coordinate
+ * @return ezcGraphCoordinate Modified coordinate
+ */
+ public function modifyChartDataPosition( ezcGraphCoordinate $coordinate )
+ {
+ return $coordinate;
+ }
+
+ /**
+ * Get axis space values
+ *
+ * Get axis space values, depending on passed parameters. If
+ * $innerBoundings is given it will be used to caclulat the axis spaces
+ * available for label rendering. If not given the legacy method will be
+ * used, which uses the xAxisSpace and yAxisSpace values calcualted by the
+ * renderer.
+ *
+ * Returns an array( $xSpace, $ySpace ), containing the irespective size in
+ * pixels. Additionally calculates the grid boundings passed by reference.
+ *
+ * @param ezcGraphRenderer $renderer
+ * @param ezcGraphBoundings $boundings
+ * @param mixed $innerBoundings
+ * @return array
+ */
+ protected function getAxisSpace( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis, $innerBoundings, &$gridBoundings )
+ {
+ if ( $innerBoundings !== null )
+ {
+ $gridBoundings = clone $innerBoundings;
+ $xSpace = abs( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 - $boundings->x0 : $boundings->x1 - $innerBoundings->x1 );
+ $ySpace = abs( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 - $boundings->y0 : $boundings->y1 - $innerBoundings->y1 );
+ }
+ else
+ {
+ $gridBoundings = new ezcGraphBoundings(
+ $boundings->x0 + ( $xSpace = abs( $renderer->xAxisSpace ) ),
+ $boundings->y0 + ( $ySpace = abs( $renderer->yAxisSpace ) ),
+ $boundings->x1 - $xSpace,
+ $boundings->y1 - $ySpace
+ );
+ }
+
+ if ( $this->outerGrid )
+ {
+ $gridBoundings = $boundings;
+ }
+
+ return array( $xSpace, $ySpace );
+ }
+
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ abstract public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis
+ );
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/chart.php b/src/TUnit/external/ezc/Graph/interfaces/chart.php
new file mode 100644
index 0000000..9c30eb7
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/chart.php
@@ -0,0 +1,296 @@
+palette = new ezcGraphPaletteTango();
+ $this->data = new ezcGraphChartDataContainer( $this );
+
+ // Add standard elements
+ $this->addElement( 'background', new ezcGraphChartElementBackground() );
+ $this->elements['background']->position = ezcGraph::CENTER | ezcGraph::MIDDLE;
+
+ $this->addElement( 'title', new ezcGraphChartElementText() );
+ $this->elements['title']->position = ezcGraph::TOP;
+ $this->renderElement['title'] = false;
+
+ $this->addElement( 'subtitle', new ezcGraphChartElementText() );
+ $this->elements['subtitle']->position = ezcGraph::TOP;
+ $this->renderElement['subtitle'] = false;
+
+ $this->addElement( 'legend', new ezcGraphChartElementLegend() );
+ $this->elements['legend']->position = ezcGraph::LEFT;
+
+ // Define standard renderer and driver
+ $this->properties['driver'] = new ezcGraphSvgDriver();
+ $this->properties['renderer'] = new ezcGraphRenderer2d();
+ $this->properties['renderer']->setDriver( $this->driver );
+
+ // Initialize other properties
+ $this->properties['renderedFile'] = null;
+ }
+
+ /**
+ * Add element to chart
+ *
+ * Add a chart element to the chart and perform the required configuration
+ * tasks for the chart element.
+ *
+ * @param string $name Element name
+ * @param ezcGraphChartElement $element Chart element
+ * @return void
+ */
+ protected function addElement( $name, ezcGraphChartElement $element )
+ {
+ $this->elements[$name] = $element;
+ $this->elements[$name]->font = $this->options->font;
+ $this->elements[$name]->setFromPalette( $this->palette );
+
+ // Render element by default
+ $this->renderElement[$name] = true;
+ }
+
+ /**
+ * Options write access
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If Option could not be found
+ * @throws ezcBaseValueException
+ * If value is out of range
+ * @param mixed $propertyName Option name
+ * @param mixed $propertyValue Option value;
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName ) {
+ case 'title':
+ case 'subtitle':
+ $this->elements[$propertyName]->title = $propertyValue;
+ $this->renderElement[$propertyName] = true;
+ break;
+ case 'background':
+ $this->elements[$propertyName]->color = $propertyValue;
+ break;
+ case 'legend':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'boolean' );
+ }
+
+ $this->renderElement['legend'] = (bool) $propertyValue;
+ break;
+ case 'renderer':
+ if ( $propertyValue instanceof ezcGraphRenderer )
+ {
+ $this->properties['renderer'] = $propertyValue;
+ $this->properties['renderer']->setDriver( $this->driver );
+ return $this->properties['renderer'];
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphRenderer' );
+ }
+ break;
+ case 'driver':
+ if ( $propertyValue instanceof ezcGraphDriver )
+ {
+ $this->properties['driver'] = $propertyValue;
+ $this->properties['renderer']->setDriver( $this->driver );
+ return $this->properties['driver'];
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphDriver' );
+ }
+ break;
+ case 'palette':
+ if ( $propertyValue instanceof ezcGraphPalette )
+ {
+ $this->properties['palette'] = $propertyValue;
+ $this->setFromPalette( $this->palette );
+ }
+ else
+ {
+ throw new ezcBaseValueException( "palette", $propertyValue, "instanceof ezcGraphPalette" );
+ }
+
+ break;
+ case 'renderedFile':
+ $this->properties['renderedFile'] = (string) $propertyValue;
+ break;
+ case 'options':
+ if ( $propertyValue instanceof ezcGraphChartOptions )
+ {
+ $this->options = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( "options", $propertyValue, "instanceof ezcGraphOptions" );
+ }
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+
+ /**
+ * Set colors and border fro this element
+ *
+ * @param ezcGraphPalette $palette Palette
+ * @return void
+ */
+ public function setFromPalette( ezcGraphPalette $palette )
+ {
+ $this->options->font->name = $palette->fontName;
+ $this->options->font->color = $palette->fontColor;
+
+ foreach ( $this->elements as $element )
+ {
+ $element->setFromPalette( $palette );
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ if ( array_key_exists( $propertyName, $this->properties ) )
+ {
+ return $this->properties[$propertyName];
+ }
+
+ if ( isset( $this->elements[$propertyName] ) )
+ {
+ return $this->elements[$propertyName];
+ }
+
+ if ( ( $propertyName === 'options' ) ||
+ ( $propertyName === 'data' ) )
+ {
+ return $this->$propertyName;
+ }
+ else
+ {
+ throw new ezcGraphNoSuchElementException( $propertyName );
+ }
+ }
+
+ /**
+ * Returns the default display type of the current chart type.
+ *
+ * @return int Display type
+ */
+ abstract public function getDefaultDisplayType();
+
+ /**
+ * Return filename of rendered file, and false if no file was yet rendered.
+ *
+ * @return mixed
+ */
+ public function getRenderedFile()
+ {
+ return ( $this->renderedFile !== null ? $this->renderedFile : false );
+ }
+
+ /**
+ * Renders this chart
+ *
+ * Creates basic visual chart elements from the chart to be processed by
+ * the renderer.
+ *
+ * @param int $width
+ * @param int $height
+ * @param string $file
+ * @return void
+ */
+ abstract public function render( $width, $height, $file = null );
+
+ /**
+ * Renders this chart to direct output
+ *
+ * Does the same as ezcGraphChart::render(), but renders directly to
+ * output and not into a file.
+ *
+ * @param int $width
+ * @param int $height
+ * @return void
+ */
+ abstract public function renderToOutput( $width, $height );
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/dataset_property.php b/src/TUnit/external/ezc/Graph/interfaces/dataset_property.php
new file mode 100644
index 0000000..72b6035
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/dataset_property.php
@@ -0,0 +1,209 @@
+
+ * $graph = new ezcGraphLineChart();
+ * $graph->data['example'] = new ezcGraphArrayDataSet( array(
+ * 'Foo' => 23,
+ * 'Bar' => 42,
+ * ) );
+ *
+ * // Set color for all data points in this data set
+ * $graph->data['example']->color = '#a40000';
+ *
+ * // Set different color for one special datapoint
+ * $graph->data['example']->color['Foo'] = '#2e3436';
+ *
+ * $graph->render( 400, 200, 'test.svg' );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+abstract class ezcGraphDataSetProperty implements ArrayAccess
+{
+ /**
+ * Default value for this property
+ *
+ * @var mixed
+ */
+ protected $defaultValue;
+
+ /**
+ * Contains specified values for single dataset elements
+ *
+ * @var array
+ */
+ protected $dataValue;
+
+ /**
+ * Contains a reference to the dataset to check for availability of data
+ * keys
+ *
+ * @var ezcGraphDataSet
+ */
+ protected $dataset;
+
+ /**
+ * Abstract method to contain the check for validity of the value
+ *
+ * @param mixed $value
+ * @return void
+ */
+ abstract protected function checkValue( &$value );
+
+ /**
+ * Constructor
+ *
+ * @param ezcGraphDataSet $dataset
+ * @ignore
+ * @return void
+ */
+ public function __construct( ezcGraphDataSet $dataset )
+ {
+ $this->dataset = $dataset;
+ }
+
+ /**
+ * Set the default value for this property
+ *
+ * @param string $name Property name
+ * @param mixed $value Property value
+ * @return void
+ */
+ public function __set( $name, $value )
+ {
+ if ( $name === 'default' &&
+ $this->checkValue( $value ) )
+ {
+ $this->defaultValue = $value;
+ }
+ }
+
+ /**
+ * Get the default value for this property
+ *
+ * @param string $name Property name
+ * @return mixed
+ */
+ public function __get( $name )
+ {
+ if ( $name === 'default' )
+ {
+ return $this->defaultValue;
+ }
+ }
+
+ /**
+ * Returns if an option exists.
+ * Allows isset() using ArrayAccess.
+ *
+ * @param string $key The name of the option to get.
+ * @return bool Wether the option exists.
+ */
+ final public function offsetExists( $key )
+ {
+ return isset( $this->dataset[$key] );
+ }
+
+ /**
+ * Returns an option value.
+ * Get an option value by ArrayAccess.
+ *
+ * @param string $key The name of the option to get.
+ * @return mixed The option value.
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ */
+ final public function offsetGet( $key )
+ {
+ if ( isset( $this->dataValue[$key] ) )
+ {
+ return $this->dataValue[$key];
+ }
+ elseif ( isset( $this->dataset[$key] ) )
+ {
+ return $this->defaultValue;
+ }
+ else
+ {
+ throw new ezcGraphNoSuchDataException( $key );
+ }
+ }
+
+ /**
+ * Set an option.
+ * Sets an option using ArrayAccess.
+ *
+ * @param string $key The option to set.
+ * @param mixed $value The value for the option.
+ * @return void
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @throws ezcBaseValueException
+ * If a the value for a property is out of range.
+ */
+ public function offsetSet( $key, $value )
+ {
+ if ( isset( $this->dataset[$key] ) &&
+ $this->checkValue( $value ) )
+ {
+ $this->dataValue[$key] = $value;
+ }
+ else
+ {
+ throw new ezcGraphNoSuchDataException( $key );
+ }
+ }
+
+ /**
+ * Unset an option.
+ * Unsets an option using ArrayAccess.
+ *
+ * @param string $key The options to unset.
+ * @return void
+ *
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @throws ezcBaseValueException
+ * If a the value for a property is out of range.
+ */
+ final public function offsetUnset( $key )
+ {
+ if ( isset( $this->dataset[$key] ) )
+ {
+ unset( $this->dataValue[$key] );
+ }
+ else
+ {
+ throw new ezcGraphNoSuchDataException( $key );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/driver.php b/src/TUnit/external/ezc/Graph/interfaces/driver.php
new file mode 100644
index 0000000..b4ddc9f
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/driver.php
@@ -0,0 +1,740 @@
+options = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( "options", $propertyValue, "instanceof ezcGraphOptions" );
+ }
+ break;
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'options':
+ return $this->options;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+
+ /**
+ * Reduces the size of a polygon
+ *
+ * The method takes a polygon defined by a list of points and reduces its
+ * size by moving all lines to the middle by the given $size value.
+ *
+ * The detection of the inner side of the polygon depends on the angle at
+ * each edge point. This method will always work for 3 edged polygones,
+ * because the smaller angle will always be on the inner side. For
+ * polygons with more then 3 edges this method may fail. For ezcGraph this
+ * is a valid simplification, because we do not have any polygones which
+ * have an inner angle >= 180 degrees.
+ *
+ * @param array(ezcGraphCoordinate) $points
+ * @param float $size
+ * @throws ezcGraphReducementFailedException
+ * @return array( ezcGraphCoordinate )
+ */
+ protected function reducePolygonSize( array $points, $size )
+ {
+ $pointCount = count( $points );
+
+ // Build normalized vectors between polygon edge points
+ $vectors = array();
+ $vectorLength = array();
+ for ( $i = 0; $i < $pointCount; ++$i )
+ {
+ $nextPoint = ( $i + 1 ) % $pointCount;
+ $vectors[$i] = ezcGraphVector::fromCoordinate( $points[$nextPoint] )
+ ->sub( $points[$i] );
+
+ // Throw exception if polygon is too small to reduce
+ $vectorLength[$i] = $vectors[$i]->length();
+ if ( $vectorLength[$i] < $size )
+ {
+ throw new ezcGraphReducementFailedException();
+ }
+ $vectors[$i]->unify();
+
+ // Remove point from list if it the same as the next point
+ if ( ( $vectors[$i]->x == $vectors[$i]->y ) && ( $vectors[$i]->x == 0 ) )
+ {
+ $pointCount--;
+ if ( $i === 0 )
+ {
+ $points = array_slice( $points, $i + 1 );
+ }
+ else
+ {
+ $points = array_merge(
+ array_slice( $points, 0, $i ),
+ array_slice( $points, $i + 1 )
+ );
+ }
+ $i--;
+ }
+ }
+
+ // Remove vectors and appendant point, if local angle equals zero
+ // dergrees.
+ for ( $i = 0; $i < $pointCount; ++$i )
+ {
+ $nextPoint = ( $i + 1 ) % $pointCount;
+
+ if ( ( abs( $vectors[$i]->x - $vectors[$nextPoint]->x ) < .0001 ) &&
+ ( abs( $vectors[$i]->y - $vectors[$nextPoint]->y ) < .0001 ) )
+ {
+ $pointCount--;
+
+ $points = array_merge(
+ array_slice( $points, 0, $i + 1 ),
+ array_slice( $points, $i + 2 )
+ );
+ $vectors = array_merge(
+ array_slice( $vectors, 0, $i + 1 ),
+ array_slice( $vectors, $i + 2 )
+ );
+ $i--;
+ }
+ }
+
+ // No reducements for lines
+ if ( $pointCount <= 2 )
+ {
+ return $points;
+ }
+
+ // Determine one of the angles - we need to know where the smaller
+ // angle is, to determine if the inner side of the polygon is on
+ // the left or right hand.
+ //
+ // This is a valid simplification for ezcGraph(, for now).
+ //
+ // The sign of the scalar products results indicates on which site
+ // the smaller angle is, when comparing the orthogonale vector of
+ // one of the vectors with the other. Why? .. use pen and paper ..
+ //
+ // It is sufficant to do this once before iterating over the points,
+ // because the inner side of the polygon is on the same side of the
+ // point for each point.
+ $last = 0;
+ $next = 1;
+
+ $sign = (
+ -$vectors[$last]->y * $vectors[$next]->x +
+ $vectors[$last]->x * $vectors[$next]->y
+ ) < 0 ? 1 : -1;
+
+ // Move points to center
+ $newPoints = array();
+ for ( $i = 0; $i < $pointCount; ++$i )
+ {
+ $last = $i;
+ $next = ( $i + 1 ) % $pointCount;
+
+ // Orthogonal vector with direction based on the side of the inner
+ // angle
+ $v = clone $vectors[$next];
+ if ( $sign > 0 )
+ {
+ $v->rotateCounterClockwise()->scalar( $size );
+ }
+ else
+ {
+ $v->rotateClockwise()->scalar( $size );
+ }
+
+ // get last vector not pointing in reverse direction
+ $lastVector = clone $vectors[$last];
+ $lastVector->scalar( -1 );
+
+ // Calculate new point: Move point to the center site of the
+ // polygon using the normalized orthogonal vectors next to the
+ // point and the size as distance to move.
+ // point + v + size / tan( angle / 2 ) * startVector
+ $newPoint = clone $vectors[$next];
+ $v ->add(
+ $newPoint
+ ->scalar(
+ $size /
+ tan(
+ $lastVector->angle( $vectors[$next] ) / 2
+ )
+ )
+ );
+
+ // A fast guess: If the movement of the point exceeds the length of
+ // the surrounding edge vectors the angle was to small to perform a
+ // valid size reducement. In this case we just reduce the length of
+ // the movement to the minimal length of the surrounding vectors.
+ // This should fit in most cases.
+ //
+ // The correct way to check would be a test, if the calculated
+ // point is still in the original polygon, but a test for a point
+ // in a polygon is too expensive.
+ $movement = $v->length();
+ if ( ( $movement > $vectorLength[$last] ) &&
+ ( $movement > $vectorLength[$next] ) )
+ {
+ $v->unify()->scalar( min( $vectorLength[$last], $vectorLength[$next] ) );
+ }
+
+ $newPoints[$next] = $v->add( $points[$next] );
+ }
+
+ return $newPoints;
+ }
+
+ /**
+ * Reduce the size of an ellipse
+ *
+ * The method returns a the edgepoints and angles for an ellipse where all
+ * borders are moved to the inner side of the ellipse by the give $size
+ * value.
+ *
+ * The method returns an
+ * array (
+ * 'center' => (ezcGraphCoordinate) New center point,
+ * 'start' => (ezcGraphCoordinate) New outer start point,
+ * 'end' => (ezcGraphCoordinate) New outer end point,
+ * )
+ *
+ * @param ezcGraphCoordinate $center
+ * @param float $width
+ * @param float $height
+ * @param float $startAngle
+ * @param float $endAngle
+ * @param float $size
+ * @throws ezcGraphReducementFailedException
+ * @return array
+ */
+ protected function reduceEllipseSize( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, $size )
+ {
+ $oldStartPoint = new ezcGraphVector(
+ $width * cos( deg2rad( $startAngle ) ) / 2,
+ $height * sin( deg2rad( $startAngle ) ) / 2
+ );
+
+ $oldEndPoint = new ezcGraphVector(
+ $width * cos( deg2rad( $endAngle ) ) / 2,
+ $height * sin( deg2rad( $endAngle ) ) / 2
+ );
+
+ // We always need radian values..
+ $degAngle = abs( $endAngle - $startAngle );
+ $startAngle = deg2rad( $startAngle );
+ $endAngle = deg2rad( $endAngle );
+
+ // Calculate normalized vectors for the lines spanning the ellipse
+ $unifiedStartVector = ezcGraphVector::fromCoordinate( $oldStartPoint )->unify();
+ $unifiedEndVector = ezcGraphVector::fromCoordinate( $oldEndPoint )->unify();
+ $startVector = ezcGraphVector::fromCoordinate( $oldStartPoint );
+ $endVector = ezcGraphVector::fromCoordinate( $oldEndPoint );
+
+ $oldStartPoint->add( $center );
+ $oldEndPoint->add( $center );
+
+ // Use orthogonal vectors of normalized ellipse spanning vectors to
+ $v = clone $unifiedStartVector;
+ $v->rotateClockwise()->scalar( $size );
+
+ // calculate new center point
+ // center + v + size / tan( angle / 2 ) * startVector
+ $centerMovement = clone $unifiedStartVector;
+ $newCenter = $v->add( $centerMovement->scalar( $size / tan( ( $endAngle - $startAngle ) / 2 ) ) )->add( $center );
+
+ // Test if center is still inside the ellipse, otherwise the sector
+ // was to small to be reduced
+ $innerBoundingBoxSize = 0.7 * min( $width, $height );
+ if ( ( $newCenter->x < ( $center->x + $innerBoundingBoxSize ) ) &&
+ ( $newCenter->x > ( $center->x - $innerBoundingBoxSize ) ) &&
+ ( $newCenter->y < ( $center->y + $innerBoundingBoxSize ) ) &&
+ ( $newCenter->y > ( $center->y - $innerBoundingBoxSize ) ) )
+ {
+ // Point is in inner bounding box -> everything is OK
+ }
+ elseif ( ( $newCenter->x < ( $center->x - $width ) ) ||
+ ( $newCenter->x > ( $center->x + $width ) ) ||
+ ( $newCenter->y < ( $center->y - $height ) ) ||
+ ( $newCenter->y > ( $center->y + $height ) ) )
+ {
+ // Quick outer boundings check
+ if ( $degAngle > 180 )
+ {
+ // Use old center for very big angles
+ $newCenter = clone $center;
+ }
+ else
+ {
+ // Do not draw for very small angles
+ throw new ezcGraphReducementFailedException();
+ }
+ }
+ else
+ {
+ // Perform exact check
+ $distance = new ezcGraphVector(
+ $newCenter->x - $center->x,
+ $newCenter->y - $center->y
+ );
+
+ // Convert elipse to circle for correct angle calculation
+ $direction = clone $distance;
+ $direction->y *= ( $width / $height );
+ $angle = $direction->angle( new ezcGraphVector( 0, 1 ) );
+
+ $outerPoint = new ezcGraphVector(
+ sin( $angle ) * $width / 2,
+ cos( $angle ) * $height / 2
+ );
+
+ // Point is not in ellipse any more
+ if ( abs( $distance->x ) > abs( $outerPoint->x ) )
+ {
+ if ( $degAngle > 180 )
+ {
+ // Use old center for very big angles
+ $newCenter = clone $center;
+ }
+ else
+ {
+ // Do not draw for very small angles
+ throw new ezcGraphReducementFailedException();
+ }
+ }
+ }
+
+ // Use start spanning vector and its orthogonal vector to calculate
+ // new start point
+ $newStartPoint = clone $oldStartPoint;
+
+ // Create tangent vector from tangent angle
+
+ // Ellipse tangent factor
+ $ellipseTangentFactor = sqrt(
+ pow( $height, 2 ) *
+ pow( cos( $startAngle ), 2 ) +
+ pow( $width, 2 ) *
+ pow( sin( $startAngle ), 2 )
+ );
+ $ellipseTangentVector = new ezcGraphVector(
+ $width * -sin( $startAngle ) / $ellipseTangentFactor,
+ $height * cos( $startAngle ) / $ellipseTangentFactor
+ );
+
+ // Reverse spanning vector
+ $innerVector = clone $unifiedStartVector;
+ $innerVector->scalar( $size )->scalar( -1 );
+
+ $newStartPoint->add( $innerVector)->add( $ellipseTangentVector->scalar( $size ) );
+ $newStartVector = clone $startVector;
+ $newStartVector->add( $ellipseTangentVector );
+
+ // Use end spanning vector and its orthogonal vector to calculate
+ // new end point
+ $newEndPoint = clone $oldEndPoint;
+
+ // Create tangent vector from tangent angle
+
+ // Ellipse tangent factor
+ $ellipseTangentFactor = sqrt(
+ pow( $height, 2 ) *
+ pow( cos( $endAngle ), 2 ) +
+ pow( $width, 2 ) *
+ pow( sin( $endAngle ), 2 )
+ );
+ $ellipseTangentVector = new ezcGraphVector(
+ $width * -sin( $endAngle ) / $ellipseTangentFactor,
+ $height * cos( $endAngle ) / $ellipseTangentFactor
+ );
+
+ // Reverse spanning vector
+ $innerVector = clone $unifiedEndVector;
+ $innerVector->scalar( $size )->scalar( -1 );
+
+ $newEndPoint->add( $innerVector )->add( $ellipseTangentVector->scalar( $size )->scalar( -1 ) );
+ $newEndVector = clone $endVector;
+ $newEndVector->add( $ellipseTangentVector );
+
+ return array(
+ 'center' => $newCenter,
+ 'start' => $newStartPoint,
+ 'end' => $newEndPoint,
+ 'startAngle' => rad2deg( $startAngle + $startVector->angle( $newStartVector ) ),
+ 'endAngle' => rad2deg( $endAngle - $endVector->angle( $newEndVector ) ),
+ );
+ }
+
+ /**
+ * Draws a single polygon.
+ *
+ * @param array $points Point array
+ * @param ezcGraphColor $color Polygon color
+ * @param mixed $filled Filled
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ abstract public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. );
+
+ /**
+ * Draws a line
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Line color
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ abstract public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. );
+
+ /**
+ * Returns boundings of text depending on the available font extension
+ *
+ * @param float $size Textsize
+ * @param ezcGraphFontOptions $font Font
+ * @param string $text Text
+ * @return ezcGraphBoundings Boundings of text
+ */
+ abstract protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text );
+
+ /**
+ * Test if string fits in a box with given font size
+ *
+ * This method splits the text up into tokens and tries to wrap the text
+ * in an optimal way to fit in the Box defined by width and height.
+ *
+ * If the text fits into the box an array with lines is returned, which
+ * can be used to render the text later:
+ * array(
+ * // Lines
+ * array( 'word', 'word', .. ),
+ * )
+ * Otherwise the function will return false.
+ *
+ * @param string $string Text
+ * @param ezcGraphCoordinate $position Topleft position of the text box
+ * @param float $width Width of textbox
+ * @param float $height Height of textbox
+ * @param int $size Fontsize
+ * @return mixed Array with lines or false on failure
+ */
+ protected function testFitStringInTextBox( $string, ezcGraphCoordinate $position, $width, $height, $size )
+ {
+ // Tokenize String
+ $tokens = preg_split( '/\s+/', $string );
+ $initialHeight = $height;
+
+ $lines = array( array() );
+ $line = 0;
+ foreach ( $tokens as $nr => $token )
+ {
+ // Add token to tested line
+ $selectedLine = $lines[$line];
+ $selectedLine[] = $token;
+
+ $boundings = $this->getTextBoundings( $size, $this->options->font, implode( ' ', $selectedLine ) );
+ // Check if line is too long
+ if ( $boundings->width > $width )
+ {
+ if ( count( $selectedLine ) == 1 )
+ {
+ // Return false if one single word does not fit into one line
+ // Scale down font size to fit this word in one line
+ return $width / $boundings->width;
+ }
+ else
+ {
+ // Put word in next line instead and reduce available height by used space
+ $lines[++$line][] = $token;
+ $height -= $size * ( 1 + $this->options->lineSpacing );
+ }
+ }
+ else
+ {
+ // Everything is ok - put token in this line
+ $lines[$line][] = $token;
+ }
+
+ // Return false if text exceeds vertical limit
+ if ( $size > $height )
+ {
+ return 1;
+ }
+ }
+
+ // Check width of last line
+ $boundings = $this->getTextBoundings( $size, $this->options->font, implode( ' ', $lines[$line] ) );
+ if ( $boundings->width > $width )
+ {
+ return 1;
+ }
+
+ // It seems to fit - return line array
+ return $lines;
+ }
+
+ /**
+ * If it is allow to shortened the string, this method tries to extract as
+ * many chars as possible to display a decent amount of characters.
+ *
+ * If no complete token (word) does fit, the largest possible amount of
+ * chars from the first word are taken. If the amount of chars is bigger
+ * then strlen( shortenedStringPostFix ) * 2 the last chars are replace by
+ * the postfix.
+ *
+ * If one complete word fits the box as many words are taken as possible
+ * including a appended shortenedStringPostFix.
+ *
+ * @param mixed $string
+ * @param ezcGraphCoordinate $position
+ * @param mixed $width
+ * @param mixed $height
+ * @param mixed $size
+ * @access protected
+ * @return void
+ */
+ protected function tryFitShortenedString( $string, ezcGraphCoordinate $position, $width, $height, $size )
+ {
+ $tokens = preg_split( '/\s+/', $string );
+
+ // Try to fit a complete word first
+ $boundings = $this->getTextBoundings(
+ $size,
+ $this->options->font,
+ reset( $tokens ) . ( $postfix = $this->options->autoShortenStringPostFix )
+ );
+
+ if ( $boundings->width > $width )
+ {
+ // Not even one word fits the box
+ $word = reset( $tokens );
+
+ // Test if first character fits the box
+ $boundigs = $this->getTextBoundings(
+ $size,
+ $this->options->font,
+ $hit = $word[0]
+ );
+
+ if ( $boundigs->width > $width )
+ {
+ // That is a really small box.
+ throw new ezcGraphFontRenderingException( $string, $size, $width, $height );
+ }
+
+ // Try to put more charactes in there
+ $postLength = strlen( $postfix );
+ $wordLength = strlen( $word );
+ for ( $i = 2; $i <= $wordLength; ++$i )
+ {
+ $string = substr( $word, 0, $i );
+ if ( strlen( $string ) > ( $postLength << 1 ) )
+ {
+ $string = substr( $string, 0, -$postLength ) . $postfix;
+ }
+
+ $boundigs = $this->getTextBoundings( $size, $this->options->font, $string );
+
+ if ( $boundigs->width < $width )
+ {
+ $hit = $string;
+ }
+ else
+ {
+ // Use last string which fit
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Try to use as many words as possible
+ $hit = reset( $tokens );
+
+ for ( $i = 2; $i < count( $tokens ); ++$i )
+ {
+ $string = implode( ' ', array_slice( $tokens, 0, $i ) ) .
+ $postfix;
+
+ $boundings = $this->getTextBoundings( $size, $this->options->font, $string );
+
+ if ( $boundings->width <= $width )
+ {
+ $hit .= ' ' . $tokens[$i - 1];
+ }
+ else
+ {
+ // Use last valid hit
+ break;
+ }
+ }
+
+ $hit .= $postfix;
+ }
+
+ return array( array( $hit ) );
+ }
+
+ /**
+ * Writes text in a box of desired size
+ *
+ * @param string $string Text
+ * @param ezcGraphCoordinate $position Top left position
+ * @param float $width Width of text box
+ * @param float $height Height of text box
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ abstract public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null );
+
+ /**
+ * Draws a sector of cirlce
+ *
+ * @param ezcGraphCoordinate $center Center of circle
+ * @param mixed $width Width
+ * @param mixed $height Height
+ * @param mixed $startAngle Start angle of circle sector
+ * @param mixed $endAngle End angle of circle sector
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ abstract public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true );
+
+ /**
+ * Draws a circular arc
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param integer $width Width of ellipse
+ * @param integer $height Height of ellipse
+ * @param integer $size Height of border
+ * @param float $startAngle Starting angle of circle sector
+ * @param float $endAngle Ending angle of circle sector
+ * @param ezcGraphColor $color Color of Border
+ * @param bool $filled Fill state
+ * @return void
+ */
+ abstract public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true );
+
+ /**
+ * Draw circle
+ *
+ * @param ezcGraphCoordinate $center Center of ellipse
+ * @param mixed $width Width of ellipse
+ * @param mixed $height height of ellipse
+ * @param ezcGraphColor $color Color
+ * @param mixed $filled Filled
+ * @return void
+ */
+ abstract public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true );
+
+ /**
+ * Draw an image
+ *
+ * @param mixed $file Image file
+ * @param ezcGraphCoordinate $position Top left position
+ * @param mixed $width Width of image in destination image
+ * @param mixed $height Height of image in destination image
+ * @return void
+ */
+ abstract public function drawImage( $file, ezcGraphCoordinate $position, $width, $height );
+
+ /**
+ * Return mime type for current image format
+ *
+ * @return string
+ */
+ abstract public function getMimeType();
+
+ /**
+ * Render image directly to output
+ *
+ * The method renders the image directly to the standard output. You
+ * normally do not want to use this function, because it makes it harder
+ * to proper cache the generated graphs.
+ *
+ * @return void
+ */
+ public function renderToOutput()
+ {
+ header( 'Content-Type: ' . $this->getMimeType() );
+ $this->render( 'php://output' );
+ }
+
+ /**
+ * Finally save image
+ *
+ * @param string $file Destination filename
+ * @return void
+ */
+ abstract public function render( $file );
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/element.php b/src/TUnit/external/ezc/Graph/interfaces/element.php
new file mode 100644
index 0000000..52233a0
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/element.php
@@ -0,0 +1,342 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->data['example'] = new ezcGraphArrayDataSet( array(
+ * 'Foo' => 23,
+ * 'Bar' => 42,
+ * ) );
+ *
+ * // Set a title and format the title element
+ * $graph->title = 'Example formatted pie chart';
+ * $graph->title->margin = 2;
+ * $graph->title->background = '#FFFFFF80';
+ * $graph->title->border = '#FFFFFF';
+ * $graph->title->borderWidth = 1;
+ * $graph->title->margin = 1;
+ * $graph->title->padding = 1;
+ *
+ * // Format the legend element
+ * $graph->legend->margin = 2;
+ * $graph->legend->background = '#FFFFFF80';
+ * $graph->legend->border = '#FFFFFF';
+ * $graph->legend->borderWidth = 1;
+ * $graph->legend->margin = 1;
+ * $graph->legend->padding = 1;
+ *
+ * $graph->background->background = '#888a85';
+ *
+ * $graph->render( 400, 250, 'element.svg' );
+ *
+ *
+ * @property string $title
+ * Title of chart element.
+ * @property ezcGraphColor $background
+ * Background color of chart element.
+ * @property ezcGraphColor $border
+ * Border color of chart element.
+ * @property int $padding
+ * Distance between border and content of element.
+ * @property int $margin
+ * Distance between outer boundings and border of an element.
+ * @property int $borderWidth
+ * Border width.
+ * @property int $position
+ * Integer defining the elements position in the chart.
+ * @property int $maxTitleHeight
+ * Maximum size of the title.
+ * @property float $portraitTitleSize
+ * Percentage of boundings which are used for the title with
+ * position left, right or center.
+ * @property float $landscapeTitleSize
+ * Percentage of boundings which are used for the title with
+ * position top or bottom.
+ * @property ezcGraphFontOptions $font
+ * Font used for this element.
+ * @property-read bool $fontCloned
+ * Indicates if font configuration was already cloned for this
+ * specific element.
+ * @property-read ezcGraphBoundings $boundings
+ * Boundings of this elements.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+abstract class ezcGraphChartElement extends ezcBaseOptions
+{
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['title'] = false;
+ $this->properties['background'] = false;
+ $this->properties['boundings'] = new ezcGraphBoundings();
+ $this->properties['border'] = false;
+ $this->properties['borderWidth'] = 0;
+ $this->properties['padding'] = 0;
+ $this->properties['margin'] = 0;
+ $this->properties['position'] = ezcGraph::LEFT;
+ $this->properties['maxTitleHeight'] = 16;
+ $this->properties['portraitTitleSize'] = .15;
+ $this->properties['landscapeTitleSize'] = .2;
+ $this->properties['font'] = new ezcGraphFontOptions();
+ $this->properties['fontCloned'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set colors and border fro this element
+ *
+ * @param ezcGraphPalette $palette Palette
+ * @return void
+ */
+ public function setFromPalette( ezcGraphPalette $palette )
+ {
+ $this->properties['border'] = $palette->elementBorderColor;
+ $this->properties['borderWidth'] = $palette->elementBorderWidth;
+ $this->properties['background'] = $palette->elementBackground;
+ $this->properties['padding'] = $palette->padding;
+ $this->properties['margin'] = $palette->margin;
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'title':
+ $this->properties['title'] = (string) $propertyValue;
+ break;
+ case 'background':
+ $this->properties['background'] = ezcGraphColor::create( $propertyValue );
+ break;
+ case 'border':
+ $this->properties['border'] = ezcGraphColor::create( $propertyValue );
+ break;
+ case 'padding':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['padding'] = (int) $propertyValue;
+ break;
+ case 'margin':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['margin'] = (int) $propertyValue;
+ break;
+ case 'borderWidth':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['borderWidth'] = (int) $propertyValue;
+ break;
+ case 'font':
+ if ( $propertyValue instanceof ezcGraphFontOptions )
+ {
+ $this->properties['font'] = $propertyValue;
+ }
+ elseif ( is_string( $propertyValue ) )
+ {
+ if ( !$this->fontCloned )
+ {
+ $this->properties['font'] = clone $this->font;
+ $this->properties['fontCloned'] = true;
+ }
+
+ $this->properties['font']->path = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' );
+ }
+ break;
+ case 'position':
+ $positions = array(
+ ezcGraph::TOP,
+ ezcGraph::BOTTOM,
+ ezcGraph::LEFT,
+ ezcGraph::RIGHT,
+ );
+
+ if ( in_array( $propertyValue, $positions, true ) )
+ {
+ $this->properties['position'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( 'position', $propertyValue, 'integer' );
+ }
+ break;
+ case 'maxTitleHeight':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties['maxTitleHeight'] = (int) $propertyValue;
+ break;
+ case 'portraitTitleSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['portraitTitleSize'] = (float) $propertyValue;
+ break;
+ case 'landscapeTitleSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['landscapeTitleSize'] = (float) $propertyValue;
+ break;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'font':
+ // Clone font configuration when requested for this element
+ if ( !$this->fontCloned )
+ {
+ $this->properties['font'] = clone $this->properties['font'];
+ $this->properties['fontCloned'] = true;
+ }
+ return $this->properties['font'];
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+
+ /**
+ * Renders this chart element
+ *
+ * This method receives and returns a part of the canvas where it can be
+ * rendered on.
+ *
+ * @param ezcGraphRenderer $renderer
+ * @param ezcGraphBoundings $boundings
+ * @return ezcGraphBoundings Part of canvas, which is still free to draw on
+ */
+ abstract public function render( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings );
+
+ /**
+ * Returns calculated boundings based on available percentual space of
+ * given bounding box specified in the elements options and direction of
+ * the box.
+ *
+ * @param ezcGraphBoundings $boundings
+ * @param int $direction
+ * @return ezcGraphBoundings
+ */
+ protected function getTitleSize( ezcGraphBoundings $boundings, $direction = ezcGraph::HORIZONTAL )
+ {
+ if ( $direction === ezcGraph::HORIZONTAL )
+ {
+ return min(
+ $this->maxTitleHeight,
+ ( $boundings->y1 - $boundings->y0 ) * $this->landscapeTitleSize
+ );
+ }
+ else
+ {
+ return min(
+ $this->maxTitleHeight,
+ ( $boundings->y1 - $boundings->y0 ) * $this->portraitTitleSize
+ );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/odometer_renderer.php b/src/TUnit/external/ezc/Graph/interfaces/odometer_renderer.php
new file mode 100644
index 0000000..ed32060
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/odometer_renderer.php
@@ -0,0 +1,51 @@
+
diff --git a/src/TUnit/external/ezc/Graph/interfaces/palette.php b/src/TUnit/external/ezc/Graph/interfaces/palette.php
new file mode 100644
index 0000000..fbaa845
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/palette.php
@@ -0,0 +1,284 @@
+colorIndex = -1;
+ $this->symbolIndex = -1;
+ }
+
+ /**
+ * Returns the requested property
+ *
+ * @param string $propertyName Name of property
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'axisColor':
+ case 'majorGridColor':
+ case 'minorGridColor':
+ case 'fontColor':
+ case 'chartBackground':
+ case 'chartBorderColor':
+ case 'elementBackground':
+ case 'elementBorderColor':
+ return ( $this->$propertyName = $this->checkColor( $this->$propertyName ) );
+
+ case 'dataSetColor':
+ $this->colorIndex = ( ( $this->colorIndex + 1 ) % count( $this->dataSetColor ) );
+ return $this->checkColor( $this->dataSetColor[ $this->colorIndex ] );
+ case 'dataSetSymbol':
+ $this->symbolIndex = ( ( $this->symbolIndex + 1 ) % count( $this->dataSetSymbol ) );
+ return $this->dataSetSymbol[ $this->symbolIndex ];
+
+ case 'fontName':
+ case 'chartBorderWidth':
+ case 'elementBorderWidth':
+ case 'padding':
+ case 'margin':
+ return $this->$propertyName;
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName Property name
+ * @param mixed $propertyValue Property value
+ * @access public
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'axisColor':
+ case 'majorGridColor':
+ case 'minorGridColor':
+ case 'fontColor':
+ case 'chartBackground':
+ case 'chartBorderColor':
+ case 'elementBackground':
+ case 'elementBorderColor':
+ $this->$propertyName = ezcGraphColor::create( $propertyValue );
+ break;
+
+ case 'dataSetColor':
+ if ( !is_array( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'array( ezcGraphColor )' );
+ }
+
+ $this->dataSetColor = array();
+ foreach ( $propertyValue as $value )
+ {
+ $this->dataSetColor[] = ezcGraphColor::create( $value );
+ }
+ $this->colorIndex = -1;
+ break;
+ case 'dataSetSymbol':
+ if ( !is_array( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'array( (int) ezcGraph::SYMBOL_TYPE )' );
+ }
+
+ $this->dataSetSymbol = array();
+ foreach ( $propertyValue as $value )
+ {
+ $this->dataSetSymbol[] = (int) $value;
+ }
+ $this->symbolIndex = -1;
+ break;
+
+ case 'fontName':
+ $this->$propertyName = (string) $propertyValue;
+ break;
+
+ case 'chartBorderWidth':
+ case 'elementBorderWidth':
+ case 'padding':
+ case 'margin':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->$propertyName = (int) $propertyValue;
+ break;
+
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/radar_renderer.php b/src/TUnit/external/ezc/Graph/interfaces/radar_renderer.php
new file mode 100644
index 0000000..be51816
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/radar_renderer.php
@@ -0,0 +1,54 @@
+
diff --git a/src/TUnit/external/ezc/Graph/interfaces/renderer.php b/src/TUnit/external/ezc/Graph/interfaces/renderer.php
new file mode 100644
index 0000000..de1418e
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/renderer.php
@@ -0,0 +1,717 @@
+
+ * $chart = new ezcGraphPieChart();
+ * $chart->driver = new ezcGraphSvgDriver();
+ *
+ *
+ * @param ezcGraphDriver $driver Output driver
+ * @return void
+ */
+ public function setDriver( ezcGraphDriver $driver )
+ {
+ $this->driver = $driver;
+ }
+
+ /**
+ * Adds a element reference for context
+ *
+ * @param ezcGraphContext $context Dataoint context
+ * @param mixed $reference Driver dependant reference
+ * @return void
+ */
+ protected function addElementReference( ezcGraphContext $context, $reference )
+ {
+ $this->elements['data'][$context->dataset][$context->datapoint][] = $reference;
+ }
+
+ /**
+ * Return all chart element references
+ *
+ * Returns element references for the data sets in the chart, so the
+ * created graphic may be enhanced later.
+ *
+ * The resulting array looks like:
+ *
+ * array (
+ * legend_url => array (
+ * $name => $url | null,
+ * ...
+ * ),
+ * legend => array (
+ * $name => $data,
+ * ...
+ * )
+ * data => array (
+ * $dataset => array (
+ * $name => $data,
+ * ...
+ * ),
+ * ...
+ * )
+ * )
+ *
+ *
+ * The legend elements won't show up in the array, if there is no legend
+ * redered. The URLs are only available, if the url property has been set
+ * on the respective dataset.
+ *
+ * The data assigned to the legends and data elements is completely direver
+ * dependent. In the SVG and Flash driver there will jsut be some IDs,
+ * which allow you to reference the affected elements or element groups
+ * inside the flash or SVG file.
+ *
+ * For bitmap formats, like in the Cairo or GD driver, $data will be an
+ * array of ezcGraphCoordinate objects, which roughly describe the outline
+ * of the referenced element. For circles and alike the resolution of this
+ * outline can be configured in the respective driver.
+ *
+ * @return array
+ */
+ public function getElementReferences()
+ {
+ return $this->elements;
+ }
+
+ /**
+ * __get
+ *
+ * @param string $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'xAxisSpace':
+ case 'yAxisSpace':
+ return $this->$propertyName;
+ case 'elements':
+ return $this->elements;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ }
+ }
+
+ /**
+ * Draw pie segment
+ *
+ * Draws a single pie segment
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of pie segment
+ * @param float $startAngle Start angle
+ * @param float $endAngle End angle
+ * @param mixed $label Label of pie segment
+ * @param bool $moveOut Move out from middle for hilighting
+ * @return void
+ */
+ abstract public function drawPieSegment(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ $startAngle = .0,
+ $endAngle = 360.,
+ $label = false,
+ $moveOut = false
+ );
+
+ /**
+ * Draw bar
+ *
+ * Draws a bar as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $position Position of data point
+ * @param float $stepSize Space which can be used for bars
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @return void
+ */
+ abstract public function drawBar(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $position,
+ $stepSize,
+ $dataNumber = 1,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ $axisPosition = 0.
+ );
+
+ /**
+ * Draw data line
+ *
+ * Draws a line as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $start Starting point
+ * @param ezcGraphCoordinate $end Ending point
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
+ * @param ezcGraphColor $fillColor Color to fill line with
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ abstract public function drawDataLine(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ $dataNumber = 1,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ ezcGraphColor $symbolColor = null,
+ ezcGraphColor $fillColor = null,
+ $axisPosition = 0.,
+ $thickness = 1.
+ );
+
+ /**
+ * Draws a highlight textbox for a datapoint.
+ *
+ * A highlight textbox for line and bar charts means a box with the current
+ * value in the graph.
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphCoordinate $end Ending point
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param ezcGraphFontOptions $font Font used for highlight string
+ * @param string $text Acutual value
+ * @param int $size Size of highlight text
+ * @param ezcGraphColor $markLines
+ * @param int $xOffset
+ * @param int $yOffset
+ * @param float $stepSize
+ * @param int $type
+ * @return void
+ */
+ abstract public function drawDataHighlightText(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphCoordinate $end,
+ $axisPosition = 0.,
+ $dataNumber = 1,
+ $dataCount = 1,
+ ezcGraphFontOptions $font,
+ $text,
+ $size,
+ ezcGraphColor $markLines = null,
+ $xOffset = 0,
+ $yOffset = 0,
+ $stepSize = 0.,
+ $type = ezcGraph::LINE
+ );
+
+ /**
+ * Draw legend
+ *
+ * Will draw a legend in the bounding box
+ *
+ * @param ezcGraphBoundings $boundings Bounding of legend
+ * @param ezcGraphChartElementLegend $legend Legend to draw
+ * @param int $type Type of legend: Protrait or landscape
+ * @return void
+ */
+ abstract public function drawLegend(
+ ezcGraphBoundings $boundings,
+ ezcGraphChartElementLegend $legend,
+ $type = ezcGraph::VERTICAL
+ );
+
+ /**
+ * Draw box
+ *
+ * Box are wrapping each major chart element and draw border, background
+ * and title to each chart element.
+ *
+ * Optionally a padding and margin for each box can be defined.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of the box
+ * @param ezcGraphColor $background Background color
+ * @param ezcGraphColor $borderColor Border color
+ * @param int $borderWidth Border width
+ * @param int $margin Margin
+ * @param int $padding Padding
+ * @param mixed $title Title of the box
+ * @param int $titleSize Size of title in the box
+ * @return ezcGraphBoundings Remaining inner boundings
+ */
+ abstract public function drawBox(
+ ezcGraphBoundings $boundings,
+ ezcGraphColor $background = null,
+ ezcGraphColor $borderColor = null,
+ $borderWidth = 0,
+ $margin = 0,
+ $padding = 0,
+ $title = false,
+ $titleSize = 16
+ );
+
+ /**
+ * Draw text
+ *
+ * Draws the provided text in the boundings
+ *
+ * @param ezcGraphBoundings $boundings Boundings of text
+ * @param string $text Text
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ abstract public function drawText(
+ ezcGraphBoundings $boundings,
+ $text,
+ $align = ezcGraph::LEFT,
+ ezcGraphRotation $rotation = null
+ );
+
+ /**
+ * Draw axis
+ *
+ * Draws an axis form the provided start point to the end point. A specific
+ * angle of the axis is not required.
+ *
+ * For the labeleing of the axis a sorted array with major steps and an
+ * array with minor steps is expected, which are build like this:
+ * array(
+ * array(
+ * 'position' => (float),
+ * 'label' => (string),
+ * )
+ * )
+ * where the label is optional.
+ *
+ * The label renderer class defines how the labels are rendered. For more
+ * documentation on this topic have a look at the basic label renderer
+ * class.
+ *
+ * Additionally it can be specified if a major and minor grid are rendered
+ * by defining a color for them. The axis label is used to add a caption
+ * for the axis.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $start Start point of axis
+ * @param ezcGraphCoordinate $end Endpoint of axis
+ * @param ezcGraphChartElementAxis $axis Axis to render
+ * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
+ * @return void
+ */
+ abstract public function drawAxis(
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphAxisLabelRenderer $labelClass = null
+ );
+
+ /**
+ * Draw axis arrow head
+ *
+ * Draw an arrow head at the specified position using specified size
+ * and direction of the error head. Repsects the axisEndStyle option in
+ * the base renderer options class.
+ *
+ * @param ezcGraphCoordinate $position
+ * @param ezcGraphVector $direction
+ * @param float $size
+ * @param ezcGraphColor $color
+ * @return void
+ */
+ protected function drawAxisArrowHead( ezcGraphCoordinate $position, ezcGraphVector $direction, $size, ezcGraphColor $color )
+ {
+ $orthogonalDirection = clone $direction;
+ $orthogonalDirection->rotateClockwise();
+
+ if ( $this->options->axisEndStyle === ezcGraph::ARROW )
+ {
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $position->x,
+ $position->y
+ ),
+ new ezcGraphCoordinate(
+ $position->x
+ - $orthogonalDirection->x * $size / 2
+ + $direction->x * $size,
+ $position->y
+ - $orthogonalDirection->y * $size / 2
+ + $direction->y * $size
+ ),
+ new ezcGraphCoordinate(
+ $position->x
+ + $orthogonalDirection->x * $size / 2
+ + $direction->x * $size,
+ $position->y
+ + $orthogonalDirection->y * $size / 2
+ + $direction->y * $size
+ ),
+ ),
+ $color,
+ true
+ );
+ }
+ elseif ( $this->options->axisEndStyle !== ezcGraph::NO_SYMBOL )
+ {
+ $topLeft = new ezcGraphCoordinate(
+ $position->x
+ + $orthogonalDirection->x * $size / 2
+ + $direction->x * $size,
+ $position->y
+ + $orthogonalDirection->y * $size / 2
+ + $direction->y * $size
+ );
+
+ $bottomRight = new ezcGraphCoordinate(
+ $position->x
+ - $orthogonalDirection->x * $size / 2,
+ $position->y
+ - $orthogonalDirection->y * $size / 2
+ );
+
+ $this->drawSymbol(
+ $boundings = new ezcGraphBoundings(
+ min( $topLeft->x, $bottomRight->x ),
+ min( $topLeft->y, $bottomRight->y ),
+ max( $topLeft->x, $bottomRight->x ),
+ max( $topLeft->y, $bottomRight->y )
+ ),
+ $color,
+ $this->options->axisEndStyle
+ );
+ }
+ }
+
+ /**
+ * Draw background image
+ *
+ * Draws a background image at the defined position. If repeat is set the
+ * background image will be repeated like any texture.
+ *
+ * @param ezcGraphBoundings $boundings Boundings for the background image
+ * @param string $file Filename of background image
+ * @param int $position Position of background image
+ * @param int $repeat Type of repetition
+ * @return void
+ */
+ abstract public function drawBackgroundImage(
+ ezcGraphBoundings $boundings,
+ $file,
+ $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
+ $repeat = ezcGraph::NO_REPEAT
+ );
+
+ /**
+ * Draw Symbol
+ *
+ * Draws a single symbol defined by the symbol constants in ezcGraph. for
+ * NO_SYMBOL a rect will be drawn.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of symbol
+ * @param ezcGraphColor $color Color of symbol
+ * @param int $symbol Type of symbol
+ * @return void
+ */
+ public function drawSymbol(
+ ezcGraphBoundings $boundings,
+ ezcGraphColor $color,
+ $symbol = ezcGraph::NO_SYMBOL )
+ {
+ switch ( $symbol )
+ {
+ case ezcGraph::NO_SYMBOL:
+ case ezcGraph::SQUARE:
+ $return = $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
+ ),
+ $color,
+ true
+ );
+
+ // Draw optional gleam
+ if ( $this->options->legendSymbolGleam !== false )
+ {
+ $this->driver->drawPolygon(
+ array(
+ $topLeft = new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
+ ),
+ $bottomRight = new ezcGraphCoordinate(
+ $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
+ ),
+ ),
+ new ezcGraphLinearGradient(
+ $bottomRight,
+ $topLeft,
+ $color->darken( -$this->options->legendSymbolGleam ),
+ $color->darken( $this->options->legendSymbolGleam )
+ ),
+ true
+ );
+ }
+ return $return;
+ case ezcGraph::BOX:
+ $return = $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
+ ),
+ $color,
+ false
+ );
+ return $return;
+ case ezcGraph::DIAMOND:
+ $return = $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x1,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y1
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ ),
+ $color,
+ true
+ );
+
+ // Draw optional gleam
+ if ( $this->options->legendSymbolGleam !== false )
+ {
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x1 - ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y1 - ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ ),
+ new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.353553391,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.353553391
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * ( 1 - 0.353553391 ),
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * ( 1 - 0.353553391 )
+ ),
+ $color->darken( -$this->options->legendSymbolGleam ),
+ $color->darken( $this->options->legendSymbolGleam )
+ ),
+ true
+ );
+ }
+ return $return;
+ case ezcGraph::BULLET:
+ $return = $this->driver->drawCircle(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ $boundings->x1 - $boundings->x0,
+ $boundings->y1 - $boundings->y0,
+ $color,
+ true
+ );
+
+ // Draw optional gleam
+ if ( $this->options->legendSymbolGleam !== false )
+ {
+ $this->driver->drawCircle(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ ( $boundings->x1 - $boundings->x0 ) * $this->options->legendSymbolGleamSize,
+ ( $boundings->y1 - $boundings->y0 ) * $this->options->legendSymbolGleamSize,
+ new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.292893219,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.292893219
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) * 0.707106781,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) * 0.707106781
+ ),
+ $color->darken( -$this->options->legendSymbolGleam ),
+ $color->darken( $this->options->legendSymbolGleam )
+ ),
+ true
+ );
+ }
+ return $return;
+ case ezcGraph::CIRCLE:
+ return $this->driver->drawCircle(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ ),
+ $boundings->x1 - $boundings->x0,
+ $boundings->y1 - $boundings->y0,
+ $color,
+ false
+ );
+ }
+ }
+
+ /**
+ * Finish rendering
+ *
+ * Method is called before the final image is renderer, so that finishing
+ * operations can be performed here.
+ *
+ * @return void
+ */
+ abstract protected function finish();
+
+ /**
+ * Reset renderer properties
+ *
+ * Reset all renderer properties, which were calculated during the
+ * rendering process, to offer a clean environment for rerendering.
+ *
+ * @return void
+ */
+ protected function resetRenderer()
+ {
+ $this->xAxisSpace = false;
+ $this->yAxisSpace = false;
+
+ // Reset driver, maintaining its configuration
+ $driverClass = get_class( $this->driver );
+ $driverOptions = $this->driver->options;
+ $this->driver = new $driverClass();
+ $this->driver->options = $driverOptions;
+ }
+
+ /**
+ * Finally renders the image
+ *
+ * @param string $file Filename of destination file
+ * @return void
+ */
+ public function render( $file = null )
+ {
+ $this->finish();
+
+ if ( $file === null )
+ {
+ $this->driver->renderToOutput();
+ }
+ else
+ {
+ $this->driver->render( $file );
+ }
+
+ $this->resetRenderer();
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/interfaces/stacked_bar_renderer.php b/src/TUnit/external/ezc/Graph/interfaces/stacked_bar_renderer.php
new file mode 100644
index 0000000..1508bc4
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/interfaces/stacked_bar_renderer.php
@@ -0,0 +1,46 @@
+
diff --git a/src/TUnit/external/ezc/Graph/math/boundings.php b/src/TUnit/external/ezc/Graph/math/boundings.php
new file mode 100644
index 0000000..948b66d
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/boundings.php
@@ -0,0 +1,109 @@
+x0 = $x0;
+ $this->y0 = $y0;
+ $this->x1 = $x1;
+ $this->y1 = $y1;
+
+ // Switch values to ensure correct order
+ if ( $this->x0 > $this->x1 )
+ {
+ $tmp = $this->x0;
+ $this->x0 = $this->x1;
+ $this->x1 = $tmp;
+ }
+
+ if ( $this->y0 > $this->y1 )
+ {
+ $tmp = $this->y0;
+ $this->y0 = $this->y1;
+ $this->y1 = $tmp;
+ }
+ }
+
+ /**
+ * Getter for calculated values depending on the boundings.
+ * - 'width': Width of bounding recangle
+ * - 'height': Height of bounding recangle
+ *
+ * @param string $name Name of property to get
+ * @return mixed Calculated value
+ */
+ public function __get( $name )
+ {
+ switch ( $name )
+ {
+ case 'width':
+ return $this->x1 - $this->x0;
+ case 'height':
+ return $this->y1 - $this->y0;
+ default:
+ throw new ezcBasePropertyNotFoundException( $name );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/math/matrix.php b/src/TUnit/external/ezc/Graph/math/matrix.php
new file mode 100644
index 0000000..24f039b
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/matrix.php
@@ -0,0 +1,511 @@
+rows = max( 1, (int) $rows );
+ $this->columns = max( 1, (int) $columns );
+
+ if ( $values !== null )
+ {
+ $this->fromArray( $values );
+ }
+ else
+ {
+ $this->init();
+ }
+ }
+
+ /**
+ * Create matrix from array
+ *
+ * Use an array with float values to set matrix values.
+ *
+ * @param array $values Array with values
+ * @return ezcGraphMatrix Modified matrix
+ */
+ public function fromArray( array $values )
+ {
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $this->matrix[$i][$j] =
+ ( isset( $values[$i][$j] )
+ ? (float) $values[$i][$j]
+ : 0 );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Init matrix
+ *
+ * Sets matrix to identity matrix.
+ *
+ * @return ezcGraphMatrix Modified matrix
+ */
+ public function init()
+ {
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $this->matrix[$i][$j] = ( $i === $j ? 1 : 0 );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns number of rows
+ *
+ * @return int Number of rows
+ */
+ public function rows()
+ {
+ return $this->rows;
+ }
+
+ /**
+ * Returns number of columns
+ *
+ * @return int Number of columns
+ */
+ public function columns()
+ {
+ return $this->columns;
+ }
+
+ /**
+ * Get a single matrix value
+ *
+ * Returns the value of the matrix at the given position
+ *
+ * @param int $i Column
+ * @param int $j Row
+ * @return float Matrix value
+ */
+ public function get( $i, $j )
+ {
+ if ( ( $i < 0 ) ||
+ ( $i >= $this->rows ) ||
+ ( $j < 0 ) ||
+ ( $j >= $this->columns ) )
+ {
+ throw new ezcGraphMatrixOutOfBoundingsException( $this->rows, $this->columns, $i, $j );
+ }
+
+ return ( !isset( $this->matrix[$i][$j] ) ? .0 : $this->matrix[$i][$j] );
+ }
+
+ /**
+ * Set a single matrix value
+ *
+ * Sets the value of the matrix at the given position.
+ *
+ * @param int $i Column
+ * @param int $j Row
+ * @param float $value Value
+ * @return ezcGraphMatrix Updated matrix
+ */
+ public function set( $i, $j, $value )
+ {
+ if ( ( $i < 0 ) ||
+ ( $i >= $this->rows ) ||
+ ( $j < 0 ) ||
+ ( $j >= $this->columns ) )
+ {
+ throw new ezcGraphMatrixOutOfBoundingsException( $this->rows, $this->columns, $i, $j );
+ }
+
+ $this->matrix[$i][$j] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Adds one matrix to the current one
+ *
+ * Calculate the sum of two matrices and returns the resulting matrix.
+ *
+ * @param ezcGraphMatrix $matrix Matrix to sum with
+ * @return ezcGraphMatrix Result matrix
+ */
+ public function add( ezcGraphMatrix $matrix )
+ {
+ if ( ( $this->rows !== $matrix->rows() ) ||
+ ( $this->columns !== $matrix->columns() ) )
+ {
+ throw new ezcGraphMatrixInvalidDimensionsException( $this->rows, $this->columns, $matrix->rows(), $matrix->columns() );
+ }
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $this->matrix[$i][$j] += $matrix->get( $i, $j );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Subtracts matrix from current one
+ *
+ * Calculate the diffenrence of two matices and returns the result matrix.
+ *
+ * @param ezcGraphMatrix $matrix subtrahend
+ * @return ezcGraphMatrix Result matrix
+ */
+ public function diff( ezcGraphMatrix $matrix )
+ {
+ if ( ( $this->rows !== $matrix->rows() ) ||
+ ( $this->columns !== $matrix->columns() ) )
+ {
+ throw new ezcGraphMatrixInvalidDimensionsException( $this->rows, $this->columns, $matrix->rows(), $matrix->columns() );
+ }
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $this->matrix[$i][$j] -= $matrix->get( $i, $j );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Scalar multiplication
+ *
+ * Multiplies matrix with the given scalar and returns the result matrix
+ *
+ * @param float $scalar Scalar
+ * @return ezcGraphMatrix Result matrix
+ */
+ public function scalar( $scalar )
+ {
+ $scalar = (float) $scalar;
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $this->matrix[$i][$j] *= $scalar;
+ }
+ }
+ }
+
+ /**
+ * Transpose matrix
+ *
+ * @return ezcGraphMatrix Transposed matrix
+ */
+ public function transpose()
+ {
+ $matrix = clone $this;
+
+ $this->rows = $matrix->columns();
+ $this->columns = $matrix->rows();
+
+ $this->matrix = array();
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $this->matrix[$i][$j] = $matrix->get( $j, $i );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Multiplies two matrices
+ *
+ * Multiply current matrix with another matrix and returns the result
+ * matrix.
+ *
+ * @param ezcGraphMatrix $matrix Second factor
+ * @return ezcGraphMatrix Result matrix
+ */
+ public function multiply( ezcGraphMatrix $matrix )
+ {
+ $mColumns = $matrix->columns();
+ if ( $this->columns !== ( $mRows = $matrix->rows() ) )
+ {
+ throw new ezcGraphMatrixInvalidDimensionsException( $this->columns, $this->rows, $mColumns, $mRows );
+ }
+
+ $result = new ezcGraphMatrix( $this->rows, $mColumns );
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $mColumns; ++$j )
+ {
+ $sum = 0;
+ for ( $k = 0; $k < $mRows; ++$k ) {
+ $sum += $this->matrix[$i][$k] * $matrix->get( $k, $j );
+ }
+
+ $result->set( $i, $j, $sum );
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Solve nonlinear equatation
+ *
+ * Tries to solve equatation given by two matrices, with assumption, that:
+ * A * x = B
+ * where $this is A, and the paramenter B. x is cosnidered as a vector
+ * x = ( x^n, x^(n-1), ..., x^2, x, 1 )
+ *
+ * Will return a polynomial solution for x.
+ *
+ * See: http://en.wikipedia.org/wiki/Gauss-Newton_algorithm
+ *
+ * @param ezcGraphMatrix $matrix B
+ * @return ezcGraphPolygon Solution of equatation
+ */
+ public function solveNonlinearEquatation( ezcGraphMatrix $matrix )
+ {
+ // Build complete equatation
+ $equatation = new ezcGraphMatrix( $this->rows, $columns = ( $this->columns + 1 ) );
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $equatation->set( $i, $j, $this->matrix[$i][$j] );
+ }
+ $equatation->set( $i, $this->columns, $matrix->get( $i, 0 ) );
+ }
+
+ // Compute upper triangular matrix on left side of equatation
+ for ( $i = 0; $i < ( $this->rows - 1 ); ++$i )
+ {
+ for ( $j = $i + 1; $j < $this->rows; ++$j )
+ {
+ if ( $equatation->get( $j, $i ) !== 0 )
+ {
+ if ( $equatation->get( $j, $i ) == 0 )
+ {
+ continue;
+ }
+ else
+ {
+ $factor = -( $equatation->get( $i, $i ) / $equatation->get( $j, $i ) );
+ }
+
+ for ( $k = $i; $k < $columns; ++$k )
+ {
+ $equatation->set( $j, $k, $equatation->get( $i, $k ) + $factor * $equatation->get( $j, $k ) );
+ }
+ }
+ }
+ }
+
+ // Normalize values on left side matrix diagonale
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ if ( ( ( $value = $equatation->get( $i, $i ) ) != 1 ) &&
+ ( $value != 0 ) )
+ {
+ $factor = 1 / $value;
+ for ( $k = $i; $k < $columns; ++$k )
+ {
+ $equatation->set( $i, $k, $equatation->get( $i, $k ) * $factor );
+ }
+ }
+ }
+
+ // Build up solving polynom
+ $polynom = new ezcGraphPolynom();
+ for ( $i = ( $this->rows - 1 ); $i >= 0; --$i )
+ {
+ for ( $j = $i + 1; $j < $this->columns; ++$j )
+ {
+ $equatation->set(
+ $i,
+ $this->columns,
+ $equatation->get( $i, $this->columns ) + ( -$equatation->get( $i, $j ) * $polynom->get( $j ) )
+ );
+ $equatation->set( $i, $j, 0 );
+ }
+ $polynom->set( $i, $equatation->get( $i, $this->columns ) );
+ }
+
+ return $polynom;
+ }
+
+ /**
+ * Build LR decomposition from matrix
+ *
+ * Use Cholesky-Crout algorithm to get LR decomposition of the current
+ * matrix.
+ *
+ * Will return an array with two matrices:
+ * array(
+ * 'l' => (ezcGraphMatrix) $left,
+ * 'r' => (ezcGraphMatrix) $right,
+ * )
+ *
+ * @return array( ezcGraphMatrix )
+ */
+ public function LRdecomposition()
+ {
+ /**
+ * Use Cholesky-Crout algorithm to get LR decomposition
+ *
+ * Input: Matrix A ($this)
+ *
+ * For i = 1 To n
+ * For j = i To n
+ * R(i,j)=A(i,j)
+ * For k = 1 TO i-1
+ * R(i,j)-=L(i,k)*R(k,j)
+ * end
+ * end
+ * For j=i+1 To n
+ * L(j,i)= A(j,i)
+ * For k = 1 TO i-1
+ * L(j,i)-=L(j,k)*R(k,i)
+ * end
+ * L(j,i)/=R(i,i)
+ * end
+ * end
+ *
+ * Output: matrices L,R
+ */
+ $l = new ezcGraphMatrix( $this->columns, $this->rows );
+ $r = new ezcGraphMatrix( $this->columns, $this->rows );
+
+ for ( $i = 0; $i < $this->columns; ++$i )
+ {
+ for ( $j = $i; $j < $this->rows; ++$j )
+ {
+ $r->set( $i, $j, $this->matrix[$i][$j] );
+ for ( $k = 0; $k <= ( $i - 1 ); ++$k )
+ {
+ $r->set( $i, $j, $r->get( $i, $j ) - $l->get( $i, $k ) * $r->get( $k, $j ) );
+ }
+ }
+
+ for ( $j = $i + 1; $j < $this->rows; ++$j )
+ {
+ $l->set( $j, $i, $this->matrix[$j][$i] );
+ for ( $k = 0; $k <= ( $i - 1 ); ++$k )
+ {
+ $l->set( $j, $i, $l->get( $j, $i ) - $l->get( $j, $k ) * $r->get( $k, $i ) );
+ }
+ $l->set( $j, $i, $l->get( $j, $i ) / $r->get( $i, $i ) );
+ }
+ }
+
+ return array(
+ 'l' => $l,
+ 'r' => $r,
+ );
+ }
+
+ /**
+ * Returns a string representation of the matrix
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $string = sprintf( "%d x %d matrix:\n", $this->rows, $this->columns );
+
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ $string .= '| ';
+ for ( $j = 0; $j < $this->columns; ++$j )
+ {
+ $string .= sprintf( '%04.2f ', $this->get( $i, $j ) );
+ }
+ $string .= "|\n";
+ }
+
+ return $string;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/math/polynom.php b/src/TUnit/external/ezc/Graph/math/polynom.php
new file mode 100644
index 0000000..70ad98c
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/polynom.php
@@ -0,0 +1,259 @@
+
+ * // Equivalent to: x^2 + .5
+ * $polynom = new ezcGraphPolynom( array( 2 => 1, 0 => .5 ) );
+ *
+ * // Calculate result for x = 1, echos: 1.5
+ * echo $polynom->evaluate( 1 ), PHP_EOL;
+ *
+ * // Build the sum with another polynom
+ * $polynom->add( new ezcGraphPolynom( array( 1 => 1 ) ) );
+ *
+ * // Print polynom, echos:
+ * // x^2 + x + 5.00e-1
+ * echo $polynom, PHP_EOL;
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphPolynom
+{
+ /**
+ * Factors of the polynom
+ *
+ * An example:
+ * Polynom:
+ * 2 * x^3 + .5 * x - 3
+ * Array:
+ * array (
+ * (int) 3 => (float) 2,
+ * (int) 1 => (float) .5,
+ * (int) 0 => (float) -3,
+ * )
+ *
+ * @var array
+ */
+ protected $values;
+
+ // @TODO: Introduce precision option for string output?
+
+ /**
+ * Constructor
+ *
+ * Constructs a polynom object from given array, where the key is the
+ * exponent and the value the factor.
+ * An example:
+ * Polynom:
+ * 2 * x^3 + .5 * x - 3
+ * Array:
+ * array (
+ * (int) 3 => (float) 2,
+ * (int) 1 => (float) .5,
+ * (int) 0 => (float) -3,
+ * )
+ *
+ * @param array $values Array with values
+ * @return ezcGraphPolynom
+ */
+ public function __construct( array $values = array() )
+ {
+ foreach ( $values as $exponent => $factor )
+ {
+ $this->values[(int) $exponent] = (float) $factor;
+ }
+ }
+
+ /**
+ * Initialise a polygon
+ *
+ * Initialise a polygon of the given order. Sets all factors to 0.
+ *
+ * @param int $order Order of polygon
+ * @return ezcGraphPolynom Created polynom
+ */
+ public function init( $order )
+ {
+ for ( $i = 0; $i <= $order; ++$i )
+ {
+ $this->values[$i] = 0;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return factor for one exponent
+ *
+ * @param int $exponent Exponent
+ * @return float Factor
+ */
+ public function get( $exponent )
+ {
+ if ( !isset( $this->values[$exponent] ) )
+ {
+ return 0;
+ }
+ else
+ {
+ return $this->values[$exponent];
+ }
+ }
+
+ /**
+ * Set the factor for one exponent
+ *
+ * @param int $exponent Exponent
+ * @param float $factor Factor
+ * @return ezcGraphPolynom Modified polynom
+ */
+ public function set( $exponent, $factor )
+ {
+ $this->values[(int) $exponent] = (float) $factor;
+
+ return $this;
+ }
+
+ /**
+ * Returns the order of the polynom
+ *
+ * @return int Polynom order
+ */
+ public function getOrder()
+ {
+ return max( array_keys( $this->values ) );
+ }
+
+ /**
+ * Adds polynom to current polynom
+ *
+ * @param ezcGraphPolynom $polynom Polynom to add
+ * @return ezcGraphPolynom Modified polynom
+ */
+ public function add( ezcGraphPolynom $polynom )
+ {
+ $order = max(
+ $this->getOrder(),
+ $polynom->getOrder()
+ );
+
+ for ( $i = 0; $i <= $order; ++$i )
+ {
+ $this->set( $i, $this->get( $i ) + $polynom->get( $i ) );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Evaluate Polynom with a given value
+ *
+ * @param float $x Value
+ * @return float Result
+ */
+ public function evaluate( $x )
+ {
+ $value = 0;
+ foreach ( $this->values as $exponent => $factor )
+ {
+ $value += $factor * pow( $x, $exponent );
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns a string represenation of the polynom
+ *
+ * @return string String representation of polynom
+ */
+ public function __toString()
+ {
+ krsort( $this->values );
+ $string = '';
+
+ foreach ( $this->values as $exponent => $factor )
+ {
+ if ( $factor == 0 )
+ {
+ continue;
+ }
+
+ $string .= ( $factor < 0 ? ' - ' : ' + ' );
+
+ $factor = abs( $factor );
+ switch ( true )
+ {
+ case abs( 1 - $factor ) < .0001:
+ // No not append, if factor is ~1
+ break;
+ case $factor < 1:
+ case $factor >= 1000:
+ $string .= sprintf( '%.2e ', $factor );
+ break;
+ case $factor >= 100:
+ $string .= sprintf( '%.0f ', $factor );
+ break;
+ case $factor >= 10:
+ $string .= sprintf( '%.1f ', $factor );
+ break;
+ default:
+ $string .= sprintf( '%.2f ', $factor );
+ break;
+ }
+
+ switch ( true )
+ {
+ case $exponent > 1:
+ $string .= sprintf( 'x^%d', $exponent );
+ break;
+ case $exponent === 1:
+ $string .= 'x';
+ break;
+ case $exponent === 0:
+ if ( abs( 1 - $factor ) < .0001 )
+ {
+ $string .= '1';
+ }
+ break;
+ }
+ }
+
+ if ( substr( $string, 0, 3 ) === ' + ' )
+ {
+ $string = substr( $string, 3 );
+ }
+ else
+ {
+ $string = '-' . substr( $string, 3 );
+ }
+
+ return trim( $string );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/math/rotation.php b/src/TUnit/external/ezc/Graph/math/rotation.php
new file mode 100644
index 0000000..0bee1bd
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/rotation.php
@@ -0,0 +1,99 @@
+rotation = (float) $rotation;
+
+ if ( $center === null )
+ {
+ $this->center = new ezcGraphCoordinate( 0, 0 );
+
+ $clockwiseRotation = deg2rad( $rotation );
+ $rotationMatrixArray = array(
+ array( cos( $clockwiseRotation ), -sin( $clockwiseRotation ), 0 ),
+ array( sin( $clockwiseRotation ), cos( $clockwiseRotation ), 0 ),
+ array( 0, 0, 1 ),
+ );
+
+ return parent::__construct( $rotationMatrixArray );
+ }
+
+ parent::__construct();
+
+ $this->center = $center;
+
+ $this->multiply( new ezcGraphTranslation( $center->x, $center->y ) );
+ $this->multiply( new ezcGraphRotation( $rotation ) );
+ $this->multiply( new ezcGraphTranslation( -$center->x, -$center->y ) );
+ }
+
+ /**
+ * Return rotaion angle in degrees
+ *
+ * @return float
+ */
+ public function getRotation()
+ {
+ return $this->rotation;
+ }
+
+ /**
+ * Return the center point of the current rotation
+ *
+ * @return ezcGraphCoordinate
+ */
+ public function getCenter()
+ {
+ return $this->center;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/math/transformation.php b/src/TUnit/external/ezc/Graph/math/transformation.php
new file mode 100644
index 0000000..7ff7c8a
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/transformation.php
@@ -0,0 +1,96 @@
+columns();
+
+ // We want to ensure, that the matrix stays 3x3
+ if ( ( $this->columns !== $matrix->rows() ) &&
+ ( $this->rows !== $mColumns ) )
+ {
+ throw new ezcGraphMatrixInvalidDimensionsException( $this->columns, $this->rows, $mColumns, $matrix->rows() );
+ }
+
+ $result = parent::multiply( $matrix );
+
+ // The matrix dimensions stay the same, so that we can modify $this.
+ for ( $i = 0; $i < $this->rows; ++$i )
+ {
+ for ( $j = 0; $j < $mColumns; ++$j )
+ {
+ $this->set( $i, $j, $result->get( $i, $j ) );
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Transform a coordinate with the current transformation matrix.
+ *
+ * @param ezcGraphCoordinate $coordinate
+ * @return ezcGraphCoordinate
+ */
+ public function transformCoordinate( ezcGraphCoordinate $coordinate )
+ {
+ $vector = new ezcGraphMatrix( 3, 1, array( array( $coordinate->x ), array( $coordinate->y ), array( 1 ) ) );
+ $vector = parent::multiply( $vector );
+
+ return new ezcGraphCoordinate( $vector->get( 0, 0 ), $vector->get( 1, 0 ) );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/math/translation.php b/src/TUnit/external/ezc/Graph/math/translation.php
new file mode 100644
index 0000000..b4f4874
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/translation.php
@@ -0,0 +1,47 @@
+
diff --git a/src/TUnit/external/ezc/Graph/math/vector.php b/src/TUnit/external/ezc/Graph/math/vector.php
new file mode 100644
index 0000000..2f45591
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/math/vector.php
@@ -0,0 +1,194 @@
+x;
+ $this->x = $this->y;
+ $this->y = -$tmp;
+
+ return $this;
+ }
+
+ /**
+ * Rotates vector to the right by 90 degrees
+ *
+ * @return void
+ */
+ public function rotateClockwise()
+ {
+ $tmp = $this->x;
+ $this->x = -$this->y;
+ $this->y = $tmp;
+
+ return $this;
+ }
+
+ /**
+ * Unifies vector length to 1
+ *
+ * @return void
+ */
+ public function unify()
+ {
+ $length = $this->length();
+ if ( $length == 0 )
+ {
+ return $this;
+ }
+
+ $this->x /= $length;
+ $this->y /= $length;
+
+ return $this;
+ }
+
+ /**
+ * Returns length of vector
+ *
+ * @return float
+ */
+ public function length()
+ {
+ return sqrt(
+ pow( $this->x, 2 ) +
+ pow( $this->y, 2 )
+ );
+ }
+
+ /**
+ * Multiplies vector with a scalar
+ *
+ * @param float $value
+ * @return void
+ */
+ public function scalar( $value )
+ {
+ $this->x *= $value;
+ $this->y *= $value;
+
+ return $this;
+ }
+
+ /**
+ * Calculates scalar product of two vectors
+ *
+ * @param ezcGraphCoordinate $vector
+ * @return void
+ */
+ public function mul( ezcGraphCoordinate $vector )
+ {
+ return $this->x * $vector->x + $this->y * $vector->y;
+ }
+
+ /**
+ * Returns the angle between two vectors in radian
+ *
+ * @param ezcGraphCoordinate $vector
+ * @return float
+ */
+ public function angle( ezcGraphCoordinate $vector )
+ {
+ if ( !$vector instanceof ezcGraphVector )
+ {
+ // Ensure beeing a vector for calling length()
+ $vector = ezcGraphVector::fromCoordinate( $vector );
+ }
+
+ $factor = $this->length() * $vector->length();
+
+ if ( $factor == 0 )
+ {
+ return false;
+ }
+ else
+ {
+ return acos( $this->mul( $vector ) / $factor );
+ }
+ }
+
+ /**
+ * Adds a vector to another vector
+ *
+ * @param ezcGraphCoordinate $vector
+ * @return void
+ */
+ public function add( ezcGraphCoordinate $vector )
+ {
+ $this->x += $vector->x;
+ $this->y += $vector->y;
+
+ return $this;
+ }
+
+ /**
+ * Subtracts a vector from another vector
+ *
+ * @param ezcGraphCoordinate $vector
+ * @return void
+ */
+ public function sub( ezcGraphCoordinate $vector )
+ {
+ $this->x -= $vector->x;
+ $this->y -= $vector->y;
+
+ return $this;
+ }
+
+ /**
+ * Creates a vector from a coordinate object
+ *
+ * @param ezcGraphCoordinate $coordinate
+ * @return ezcGraphVector
+ */
+ public static function fromCoordinate( ezcGraphCoordinate $coordinate )
+ {
+ return new ezcGraphVector( $coordinate->x, $coordinate->y );
+ }
+
+ /**
+ * Transform vector using transformation matrix
+ *
+ * @param ezcGraphTransformation $transformation
+ * @return ezcGraphVector
+ */
+ public function transform( ezcGraphTransformation $transformation )
+ {
+ $result = $transformation->transformCoordinate( $this );
+
+ $this->x = $result->x;
+ $this->y = $result->y;
+
+ return $this;
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/cairo_driver.php b/src/TUnit/external/ezc/Graph/options/cairo_driver.php
new file mode 100644
index 0000000..7cf7a81
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/cairo_driver.php
@@ -0,0 +1,100 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->background->color = '#FFFFFFFF';
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->driver = new ezcGraphCairoDriver();
+ *
+ * // No options yet.
+ *
+ * $graph->render( 400, 200, 'tutorial_driver_cairo.png' );
+ *
+ *
+ * @property float $imageMapResolution
+ * Degree step used to interpolate round image primitives by
+ * polygons for image maps
+ * @property float $circleResolution
+ * Resolution for circles, until I understand how to draw ellipses
+ * with SWFShape::curveTo()
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphCairoDriverOptions extends ezcGraphDriverOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['imageMapResolution'] = 10;
+ $this->properties['circleResolution'] = 2.;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'imageMapResolution':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['imageMapResolution'] = (int) $propertyValue;
+ break;
+ case 'circleResolution':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['circleResolution'] = (float) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/chart.php b/src/TUnit/external/ezc/Graph/options/chart.php
new file mode 100644
index 0000000..468e5f8
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/chart.php
@@ -0,0 +1,107 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzBlue();
+ * $graph->title = 'Access statistics';
+ *
+ * // Global font options
+ * $graph->options->font->name = 'serif';
+ *
+ * // Special font options for sub elements
+ * $graph->title->background = '#EEEEEC';
+ * $graph->title->font->name = 'sans-serif';
+ *
+ * $graph->options->font->maxFontSize = 8;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->render( 400, 150, 'tutorial_chart_title.svg' );
+ *
+ *
+ * @property int $width
+ * Width of the chart.
+ * @property int $height
+ * Height of the chart.
+ * @property ezcGraphFontOptions $font
+ * Font used in the graph.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphChartOptions extends ezcBaseOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['width'] = null;
+ $this->properties['height'] = null;
+ $this->properties['font'] = new ezcGraphFontOptions();
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'width':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['width'] = (int) $propertyValue;
+ break;
+ case 'height':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['height'] = (int) $propertyValue;
+ break;
+ case 'font':
+ $this->properties['font']->path = $propertyValue;
+ break;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/driver.php b/src/TUnit/external/ezc/Graph/options/driver.php
new file mode 100644
index 0000000..4b646b5
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/driver.php
@@ -0,0 +1,172 @@
+
+ * require_once 'tutorial_autoload.php';
+ *
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzBlue();
+ * $graph->title = 'Access statistics';
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * // Do not shorten strings automatically if they do not fit in the assigned
+ * // space with the minimum font size.
+ * $graph->driver->options->autoShortenString = false;
+ *
+ * $graph->render( 400, 150, 'tutorial_chart_title.svg' );
+ *
+ *
+ * @property int $width
+ * Width of the chart.
+ * @property int $height
+ * Height of the chart.
+ * @property float $shadeCircularArc
+ * Percent to darken circular arcs at the sides
+ * @property float $lineSpacing
+ * Percent of font size used for line spacing
+ * @property int $font
+ * Font used in the graph.
+ * @property bool $autoShortenString
+ * Automatically shorten string if it do not fit into the available
+ * space, even with the minimum font size used. Deactivating this
+ * setting will result in ezcGraphFontRenderingException exceptions,
+ * informing you about the actual string which did not fit.
+ * @property string $autoShortenStringPostFix
+ * String to append to shortened strings, if there is enough space
+ * left for the postfix.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+abstract class ezcGraphDriverOptions extends ezcBaseOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['width'] = null;
+ $this->properties['height'] = null;
+
+ $this->properties['lineSpacing'] = .1;
+ $this->properties['shadeCircularArc'] = .5;
+ $this->properties['font'] = new ezcGraphFontOptions();
+ $this->properties['font']->color = ezcGraphColor::fromHex( '#000000' );
+
+ $this->properties['autoShortenString'] = true;
+ $this->properties['autoShortenStringPostFix'] = '..';
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'width':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['width'] = (int) $propertyValue;
+ break;
+ case 'height':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['height'] = (int) $propertyValue;
+ break;
+ case 'lineSpacing':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['lineSpacing'] = (float) $propertyValue;
+ break;
+ case 'shadeCircularArc':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['shadeCircularArc'] = (float) $propertyValue;
+ break;
+ case 'font':
+ if ( $propertyValue instanceof ezcGraphFontOptions )
+ {
+ $this->properties['font'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' );
+ }
+ break;
+ case 'autoShortenString':
+ if ( is_bool( $propertyValue ) )
+ {
+ $this->properties['autoShortenString'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'boolean' );
+ }
+ break;
+ case 'autoShortenStringPostFix':
+ $this->properties['autoShortenStringPostFix'] = (string) $propertyValue;
+ break;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/flash_driver.php b/src/TUnit/external/ezc/Graph/options/flash_driver.php
new file mode 100644
index 0000000..6c32bbb
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/flash_driver.php
@@ -0,0 +1,107 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->driver = new ezcGraphFlashDriver();
+ * $graph->driver->options->compresion = 0;
+ *
+ * $graph->options->font = 'tutorial_font.fdb';
+ *
+ * $graph->driver->options->compression = 7;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->render( 400, 200, 'tutorial_driver_flash.swf' );
+ *
+ *
+ * @property int $compression
+ * Compression level used for generated flash file
+ * @see http://php.net/manual/en/function.swfmovie.save.php
+ * @property float $circleResolution
+ * Resolution for circles, until I understand how to draw ellipses
+ * with SWFShape::curveTo()
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphFlashDriverOptions extends ezcGraphDriverOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['compression'] = 9;
+ $this->properties['circleResolution'] = 2.;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'compression':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 9 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 9' );
+ }
+
+ $this->properties['compression'] = max( 0, min( 9, (int) $propertyValue ) );
+ break;
+ case 'circleResolution':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['circleResolution'] = (float) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/font.php b/src/TUnit/external/ezc/Graph/options/font.php
new file mode 100644
index 0000000..c8dedc0
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/font.php
@@ -0,0 +1,312 @@
+options->font. This takes effect on all chart
+ * elements unless you intentionally access the font configuration of an
+ * individual chart element. The following example shows, how this works.
+ *
+ *
+ * $graph = new ezcGraphPieChart();
+ * $graph->title = 'Access statistics';
+ *
+ * // Set the maximum font size to 8 for all chart elements
+ * $graph->options->font->maxFontSize = 8;
+ *
+ * // Set the font size for the title independently to 14
+ * $graph->title->font->maxFontSize = 14;
+ *
+ * // The following only affects all elements except the // title element,
+ * // which now has its own font configuration.
+ * //
+ * // Keep in mind that the specified font is driver specific. A pure name
+ * // works for the SVG driver, used here. The GD driver for example
+ * // requires a path to a TTF file.
+ * $graph->options->font->name = 'serif';
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ *
+ * @property string $name
+ * Name of font.
+ * @property string $path
+ * Path to font file.
+ * @property int $type
+ * Type of used font. May be one of the following:
+ * - TTF_FONT Native TTF fonts
+ * - PS_FONT PostScript Type1 fonts
+ * - FT2_FONT FreeType 2 fonts
+ * The type is normally automatically detected when you set the path
+ * to the font file.
+ * @property float $minFontSize
+ * Minimum font size for displayed texts.
+ * @property float $maxFontSize
+ * Maximum font size for displayed texts.
+ * @property float $minimalUsedFont
+ * The minimal used font size for the current element group. This
+ * property is set by the driver to maintain this information and
+ * should not be used to configure the apperance of the chart. See
+ * $minFontSize instead.
+ * @property ezcGraphColor $color
+ * Font color.
+ * @property ezcGraphColor $background
+ * Background color. The actual area filled with the background color
+ * is influenced by the settings $padding and $minimizeBorder.
+ * @property ezcGraphColor $border
+ * Border color for the text. The distance between the text and
+ * border is defined by the properties $padding and $minimizeBorder.
+ * @property int $borderWidth
+ * With of the border. To enable the border you need to set the
+ * $border property to some color.
+ * @property int $padding
+ * Padding between text and border.
+ * @property bool $minimizeBorder
+ * Fit the border exactly around the text, or use the complete
+ * possible space. This setting is only relevant, when a border
+ * color has been set for the font.
+ * @property bool $textShadow
+ * Draw shadow for texts. The color of the shadow is defined in
+ * the property $textShadowColor.
+ * @property int $textShadowOffset
+ * Offset for text shadow. This defines the distance the shadow
+ * is moved to the bottom left relative from the text position.
+ * @property ezcGraphColor $textShadowColor
+ * Color of text shadow. If left at the default value "false""
+ * the inverse color of the text color will be used.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphFontOptions extends ezcBaseOptions
+{
+ /**
+ * Indicates if path already has been checked for correct font
+ *
+ * @var bool
+ */
+ protected $pathChecked = false;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['name'] = 'sans-serif';
+// $this->properties['path'] = 'Graph/tests/data/font.ttf';
+ $this->properties['path'] = '';
+ $this->properties['type'] = ezcGraph::TTF_FONT;
+
+ $this->properties['minFontSize'] = 6;
+ $this->properties['maxFontSize'] = 96;
+ $this->properties['minimalUsedFont'] = 96;
+ $this->properties['color'] = ezcGraphColor::fromHex( '#000000' );
+
+ $this->properties['background'] = false;
+ $this->properties['border'] = false;
+ $this->properties['borderWidth'] = 1;
+ $this->properties['padding'] = 0;
+ $this->properties['minimizeBorder'] = true;
+
+ $this->properties['textShadow'] = false;
+ $this->properties['textShadowOffset'] = 1;
+ $this->properties['textShadowColor'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'minFontSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 1' );
+ }
+
+ // Ensure min font size is smaller or equal max font size.
+ if ( $propertyValue > $this->properties['maxFontSize'] )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float <= ' . $this->properties['maxFontSize'] );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ case 'maxFontSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 1' );
+ }
+
+ // Ensure max font size is greater or equal min font size.
+ if ( $propertyValue < $this->properties['minFontSize'] )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float >= ' . $this->properties['minFontSize'] );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ case 'minimalUsedFont':
+ $propertyValue = (float) $propertyValue;
+ if ( $propertyValue < $this->minimalUsedFont )
+ {
+ $this->properties['minimalUsedFont'] = $propertyValue;
+ }
+ break;
+
+ case 'color':
+ case 'background':
+ case 'border':
+ case 'textShadowColor':
+ $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue );
+ break;
+
+ case 'borderWidth':
+ case 'padding':
+ case 'textShadowOffset':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+
+ case 'minimizeBorder':
+ case 'textShadow':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+ $this->properties[$propertyName] = (bool) $propertyValue;
+ break;
+
+ case 'name':
+ if ( is_string( $propertyValue ) )
+ {
+ $this->properties['name'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'string' );
+ }
+ break;
+ case 'path':
+ if ( is_file( $propertyValue ) && is_readable( $propertyValue ) )
+ {
+ $this->properties['path'] = $propertyValue;
+ $parts = pathinfo( $this->properties['path'] );
+ switch ( strtolower( $parts['extension'] ) )
+ {
+ case 'fdb':
+ $this->properties['type'] = ezcGraph::PALM_FONT;
+ break;
+ case 'pfb':
+ $this->properties['type'] = ezcGraph::PS_FONT;
+ break;
+ case 'ttf':
+ $this->properties['type'] = ezcGraph::TTF_FONT;
+ break;
+ case 'svg':
+ $this->properties['type'] = ezcGraph::SVG_FONT;
+ $this->properties['name'] = ezcGraphSvgFont::getFontName( $propertyValue );
+ break;
+ default:
+ throw new ezcGraphUnknownFontTypeException( $propertyValue, $parts['extension'] );
+ }
+ $this->pathChecked = true;
+ }
+ else
+ {
+ throw new ezcBaseFileNotFoundException( $propertyValue, 'font' );
+ }
+ break;
+ case 'type':
+ if ( is_int( $propertyValue ) )
+ {
+ $this->properties['type'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int' );
+ }
+ break;
+ default:
+ throw new ezcBasePropertyNotFoundException( $propertyName );
+ break;
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'textShadowColor':
+ // Use inverted font color if false
+ if ( $this->properties['textShadowColor'] === false )
+ {
+ $this->properties['textShadowColor'] = $this->properties['color']->invert();
+ }
+
+ return $this->properties['textShadowColor'];
+ case 'path':
+ if ( $this->pathChecked === false )
+ {
+ // Enforce call of path check
+ $this->__set( 'path', $this->properties['path'] );
+ }
+ // No break to use parent return
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/gd_driver.php b/src/TUnit/external/ezc/Graph/options/gd_driver.php
new file mode 100644
index 0000000..d03c1bf
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/gd_driver.php
@@ -0,0 +1,181 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzGreen();
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->driver = new ezcGraphGdDriver();
+ * $graph->options->font = 'tutorial_font.ttf';
+ *
+ * // Generate a Jpeg with lower quality. The default settings result in a better
+ * // quality image
+ * $graph->driver->options->supersampling = 1;
+ * $graph->driver->options->jpegQuality = 100;
+ * $graph->driver->options->imageFormat = IMG_JPEG;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->render( 400, 200, 'tutorial_dirver_gd.jpg' );
+ *
+ *
+ * @property int $imageFormat
+ * Type of generated image.
+ * Should be one of those: IMG_PNG, IMG_JPEG
+ * @property int $jpegQuality
+ * Quality of generated jpeg
+ * @property int $detail
+ * Count of degrees to render one polygon for in circular arcs
+ * @property int $supersampling
+ * Factor of supersampling used to simulate antialiasing
+ * @property string $background
+ * Background image to put the graph on
+ * @property string $resampleFunction
+ * Function used to resample / resize images
+ * @property bool $forceNativeTTF
+ * Force use of native ttf functions instead of free type 2
+ * @property float $imageMapResolution
+ * Degree step used to interpolate round image primitives by
+ * polygons for image maps
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphGdDriverOptions extends ezcGraphDriverOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['imageFormat'] = IMG_PNG;
+ $this->properties['jpegQuality'] = 70;
+ $this->properties['detail'] = 1;
+ $this->properties['shadeCircularArc'] = .5;
+ $this->properties['supersampling'] = 2;
+ $this->properties['background'] = false;
+ $this->properties['resampleFunction'] = 'imagecopyresampled';
+ $this->properties['forceNativeTTF'] = false;
+ $this->properties['imageMapResolution'] = 10;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'imageFormat':
+ if ( imagetypes() & $propertyValue )
+ {
+ $this->properties['imageFormat'] = (int) $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'Unsupported image type.' );
+ }
+ break;
+ case 'jpegQuality':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 100 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= int <= 100' );
+ }
+
+ $this->properties['jpegQuality'] = (int) $propertyValue;
+ break;
+ case 'detail':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['detail'] = (int) $propertyValue;
+ break;
+ case 'supersampling':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['supersampling'] = (int) $propertyValue;
+ break;
+ case 'background':
+ if ( $propertyValue === false ||
+ ( is_file( $propertyValue ) && is_readable( $propertyValue ) ) )
+ {
+ $this->properties['background'] = realpath( $propertyValue );
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'readable file' );
+ }
+ break;
+ case 'resampleFunction':
+ if ( ezcBaseFeatures::hasFunction( $propertyValue ) )
+ {
+ $this->properties['resampleFunction'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'function' );
+ }
+ break;
+ case 'forceNativeTTF':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['forceNativeTTF'] = (bool) $propertyValue;
+ break;
+ case 'imageMapResolution':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties['imageMapResolution'] = (int) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/line_chart.php b/src/TUnit/external/ezc/Graph/options/line_chart.php
new file mode 100644
index 0000000..db83c18
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/line_chart.php
@@ -0,0 +1,209 @@
+
+ * $graph = new ezcGraphLineChart();
+ * $graph->title = 'Wikipedia articles';
+ *
+ * $graph->options->fillLines = 220;
+ * $graph->options->lineThickness = 3;
+ *
+ * // Add data
+ * foreach ( $wikidata as $language => $data )
+ * {
+ * $graph->data[$language] = new ezcGraphArrayDataSet( $data );
+ * }
+ *
+ * $graph->render( 400, 150, 'tutorial_line_chart.svg' );
+ *
+ *
+ * @property float $lineThickness
+ * Thickness of chart lines
+ * @property mixed $fillLines
+ * Status wheather the space between line and axis should get filled.
+ * - FALSE to not fill the space at all.
+ * - (int) Opacity used to fill up the space with the lines color.
+ * @property int $symbolSize
+ * Size of symbols in line chart.
+ * @property ezcGraphFontOptions $highlightFont
+ * Font configuration for highlight tests
+ * @property int $highlightSize
+ * Size of highlight blocks
+ * @property bool $highlightLines
+ * If true, it adds lines to highlight the values position on the
+ * axis.
+ * @property true $stackBars
+ * Stack bars
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphLineChartOptions extends ezcGraphChartOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['lineThickness'] = 1;
+ $this->properties['fillLines'] = false;
+ $this->properties['symbolSize'] = 8;
+ $this->properties['highlightFont'] = new ezcGraphFontOptions();
+ $this->properties['highlightFontCloned'] = false;
+ $this->properties['highlightSize'] = 14;
+ $this->properties['highlightXOffset'] = 0;
+ $this->properties['highlightYOffset'] = 0;
+ $this->properties['highlightLines'] = false;
+ $this->properties['stackBars'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'lineThickness':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+ case 'symbolSize':
+ case 'highlightSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+ case 'highlightXOffset':
+ case 'highlightYOffset':
+ if ( !is_numeric ( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int' );
+ }
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+ case 'fillLines':
+ if ( ( $propertyValue !== false ) &&
+ !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 255 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= int <= 255' );
+ }
+
+ $this->properties[$propertyName] = (
+ $propertyValue === false
+ ? false
+ : (int) $propertyValue );
+ break;
+ case 'highlightFont':
+ if ( $propertyValue instanceof ezcGraphFontOptions )
+ {
+ $this->properties['highlightFont'] = $propertyValue;
+ }
+ elseif ( is_string( $propertyValue ) )
+ {
+ if ( !$this->properties['highlightFontCloned'] )
+ {
+ $this->properties['highlightFont'] = clone $this->font;
+ $this->properties['highlightFontCloned'] = true;
+ }
+
+ $this->properties['highlightFont']->path = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' );
+ }
+ break;
+ $this->properties['highlightSize'] = max( 1, (int) $propertyValue );
+ break;
+ case 'highlightLines':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['highlightLines'] = $propertyValue;
+ break;
+ case 'stackBars':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['stackBars'] = $propertyValue;
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'highlightFont':
+ // Clone font configuration when requested for this element
+ if ( !$this->properties['highlightFontCloned'] )
+ {
+ $this->properties['highlightFont'] = clone $this->properties['font'];
+ $this->properties['highlightFontCloned'] = true;
+ }
+ return $this->properties['highlightFont'];
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/odometer_chart.php b/src/TUnit/external/ezc/Graph/options/odometer_chart.php
new file mode 100644
index 0000000..c97edff
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/odometer_chart.php
@@ -0,0 +1,113 @@
+
+ * $graph = new ezcGraphOdoMeterChart();
+ *
+ * $graph->data['Test'] = new ezcGraphArrayDataSet( array( 0, 1, 23, 30 ) );
+ *
+ * $graph->options->odometerHeight = .3;
+ * $graph->options->borderColor = '#2e3436';
+ *
+ * $graph->render( 150, 50, 'odometer.svg' );
+ *
+ *
+ * @property ezcGraphColor $borderColor
+ * Color of border around odometer chart
+ * @property int $borderWidth
+ * Width of border around odometer chart
+ * @property ezcGraphColor $startColor
+ * Start color of grdient used as the odometer chart background.
+ * @property ezcGraphColor $endColor
+ * End color of grdient used as the odometer chart background.
+ * @property int $markerWidth
+ * Width of odometer markers
+ * @property float $odometerHeight
+ * Height consumed by odometer chart
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphOdometerChartOptions extends ezcGraphChartOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['borderColor'] = ezcGraphColor::create( '#000000' );
+ $this->properties['borderWidth'] = 0;
+
+ $this->properties['startColor'] = ezcGraphColor::create( '#4e9a06A0' );
+ $this->properties['endColor'] = ezcGraphColor::create( '#A40000A0' );
+
+ $this->properties['markerWidth'] = 2;
+
+ $this->properties['odometerHeight'] = 0.5;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'borderWidth':
+ case 'markerWidth':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+
+ case 'borderColor':
+ case 'startColor':
+ case 'endColor':
+ $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue );
+ break;
+
+ case 'odometerHeight':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/pie_chart.php b/src/TUnit/external/ezc/Graph/options/pie_chart.php
new file mode 100644
index 0000000..97f109a
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/pie_chart.php
@@ -0,0 +1,151 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzRed();
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->options->label = '%1$s (%3$.1f)';
+ * $graph->options->percentThreshold = .05;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ * $graph->data['Access statistics']->highlight['Explorer'] = true;
+ *
+ * $graph->render( 400, 150, 'tutorial_pie_chart_options.svg' );
+ *
+ *
+ * @property string $label
+ * String used to label pies
+ * %1$s Name of pie
+ * %2$d Value of pie
+ * %3$.1f Percentage
+ * @property callback $labelCallback
+ * Callback function to format pie chart labels.
+ * Function will receive 3 parameters:
+ * string function( label, value, percent )
+ * @property float $sum
+ * Fixed sum of values. This should be used for incomplete pie
+ * charts.
+ * @property float $percentThreshold
+ * Values with a lower percentage value are aggregated.
+ * @property float $absoluteThreshold
+ * Values with a lower absolute value are aggregated.
+ * @property string $summarizeCaption
+ * Caption for values summarized because they are lower then the
+ * configured tresh hold.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphPieChartOptions extends ezcGraphChartOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['label'] = '%1$s: %2$d (%3$.1f%%)';
+ $this->properties['labelCallback'] = null;
+ $this->properties['sum'] = false;
+
+ $this->properties['percentThreshold'] = .0;
+ $this->properties['absoluteThreshold'] = .0;
+ $this->properties['summarizeCaption'] = 'Misc';
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'label':
+ $this->properties['label'] = (string) $propertyValue;
+ break;
+ case 'labelCallback':
+ if ( is_callable( $propertyValue ) )
+ {
+ $this->properties['labelCallback'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'callback function' );
+ }
+ break;
+ case 'sum':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['sum'] = (float) $propertyValue;
+ break;
+ case 'percentThreshold':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['percentThreshold'] = (float) $propertyValue;
+ break;
+ case 'absoluteThreshold':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['absoluteThreshold'] = (float) $propertyValue;
+ break;
+ case 'summarizeCaption':
+ $this->properties['summarizeCaption'] = (string) $propertyValue;
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/radar_chart.php b/src/TUnit/external/ezc/Graph/options/radar_chart.php
new file mode 100644
index 0000000..8221f1e
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/radar_chart.php
@@ -0,0 +1,172 @@
+
+ * $wikidata = include 'tutorial_wikipedia_data.php';
+ *
+ * $graph = new ezcGraphRadarChart();
+ * $graph->title = 'Wikipedia articles';
+ *
+ * $graph->options->fillLines = 220;
+ *
+ * // Add data
+ * foreach ( $wikidata as $language => $data )
+ * {
+ * $graph->data[$language] = new ezcGraphArrayDataSet( $data );
+ * $graph->data[$language][] = reset( $data );
+ * }
+ *
+ * $graph->render( 400, 150, 'tutorial_radar_chart.svg' );
+ *
+ *
+ * @property float $lineThickness
+ * Theickness of chart lines
+ * @property mixed $fillLines
+ * Status wheather the space between line and axis should get filled.
+ * - FALSE to not fill the space at all.
+ * - (int) Opacity used to fill up the space with the lines color.
+ * @property int $symbolSize
+ * Size of symbols in line chart.
+ * @property ezcGraphFontOptions $highlightFont
+ * Font configuration for highlight tests
+ * @property int $highlightSize
+ * Size of highlight blocks
+ * @property bool $highlightRadars
+ * If true, it adds lines to highlight the values position on the
+ * axis.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphRadarChartOptions extends ezcGraphChartOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['lineThickness'] = 1;
+ $this->properties['fillLines'] = false;
+ $this->properties['symbolSize'] = 8;
+ $this->properties['highlightFont'] = new ezcGraphFontOptions();
+ $this->properties['highlightFontCloned'] = false;
+ $this->properties['highlightSize'] = 14;
+ $this->properties['highlightRadars'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'lineThickness':
+ case 'symbolSize':
+ case 'highlightSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 1' );
+ }
+
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+ case 'fillLines':
+ if ( ( $propertyValue !== false ) &&
+ !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 255 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= int <= 255' );
+ }
+
+ $this->properties[$propertyName] = (
+ $propertyValue === false
+ ? false
+ : (int) $propertyValue );
+ break;
+ case 'highlightFont':
+ if ( $propertyValue instanceof ezcGraphFontOptions )
+ {
+ $this->properties['highlightFont'] = $propertyValue;
+ }
+ elseif ( is_string( $propertyValue ) )
+ {
+ if ( !$this->properties['highlightFontCloned'] )
+ {
+ $this->properties['highlightFont'] = clone $this->font;
+ $this->properties['highlightFontCloned'] = true;
+ }
+
+ $this->properties['highlightFont']->path = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphFontOptions' );
+ }
+ break;
+ $this->properties['highlightSize'] = max( 1, (int) $propertyValue );
+ break;
+ case 'highlightRadars':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['highlightRadars'] = $propertyValue;
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'highlightFont':
+ // Clone font configuration when requested for this element
+ if ( !$this->properties['highlightFontCloned'] )
+ {
+ $this->properties['highlightFont'] = clone $this->properties['font'];
+ $this->properties['highlightFontCloned'] = true;
+ }
+ return $this->properties['highlightFont'];
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/renderer.php b/src/TUnit/external/ezc/Graph/options/renderer.php
new file mode 100644
index 0000000..7f2e6a5
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/renderer.php
@@ -0,0 +1,248 @@
+
+ * $wikidata = include 'tutorial_wikipedia_data.php';
+ *
+ * $graph = new ezcGraphBarChart();
+ * $graph->title = 'Wikipedia articles';
+ *
+ * // Add data
+ * foreach ( $wikidata as $language => $data )
+ * {
+ * $graph->data[$language] = new ezcGraphArrayDataSet( $data );
+ * }
+ *
+ * // $graph->renderer = new ezcGraphRenderer2d();
+ *
+ * $graph->renderer->options->barMargin = .2;
+ * $graph->renderer->options->barPadding = .2;
+ *
+ * $graph->renderer->options->dataBorder = 0;
+ *
+ * $graph->render( 400, 150, 'tutorial_bar_chart_options.svg' );
+ *
+ *
+ * For additional options, which are special to some chart type you may
+ * also want to check the option classes for the repective chart type you
+ * are using and the elements of the chart. The chart type dependant option
+ * classes are:
+ *
+ * - ezcGraphLineChartOptions
+ * - ezcGraphPieChartOptions
+ * - ezcGraphRadarChartOptions
+ *
+ * There may be additional options dependant on the renderer you are using.
+ * You may want to check the extensions of this class:
+ *
+ * - ezcGraphRenderer2dOptions
+ * - ezcGraphRenderer3dOptions
+ *
+ * @property float $maxLabelHeight
+ * Percent of chart height used as maximum height for pie chart
+ * labels.
+ * @property bool $showSymbol
+ * Indicates wheather to show the line between pie elements and
+ * labels.
+ * @property float $symbolSize
+ * Size of symbols used to connect a label with a pie.
+ * @property float $moveOut
+ * Percent to move pie chart elements out of the middle on highlight.
+ * @property int $titlePosition
+ * Position of title in a box.
+ * @property int $titleAlignement
+ * Alignement of box titles.
+ * @property float $dataBorder
+ * Factor to darken border of data elements, like lines, bars and
+ * pie segments.
+ * @property float $barMargin
+ * Percentual distance between bar blocks.
+ * @property float $barPadding
+ * Percentual distance between bars.
+ * @property float $pieChartOffset
+ * Offset for starting with first pie chart segment in degrees.
+ * @property float $legendSymbolGleam
+ * Opacity of gleam in legend symbols
+ * @property float $legendSymbolGleamSize
+ * Size of gleam in legend symbols
+ * @property float $legendSymbolGleamColor
+ * Color of gleam in legend symbols
+ * @property float $pieVerticalSize
+ * Percent of vertical space used for maximum pie chart size.
+ * @property float $pieHorizontalSize
+ * Percent of horizontal space used for maximum pie chart size.
+ * @property float $pieChartSymbolColor
+ * Color of pie chart symbols
+ * @property float $pieChartGleam
+ * Enhance pie chart with gleam on top.
+ * @property float $pieChartGleamColor
+ * Color used for gleam on pie charts.
+ * @property float $pieChartGleamBorder
+ * Do not draw gleam on an outer border of this size.
+ * @property bool $syncAxisFonts
+ * Synchronize fonts of axis. With the defaut true value, the only
+ * the fonts of the yAxis will be used.
+ * @property bool $axisEndStyle
+ * Style of axis end markers. Defauls to arrow heads, but you may
+ * also use all symbol constants defined ein the ezcGraph class,
+ * especially ezcGraph::NO_SYMBOL.
+ * @property bool $shortAxis
+ * Defines wheather to render the axis extending the chart boundings
+ * or stop them at the chart boundings. Deafults to false.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphRendererOptions extends ezcGraphChartOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['maxLabelHeight'] = .10;
+ $this->properties['showSymbol'] = true;
+ $this->properties['symbolSize'] = 6;
+ $this->properties['moveOut'] = .1;
+ $this->properties['titlePosition'] = ezcGraph::TOP;
+ $this->properties['titleAlignement'] = ezcGraph::MIDDLE | ezcGraph::CENTER;
+ $this->properties['dataBorder'] = .5;
+ $this->properties['barMargin'] = .1;
+ $this->properties['barPadding'] = .05;
+ $this->properties['pieChartOffset'] = 0;
+ $this->properties['pieChartSymbolColor'] = ezcGraphColor::fromHex( '#000000' );
+ $this->properties['pieChartGleam'] = false;
+ $this->properties['pieChartGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' );
+ $this->properties['pieChartGleamBorder'] = 0;
+ $this->properties['legendSymbolGleam'] = false;
+ $this->properties['legendSymbolGleamSize'] = .9;
+ $this->properties['legendSymbolGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' );
+ $this->properties['pieVerticalSize'] = .5;
+ $this->properties['pieHorizontalSize'] = .25;
+ $this->properties['syncAxisFonts'] = true;
+ $this->properties['axisEndStyle'] = ezcGraph::ARROW;
+ $this->properties['shortAxis'] = false;
+
+ parent::__construct( $options );
+ }
+
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'dataBorder':
+ case 'pieChartGleam':
+ case 'legendSymbolGleam':
+ if ( $propertyValue !== false &&
+ !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= float <= 1' );
+ }
+
+ $this->properties[$propertyName] = (
+ $propertyValue === false
+ ? false
+ : (float) $propertyValue );
+ break;
+
+ case 'maxLabelHeight':
+ case 'moveOut':
+ case 'barMargin':
+ case 'barPadding':
+ case 'legendSymbolGleamSize':
+ case 'pieVerticalSize':
+ case 'pieHorizontalSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ case 'symbolSize':
+ case 'titlePosition':
+ case 'titleAlignement':
+ case 'pieChartGleamBorder':
+ case 'axisEndStyle':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'int >= 0' );
+ }
+
+ $this->properties[$propertyName] = (int) $propertyValue;
+ break;
+
+ case 'showSymbol':
+ case 'syncAxisFonts':
+ case 'shortAxis':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+ $this->properties[$propertyName] = (bool) $propertyValue;
+ break;
+
+ case 'pieChartOffset':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 360 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 360' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ case 'pieChartSymbolColor':
+ case 'pieChartGleamColor':
+ case 'legendSymbolGleamColor':
+ $this->properties[$propertyName] = ezcGraphColor::create( $propertyValue );
+ break;
+
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/renderer_2d.php b/src/TUnit/external/ezc/Graph/options/renderer_2d.php
new file mode 100644
index 0000000..fde2574
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/renderer_2d.php
@@ -0,0 +1,120 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteBlack();
+ * $graph->title = 'Access statistics';
+ * $graph->options->label = '%2$d (%3$.1f%%)';
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ * $graph->data['Access statistics']->highlight['Explorer'] = true;
+ *
+ * // $graph->renderer = new ezcGraphRenderer2d();
+ *
+ * $graph->renderer->options->moveOut = .2;
+ *
+ * $graph->renderer->options->pieChartOffset = 63;
+ *
+ * $graph->renderer->options->pieChartGleam = .3;
+ * $graph->renderer->options->pieChartGleamColor = '#FFFFFF';
+ * $graph->renderer->options->pieChartGleamBorder = 2;
+ *
+ * $graph->renderer->options->pieChartShadowSize = 3;
+ * $graph->renderer->options->pieChartShadowColor = '#000000';
+ *
+ * $graph->renderer->options->legendSymbolGleam = .5;
+ * $graph->renderer->options->legendSymbolGleamSize = .9;
+ * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
+ *
+ * $graph->renderer->options->pieChartSymbolColor = '#BABDB688';
+ *
+ * $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' );
+ *
+ *
+ * @property int $pieChartShadowSize
+ * Size of shadows.
+ * @property float $pieChartShadowTransparency
+ * Used transparency for pie chart shadows.
+ * @property float $pieChartShadowColor
+ * Color used for pie chart shadows.
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphRenderer2dOptions extends ezcGraphRendererOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['pieChartShadowSize'] = 0;
+ $this->properties['pieChartShadowTransparency'] = .3;
+ $this->properties['pieChartShadowColor'] = ezcGraphColor::fromHex( '#000000' );
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'pieChartShadowSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float >= 0' );
+ }
+
+ $this->properties['pieChartShadowSize'] = (int) $propertyValue;
+ break;
+ case 'pieChartShadowTransparency':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['pieChartShadowTransparency'] = (float) $propertyValue;
+ break;
+ case 'pieChartShadowColor':
+ $this->properties['pieChartShadowColor'] = ezcGraphColor::create( $propertyValue );
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/renderer_3d.php b/src/TUnit/external/ezc/Graph/options/renderer_3d.php
new file mode 100644
index 0000000..a5834ec
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/renderer_3d.php
@@ -0,0 +1,189 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzRed();
+ * $graph->title = 'Access statistics';
+ * $graph->options->label = '%2$d (%3$.1f%%)';
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ * $graph->data['Access statistics']->highlight['Explorer'] = true;
+ *
+ * $graph->renderer = new ezcGraphRenderer3d();
+ *
+ * $graph->renderer->options->moveOut = .2;
+ *
+ * $graph->renderer->options->pieChartOffset = 63;
+ *
+ * $graph->renderer->options->pieChartGleam = .3;
+ * $graph->renderer->options->pieChartGleamColor = '#FFFFFF';
+ *
+ * $graph->renderer->options->pieChartShadowSize = 5;
+ * $graph->renderer->options->pieChartShadowColor = '#000000';
+ *
+ * $graph->renderer->options->legendSymbolGleam = .5;
+ * $graph->renderer->options->legendSymbolGleamSize = .9;
+ * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
+ *
+ * $graph->renderer->options->pieChartSymbolColor = '#55575388';
+ *
+ * $graph->renderer->options->pieChartHeight = 5;
+ * $graph->renderer->options->pieChartRotation = .8;
+ *
+ * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' );
+ *
+ *
+ * @property bool $seperateLines
+ * Indicates wheather the full depth should be used for each line in
+ * the chart, or beeing seperated by the count of lines.
+ * @property float $fillAxis
+ * Transparency used to fill the axis polygon.
+ * @property float $fillGrid
+ * Transparency used to fill the grid lines.
+ * @property float $depth
+ * Part of picture used to simulate depth of three dimensional chart.
+ * @property float $pieChartHeight
+ * Height of the pie charts border.
+ * @property float $pieChartRotation
+ * Rotation of pie chart. Defines the percent of width used to
+ * calculate the height of the ellipse.
+ * @property int $pieChartShadowSize
+ * Size of shadows.
+ * @property float $pieChartShadowTransparency
+ * Used transparency for pie chart shadows.
+ * @property float $pieChartShadowColor
+ * Color used for pie chart shadows.
+ * @property float $barDarkenSide
+ * Factor to darken the color used for the bars side polygon.
+ * @property float $barDarkenTop
+ * Factor to darken the color used for the bars top polygon.
+ * @property float $barChartGleam
+ * Transparancy for gleam on bar charts
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphRenderer3dOptions extends ezcGraphRendererOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['seperateLines'] = true;
+ $this->properties['fillAxis'] = .8;
+ $this->properties['fillGrid'] = 0;
+ $this->properties['depth'] = .1;
+ $this->properties['pieChartHeight'] = 10.;
+ $this->properties['pieChartRotation'] = .6;
+ $this->properties['pieChartShadowSize'] = 0;
+ $this->properties['pieChartShadowTransparency'] = .3;
+ $this->properties['pieChartShadowColor'] = ezcGraphColor::fromHex( '#000000' );
+ $this->properties['pieChartGleam'] = false;
+ $this->properties['pieChartGleamColor'] = ezcGraphColor::fromHex( '#FFFFFF' );
+ $this->properties['barDarkenSide'] = .2;
+ $this->properties['barDarkenTop'] = .4;
+ $this->properties['barChartGleam'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'fillAxis':
+ case 'fillGrid':
+ if ( $propertyValue !== false &&
+ !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'false OR 0 <= float <= 1' );
+ }
+
+ $this->properties[$propertyName] = (
+ $propertyValue === false
+ ? false
+ : (float) $propertyValue );
+ break;
+
+ case 'depth':
+ case 'pieChartRotation':
+ case 'pieChartShadowTransparency':
+ case 'barDarkenSide':
+ case 'barDarkenTop':
+ case 'barChartGleam':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ case 'pieChartHeight':
+ case 'pieChartShadowSize':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue <= 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties[$propertyName] = (float) $propertyValue;
+ break;
+
+ case 'seperateLines':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['seperateLines'] = $propertyValue;
+ break;
+ case 'pieChartShadowColor':
+ $this->properties['pieChartShadowColor'] = ezcGraphColor::create( $propertyValue );
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/options/svg_driver.php b/src/TUnit/external/ezc/Graph/options/svg_driver.php
new file mode 100644
index 0000000..45e9a0c
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/options/svg_driver.php
@@ -0,0 +1,272 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->background->color = '#FFFFFFFF';
+ * $graph->title = 'Access statistics';
+ * $graph->legend = false;
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ *
+ * $graph->driver->options->templateDocument = dirname( __FILE__ ) . '/template.svg';
+ * $graph->driver->options->graphOffset = new ezcGraphCoordinate( 25, 40 );
+ * $graph->driver->options->insertIntoGroup = 'ezcGraph';
+ *
+ * $graph->render( 400, 200, 'tutorial_driver_svg.svg' );
+ *
+ *
+ * @property string $encoding
+ * Encoding of the SVG XML document
+ * @property float $assumedNumericCharacterWidth
+ * Assumed percentual average width of chars in numeric strings with
+ * the used font.
+ * @property float $assumedTextCharacterWidth
+ * Assumed percentual average width of chars in non numeric strings
+ * with the used font.
+ * @property string $strokeLineCap
+ * This specifies the shape to be used at the end of open subpaths
+ * when they are stroked.
+ * @property string $strokeLineJoin
+ * This specifies the shape to be used at the edges of paths.
+ * @property string $shapeRendering
+ * "The creator of SVG content might want to provide a hint to the
+ * implementation about what tradeoffs to make as it renders vector
+ * graphics elements such as 'path' elements and basic shapes such as
+ * circles and rectangles."
+ * @property string $colorRendering
+ * "The creator of SVG content might want to provide a hint to the
+ * implementation about how to make speed vs. quality tradeoffs as it
+ * performs color interpolation and compositing. The
+ * 'color-rendering' property provides a hint to the SVG user agent
+ * about how to optimize its color interpolation and compositing
+ * operations."
+ * @property string $textRendering
+ * "The creator of SVG content might want to provide a hint to the
+ * implementation about what tradeoffs to make as it renders text."
+ * @property mixed $templateDocument
+ * Use existing SVG document as template to insert graph into. If
+ * insertIntoGroup is not set, a new group will be inserted in the
+ * svg root node.
+ * @property mixed $insertIntoGroup
+ * ID of a SVG group node to insert the graph. Only works with a
+ * custom template document.
+ * @property ezcGraphCoordinate $graphOffset
+ * Offset of the graph in the svg.
+ * @property string $idPrefix
+ * Prefix used for the ids in SVG documents.
+ * @property string $linkCursor
+ * CSS value for cursor property used for linked SVG elements
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphSvgDriverOptions extends ezcGraphDriverOptions
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['encoding'] = null;
+ $this->properties['assumedNumericCharacterWidth'] = .62;
+ $this->properties['assumedTextCharacterWidth'] = .53;
+ $this->properties['strokeLineJoin'] = 'round';
+ $this->properties['strokeLineCap'] = 'round';
+ $this->properties['shapeRendering'] = 'geometricPrecision';
+ $this->properties['colorRendering'] = 'optimizeQuality';
+ $this->properties['textRendering'] = 'optimizeLegibility';
+ $this->properties['templateDocument'] = false;
+ $this->properties['insertIntoGroup'] = false;
+ $this->properties['graphOffset'] = new ezcGraphCoordinate( 0, 0 );
+ $this->properties['idPrefix'] = 'ezcGraph';
+ $this->properties['linkCursor'] = 'pointer';
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * Set an option value
+ *
+ * @param string $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBasePropertyNotFoundException
+ * If a property is not defined in this class
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'assumedNumericCharacterWidth':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['assumedNumericCharacterWidth'] = (float) $propertyValue;
+ break;
+ case 'assumedTextCharacterWidth':
+ if ( !is_numeric( $propertyValue ) ||
+ ( $propertyValue < 0 ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'float > 0' );
+ }
+
+ $this->properties['assumedTextCharacterWidth'] = (float) $propertyValue;
+ break;
+ case 'strokeLineJoin':
+ $values = array(
+ 'round',
+ 'miter',
+ 'bevel',
+ 'inherit',
+ );
+
+ if ( in_array( $propertyValue, $values, true ) )
+ {
+ $this->properties['strokeLineJoin'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) );
+ }
+ break;
+ case 'strokeLineCap':
+ $values = array(
+ 'round',
+ 'butt',
+ 'square',
+ 'inherit',
+ );
+
+ if ( in_array( $propertyValue, $values, true ) )
+ {
+ $this->properties['strokeLineCap'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) );
+ }
+ break;
+ case 'shapeRendering':
+ $values = array(
+ 'auto',
+ 'optimizeSpeed',
+ 'crispEdges',
+ 'geometricPrecision',
+ 'inherit',
+ );
+
+ if ( in_array( $propertyValue, $values, true ) )
+ {
+ $this->properties['shapeRendering'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) );
+ }
+ break;
+ case 'colorRendering':
+ $values = array(
+ 'auto',
+ 'optimizeSpeed',
+ 'optimizeQuality',
+ 'inherit',
+ );
+
+ if ( in_array( $propertyValue, $values, true ) )
+ {
+ $this->properties['colorRendering'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) );
+ }
+ break;
+ case 'textRendering':
+ $values = array(
+ 'auto',
+ 'optimizeSpeed',
+ 'optimizeLegibility',
+ 'geometricPrecision',
+ 'inherit',
+ );
+
+ if ( in_array( $propertyValue, $values, true ) )
+ {
+ $this->properties['textRendering'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, implode( $values, ', ' ) );
+ }
+ break;
+ case 'templateDocument':
+ if ( !is_file( $propertyValue ) || !is_readable( $propertyValue ) )
+ {
+ throw new ezcBaseFileNotFoundException( $propertyValue );
+ }
+ else
+ {
+ $this->properties['templateDocument'] = realpath( $propertyValue );
+ }
+ break;
+ case 'insertIntoGroup':
+ if ( !is_string( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'string' );
+ }
+ else
+ {
+ $this->properties['insertIntoGroup'] = $propertyValue;
+ }
+ break;
+ case 'graphOffset':
+ if ( $propertyValue instanceof ezcGraphCoordinate )
+ {
+ $this->properties['graphOffset'] = $propertyValue;
+ }
+ else
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'ezcGraphCoordinate' );
+ }
+ break;
+ case 'idPrefix':
+ $this->properties['idPrefix'] = (string) $propertyValue;
+ break;
+ case 'encoding':
+ $this->properties['encoding'] = (string) $propertyValue;
+ break;
+ case 'linkCursor':
+ $this->properties['linkCursor'] = (string) $propertyValue;
+ break;
+ default:
+ parent::__set( $propertyName, $propertyValue );
+ break;
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/palette/black.php b/src/TUnit/external/ezc/Graph/palette/black.php
new file mode 100644
index 0000000..86f7fc3
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/palette/black.php
@@ -0,0 +1,114 @@
+
diff --git a/src/TUnit/external/ezc/Graph/palette/ez.php b/src/TUnit/external/ezc/Graph/palette/ez.php
new file mode 100644
index 0000000..2d31a38
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/palette/ez.php
@@ -0,0 +1,97 @@
+
diff --git a/src/TUnit/external/ezc/Graph/palette/ez_blue.php b/src/TUnit/external/ezc/Graph/palette/ez_blue.php
new file mode 100644
index 0000000..ada89f7
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/palette/ez_blue.php
@@ -0,0 +1,90 @@
+
diff --git a/src/TUnit/external/ezc/Graph/palette/ez_green.php b/src/TUnit/external/ezc/Graph/palette/ez_green.php
new file mode 100644
index 0000000..16bd6ef
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/palette/ez_green.php
@@ -0,0 +1,90 @@
+
diff --git a/src/TUnit/external/ezc/Graph/palette/ez_red.php b/src/TUnit/external/ezc/Graph/palette/ez_red.php
new file mode 100644
index 0000000..4d825a0
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/palette/ez_red.php
@@ -0,0 +1,90 @@
+
diff --git a/src/TUnit/external/ezc/Graph/palette/tango.php b/src/TUnit/external/ezc/Graph/palette/tango.php
new file mode 100644
index 0000000..e13f802
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/palette/tango.php
@@ -0,0 +1,87 @@
+
diff --git a/src/TUnit/external/ezc/Graph/renderer/2d.php b/src/TUnit/external/ezc/Graph/renderer/2d.php
new file mode 100644
index 0000000..e1b0951
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/2d.php
@@ -0,0 +1,1885 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteBlack();
+ * $graph->title = 'Access statistics';
+ * $graph->options->label = '%2$d (%3$.1f%%)';
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ * $graph->data['Access statistics']->highlight['Explorer'] = true;
+ *
+ * // $graph->renderer = new ezcGraphRenderer2d();
+ *
+ * $graph->renderer->options->moveOut = .2;
+ *
+ * $graph->renderer->options->pieChartOffset = 63;
+ *
+ * $graph->renderer->options->pieChartGleam = .3;
+ * $graph->renderer->options->pieChartGleamColor = '#FFFFFF';
+ * $graph->renderer->options->pieChartGleamBorder = 2;
+ *
+ * $graph->renderer->options->pieChartShadowSize = 3;
+ * $graph->renderer->options->pieChartShadowColor = '#000000';
+ *
+ * $graph->renderer->options->legendSymbolGleam = .5;
+ * $graph->renderer->options->legendSymbolGleamSize = .9;
+ * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
+ *
+ * $graph->renderer->options->pieChartSymbolColor = '#BABDB688';
+ *
+ * $graph->render( 400, 150, 'tutorial_pie_chart_pimped.svg' );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphRenderer2d
+ extends
+ ezcGraphRenderer
+ implements
+ ezcGraphRadarRenderer, ezcGraphStackedBarsRenderer, ezcGraphOdometerRenderer
+{
+
+ /**
+ * Pie segment labels divided into two array, containing the labels on the
+ * left and right side of the pie chart center.
+ *
+ * @var array
+ */
+ protected $pieSegmentLabels = array(
+ 0 => array(),
+ 1 => array(),
+ );
+
+ /**
+ * Contains the boundings used for pie segments
+ *
+ * @var ezcGraphBoundings
+ */
+ protected $pieSegmentBoundings = false;
+
+ /**
+ * Array with symbols for post processing, which ensures, that the symbols
+ * are rendered topmost.
+ *
+ * @var array
+ */
+ protected $linePostSymbols = array();
+
+ /**
+ * Options
+ *
+ * @var ezcGraphRenderer2dOptions
+ */
+ protected $options;
+
+ /**
+ * Collect axis labels, so that the axis are drawn, when all axis spaces
+ * are known.
+ *
+ * @var array
+ */
+ protected $axisLabels = array();
+
+ /**
+ * Collects circle sectors to draw shadow in background of all circle
+ * sectors.
+ *
+ * @var array
+ */
+ protected $circleSectors = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->options = new ezcGraphRenderer2dOptions( $options );
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'options':
+ return $this->options;
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+
+ /**
+ * Draw pie segment
+ *
+ * Draws a single pie segment
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of pie segment
+ * @param float $startAngle Start angle
+ * @param float $endAngle End angle
+ * @param mixed $label Label of pie segment
+ * @param bool $moveOut Move out from middle for hilighting
+ * @return void
+ */
+ public function drawPieSegment(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ $startAngle = .0,
+ $endAngle = 360.,
+ $label = false,
+ $moveOut = false )
+ {
+ // Apply offset
+ $startAngle += $this->options->pieChartOffset;
+ $endAngle += $this->options->pieChartOffset;
+
+ // Calculate position and size of pie
+ $center = new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) / 2,
+ $boundings->y0 + ( $boundings->height ) / 2
+ );
+
+ // Limit radius to fourth of width and half of height at maximum
+ $radius = min(
+ ( $boundings->width ) * $this->options->pieHorizontalSize,
+ ( $boundings->height ) * $this->options->pieVerticalSize
+ );
+
+ // Move pie segment out of the center
+ if ( $moveOut )
+ {
+ $direction = ( $endAngle + $startAngle ) / 2;
+
+ $center = new ezcGraphCoordinate(
+ $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ),
+ $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) )
+ );
+ }
+
+ // Add circle sector to queue
+ $this->circleSectors[] = array(
+ 'center' => $center,
+ 'context' => $context,
+ 'width' => $radius * 2 * ( 1 - $this->options->moveOut ),
+ 'height' => $radius * 2 * ( 1 - $this->options->moveOut ),
+ 'start' => $startAngle,
+ 'end' => $endAngle,
+ 'color' => $color,
+ );
+
+ if ( $label )
+ {
+ // Determine position of label
+ $direction = ( $endAngle + $startAngle ) / 2;
+ $pieSegmentCenter = new ezcGraphCoordinate(
+ $center->x + cos( deg2rad( $direction ) ) * $radius,
+ $center->y + sin( deg2rad( $direction ) ) * $radius
+ );
+
+ // Split labels up into left an right size and index them on their
+ // y position
+ $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][$pieSegmentCenter->y] = array(
+ new ezcGraphCoordinate(
+ $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3,
+ $center->y + sin( deg2rad( $direction ) ) * $radius * 2 / 3
+ ),
+ $label,
+ $context
+ );
+ }
+
+ if ( !$this->pieSegmentBoundings )
+ {
+ $this->pieSegmentBoundings = $boundings;
+ }
+ }
+
+ /**
+ * Draws the collected circle sectors
+ *
+ * All circle sectors are collected and drawn later to be able to render
+ * the shadows of the pie segments in the back of all pie segments.
+ *
+ * @return void
+ */
+ protected function finishCircleSectors()
+ {
+ // Add circle sector sides to simple z buffer prioriry list
+ if ( $this->options->pieChartShadowSize > 0 )
+ {
+ foreach ( $this->circleSectors as $circleSector )
+ {
+ $this->driver->drawCircleSector(
+ new ezcGraphCoordinate(
+ $circleSector['center']->x + $this->options->pieChartShadowSize,
+ $circleSector['center']->y + $this->options->pieChartShadowSize
+ ),
+ $circleSector['width'],
+ $circleSector['height'],
+ $circleSector['start'],
+ $circleSector['end'],
+ $this->options->pieChartShadowColor->transparent( $this->options->pieChartShadowTransparency ),
+ true
+ );
+ }
+ }
+
+ foreach ( $this->circleSectors as $circleSector )
+ {
+ // Draw circle sector
+ $this->addElementReference(
+ $circleSector['context'],
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'],
+ $circleSector['height'],
+ $circleSector['start'],
+ $circleSector['end'],
+ $circleSector['color'],
+ true
+ )
+ );
+
+ $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'],
+ $circleSector['height'],
+ $circleSector['start'],
+ $circleSector['end'],
+ $darkenedColor,
+ false
+ );
+
+ if ( $this->options->pieChartGleam !== false )
+ {
+ $gradient = new ezcGraphLinearGradient(
+ $circleSector['center'],
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y - $circleSector['height'] / 2
+ ),
+ $this->options->pieChartGleamColor->transparent( 1 ),
+ $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
+ );
+
+ $this->addElementReference( $circleSector['context'],
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
+ $circleSector['height'] - $this->options->pieChartGleamBorder * 2,
+ $circleSector['start'],
+ $circleSector['end'],
+ $gradient,
+ true
+ )
+ );
+
+ $gradient = new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y + $circleSector['height'] / 4
+ ),
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y + $circleSector['height'] / 2
+ ),
+ $this->options->pieChartGleamColor->transparent( 1 ),
+ $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
+ );
+
+ $this->addElementReference( $circleSector['context'],
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
+ $circleSector['height'] - $this->options->pieChartGleamBorder * 2,
+ $circleSector['start'],
+ $circleSector['end'],
+ $gradient,
+ true
+ )
+ );
+ }
+ }
+ }
+
+ /**
+ * Draws the collected pie segment labels
+ *
+ * All labels are collected and drawn later to be able to partition the
+ * available space for the labels woth knowledge of the overall label
+ * count and their required size and optimal position.
+ *
+ * @return void
+ */
+ protected function finishPieSegmentLabels()
+ {
+ if ( $this->pieSegmentBoundings === false )
+ {
+ return true;
+ }
+
+ $boundings = $this->pieSegmentBoundings;
+
+ // Calculate position and size of pie
+ $center = new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) / 2,
+ $boundings->y0 + ( $boundings->height ) / 2
+ );
+
+ // Limit radius to fourth of width and half of height at maximum
+ $radius = min(
+ ( $boundings->width ) * $this->options->pieHorizontalSize,
+ ( $boundings->height ) * $this->options->pieVerticalSize
+ );
+
+ $pieChartHeight = min(
+ $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4,
+ $boundings->height
+ );
+ $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2;
+
+ // Calculate maximum height of labels
+ $labelHeight = min(
+ ( count( $this->pieSegmentLabels[0] )
+ ? $pieChartHeight / count( $this->pieSegmentLabels[0] )
+ : $pieChartHeight
+ ),
+ ( count( $this->pieSegmentLabels[1] )
+ ? $pieChartHeight / count( $this->pieSegmentLabels[1] )
+ : $pieChartHeight
+ ),
+ ( $pieChartHeight ) * $this->options->maxLabelHeight
+ );
+
+ $symbolSize = $this->options->symbolSize;
+
+ foreach ( $this->pieSegmentLabels as $side => $labelPart )
+ {
+ $minHeight = $pieChartYPosition;
+ $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight;
+
+ // Sort to draw topmost label first
+ ksort( $labelPart );
+ $sign = ( $side ? -1 : 1 );
+
+ foreach ( $labelPart as $height => $label )
+ {
+ if ( ( $height - $labelHeight / 2 ) > $minHeight )
+ {
+ $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight );
+ $minHeight += $share;
+ $toShare -= $share;
+ }
+
+ // Determine position of label
+ $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare;
+ $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius;
+
+ $labelPosition = new ezcGraphCoordinate(
+ $center->x -
+ $sign * (
+ abs( $verticalDistance ) > 1
+ // If vertical distance to center is greater then the
+ // radius, use the centerline for the horizontal
+ // position
+ ? max (
+ 5,
+ abs( $label[0]->x - $center->x )
+ )
+ // Else place the label outside of the pie chart
+ : ( cos ( asin ( $verticalDistance ) ) * $radius +
+ $symbolSize * (int) $this->options->showSymbol
+ )
+ ),
+ $minHeight + $labelHeight / 2
+ );
+
+ if ( $this->options->showSymbol )
+ {
+ // Draw label
+ $this->driver->drawLine(
+ $label[0],
+ $labelPosition,
+ $this->options->pieChartSymbolColor,
+ 1
+ );
+
+ $this->driver->drawCircle(
+ $label[0],
+ $symbolSize,
+ $symbolSize,
+ $this->options->pieChartSymbolColor,
+ true
+ );
+ $this->driver->drawCircle(
+ $labelPosition,
+ $symbolSize,
+ $symbolSize,
+ $this->options->pieChartSymbolColor,
+ true
+ );
+ }
+
+ $this->addElementReference(
+ $label[2],
+ $this->driver->drawTextBox(
+ $label[1],
+ new ezcGraphCoordinate(
+ ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ),
+ $minHeight
+ ),
+ ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ),
+ $labelHeight,
+ ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE
+ )
+ );
+
+ // Add used space to minHeight
+ $minHeight += $labelHeight;
+ }
+ }
+ }
+
+ /**
+ * Draw the collected line symbols
+ *
+ * Symbols for the data lines are collected and delayed to ensure that
+ * they are not covered and hidden by other data lines.
+ *
+ * @return void
+ */
+ protected function finishLineSymbols()
+ {
+ foreach ( $this->linePostSymbols as $symbol )
+ {
+ $this->addElementReference(
+ $symbol['context'],
+ $this->drawSymbol(
+ $symbol['boundings'],
+ $symbol['color'],
+ $symbol['symbol']
+ )
+ );
+ }
+ }
+
+ /**
+ * Draw bar
+ *
+ * Draws a bar as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $position Position of data point
+ * @param float $stepSize Space which can be used for bars
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @return void
+ */
+ public function drawBar(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $position,
+ $stepSize,
+ $dataNumber = 1,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ $axisPosition = 0. )
+ {
+ // Apply margin
+ $margin = $stepSize * $this->options->barMargin;
+ $padding = $stepSize * $this->options->barPadding;
+ $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
+ $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2;
+
+ $barPointArray = array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
+ $boundings->y0 + ( $boundings->height ) * $axisPosition
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
+ $boundings->y0 + ( $boundings->height ) * $position->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
+ $boundings->y0 + ( $boundings->height ) * $position->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
+ $boundings->y0 + ( $boundings->height ) * $axisPosition
+ ),
+ );
+
+ $this->addElementReference(
+ $context,
+ $this->driver->drawPolygon(
+ $barPointArray,
+ $color,
+ true
+ )
+ );
+
+ if ( $this->options->dataBorder > 0 )
+ {
+ $darkened = $color->darken( $this->options->dataBorder );
+ $this->driver->drawPolygon(
+ $barPointArray,
+ $darkened,
+ false,
+ 1
+ );
+ }
+ }
+
+ /**
+ * Draw stacked bar
+ *
+ * Draws a stacked bar part as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $start
+ * @param ezcGraphCoordinate $position
+ * @param float $stepSize Space which can be used for bars
+ * @param int $symbol Symbol to draw for line
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @return void
+ */
+ public function drawStackedBar(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $position,
+ $stepSize,
+ $symbol = ezcGraph::NO_SYMBOL,
+ $axisPosition = 0. )
+ {
+ // Apply margin
+ $margin = $stepSize * $this->options->barMargin;
+ $barWidth = $stepSize - $margin;
+ $offset = - $stepSize / 2 + $margin / 2;
+
+ $barPointArray = array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
+ $boundings->y0 + ( $boundings->height ) * $start->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset,
+ $boundings->y0 + ( $boundings->height ) * $position->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
+ $boundings->y0 + ( $boundings->height ) * $position->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $position->x + $offset + $barWidth,
+ $boundings->y0 + ( $boundings->height ) * $start->y
+ ),
+ );
+
+ $this->addElementReference(
+ $context,
+ $this->driver->drawPolygon(
+ $barPointArray,
+ $color,
+ true
+ )
+ );
+
+ if ( $this->options->dataBorder > 0 )
+ {
+ $darkened = $color->darken( $this->options->dataBorder );
+ $this->driver->drawPolygon(
+ $barPointArray,
+ $darkened,
+ false,
+ 1
+ );
+ }
+ }
+
+ /**
+ * Draw data line
+ *
+ * Draws a line as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $start Starting point
+ * @param ezcGraphCoordinate $end Ending point
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
+ * @param ezcGraphColor $fillColor Color to fill line with
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawDataLine(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ $dataNumber = 1,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ ezcGraphColor $symbolColor = null,
+ ezcGraphColor $fillColor = null,
+ $axisPosition = 0.,
+ $thickness = 1. )
+ {
+ // Perhaps fill up line
+ if ( $fillColor !== null &&
+ $start->x != $end->x )
+ {
+ $startValue = $axisPosition - $start->y;
+ $endValue = $axisPosition - $end->y;
+
+ if ( ( $startValue == 0 ) ||
+ ( $endValue == 0 ) ||
+ ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) )
+ {
+ // Values have the same sign or are on the axis
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $start->x,
+ $boundings->y0 + ( $boundings->height ) * $start->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $end->x,
+ $boundings->y0 + ( $boundings->height ) * $end->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $end->x,
+ $boundings->y0 + ( $boundings->height ) * $axisPosition
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $start->x,
+ $boundings->y0 + ( $boundings->height ) * $axisPosition
+ ),
+ ),
+ $fillColor,
+ true
+ );
+ }
+ else
+ {
+ // values are on differente sides of the axis - split the filled polygon
+ $startDiff = abs( $axisPosition - $start->y );
+ $endDiff = abs( $axisPosition - $end->y );
+
+ $cuttingPosition = $startDiff / ( $endDiff + $startDiff );
+ $cuttingPoint = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $cuttingPosition,
+ $axisPosition
+ );
+
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $start->x,
+ $boundings->y0 + ( $boundings->height ) * $axisPosition
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $start->x,
+ $boundings->y0 + ( $boundings->height ) * $start->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x,
+ $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y
+ ),
+ ),
+ $fillColor,
+ true
+ );
+
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $end->x,
+ $boundings->y0 + ( $boundings->height ) * $axisPosition
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $end->x,
+ $boundings->y0 + ( $boundings->height ) * $end->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $cuttingPoint->x,
+ $boundings->y0 + ( $boundings->height ) * $cuttingPoint->y
+ ),
+ ),
+ $fillColor,
+ true
+ );
+ }
+ }
+
+ // Draw line
+ $this->driver->drawLine(
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $start->x,
+ $boundings->y0 + ( $boundings->height ) * $start->y
+ ),
+ new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $end->x,
+ $boundings->y0 + ( $boundings->height ) * $end->y
+ ),
+ $color,
+ $thickness
+ );
+
+ // Draw line symbol
+ if ( $symbol !== ezcGraph::NO_SYMBOL )
+ {
+ if ( $symbolColor === null )
+ {
+ $symbolColor = $color;
+ }
+
+ $this->linePostSymbols[] = array(
+ 'boundings' => new ezcGraphBoundings(
+ $boundings->x0 + ( $boundings->width ) * $end->x - $this->options->symbolSize / 2,
+ $boundings->y0 + ( $boundings->height ) * $end->y - $this->options->symbolSize / 2,
+ $boundings->x0 + ( $boundings->width ) * $end->x + $this->options->symbolSize / 2,
+ $boundings->y0 + ( $boundings->height ) * $end->y + $this->options->symbolSize / 2
+ ),
+ 'color' => $symbolColor,
+ 'context' => $context,
+ 'symbol' => $symbol,
+ );
+ }
+ }
+
+ /**
+ * Returns a coordinate in the given bounding box for the given angle
+ * radius with the center as base point.
+ *
+ * @param ezcGraphBoundings $boundings
+ * @param ezcGraphCoordinate $center
+ * @param float $angle
+ * @param float $radius
+ * @return float
+ */
+ protected function getCoordinateFromAngleAndRadius(
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $center,
+ $angle,
+ $radius
+ )
+ {
+ $direction = new ezcGraphCoordinate(
+ sin( $angle ) * $boundings->width / 2,
+ -cos( $angle ) * $boundings->height / 2
+ );
+
+ $offset = new ezcGraphCoordinate(
+ sin( $angle ) * $this->xAxisSpace,
+ -cos( $angle ) * $this->yAxisSpace
+ );
+
+ $direction->x -= 2 * $offset->x;
+ $direction->y -= 2 * $offset->y;
+
+ return new ezcGraphCoordinate(
+ $boundings->x0 +
+ $center->x +
+ $offset->x +
+ $direction->x * $radius,
+ $boundings->y0 +
+ $center->y +
+ $offset->y +
+ $direction->y * $radius
+ );
+ }
+
+ /**
+ * Draw radar chart data line
+ *
+ * Draws a line as a data element in a radar chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $center Center of radar chart
+ * @param ezcGraphCoordinate $start Starting point
+ * @param ezcGraphCoordinate $end Ending point
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
+ * @param ezcGraphColor $fillColor Color to fill line with
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawRadarDataLine(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $center,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ $dataNumber = 1,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ ezcGraphColor $symbolColor = null,
+ ezcGraphColor $fillColor = null,
+ $thickness = 1.
+ )
+ {
+ // Calculate line points from chart coordinates
+ $start = $this->getCoordinateFromAngleAndRadius(
+ $boundings,
+ $center,
+ $start->x * 2 * M_PI,
+ $start->y
+ );
+ $end = $this->getCoordinateFromAngleAndRadius(
+ $boundings,
+ $center,
+ $end->x * 2 * M_PI,
+ $end->y
+ );
+
+ // Fill line
+ if ( $fillColor !== null )
+ {
+ $this->driver->drawPolygon(
+ array(
+ $start,
+ $end,
+ new ezcGraphCoordinate(
+ $boundings->x0 + $center->x,
+ $boundings->y0 + $center->y
+ ),
+ ),
+ $fillColor,
+ true
+ );
+ }
+
+ // Draw line
+ $this->driver->drawLine(
+ $start,
+ $end,
+ $color,
+ $thickness
+ );
+
+ if ( $symbol !== ezcGraph::NO_SYMBOL )
+ {
+ $this->drawSymbol(
+ new ezcGraphBoundings(
+ $end->x - $this->options->symbolSize / 2,
+ $end->y - $this->options->symbolSize / 2,
+ $end->x + $this->options->symbolSize / 2,
+ $end->y + $this->options->symbolSize / 2
+ ),
+ $symbolColor,
+ $symbol
+ );
+ }
+ }
+
+ /**
+ * Draws a highlight textbox for a datapoint.
+ *
+ * A highlight textbox for line and bar charts means a box with the current
+ * value in the graph.
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphCoordinate $end Ending point
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param ezcGraphFontOptions $font Font used for highlight string
+ * @param string $text Acutual value
+ * @param int $size Size of highlight text
+ * @param ezcGraphColor $markLines
+ * @param int $xOffset
+ * @param int $yOffset
+ * @param float $stepSize
+ * @param int $type
+ * @return void
+ */
+ public function drawDataHighlightText(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphCoordinate $end,
+ $axisPosition = 0.,
+ $dataNumber = 1,
+ $dataCount = 1,
+ ezcGraphFontOptions $font,
+ $text,
+ $size,
+ ezcGraphColor $markLines = null,
+ $xOffset = 0,
+ $yOffset = 0,
+ $stepSize = 0.,
+ $type = ezcGraph::LINE )
+ {
+ // Bar specific calculations
+ if ( $type !== ezcGraph::LINE )
+ {
+ $margin = $stepSize * $this->options->barMargin;
+ $padding = $stepSize * $this->options->barPadding;
+ $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
+ $offset = -( $dataNumber + ( $dataCount - 1 ) / -2 ) * ( $barWidth + $padding );
+ }
+
+ $this->driver->options->font = $font;
+ $width = $boundings->width / $dataCount;
+
+ $dataPoint = new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->width ) * $end->x + $xOffset +
+ ( $type === ezcGraph::LINE ? 0 : $offset ),
+ $boundings->y0 + ( $boundings->height ) * $end->y + $yOffset
+ );
+
+ if ( $end->y < $axisPosition )
+ {
+ $this->driver->drawTextBox(
+ $text,
+ new ezcGraphCoordinate(
+ $dataPoint->x - $width / 2,
+ $dataPoint->y - $size - $font->padding - $this->options->symbolSize
+ ),
+ $width,
+ $size,
+ ezcGraph::CENTER | ezcGraph::BOTTOM
+ );
+ }
+ else
+ {
+ $this->driver->drawTextBox(
+ $text,
+ new ezcGraphCoordinate(
+ $dataPoint->x - $width / 2,
+ $dataPoint->y + $font->padding + $this->options->symbolSize
+ ),
+ $width,
+ $size,
+ ezcGraph::CENTER | ezcGraph::TOP
+ );
+ }
+ }
+
+ /**
+ * Draw legend
+ *
+ * Will draw a legend in the bounding box
+ *
+ * @param ezcGraphBoundings $boundings Bounding of legend
+ * @param ezcGraphChartElementLegend $legend Legend to draw;
+ * @param int $type Type of legend: Protrait or landscape
+ * @return void
+ */
+ public function drawLegend(
+ ezcGraphBoundings $boundings,
+ ezcGraphChartElementLegend $legend,
+ $type = ezcGraph::VERTICAL )
+ {
+ $labels = $legend->labels;
+
+ // Calculate boundings of each label
+ if ( $type & ezcGraph::VERTICAL )
+ {
+ $labelWidth = $boundings->width;
+ $labelHeight = min(
+ ( $boundings->height ) / count( $labels ) - $legend->spacing,
+ $legend->symbolSize + 2 * $legend->padding
+ );
+ }
+ else
+ {
+ $labelWidth = ( $boundings->width ) / count( $labels ) - $legend->spacing;
+ $labelHeight = min(
+ $boundings->height,
+ $legend->symbolSize + 2 * $legend->padding
+ );
+ }
+
+ $symbolSize = $labelHeight - 2 * $legend->padding;
+
+ // Draw all labels
+ $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 );
+ foreach ( $labels as $label )
+ {
+ $this->elements['legend_url'][$label['label']] = $label['url'];
+
+ $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol(
+ new ezcGraphBoundings(
+ $labelPosition->x + $legend->padding,
+ $labelPosition->y + $legend->padding,
+ $labelPosition->x + $legend->padding + $symbolSize,
+ $labelPosition->y + $legend->padding + $symbolSize
+ ),
+ $label['color'],
+ $label['symbol']
+ );
+
+ $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox(
+ $label['label'],
+ new ezcGraphCoordinate(
+ $labelPosition->x + 2 * $legend->padding + $symbolSize,
+ $labelPosition->y + $legend->padding
+ ),
+ $labelWidth - $symbolSize - 3 * $legend->padding,
+ $labelHeight - 2 * $legend->padding,
+ ezcGraph::LEFT | ezcGraph::MIDDLE
+ );
+
+ $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing );
+ $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 );
+ }
+ }
+
+ /**
+ * Draw box
+ *
+ * Box are wrapping each major chart element and draw border, background
+ * and title to each chart element.
+ *
+ * Optionally a padding and margin for each box can be defined.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of the box
+ * @param ezcGraphColor $background Background color
+ * @param ezcGraphColor $borderColor Border color
+ * @param int $borderWidth Border width
+ * @param int $margin Margin
+ * @param int $padding Padding
+ * @param mixed $title Title of the box
+ * @param int $titleSize Size of title in the box
+ * @return ezcGraphBoundings Remaining inner boundings
+ */
+ public function drawBox(
+ ezcGraphBoundings $boundings,
+ ezcGraphColor $background = null,
+ ezcGraphColor $borderColor = null,
+ $borderWidth = 0,
+ $margin = 0,
+ $padding = 0,
+ $title = false,
+ $titleSize = 16 )
+ {
+ // Apply margin
+ $boundings->x0 += $margin;
+ $boundings->y0 += $margin;
+ $boundings->x1 -= $margin;
+ $boundings->y1 -= $margin;
+
+ if ( $background instanceof ezcGraphColor )
+ {
+ // Draw box background
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
+ ),
+ $background,
+ true
+ );
+ }
+
+ if ( ( $borderColor instanceof ezcGraphColor ) &&
+ ( $borderWidth > 0 ) )
+ {
+ // Draw border
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
+ ),
+ $borderColor,
+ false,
+ $borderWidth
+ );
+
+ // Reduce local boundings by borderWidth
+ $boundings->x0 += $borderWidth;
+ $boundings->y0 += $borderWidth;
+ $boundings->x1 -= $borderWidth;
+ $boundings->y1 -= $borderWidth;
+ }
+
+ // Apply padding
+ $boundings->x0 += $padding;
+ $boundings->y0 += $padding;
+ $boundings->x1 -= $padding;
+ $boundings->y1 -= $padding;
+
+ // Add box title
+ if ( $title !== false )
+ {
+ switch ( $this->options->titlePosition )
+ {
+ case ezcGraph::TOP:
+ $this->driver->drawTextBox(
+ $title,
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ $boundings->width,
+ $titleSize,
+ $this->options->titleAlignement
+ );
+
+ $boundings->y0 += $titleSize + $padding;
+ $boundings->y1 -= $titleSize + $padding;
+ break;
+ case ezcGraph::BOTTOM:
+ $this->driver->drawTextBox(
+ $title,
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ),
+ $boundings->width,
+ $titleSize,
+ $this->options->titleAlignement
+ );
+
+ $boundings->y1 -= $titleSize + $padding;
+ break;
+ }
+ }
+
+ return $boundings;
+ }
+
+ /**
+ * Draw text
+ *
+ * Draws the provided text in the boundings
+ *
+ * @param ezcGraphBoundings $boundings Boundings of text
+ * @param string $text Text
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ public function drawText(
+ ezcGraphBoundings $boundings,
+ $text,
+ $align = ezcGraph::LEFT,
+ ezcGraphRotation $rotation = null )
+ {
+ $this->driver->drawTextBox(
+ $text,
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ $boundings->width,
+ $boundings->height,
+ $align,
+ $rotation
+ );
+ }
+
+ /**
+ * Draw grid line
+ *
+ * Draw line for the grid in the chart background
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Color of the grid line
+ * @return void
+ */
+ public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
+ {
+ $this->driver->drawLine(
+ $start,
+ $end,
+ $color,
+ 1
+ );
+ }
+
+ /**
+ * Draw step line
+ *
+ * Draw a step (marker for label position) on a axis.
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Color of the grid line
+ * @return void
+ */
+ public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
+ {
+ $this->driver->drawLine(
+ $start,
+ $end,
+ $color,
+ 1
+ );
+ }
+
+ /**
+ * Draw axis
+ *
+ * Draws an axis form the provided start point to the end point. A specific
+ * angle of the axis is not required.
+ *
+ * For the labeleing of the axis a sorted array with major steps and an
+ * array with minor steps is expected, which are build like this:
+ * array(
+ * array(
+ * 'position' => (float),
+ * 'label' => (string),
+ * )
+ * )
+ * where the label is optional.
+ *
+ * The label renderer class defines how the labels are rendered. For more
+ * documentation on this topic have a look at the basic label renderer
+ * class.
+ *
+ * Additionally it can be specified if a major and minor grid are rendered
+ * by defining a color for them. The axis label is used to add a caption
+ * for the axis.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $start Start point of axis
+ * @param ezcGraphCoordinate $end Endpoint of axis
+ * @param ezcGraphChartElementAxis $axis Axis to render
+ * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
+ * @return void
+ */
+ public function drawAxis(
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphAxisLabelRenderer $labelClass = null,
+ ezcGraphBoundings $innerBoundings = null )
+ {
+ // Legacy axis drawing for BC reasons
+ if ( $innerBoundings === null )
+ {
+ return $this->legacyDrawAxis( $boundings, $start, $end, $axis, $labelClass );
+ }
+
+ // Calculate axis start and end points depending on inner boundings
+ if ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM ) )
+ {
+ $innerStart = new ezcGraphCoordinate(
+ $start->x + $boundings->x0,
+ ( $axis->position === ezcGraph::TOP ? $innerBoundings->y0 : $innerBoundings->y1 )
+ );
+ $innerEnd = new ezcGraphCoordinate(
+ $end->x + $boundings->x0,
+ ( $axis->position === ezcGraph::TOP ? $innerBoundings->y1 : $innerBoundings->y0 )
+ );
+ }
+ else
+ {
+ $innerStart = new ezcGraphCoordinate(
+ ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x0 : $innerBoundings->x1 ),
+ $start->y + $boundings->y0
+ );
+ $innerEnd = new ezcGraphCoordinate(
+ ( $axis->position === ezcGraph::LEFT ? $innerBoundings->x1 : $innerBoundings->x0 ),
+ $end->y + $boundings->y0
+ );
+ }
+
+ // Shorten axis, if requested
+ if ( $this->options->shortAxis )
+ {
+ $start = clone $innerStart;
+ $end = clone $innerEnd;
+ }
+ else
+ {
+ $start->x += $boundings->x0;
+ $start->y += $boundings->y0;
+ $end->x += $boundings->x0;
+ $end->y += $boundings->y0;
+ }
+
+ // Determine normalized direction
+ $direction = new ezcGraphVector(
+ $start->x - $end->x,
+ $start->y - $end->y
+ );
+ $direction->unify();
+
+ // Draw axis
+ $this->driver->drawLine(
+ $start,
+ $end,
+ $axis->border,
+ 1
+ );
+
+ // Draw small arrowhead
+ $this->drawAxisArrowHead(
+ $end,
+ $direction,
+ max(
+ $axis->minArrowHeadSize,
+ min(
+ $axis->maxArrowHeadSize,
+ abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
+ )
+ ),
+ $axis->border
+ );
+
+ // Draw axis label, if set
+ $this->drawAxisLabel( $end, $innerBoundings, $axis );
+
+ // If font should not be synchronized, use font configuration from
+ // each axis
+ if ( $this->options->syncAxisFonts === false )
+ {
+ $this->driver->options->font = $axis->font;
+ }
+
+ $labelClass->renderLabels(
+ $this,
+ $boundings,
+ $innerStart,
+ $innerEnd,
+ $axis,
+ $innerBoundings
+ );
+ }
+
+ /**
+ * Draw axis label
+ *
+ * Draw labels at the end of an axis.
+ *
+ * @param ezcGraphCoordinate $position
+ * @param ezcGraphBoundings $boundings
+ * @param ezcGraphChartElementAxis $axis
+ * @return void
+ */
+ protected function drawAxisLabel( ezcGraphCoordinate $position, ezcGraphBoundings $boundings, ezcGraphChartElementAxis $axis )
+ {
+ if ( $axis->label === false )
+ {
+ return;
+ }
+
+ $width = $boundings->width;
+ switch ( $axis->position )
+ {
+ case ezcGraph::TOP:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $position->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ),
+ $position->y - $axis->labelMargin - $axis->labelSize
+ ),
+ $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::TOP | ezcGraph::RIGHT
+ );
+ break;
+ case ezcGraph::BOTTOM:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $position->x + $axis->labelMargin,
+ $position->y + $axis->labelMargin
+ ),
+ $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::TOP | ezcGraph::LEFT
+ );
+ break;
+ case ezcGraph::LEFT:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $position->x - $width,
+ $position->y - $axis->labelSize - $axis->labelMargin
+ ),
+ $width - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::BOTTOM | ezcGraph::RIGHT
+ );
+ break;
+ case ezcGraph::RIGHT:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $position->x,
+ $position->y - $axis->labelSize - $axis->labelMargin
+ ),
+ $width - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::BOTTOM | ezcGraph::LEFT
+ );
+ break;
+ }
+ }
+
+ /**
+ * Draw axis
+ *
+ * Draws an axis form the provided start point to the end point. A specific
+ * angle of the axis is not required.
+ *
+ * For the labeleing of the axis a sorted array with major steps and an
+ * array with minor steps is expected, which are build like this:
+ * array(
+ * array(
+ * 'position' => (float),
+ * 'label' => (string),
+ * )
+ * )
+ * where the label is optional.
+ *
+ * The label renderer class defines how the labels are rendered. For more
+ * documentation on this topic have a look at the basic label renderer
+ * class.
+ *
+ * Additionally it can be specified if a major and minor grid are rendered
+ * by defining a color for them. The axis label is used to add a caption
+ * for the axis.
+ *
+ * This function is deprecated and will be removed in favor of its
+ * reimplementation using the innerBoundings parameter.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $start Start point of axis
+ * @param ezcGraphCoordinate $end Endpoint of axis
+ * @param ezcGraphChartElementAxis $axis Axis to render
+ * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
+ * @apichange
+ * @return void
+ */
+ protected function legacyDrawAxis(
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphAxisLabelRenderer $labelClass = null )
+ {
+ // Store axis space for use by label renderer
+ switch ( $axis->position )
+ {
+ case ezcGraph::TOP:
+ case ezcGraph::BOTTOM:
+ $this->xAxisSpace = ( $boundings->width ) * $axis->axisSpace;
+ break;
+ case ezcGraph::LEFT:
+ case ezcGraph::RIGHT:
+ $this->yAxisSpace = ( $boundings->height ) * $axis->axisSpace;
+ break;
+ }
+
+ // Clone boundings because of internal modifications
+ $boundings = clone $boundings;
+
+ $start->x += $boundings->x0;
+ $start->y += $boundings->y0;
+ $end->x += $boundings->x0;
+ $end->y += $boundings->y0;
+
+ // Shorten drawn axis, if requested.
+ if ( ( $this->options->shortAxis === true ) &&
+ ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM ) ) )
+ {
+ $axisStart = clone $start;
+ $axisEnd = clone $end;
+
+ $axisStart->y += $boundings->height * $axis->axisSpace *
+ ( $axis->position === ezcGraph::TOP ? 1 : -1 );
+ $axisEnd->y -= $boundings->height * $axis->axisSpace *
+ ( $axis->position === ezcGraph::TOP ? 1 : -1 );
+ }
+ elseif ( ( $this->options->shortAxis === true ) &&
+ ( ( $axis->position === ezcGraph::LEFT ) ||
+ ( $axis->position === ezcGraph::RIGHT ) ) )
+ {
+ $axisStart = clone $start;
+ $axisEnd = clone $end;
+
+ $axisStart->x += $boundings->width * $axis->axisSpace *
+ ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
+ $axisEnd->x -= $boundings->width * $axis->axisSpace *
+ ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
+ }
+ else
+ {
+ $axisStart = $start;
+ $axisEnd = $end;
+ }
+
+ // Determine normalized direction
+ $direction = new ezcGraphVector(
+ $start->x - $end->x,
+ $start->y - $end->y
+ );
+ $direction->unify();
+
+ // Draw axis
+ $this->driver->drawLine(
+ $axisStart,
+ $axisEnd,
+ $axis->border,
+ 1
+ );
+
+ // Draw small arrowhead
+ $this->drawAxisArrowHead(
+ $axisEnd,
+ $direction,
+ max(
+ $axis->minArrowHeadSize,
+ min(
+ $axis->maxArrowHeadSize,
+ abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
+ )
+ ),
+ $axis->border
+ );
+
+ // Draw axis label, if set
+ $this->drawAxisLabel( $end, $boundings, $axis );
+
+ // Collect axis labels and draw, when all axisSpaces are collected
+ $this->axisLabels[] = array(
+ 'object' => $labelClass,
+ 'boundings' => $boundings,
+ 'start' => clone $start,
+ 'end' => clone $end,
+ 'axis' => $axis,
+ );
+
+ if ( $this->xAxisSpace && $this->yAxisSpace )
+ {
+ $this->drawAxisLabels();
+ }
+ }
+
+ /**
+ * Draw all left axis labels
+ *
+ * @return void
+ */
+ protected function drawAxisLabels()
+ {
+ foreach ( $this->axisLabels as $nr => $axisLabel )
+ {
+ // If font should not be synchronized, use font configuration from
+ // each axis
+ if ( $this->options->syncAxisFonts === false )
+ {
+ $this->driver->options->font = $axisLabel['axis']->font;
+ }
+
+ $start = $axisLabel['start'];
+ $end = $axisLabel['end'];
+
+ $direction = new ezcGraphVector(
+ $end->x - $start->x,
+ $end->y - $start->y
+ );
+ $direction->unify();
+
+ // Convert elipse to circle for correct angle calculation
+ $direction->y *= ( $this->xAxisSpace / $this->yAxisSpace );
+ $angle = $direction->angle( new ezcGraphVector( 0, 1 ) );
+
+ $movement = new ezcGraphVector(
+ sin( $angle ) * $this->xAxisSpace * ( $direction->x < 0 ? -1 : 1 ),
+ cos( $angle ) * $this->yAxisSpace
+ );
+
+ $start->x += $movement->x;
+ $start->y += $movement->y;
+ $end->x -= $movement->x;
+ $end->y -= $movement->y;
+
+ $axisLabel['object']->renderLabels(
+ $this,
+ $axisLabel['boundings'],
+ $start,
+ $end,
+ $axisLabel['axis']
+ );
+
+ // Prevent from redrawing axis on more then 2 axis.
+ unset( $this->axisLabels[$nr] );
+ }
+ }
+
+ /**
+ * Draw background image
+ *
+ * Draws a background image at the defined position. If repeat is set the
+ * background image will be repeated like any texture.
+ *
+ * @param ezcGraphBoundings $boundings Boundings for the background image
+ * @param string $file Filename of background image
+ * @param int $position Position of background image
+ * @param int $repeat Type of repetition
+ * @return void
+ */
+ public function drawBackgroundImage(
+ ezcGraphBoundings $boundings,
+ $file,
+ $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
+ $repeat = ezcGraph::NO_REPEAT )
+ {
+ $imageData = getimagesize( $file );
+ $imageWidth = $imageData[0];
+ $imageHeight = $imageData[1];
+
+ $imageWidth = min( $imageWidth, $boundings->width );
+ $imageHeight = min( $imageHeight, $boundings->height );
+
+ $imagePosition = new ezcGraphCoordinate(
+ $boundings->x0,
+ $boundings->y0
+ );
+
+ // Determine x position
+ switch ( true ) {
+ case ( $repeat & ezcGraph::HORIZONTAL ):
+ // If is repeated on this axis fall back to position zero
+ case ( $position & ezcGraph::LEFT ):
+ $imagePosition->x = $boundings->x0;
+ break;
+ case ( $position & ezcGraph::RIGHT ):
+ $imagePosition->x = max(
+ $boundings->x1 - $imageWidth,
+ $boundings->x0
+ );
+ break;
+ default:
+ $imagePosition->x = max(
+ $boundings->x0 + ( $boundings->width - $imageWidth ) / 2,
+ $boundings->x0
+ );
+ break;
+ }
+
+ // Determine y position
+ switch ( true ) {
+ case ( $repeat & ezcGraph::VERTICAL ):
+ // If is repeated on this axis fall back to position zero
+ case ( $position & ezcGraph::TOP ):
+ $imagePosition->y = $boundings->y0;
+ break;
+ case ( $position & ezcGraph::BOTTOM ):
+ $imagePosition->y = max(
+ $boundings->y1 - $imageHeight,
+ $boundings->y0
+ );
+ break;
+ default:
+ $imagePosition->y = max(
+ $boundings->y0 + ( $boundings->height - $imageHeight ) / 2,
+ $boundings->y0
+ );
+ break;
+ }
+
+ // Texturize backround based on position and repetition
+ $position = new ezcGraphCoordinate(
+ $imagePosition->x,
+ $imagePosition->y
+ );
+
+ do
+ {
+ $position->y = $imagePosition->y;
+
+ do
+ {
+ $this->driver->drawImage(
+ $file,
+ $position,
+ $imageWidth,
+ $imageHeight
+ );
+
+ $position->y += $imageHeight;
+ }
+ while ( ( $position->y < $boundings->y1 ) &&
+ ( $repeat & ezcGraph::VERTICAL ) );
+
+ $position->x += $imageWidth;
+ }
+ while ( ( $position->x < $boundings->x1 ) &&
+ ( $repeat & ezcGraph::HORIZONTAL ) );
+ }
+
+ /**
+ * Call all postprocessing functions
+ *
+ * @return void
+ */
+ protected function finish()
+ {
+ $this->finishCircleSectors();
+ $this->finishPieSegmentLabels();
+ $this->finishLineSymbols();
+
+ return true;
+ }
+
+ /**
+ * Reset renderer properties
+ *
+ * Reset all renderer properties, which were calculated during the
+ * rendering process, to offer a clean environment for rerendering.
+ *
+ * @return void
+ */
+ protected function resetRenderer()
+ {
+ parent::resetRenderer();
+
+ // Also reset special 2D renderer options
+ $this->pieSegmentLabels = array(
+ 0 => array(),
+ 1 => array(),
+ );
+ $this->pieSegmentBoundings = false;
+ $this->linePostSymbols = array();
+ $this->axisLabels = array();
+ $this->circleSectors = array();
+ }
+
+ /**
+ * Render odometer chart
+ *
+ * @param ezcGraphBoundings $boundings
+ * @param ezcGraphChartElementAxis $axis
+ * @param ezcGraphOdometerChartOptions $options
+ * @return ezcGraphBoundings
+ */
+ public function drawOdometer(
+ ezcGraphBoundings $boundings,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphOdometerChartOptions $options )
+ {
+ $height = $boundings->height * $options->odometerHeight;
+
+ // Draw axis
+ $oldAxisSpace = $axis->axisSpace;
+ $axis->axisSpace = 0;
+
+ $axis->render( $this, $boundings );
+
+ // Reset axisspaces to correct values
+ $this->xAxisSpace = $boundings->width * $oldAxisSpace;
+ $this->yAxisSpace = ( $boundings->height - $height ) / 2;
+
+ $this->drawAxisLabels();
+
+ // Reduce size of chart boundings respecting requested odometer height
+ $boundings->x0 += $this->xAxisSpace;
+ $boundings->x1 -= $this->xAxisSpace;
+ $boundings->y0 += $this->yAxisSpace;
+ $boundings->y1 -= $this->yAxisSpace;
+
+ $gradient = new ezcGraphLinearGradient(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ $options->startColor,
+ $options->endColor
+ );
+
+ // Simply draw box with gradient and optional border
+ $this->drawBox(
+ $boundings,
+ $gradient,
+ $options->borderColor,
+ $options->borderWidth
+ );
+
+ // Return modified chart boundings
+ return $boundings;
+ }
+
+ /**
+ * Draw a single odometer marker.
+ *
+ * @param ezcGraphBoundings $boundings
+ * @param ezcGraphCoordinate $position
+ * @param int $symbol
+ * @param ezcGraphColor $color
+ * @param int $width
+ */
+ public function drawOdometerMarker(
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $position,
+ $symbol,
+ ezcGraphColor $color,
+ $width )
+ {
+ $this->driver->drawLine(
+ new ezcGraphCoordinate(
+ $xPos = $boundings->x0 + ( $position->x * $boundings->width ),
+ $boundings->y0
+ ),
+ new ezcGraphCoordinate(
+ $xPos,
+ $boundings->y1
+ ),
+ $color,
+ $width
+ );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/3d.php b/src/TUnit/external/ezc/Graph/renderer/3d.php
new file mode 100644
index 0000000..a630398
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/3d.php
@@ -0,0 +1,2424 @@
+
+ * $graph = new ezcGraphPieChart();
+ * $graph->palette = new ezcGraphPaletteEzRed();
+ * $graph->title = 'Access statistics';
+ * $graph->options->label = '%2$d (%3$.1f%%)';
+ *
+ * $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
+ * 'Mozilla' => 19113,
+ * 'Explorer' => 10917,
+ * 'Opera' => 1464,
+ * 'Safari' => 652,
+ * 'Konqueror' => 474,
+ * ) );
+ * $graph->data['Access statistics']->highlight['Explorer'] = true;
+ *
+ * $graph->renderer = new ezcGraphRenderer3d();
+ *
+ * $graph->renderer->options->moveOut = .2;
+ *
+ * $graph->renderer->options->pieChartOffset = 63;
+ *
+ * $graph->renderer->options->pieChartGleam = .3;
+ * $graph->renderer->options->pieChartGleamColor = '#FFFFFF';
+ *
+ * $graph->renderer->options->pieChartShadowSize = 5;
+ * $graph->renderer->options->pieChartShadowColor = '#000000';
+ *
+ * $graph->renderer->options->legendSymbolGleam = .5;
+ * $graph->renderer->options->legendSymbolGleamSize = .9;
+ * $graph->renderer->options->legendSymbolGleamColor = '#FFFFFF';
+ *
+ * $graph->renderer->options->pieChartSymbolColor = '#55575388';
+ *
+ * $graph->renderer->options->pieChartHeight = 5;
+ * $graph->renderer->options->pieChartRotation = .8;
+ *
+ * $graph->render( 400, 150, 'tutorial_pie_chart_3d.svg' );
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphRenderer3d
+ extends
+ ezcGraphRenderer
+ implements
+ ezcGraphStackedBarsRenderer
+{
+
+ /**
+ * Pie segment labels divided into two array, containing the labels on the
+ * left and right side of the pie chart center.
+ *
+ * @var array
+ */
+ protected $pieSegmentLabels = array(
+ 0 => array(),
+ 1 => array(),
+ );
+
+ /**
+ * Contains the boundings used for pie segments
+ *
+ * @var ezcGraphBoundings
+ */
+ protected $pieSegmentBoundings = false;
+
+ /**
+ * Array with symbols for post processing, which ensures, that the symbols
+ * are rendered topmost.
+ *
+ * @var array
+ */
+ protected $linePostSymbols = array();
+
+ /**
+ * Array containing lines from the axis and grid which should be redrawn on
+ * top of the data.
+ *
+ * @var array
+ */
+ protected $frontLines = array();
+
+ /**
+ * Collects circle sectors to draw shadow in background of all circle
+ * sectors.
+ *
+ * @var array
+ */
+ protected $circleSectors = array();
+
+ /**
+ * Collects bar sides to draw them in a post processing step to simulate
+ * a simple z buffer.
+ * array(
+ * array(
+ * 'index' => (int) // used for sorting
+ * 'context' => ezcGraphContext // context of call
+ * 'method' => (string) // method of driver to call
+ * 'parameters' => array // parameters for method call
+ * ), ...
+ * )
+ *
+ * @var array
+ */
+ protected $barPostProcessing = array();
+
+ /**
+ * Options
+ *
+ * @var ezcGraphRenderer3dOptions
+ */
+ protected $options;
+
+ /**
+ * Depth of displayed pseudo three dimensional line chart elements.
+ *
+ * @var float
+ */
+ protected $depth = false;
+
+ /**
+ * Factor to reduce the width according to depth
+ *
+ * @var float
+ */
+ protected $xDepthFactor = false;
+
+ /**
+ * Factor to reduce the height according to depth
+ *
+ * @var float
+ */
+ protected $yDepthFactor = false;
+
+ /**
+ * Boundings for the chart data
+ *
+ * @var ezcGraphBoundings
+ */
+ protected $dataBoundings = false;
+
+ /**
+ * Collect axis labels, so that the axis are drawn, when all axis spaces
+ * are known.
+ *
+ * @var array
+ */
+ protected $axisLabels = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->options = new ezcGraphRenderer3dOptions( $options );
+ }
+
+ /**
+ * __get
+ *
+ * @param mixed $propertyName
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return mixed
+ * @ignore
+ */
+ public function __get( $propertyName )
+ {
+ switch ( $propertyName )
+ {
+ case 'options':
+ return $this->options;
+ default:
+ return parent::__get( $propertyName );
+ }
+ }
+
+ /**
+ * Calculate the display coordinate from a coordinate
+ *
+ * Calculates the display coordinate of a coordinate depending on the
+ * depth setting and the distance of the coordinate to the front of the
+ * chart.
+ *
+ * @param ezcGraphCoordinate $c Coordinate
+ * @param float $front Distance to front (0 - 1)
+ * @return ezcGraphCoordinate Resulting coordinate
+ */
+ protected function get3dCoordinate( ezcGraphCoordinate $c, $front = 1. )
+ {
+ return new ezcGraphCoordinate(
+ ( $c->x - $this->dataBoundings->x0 ) * $this->xDepthFactor + $this->dataBoundings->x0 + $this->depth * $front,
+ ( $c->y - $this->dataBoundings->y0 ) * $this->yDepthFactor + $this->dataBoundings->y0 + $this->depth * ( 1 - $front )
+ );
+ }
+
+ /**
+ * Draw pie segment
+ *
+ * Draws a single pie segment
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of pie segment
+ * @param float $startAngle Start angle
+ * @param float $endAngle End angle
+ * @param mixed $label Label of pie segment
+ * @param bool $moveOut Move out from middle for hilighting
+ * @return void
+ */
+ public function drawPieSegment(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ $startAngle = .0,
+ $endAngle = 360.,
+ $label = false,
+ $moveOut = false )
+ {
+ // Apply offset
+ $startAngle += $this->options->pieChartOffset;
+ $endAngle += $this->options->pieChartOffset;
+
+ // Calculate position and size of pie
+ $center = new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ - $this->options->pieChartHeight / 2
+ );
+
+ // Limit radius to fourth of width and half of height at maximum
+ $radius = min(
+ ( $boundings->x1 - $boundings->x0 ) * $this->options->pieHorizontalSize,
+ ( $boundings->y1 - $boundings->y0 ) * $this->options->pieVerticalSize
+ );
+
+ // Move pie segment out of the center
+ if ( $moveOut )
+ {
+ $direction = ( $endAngle + $startAngle ) / 2;
+
+ $center = new ezcGraphCoordinate(
+ $center->x + $this->options->moveOut * $radius * cos( deg2rad( $direction ) ),
+ $center->y + $this->options->moveOut * $radius * sin( deg2rad( $direction ) ) * $this->options->pieChartRotation
+ );
+ }
+
+ // Add circle sector to queue
+ $this->circleSectors[] = array(
+ 'center' => $center,
+ 'context' => $context,
+ 'width' => $radius * 2 * ( 1 - $this->options->moveOut ),
+ 'height' => $radius * 2 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation - $this->options->pieChartHeight,
+ 'start' => $startAngle,
+ 'end' => $endAngle,
+ 'color' => $color,
+ );
+
+ if ( $label )
+ {
+ // Determine position of label
+ $direction = ( $endAngle + $startAngle ) / 2;
+ $pieSegmentCenter = new ezcGraphCoordinate(
+ $center->x + cos( deg2rad( $direction ) ) * $radius,
+ $center->y + sin( deg2rad( $direction ) ) * $radius * $this->options->pieChartRotation
+ );
+
+ // Split labels up into left a right site and index them on their
+ // y position
+ $this->pieSegmentLabels[(int) ($pieSegmentCenter->x > $center->x)][(int) ( $pieSegmentCenter->y * 100 )] = array(
+ new ezcGraphCoordinate(
+ $center->x + cos( deg2rad( $direction ) ) * $radius * 2 / 3 * ( 1 - $this->options->moveOut ),
+ $center->y + sin( deg2rad( $direction ) ) * ( $radius - $this->options->pieChartHeight ) * 2 / 3 * ( 1 - $this->options->moveOut ) * $this->options->pieChartRotation
+ ),
+ $label,
+ $context,
+ );
+ }
+
+ if ( !$this->pieSegmentBoundings )
+ {
+ $this->pieSegmentBoundings = $boundings;
+ }
+ }
+
+ /**
+ * Draws the collected pie segment labels
+ *
+ * All labels are collected and drawn later to be able to partition the
+ * available space for the labels woth knowledge of the overall label
+ * count and their required size and optimal position.
+ *
+ * @return void
+ */
+ protected function finishPieSegmentLabels()
+ {
+ if ( $this->pieSegmentBoundings === false )
+ {
+ return true;
+ }
+
+ $boundings = $this->pieSegmentBoundings;
+
+ // Calculate position and size of pie
+ $center = new ezcGraphCoordinate(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 ) / 2,
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 ) / 2
+ );
+
+ // Limit radius to fourth of width and half of height at maximum
+ $radius = min(
+ ( $boundings->width ) * $this->options->pieHorizontalSize,
+ ( $boundings->height ) * $this->options->pieVerticalSize
+ );
+
+ $pieChartHeight = min(
+ $radius * 2 + $radius / max( 1, count ( $this->pieSegmentLabels[0] ), count( $this->pieSegmentLabels[1] ) ) * 4,
+ $boundings->height
+ );
+ $pieChartYPosition = $boundings->y0 + ( ( $boundings->height ) - $pieChartHeight ) / 2;
+
+ // Calculate maximum height of labels
+ $labelHeight = min(
+ ( count( $this->pieSegmentLabels[0] )
+ ? $pieChartHeight / count( $this->pieSegmentLabels[0] )
+ : $pieChartHeight
+ ),
+ ( count( $this->pieSegmentLabels[1] )
+ ? $pieChartHeight / count( $this->pieSegmentLabels[1] )
+ : $pieChartHeight
+ ),
+ ( $pieChartHeight ) * $this->options->maxLabelHeight
+ );
+
+ $symbolSize = $this->options->symbolSize;
+
+ foreach ( $this->pieSegmentLabels as $side => $labelPart )
+ {
+ $minHeight = $pieChartYPosition;
+ $toShare = $pieChartHeight - count( $labelPart ) * $labelHeight;
+
+ // Sort to draw topmost label first
+ ksort( $labelPart );
+ $sign = ( $side ? -1 : 1 );
+
+ foreach ( $labelPart as $height => $label )
+ {
+ $height = (int) ( $height / 100 );
+
+ if ( ( $height - $labelHeight / 2 ) > $minHeight )
+ {
+ $share = min( $toShare, ( $height - $labelHeight / 2) - $minHeight );
+ $minHeight += $share;
+ $toShare -= $share;
+ }
+
+ // Determine position of label
+ $minHeight += max( 0, $height - $minHeight - $labelHeight ) / $pieChartHeight * $toShare;
+ $verticalDistance = ( $center->y - $minHeight - $labelHeight / 2 ) / $radius;
+
+ $labelPosition = new ezcGraphCoordinate(
+ $center->x -
+ $sign * (
+ abs( $verticalDistance ) > 1
+ // If vertical distance to center is greater then the
+ // radius, use the centerline for the horizontal
+ // position
+ ? max (
+ 5,
+ abs( $label[0]->x - $center->x )
+ )
+ // Else place the label outside of the pie chart
+ : ( cos ( asin ( $verticalDistance ) ) * $radius +
+ $symbolSize * (int) $this->options->showSymbol
+ )
+ ),
+ $minHeight + $labelHeight / 2
+ );
+
+ if ( $this->options->showSymbol )
+ {
+ // Draw label
+ $this->driver->drawLine(
+ $label[0],
+ $labelPosition,
+ $this->options->pieChartSymbolColor,
+ 1
+ );
+
+ $this->driver->drawCircle(
+ $label[0],
+ $symbolSize,
+ $symbolSize,
+ $this->options->pieChartSymbolColor,
+ true
+ );
+ $this->driver->drawCircle(
+ $labelPosition,
+ $symbolSize,
+ $symbolSize,
+ $this->options->pieChartSymbolColor,
+ true
+ );
+ }
+
+ $this->addElementReference( $label[2],
+ $this->driver->drawTextBox(
+ $label[1],
+ new ezcGraphCoordinate(
+ ( !$side ? $boundings->x0 : $labelPosition->x + $symbolSize ),
+ $minHeight
+ ),
+ ( !$side ? $labelPosition->x - $boundings->x0 - $symbolSize : $boundings->x1 - $labelPosition->x - $symbolSize ),
+ $labelHeight,
+ ( !$side ? ezcGraph::RIGHT : ezcGraph::LEFT ) | ezcGraph::MIDDLE
+ )
+ );
+
+ // Add used space to minHeight
+ $minHeight += $labelHeight;
+ }
+ }
+ }
+
+ /**
+ * Draws the collected circle sectors
+ *
+ * All circle sectors are collected and drawn later to be able to render
+ * the shadows of the pie segments in the back of all pie segments, and
+ * ensure the correct drawing order for all pie segment elements.
+ *
+ * @return void
+ */
+ protected function finishCirleSectors()
+ {
+ $zBuffer = array();
+
+ $shadows = array();
+ $shadowCenter = false;
+ $shadowEndAngle = false;
+
+ // Add circle sector sides to simple z buffer prioriry list
+ foreach ( $this->circleSectors as $circleSector )
+ {
+ // Draw shadow if wanted
+ if ( $this->options->pieChartShadowSize > 0 )
+ {
+ if ( $shadowEndAngle === false )
+ {
+ $shadowStartAngle = $circleSector['start'];
+ $shadowEndAngle = $circleSector['end'];
+ $shadowCenter = $circleSector['center'];
+ }
+ elseif ( $circleSector['center'] == $shadowCenter )
+ {
+ $shadowEndAngle = $circleSector['end'];
+ }
+ else
+ {
+ $shadows[] = array(
+ 'center' => $shadowCenter,
+ 'start' => $shadowStartAngle,
+ 'end' => $shadowEndAngle,
+ 'width' => $circleSector['width'],
+ 'height' => $circleSector['height'],
+ );
+
+ $shadowCenter = $circleSector['center'];
+ $shadowStartAngle = $circleSector['start'];
+ $shadowEndAngle = $circleSector['end'];
+ }
+ }
+
+ $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
+
+ $center = (int) ( $circleSector['center']->y + sin( deg2rad( $circleSector['start'] + ( $circleSector['end'] - $circleSector['start'] ) / 2 ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight / 2 + 1 );
+
+ $zBuffer[$center][] = array(
+ 'method' => 'drawCircularArc',
+ 'paramenters' => array(
+ $circleSector['center'],
+ $circleSector['width'],
+ $circleSector['height'],
+ $this->options->pieChartHeight,
+ $circleSector['start'],
+ $circleSector['end'],
+ $circleSector['color']
+ )
+ );
+
+ // Left side
+ $polygonPoints = array(
+ $circleSector['center'],
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y + $this->options->pieChartHeight
+ ),
+ new ezcGraphCoordinate(
+ $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2,
+ $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight
+ ),
+ new ezcGraphCoordinate(
+ $circleSector['center']->x + cos( deg2rad( $circleSector['start'] ) ) * $circleSector['width'] / 2,
+ $circleSector['center']->y + sin( deg2rad( $circleSector['start'] ) ) * $circleSector['height'] / 2
+ ),
+ );
+
+ // Get average y coordinate for polygon to use for zBuffer
+ $center = 0;
+ foreach ( $polygonPoints as $point )
+ {
+ $center += $point->y;
+ }
+ $center = (int) ( $center / count( $polygonPoints ) );
+
+ $zBuffer[$center][] = array(
+ 'method' => 'drawPolygon',
+ 'paramenters' => array(
+ $polygonPoints,
+ $circleSector['color'],
+ true
+ ),
+ );
+
+ $zBuffer[$center][] = array(
+ 'method' => 'drawPolygon',
+ 'paramenters' => array(
+ $polygonPoints,
+ $darkenedColor,
+ false
+ ),
+ );
+
+ // Right side
+ $polygonPoints = array(
+ $circleSector['center'],
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y + $this->options->pieChartHeight
+ ),
+ new ezcGraphCoordinate(
+ $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2,
+ $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2 + $this->options->pieChartHeight
+ ),
+ new ezcGraphCoordinate(
+ $circleSector['center']->x + cos( deg2rad( $circleSector['end'] ) ) * $circleSector['width'] / 2,
+ $circleSector['center']->y + sin( deg2rad( $circleSector['end'] ) ) * $circleSector['height'] / 2
+ ),
+ );
+
+ // Get average y coordinate for polygon to use for zBuffer
+ $center = 0;
+ foreach ( $polygonPoints as $point )
+ {
+ $center += $point->y;
+ }
+ $center = (int) ( $center / count( $polygonPoints ) );
+
+ $zBuffer[$center][] = array(
+ 'method' => 'drawPolygon',
+ 'paramenters' => array(
+ $polygonPoints,
+ $circleSector['color'],
+ true
+ ),
+ );
+
+ $zBuffer[$center][] = array(
+ 'method' => 'drawPolygon',
+ 'paramenters' => array(
+ $polygonPoints,
+ $darkenedColor,
+ false
+ ),
+ );
+ }
+
+ if ( $this->options->pieChartShadowSize > 0 )
+ {
+ $shadows[] = array(
+ 'center' => $shadowCenter,
+ 'start' => $shadowStartAngle,
+ 'end' => $shadowEndAngle,
+ 'width' => $circleSector['width'],
+ 'height' => $circleSector['height'],
+ );
+ }
+
+ // Draw collected shadows
+ foreach ( $shadows as $circleSector )
+ {
+ for ( $i = $this->options->pieChartShadowSize; $i > 0; --$i )
+ {
+ $startAngle = $circleSector['start'];
+ $endAngle = $circleSector['end'];
+
+ $startAngle = $circleSector['start'] - ( $this->options->pieChartShadowSize - $i );
+ $endAngle = $circleSector['end'] + ( $this->options->pieChartShadowSize - $i );
+
+ if ( ( $endAngle - $startAngle ) >= 360 )
+ {
+ $this->driver->drawCircle(
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y + $this->options->pieChartHeight
+ ),
+ $circleSector['width'] + $i * 2,
+ $circleSector['height'] + $i * 2,
+ $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ),
+ true
+ );
+ }
+ else
+ {
+ $this->driver->drawCircleSector(
+ new ezcGraphCoordinate(
+ $circleSector['center']->x,
+ $circleSector['center']->y + $this->options->pieChartHeight
+ ),
+ $circleSector['width'] + $i * 2,
+ $circleSector['height'] + $i * 2,
+ $startAngle,
+ $endAngle,
+ $this->options->pieChartShadowColor->transparent( 1 - ( $this->options->pieChartShadowTransparency / $this->options->pieChartShadowSize ) ),
+ true
+ );
+ }
+ }
+ }
+
+ ksort( $zBuffer );
+ foreach ( $zBuffer as $sides )
+ {
+ foreach ( $sides as $side )
+ {
+ call_user_func_array( array( $this->driver, $side['method'] ), $side['paramenters'] );
+ }
+ }
+
+ // Draw circle sector for front
+ foreach ( $this->circleSectors as $circleSector )
+ {
+ $this->addElementReference( $circleSector['context'],
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'],
+ $circleSector['height'],
+ $circleSector['start'],
+ $circleSector['end'],
+ $circleSector['color'],
+ true
+ )
+ );
+
+ if ( $this->options->pieChartGleam !== false )
+ {
+ $gradient = new ezcGraphLinearGradient(
+ $circleSector['center'],
+ new ezcGraphCoordinate(
+ $circleSector['center']->x - $circleSector['width'] / 2,
+ $circleSector['center']->y - $circleSector['height'] / 2
+ ),
+ $this->options->pieChartGleamColor->transparent( 1 ),
+ $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam )
+ );
+
+ $this->addElementReference( $circleSector['context'],
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'] - $this->options->pieChartGleamBorder * 2,
+ $circleSector['height'] - $this->options->pieChartGleamBorder * 2 * $this->options->pieChartRotation,
+ $circleSector['start'],
+ $circleSector['end'],
+ $gradient,
+ true
+ )
+ );
+ }
+
+ $darkenedColor = $circleSector['color']->darken( $this->options->dataBorder );
+ $this->driver->drawCircleSector(
+ $circleSector['center'],
+ $circleSector['width'],
+ $circleSector['height'],
+ $circleSector['start'],
+ $circleSector['end'],
+ $darkenedColor,
+ false
+ );
+
+ if ( $this->options->pieChartGleam !== false )
+ {
+ $radialGradient = new ezcGraphRadialGradient(
+ new ezcGraphCoordinate(
+ $circleSector['center']->x + $circleSector['width'] / 2 * cos( deg2rad( 135 ) ),
+ $circleSector['center']->y + $circleSector['height'] / 2 * sin( deg2rad( 135 ) )
+ ),
+ $circleSector['width'],
+ $circleSector['height'],
+ $this->options->pieChartGleamColor->transparent( $this->options->pieChartGleam ),
+ $this->options->pieChartGleamColor->transparent( .8 )
+ );
+
+ $this->driver->drawCircularArc(
+ $circleSector['center'],
+ $circleSector['width'],
+ $circleSector['height'],
+ 0,
+ $circleSector['start'],
+ $circleSector['end'],
+ $radialGradient,
+ false
+ );
+ }
+ }
+ }
+
+ /**
+ * Draw collected front lines
+ *
+ * Draw all grid and axis lines, which should be redrawn in front of the
+ * data.
+ *
+ * @return void
+ */
+ protected function finishFrontLines()
+ {
+ foreach ( $this->frontLines as $line )
+ {
+ $this->driver->drawLine(
+ $line[0],
+ $line[1],
+ $line[2],
+ $line[3]
+ );
+ }
+ }
+
+ /**
+ * Draw the collected line symbols
+ *
+ * Symbols for the data lines are collected and delayed to ensure that
+ * they are not covered and hidden by other data lines.
+ *
+ * @return void
+ */
+ protected function finishLineSymbols()
+ {
+ foreach ( $this->linePostSymbols as $symbol )
+ {
+ $this->addElementReference( $symbol['context'],
+ $this->drawSymbol(
+ $symbol['boundings'],
+ $symbol['color'],
+ $symbol['symbol']
+ )
+ );
+ }
+ }
+
+ /**
+ * Draws a bar with a rectangular ground shape.
+ *
+ * @param ezcGraphContext $context
+ * @param ezcGraphColor $color
+ * @param ezcGraphCoordinate $position
+ * @param float $barWidth
+ * @param float $offset
+ * @param float $axisPosition
+ * @param float $startDepth
+ * @param float $midDepth
+ * @param float $endDepth
+ * @return void
+ */
+ protected function drawRectangularBar(
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $position,
+ $barWidth,
+ $offset,
+ $axisPosition,
+ $startDepth,
+ $midDepth,
+ $endDepth )
+ {
+ $barPolygonArray = array(
+ new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset,
+ $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ ),
+ new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset,
+ $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ ),
+ new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth,
+ $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ ),
+ new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth,
+ $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ ),
+ );
+
+ // Draw right bar side
+ $this->barPostProcessing[] = array(
+ 'index' => $barPolygonArray[2]->x + ( 1 - $position->y ),
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $endDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $endDepth ),
+ ),
+ $color->darken( $this->options->barDarkenSide ),
+ true
+ ),
+ );
+
+ // Draw top side
+ $this->barPostProcessing[] = array(
+ 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ),
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
+ ? array(
+ $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $endDepth ),
+ $this->get3dCoordinate( $barPolygonArray[1], $endDepth ),
+ )
+ : array(
+ $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $endDepth ),
+ $this->get3dCoordinate( $barPolygonArray[0], $endDepth ),
+ )
+ ),
+ $color->darken( $this->options->barDarkenTop ),
+ true
+ ),
+ );
+
+ // Draw top side gleam
+ if ( $this->options->barChartGleam !== false )
+ {
+ $this->barPostProcessing[] = array(
+ 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ),
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
+ ? array(
+ $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $endDepth ),
+ $this->get3dCoordinate( $barPolygonArray[1], $endDepth ),
+ )
+ : array(
+ $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $endDepth ),
+ $this->get3dCoordinate( $barPolygonArray[0], $endDepth ),
+ )
+ ),
+ new ezcGraphLinearGradient(
+ ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
+ ? $this->get3dCoordinate( $barPolygonArray[2], $endDepth )
+ : $this->get3dCoordinate( $barPolygonArray[3], $endDepth )
+ ),
+ ( $barPolygonArray[1]->y < $barPolygonArray[3]->y
+ ? $this->get3dCoordinate( $barPolygonArray[1], $startDepth )
+ : $this->get3dCoordinate( $barPolygonArray[0], $startDepth )
+ ),
+ ezcGraphColor::fromHex( '#FFFFFFFF' ),
+ ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam )
+ ),
+ true
+ ),
+ );
+ }
+
+ // Draw front side
+ $this->barPostProcessing[] = array(
+ 'index' => $barPolygonArray[1]->x + ( 1 - $position->y ),
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
+ ),
+ $color,
+ true
+ ),
+ );
+
+ // Draw front side gleam
+ if ( $this->options->barChartGleam !== false )
+ {
+ $this->barPostProcessing[] = array(
+ 'index' => $barPolygonArray[1]->x + 1 + ( 1 - $position->y ),
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( $barPolygonArray[0], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[2], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
+ ),
+ new ezcGraphLinearGradient(
+ $this->get3dCoordinate( $barPolygonArray[3], $startDepth ),
+ $this->get3dCoordinate( $barPolygonArray[1], $startDepth ),
+ ezcGraphColor::fromHex( '#FFFFFFFF' ),
+ ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam )
+ ),
+ true
+ ),
+ );
+ }
+ }
+
+ /**
+ * Draws a bar with a diamond ground shape.
+ *
+ * @param ezcGraphContext $context
+ * @param ezcGraphColor $color
+ * @param ezcGraphCoordinate $position
+ * @param float $barWidth
+ * @param float $offset
+ * @param float $axisPosition
+ * @param float $startDepth
+ * @param float $midDepth
+ * @param float $endDepth
+ * @return void
+ */
+ protected function drawDiamondBar(
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $position,
+ $barWidth,
+ $offset,
+ $axisPosition,
+ $startDepth,
+ $midDepth,
+ $endDepth )
+ {
+ $barCoordinateArray = array(
+ // The bottom point of the diamond is moved to .7 instead
+ // of .5 because it looks more correct, even it is wrong...
+ 'x' => array(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset,
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .7,
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth,
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth * .3,
+ ),
+ 'y' => array(
+ $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ),
+ $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) ),
+ ),
+ );
+
+ // Left side
+ $this->barPostProcessing[] = array(
+ 'index' => $barCoordinateArray['x'][0],
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][0] ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $barCoordinateArray['y'][1] ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ),
+ ),
+ $color,
+ true
+ ),
+ );
+
+ // Right side
+ $this->barPostProcessing[] = array(
+ 'index' => $barCoordinateArray['x'][1],
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][0] ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $barCoordinateArray['y'][1] ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][1] ), $startDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $barCoordinateArray['y'][0] ), $startDepth ),
+ ),
+ $color->darken( $this->options->barDarkenSide ),
+ true
+ ),
+ );
+
+ $topLocation = min(
+ $barCoordinateArray['y'][0],
+ $barCoordinateArray['y'][1]
+ );
+
+ // Top side
+ $this->barPostProcessing[] = array(
+ 'index' => $barCoordinateArray['x'][0],
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ),
+ ),
+ $color->darken( $this->options->barDarkenTop ),
+ true
+ ),
+ );
+
+ // Top side gleam
+ if ( $this->options->barChartGleam !== false )
+ {
+ $this->barPostProcessing[] = array(
+ 'index' => $barCoordinateArray['x'][0] + 1,
+ 'method' => 'drawPolygon',
+ 'context' => $context,
+ 'parameters' => array(
+ array(
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][1], $topLocation ), $startDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][3], $topLocation ), $endDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ),
+ ),
+ new ezcGraphLinearGradient(
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][2], $topLocation ), $midDepth ),
+ $this->get3dCoordinate( new ezcGraphCoordinate( $barCoordinateArray['x'][0], $topLocation ), $midDepth ),
+ ezcGraphColor::fromHex( '#FFFFFFFF' ),
+ ezcGraphColor::fromHex( '#FFFFFF' )->transparent( 1 - $this->options->barChartGleam )
+ ),
+ true
+ ),
+ );
+ }
+ }
+
+ /**
+ * Draws a bar with a circular ground shape.
+ *
+ * @param ezcGraphContext $context
+ * @param ezcGraphColor $color
+ * @param ezcGraphCoordinate $position
+ * @param float $barWidth
+ * @param float $offset
+ * @param float $axisPosition
+ * @param float $startDepth
+ * @param float $midDepth
+ * @param float $endDepth
+ * @param int $symbol
+ * @return void
+ */
+ protected function drawCircularBar(
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $position,
+ $barWidth,
+ $offset,
+ $axisPosition,
+ $startDepth,
+ $midDepth,
+ $endDepth,
+ $symbol )
+ {
+ $barCenterTop = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2,
+ $this->dataBoundings->y0 + $this->yAxisSpace + $position->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+
+ );
+ $barCenterBottom = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $position->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ) + $offset + $barWidth / 2,
+ $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ );
+
+ if ( $barCenterTop->y > $barCenterBottom->y )
+ {
+ $tmp = $barCenterTop;
+ $barCenterTop = $barCenterBottom;
+ $barCenterBottom = $tmp;
+ }
+
+ $this->barPostProcessing[] = array(
+ 'index' => $barCenterBottom->x,
+ 'method' => 'drawCircularArc',
+ 'context' => $context,
+ 'parameters' => array(
+ $this->get3dCoordinate( $barCenterTop, $midDepth ),
+ $barWidth,
+ $barWidth / 2,
+ ( $barCenterBottom->y - $barCenterTop->y ) * $this->yDepthFactor,
+ 0,
+ 180,
+ $color
+ ),
+ );
+
+ $this->barPostProcessing[] = array(
+ 'index' => $barCenterBottom->x + 1,
+ 'method' => 'drawCircle',
+ 'context' => $context,
+ 'parameters' => array(
+ $top = $this->get3dCoordinate( $barCenterTop, $midDepth ),
+ $barWidth,
+ $barWidth / 2,
+ ( $symbol === ezcGraph::CIRCLE
+ ? new ezcGraphLinearGradient(
+ new ezcGraphCoordinate(
+ $top->x - $barWidth / 2,
+ $top->y
+ ),
+ new ezcGraphCoordinate(
+ $top->x + $barWidth / 2,
+ $top->y
+ ),
+ $color->darken( $this->options->barDarkenTop ),
+ $color
+ )
+ : $color
+ )
+ ),
+ );
+ }
+
+ /**
+ * Draw bar
+ *
+ * Draws a bar as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $position Position of data point
+ * @param float $stepSize Space which can be used for bars
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @return void
+ */
+ public function drawBar(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $position,
+ $stepSize,
+ $dataNumber = 1,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ $axisPosition = 0. )
+ {
+ // Apply margin
+ $margin = $stepSize * $this->options->barMargin;
+ $padding = $stepSize * $this->options->barPadding;
+ $barWidth = ( $stepSize - $margin ) / $dataCount - $padding;
+ $offset = - $stepSize / 2 + $margin / 2 + ( $dataCount - $dataNumber - 1 ) * ( $padding + $barWidth ) + $padding / 2;
+
+ if ( $barWidth < 0 )
+ {
+ $offset -= $barWidth = abs( $barWidth );
+ }
+
+ $startDepth = $this->options->barMargin;
+ $midDepth = .5;
+ $endDepth = 1 - $this->options->barMargin;
+
+ switch ( $symbol )
+ {
+ case ezcGraph::NO_SYMBOL:
+ $this->drawRectangularBar(
+ $context,
+ $color,
+ $position,
+ $barWidth,
+ $offset,
+ $axisPosition,
+ $startDepth,
+ $midDepth,
+ $endDepth
+ );
+ break;
+ case ezcGraph::DIAMOND:
+ $this->drawDiamondBar(
+ $context,
+ $color,
+ $position,
+ $barWidth,
+ $offset,
+ $axisPosition,
+ $startDepth,
+ $midDepth,
+ $endDepth
+ );
+ break;
+ case ezcGraph::BULLET:
+ case ezcGraph::CIRCLE:
+ $this->drawCircularBar(
+ $context,
+ $color,
+ $position,
+ $barWidth,
+ $offset,
+ $axisPosition,
+ $startDepth,
+ $midDepth,
+ $endDepth,
+ $symbol
+ );
+ break;
+ }
+ }
+
+ /**
+ * Draw stacked bar
+ *
+ * Draws a stacked bar part as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $start
+ * @param ezcGraphCoordinate $position
+ * @param float $stepSize Space which can be used for bars
+ * @param int $symbol Symbol to draw for line
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @return void
+ */
+ public function drawStackedBar(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $position,
+ $stepSize,
+ $symbol = ezcGraph::NO_SYMBOL,
+ $axisPosition = 0. )
+ {
+ // Apply margin
+ $margin = $stepSize * $this->options->barMargin;
+ $barWidth = $stepSize - $margin;
+ $offset = - $stepSize / 2 + $margin / 2;
+
+ if ( $barWidth < 0 )
+ {
+ $offset -= $barWidth = abs( $barWidth );
+ }
+
+ $startDepth = $this->options->barMargin;
+ $midDepth = .5;
+ $endDepth = 1 - $this->options->barMargin;
+
+ switch ( $symbol )
+ {
+ case ezcGraph::NO_SYMBOL:
+ case ezcGraph::DIAMOND:
+ case ezcGraph::BULLET:
+ case ezcGraph::CIRCLE:
+ $this->drawRectangularBar(
+ $context,
+ $color,
+ $position,
+ $barWidth,
+ $offset,
+ $start->y,
+ $startDepth,
+ $midDepth,
+ $endDepth
+ );
+ break;
+ }
+ }
+
+ /**
+ * Draw all collected bar elements
+ *
+ * Draw all collected bar elements after sorting them depending of their
+ * position to simulate simple z buffering.
+ *
+ * @access protected
+ * @return void
+ */
+ protected function finishBars()
+ {
+ if ( !count( $this->barPostProcessing ) )
+ {
+ return true;
+ }
+
+ $zIndexArray = array();
+ foreach ( $this->barPostProcessing as $key => $barPolygon )
+ {
+ $zIndexArray[$key] = $barPolygon['index'];
+ }
+
+ array_multisort(
+ $zIndexArray, SORT_ASC, SORT_NUMERIC,
+ $this->barPostProcessing
+ );
+
+ foreach ( $this->barPostProcessing as $bar )
+ {
+ $this->addElementReference( $bar['context'],
+ call_user_func_array(
+ array( $this->driver, $bar['method'] ),
+ $bar['parameters']
+ )
+ );
+ }
+ }
+
+ /**
+ * Draw data line
+ *
+ * Draws a line as a data element in a line chart
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphColor $color Color of line
+ * @param ezcGraphCoordinate $start Starting point
+ * @param ezcGraphCoordinate $end Ending point
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param int $symbol Symbol to draw for line
+ * @param ezcGraphColor $symbolColor Color of the symbol, defaults to linecolor
+ * @param ezcGraphColor $fillColor Color to fill line with
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @param float $thickness Line thickness
+ * @return void
+ */
+ public function drawDataLine(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphColor $color,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ $dataNumber = 0,
+ $dataCount = 1,
+ $symbol = ezcGraph::NO_SYMBOL,
+ ezcGraphColor $symbolColor = null,
+ ezcGraphColor $fillColor = null,
+ $axisPosition = 0.,
+ $thickness = 1. )
+ {
+ // Calculate line width based on options
+ if ( $this->options->seperateLines )
+ {
+ $startDepth = ( 1 / $dataCount ) * $dataNumber;
+ $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 );
+ }
+ else
+ {
+ $startDepth = false;
+ $endDepth = true;
+ }
+
+ // Determine Coordinates depending on boundings and data point position
+ $startCoord = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
+ $this->dataBoundings->y0 + $this->yAxisSpace + $start->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ );
+ $endCoord = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
+ $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ );
+
+ // 3D-fy coordinates
+ $linePolygonPoints = array(
+ $this->get3dCoordinate( $startCoord, $startDepth ),
+ $this->get3dCoordinate( $endCoord, $startDepth ),
+ $this->get3dCoordinate( $endCoord, $endDepth ),
+ $this->get3dCoordinate( $startCoord, $endDepth ),
+ );
+
+ $startAxisCoord = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $start->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
+ $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ );
+ $endAxisCoord = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
+ $this->dataBoundings->y0 + $this->yAxisSpace + $axisPosition * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ );
+
+ // 3D-fy coordinates
+ $axisPolygonPoints = array(
+ $this->get3dCoordinate( $startAxisCoord, $startDepth ),
+ $this->get3dCoordinate( $endAxisCoord, $startDepth ),
+ $this->get3dCoordinate( $endAxisCoord, $endDepth ),
+ $this->get3dCoordinate( $startAxisCoord, $endDepth ),
+ );
+
+ // Perhaps fill up line
+ if ( $fillColor !== null &&
+ $start->x != $end->x )
+ {
+ $startValue = $axisPosition - $start->y;
+ $endValue = $axisPosition - $end->y;
+
+ if ( ( $startValue == 0 ) ||
+ ( $endValue == 0 ) ||
+ ( $startValue / abs( $startValue ) == $endValue / abs( $endValue ) ) )
+ {
+ // Values have the same sign or are on the axis
+ $this->driver->drawPolygon(
+ array(
+ $linePolygonPoints[0],
+ $linePolygonPoints[1],
+ $this->get3dCoordinate( $endAxisCoord, $startDepth ),
+ $this->get3dCoordinate( $startAxisCoord, $startDepth ),
+ ),
+ $fillColor,
+ true
+ );
+ }
+ else
+ {
+ // values are on differente sides of the axis - split the filled polygon
+ $startDiff = abs( $axisPosition - $start->y );
+ $endDiff = abs( $axisPosition - $end->y );
+
+ $cuttingPosition = $startDiff / ( $endDiff + $startDiff );
+ $cuttingPoint = new ezcGraphCoordinate(
+ $startCoord->x + ( $endCoord->x - $startCoord->x ) * $cuttingPosition,
+ $startAxisCoord->y
+ );
+
+ $this->driver->drawPolygon(
+ array(
+ $this->get3dCoordinate( $startAxisCoord, $startDepth ),
+ $linePolygonPoints[0],
+ $this->get3dCoordinate( $cuttingPoint, $startDepth ),
+ ),
+ $fillColor,
+ true
+ );
+
+ $this->driver->drawPolygon(
+ array(
+ $this->get3dCoordinate( $endAxisCoord, $startDepth ),
+ $linePolygonPoints[1],
+ $this->get3dCoordinate( $cuttingPoint, $startDepth ),
+ ),
+ $fillColor,
+ true
+ );
+ }
+
+ // Draw closing foo
+ $this->driver->drawPolygon(
+ array(
+ $linePolygonPoints[2],
+ $linePolygonPoints[1],
+ $this->get3dCoordinate( $endAxisCoord, $startDepth ),
+ $this->get3dCoordinate( $endAxisCoord, $endDepth ),
+ ),
+ $fillColor,
+ true
+ );
+ }
+
+
+ // Draw line
+ $this->driver->drawPolygon(
+ $linePolygonPoints,
+ $color,
+ true,
+ $thickness
+ );
+
+ // Draw polygon border
+ if ( $this->options->dataBorder > 0 )
+ {
+ $this->driver->drawPolygon(
+ $linePolygonPoints,
+ $color->darken( $this->options->dataBorder ),
+ false,
+ $thickness
+ );
+ }
+
+ // Draw line symbol
+ if ( $this->options->showSymbol &&
+ ( $symbol !== ezcGraph::NO_SYMBOL ) )
+ {
+ if ( $symbolColor === null )
+ {
+ $symbolColor = $color;
+ }
+
+ $this->linePostSymbols[] = array(
+ 'boundings' => new ezcGraphBoundings(
+ $linePolygonPoints[2]->x - $this->options->symbolSize / 2,
+ $linePolygonPoints[2]->y - $this->options->symbolSize / 2,
+ $linePolygonPoints[2]->x + $this->options->symbolSize / 2,
+ $linePolygonPoints[2]->y + $this->options->symbolSize / 2
+ ),
+ 'color' => $symbolColor,
+ 'context' => $context,
+ 'symbol' => $symbol,
+ );
+ }
+ }
+
+ /**
+ * Draws a highlight textbox for a datapoint.
+ *
+ * A highlight textbox for line and bar charts means a box with the current
+ * value in the graph.
+ *
+ * @param ezcGraphBoundings $boundings Chart boundings
+ * @param ezcGraphContext $context Context of call
+ * @param ezcGraphCoordinate $end Ending point
+ * @param float $axisPosition Position of axis for drawing filled lines
+ * @param int $dataNumber Number of dataset
+ * @param int $dataCount Count of datasets in chart
+ * @param ezcGraphFontOptions $font Font used for highlight string
+ * @param string $text Acutual value
+ * @param int $size Size of highlight text
+ * @param ezcGraphColor $markLines
+ * @param int $xOffset
+ * @param int $yOffset
+ * @param float $stepSize
+ * @param int $type
+ * @return void
+ */
+ public function drawDataHighlightText(
+ ezcGraphBoundings $boundings,
+ ezcGraphContext $context,
+ ezcGraphCoordinate $end,
+ $axisPosition = 0.,
+ $dataNumber = 1,
+ $dataCount = 1,
+ ezcGraphFontOptions $font,
+ $text,
+ $size,
+ ezcGraphColor $markLines = null,
+ $xOffset = 0,
+ $yOffset = 0,
+ $stepSize = 0.,
+ $type = ezcGraph::LINE )
+ {
+ $this->driver->options->font = $font;
+ $width = $this->dataBoundings->width / $dataCount;
+
+ // Calculate line width based on options
+ if ( $this->options->seperateLines )
+ {
+ $endDepth = ( 1 / $dataCount ) * ( $dataNumber + 1 );
+ }
+ else
+ {
+ $endDepth = true;
+ }
+
+ $dataPoint = new ezcGraphCoordinate(
+ $this->dataBoundings->x0 + $this->xAxisSpace + $end->x * ( $this->dataBoundings->x1 - ( $this->dataBoundings->x0 + 2 * $this->xAxisSpace ) ),
+ $this->dataBoundings->y0 + $this->yAxisSpace + $end->y * ( $this->dataBoundings->y1 - ( $this->dataBoundings->y0 + 2 * $this->yAxisSpace ) )
+ );
+
+ if ( $end->y < $axisPosition )
+ {
+ $this->driver->drawTextBox(
+ $text,
+ $this->get3dCoordinate( new ezcGraphCoordinate(
+ $dataPoint->x - $width / 2,
+ $dataPoint->y - $size - $font->padding - $this->options->symbolSize
+ ), $endDepth ),
+ $width * $this->xDepthFactor,
+ $size,
+ ezcGraph::CENTER | ezcGraph::BOTTOM
+ );
+ }
+ else
+ {
+ $this->driver->drawTextBox(
+ $text,
+ $this->get3dCoordinate( new ezcGraphCoordinate(
+ $dataPoint->x - $width / 2,
+ $dataPoint->y + $font->padding + $this->options->symbolSize
+ ), $endDepth ),
+ $width * $this->xDepthFactor,
+ $size,
+ ezcGraph::CENTER | ezcGraph::TOP
+ );
+ }
+ }
+
+ /**
+ * Draw legend
+ *
+ * Will draw a legend in the bounding box
+ *
+ * @param ezcGraphBoundings $boundings Bounding of legend
+ * @param ezcGraphChartElementLegend $legend Legend to draw;
+ * @param int $type Type of legend: Protrait or landscape
+ * @return void
+ */
+ public function drawLegend(
+ ezcGraphBoundings $boundings,
+ ezcGraphChartElementLegend $legend,
+ $type = ezcGraph::VERTICAL )
+ {
+ $labels = $legend->labels;
+
+ // Calculate boundings of each label
+ if ( $type & ezcGraph::VERTICAL )
+ {
+ $labelWidth = $boundings->x1 - $boundings->x0;
+ $labelHeight = min(
+ ( $boundings->y1 - $boundings->y0 ) / count( $labels ) - $legend->spacing,
+ $legend->symbolSize + 2 * $legend->padding
+ );
+ }
+ else
+ {
+ $labelWidth = ( $boundings->x1 - $boundings->x0 ) / count( $labels ) - $legend->spacing;
+ $labelHeight = min(
+ $boundings->height,
+ $legend->symbolSize + 2 * $legend->padding
+ );
+ }
+
+ $symbolSize = $labelHeight - 2 * $legend->padding;
+
+ // Draw all labels
+ $labelPosition = new ezcGraphCoordinate( $boundings->x0, $boundings->y0 );
+ foreach ( $labels as $label )
+ {
+ $this->elements['legend_url'][$label['label']] = $label['url'];
+
+ $this->elements['legend'][$label['label']]['symbol'] = $this->drawSymbol(
+ new ezcGraphBoundings(
+ $labelPosition->x + $legend->padding,
+ $labelPosition->y + $legend->padding,
+ $labelPosition->x + $legend->padding + $symbolSize,
+ $labelPosition->y + $legend->padding + $symbolSize
+ ),
+ $label['color'],
+ $label['symbol']
+ );
+
+ $this->elements['legend'][$label['label']]['text'] = $this->driver->drawTextBox(
+ $label['label'],
+ new ezcGraphCoordinate(
+ $labelPosition->x + 2 * $legend->padding + $symbolSize,
+ $labelPosition->y + $legend->padding
+ ),
+ $labelWidth - $symbolSize - 3 * $legend->padding,
+ $labelHeight - 2 * $legend->padding,
+ ezcGraph::LEFT | ezcGraph::MIDDLE
+ );
+
+ $labelPosition->x += ( $type === ezcGraph::VERTICAL ? 0 : $labelWidth + $legend->spacing );
+ $labelPosition->y += ( $type === ezcGraph::VERTICAL ? $labelHeight + $legend->spacing : 0 );
+ }
+ }
+
+ /**
+ * Draw box
+ *
+ * Box are wrapping each major chart element and draw border, background
+ * and title to each chart element.
+ *
+ * Optionally a padding and margin for each box can be defined.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of the box
+ * @param ezcGraphColor $background Background color
+ * @param ezcGraphColor $borderColor Border color
+ * @param int $borderWidth Border width
+ * @param int $margin Margin
+ * @param int $padding Padding
+ * @param mixed $title Title of the box
+ * @param int $titleSize Size of title in the box
+ * @return ezcGraphBoundings Remaining inner boundings
+ */
+ public function drawBox(
+ ezcGraphBoundings $boundings,
+ ezcGraphColor $background = null,
+ ezcGraphColor $borderColor = null,
+ $borderWidth = 0,
+ $margin = 0,
+ $padding = 0,
+ $title = false,
+ $titleSize = 16 )
+ {
+ // Apply margin
+ $boundings->x0 += $margin;
+ $boundings->y0 += $margin;
+ $boundings->x1 -= $margin;
+ $boundings->y1 -= $margin;
+
+ if ( $background instanceof ezcGraphColor )
+ {
+ // Draw box background
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
+ ),
+ $background,
+ true
+ );
+ }
+
+ if ( ( $borderColor instanceof ezcGraphColor ) &&
+ ( $borderWidth > 0 ) )
+ {
+ // Draw border
+ $this->driver->drawPolygon(
+ array(
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y0 ),
+ new ezcGraphCoordinate( $boundings->x1, $boundings->y1 ),
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 ),
+ ),
+ $borderColor,
+ false,
+ $borderWidth
+ );
+
+ // Reduce local boundings by borderWidth
+ $boundings->x0 += $borderWidth;
+ $boundings->y0 += $borderWidth;
+ $boundings->x1 -= $borderWidth;
+ $boundings->y1 -= $borderWidth;
+ }
+
+ // Apply padding
+ $boundings->x0 += $padding;
+ $boundings->y0 += $padding;
+ $boundings->x1 -= $padding;
+ $boundings->y1 -= $padding;
+
+ // Add box title
+ if ( $title !== false )
+ {
+ switch ( $this->options->titlePosition )
+ {
+ case ezcGraph::TOP:
+ $this->driver->drawTextBox(
+ $title,
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y0 ),
+ $boundings->x1 - $boundings->x0,
+ $titleSize,
+ $this->options->titleAlignement
+ );
+
+ $boundings->y0 += $titleSize + $padding;
+ $boundings->y1 -= $titleSize + $padding;
+ break;
+ case ezcGraph::BOTTOM:
+ $this->driver->drawTextBox(
+ $title,
+ new ezcGraphCoordinate( $boundings->x0, $boundings->y1 - $titleSize ),
+ $boundings->x1 - $boundings->x0,
+ $titleSize,
+ $this->options->titleAlignement
+ );
+
+ $boundings->y1 -= $titleSize + $padding;
+ break;
+ }
+ }
+
+ return $boundings;
+ }
+
+ /**
+ * Draw text
+ *
+ * Draws the provided text in the boundings
+ *
+ * @param ezcGraphBoundings $boundings Boundings of text
+ * @param string $text Text
+ * @param int $align Alignement of text
+ * @param ezcGraphRotation $rotation
+ * @return void
+ */
+ public function drawText(
+ ezcGraphBoundings $boundings,
+ $text,
+ $align = ezcGraph::LEFT,
+ ezcGraphRotation $rotation = null )
+ {
+ if ( $this->depth === false )
+ {
+ // We are not 3d for now, wg. rendering normal text boxes like the
+ // title
+ $topleft = new ezcGraphCoordinate(
+ $boundings->x0,
+ $boundings->y0
+ );
+ $bottomright = new ezcGraphCoordinate(
+ $boundings->x1,
+ $boundings->y1
+ );
+ }
+ else
+ {
+ // The 3d part started
+ $topleft = $this->get3dCoordinate(
+ new ezcGraphCoordinate(
+ $boundings->x0,
+ $boundings->y0
+ ), false
+ );
+ $bottomright = $this->get3dCoordinate(
+ new ezcGraphCoordinate(
+ $boundings->x1,
+ $boundings->y1
+ ), false
+ );
+
+ // Also modify rotation accordingly
+ if ( $rotation !== null )
+ {
+ $rotation = new ezcGraphRotation(
+ $rotation->getRotation(),
+ $this->get3dCoordinate( $rotation->getCenter(), false )
+ );
+ }
+ }
+
+ $this->driver->drawTextBox(
+ $text,
+ $topleft,
+ $bottomright->x - $topleft->x,
+ $bottomright->y - $topleft->y,
+ $align,
+ $rotation
+ );
+ }
+
+ /**
+ * Draw grid line
+ *
+ * Draw line for the grid in the chart background
+ *
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Color of the grid line
+ * @return void
+ */
+ public function drawGridLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
+ {
+ $gridPolygonCoordinates = array(
+ $this->get3dCoordinate( $start, false ),
+ $this->get3dCoordinate( $end, false ),
+ $this->get3dCoordinate( $end, true ),
+ $this->get3dCoordinate( $start, true ),
+ );
+
+ // Draw grid polygon
+ if ( $this->options->fillGrid === 0 )
+ {
+ $this->driver->drawLine(
+ $gridPolygonCoordinates[2],
+ $gridPolygonCoordinates[3],
+ $color
+ );
+ }
+ else
+ {
+ if ( $this->options->fillGrid === 1 )
+ {
+ $this->driver->drawPolygon(
+ $gridPolygonCoordinates,
+ $color,
+ true
+ );
+ }
+ else
+ {
+ $this->driver->drawPolygon(
+ $gridPolygonCoordinates,
+ $color->transparent( $this->options->fillGrid ),
+ true
+ );
+ }
+
+ // Draw grid lines - scedule some for later to be drawn in front of
+ // the data
+ $this->frontLines[] = array(
+ $gridPolygonCoordinates[0],
+ $gridPolygonCoordinates[1],
+ $color,
+ 1
+ );
+
+ $this->frontLines[] = array(
+ $gridPolygonCoordinates[1],
+ $gridPolygonCoordinates[2],
+ $color,
+ 1
+ );
+
+ $this->driver->drawLine(
+ $gridPolygonCoordinates[2],
+ $gridPolygonCoordinates[3],
+ $color,
+ 1
+ );
+
+ $this->frontLines[] = array(
+ $gridPolygonCoordinates[3],
+ $gridPolygonCoordinates[0],
+ $color,
+ 1
+ );
+ }
+ }
+
+ /**
+ * Draw step line
+ *
+ * Draw a step (marker for label position) on a axis.
+ *
+ * @param ezcGraphCoordinate $start Start point
+ * @param ezcGraphCoordinate $end End point
+ * @param ezcGraphColor $color Color of the grid line
+ * @return void
+ */
+ public function drawStepLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color )
+ {
+ $stepPolygonCoordinates = array(
+ $this->get3dCoordinate( $start, true ),
+ $this->get3dCoordinate( $end, true ),
+ $this->get3dCoordinate( $end, false ),
+ $this->get3dCoordinate( $start, false ),
+ );
+
+ // Draw step polygon
+ if ( ( $this->options->fillAxis > 0 ) &&
+ ( $this->options->fillAxis < 1 ) )
+ {
+ $this->driver->drawPolygon(
+ $stepPolygonCoordinates,
+ $color->transparent( $this->options->fillAxis ),
+ true
+ );
+
+ $this->driver->drawPolygon(
+ $stepPolygonCoordinates,
+ $color,
+ false
+ );
+ }
+ else
+ {
+ $this->driver->drawPolygon(
+ $stepPolygonCoordinates,
+ $color,
+ ! (bool) $this->options->fillAxis
+ );
+ }
+ }
+
+ /**
+ * Draw axis
+ *
+ * Draws an axis form the provided start point to the end point. A specific
+ * angle of the axis is not required.
+ *
+ * For the labeleing of the axis a sorted array with major steps and an
+ * array with minor steps is expected, which are build like this:
+ * array(
+ * array(
+ * 'position' => (float),
+ * 'label' => (string),
+ * )
+ * )
+ * where the label is optional.
+ *
+ * The label renderer class defines how the labels are rendered. For more
+ * documentation on this topic have a look at the basic label renderer
+ * class.
+ *
+ * Additionally it can be specified if a major and minor grid are rendered
+ * by defining a color for them. The axis label is used to add a caption
+ * for the axis.
+ *
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $start Start point of axis
+ * @param ezcGraphCoordinate $end Endpoint of axis
+ * @param ezcGraphChartElementAxis $axis Axis to render
+ * @param ezcGraphAxisLabelRenderer $labelClass Used label renderer
+ * @return void
+ */
+ public function drawAxis(
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphAxisLabelRenderer $labelClass = null )
+ {
+ // Calculate used space for three dimensional effects
+ if ( $this->depth === false )
+ {
+ $this->depth = min(
+ ( $boundings->x1 - $boundings->x0 ) * $this->options->depth,
+ ( $boundings->y1 - $boundings->y0 ) * $this->options->depth
+ );
+
+ $this->xDepthFactor = 1 - $this->depth / ( $boundings->x1 - $boundings->x0 );
+ $this->yDepthFactor = 1 - $this->depth / ( $boundings->y1 - $boundings->y0 );
+
+ $this->dataBoundings = clone $boundings;
+ }
+
+ // Clone boundings to not be affected by internal mofifications
+ $boundings = clone $boundings;
+
+ switch ( $axis->position )
+ {
+ case ezcGraph::TOP:
+ case ezcGraph::BOTTOM:
+ $this->xAxisSpace = ( $this->dataBoundings->x1 - $this->dataBoundings->x0 ) * $axis->axisSpace;
+ break;
+ case ezcGraph::LEFT:
+ case ezcGraph::RIGHT:
+ $this->yAxisSpace = ( $this->dataBoundings->y1 - $this->dataBoundings->y0 ) * $axis->axisSpace;
+ break;
+ }
+
+ // Determine normalized direction
+ $direction = new ezcGraphVector(
+ $start->x - $end->x,
+ $start->y - $end->y
+ );
+ $direction->unify();
+
+ $start->x += $boundings->x0;
+ $start->y += $boundings->y0;
+ $end->x += $boundings->x0;
+ $end->y += $boundings->y0;
+
+ // Shorten drawn axis, if requested.
+ if ( ( $this->options->shortAxis === true ) &&
+ ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM ) ) )
+ {
+ $axisStart = clone $start;
+ $axisEnd = clone $end;
+
+ $axisStart->y += $boundings->height * $axis->axisSpace *
+ ( $axis->position === ezcGraph::TOP ? 1 : -1 );
+ $axisEnd->y -= $boundings->height * $axis->axisSpace *
+ ( $axis->position === ezcGraph::TOP ? 1 : -1 );
+ }
+ elseif ( ( $this->options->shortAxis === true ) &&
+ ( ( $axis->position === ezcGraph::LEFT ) ||
+ ( $axis->position === ezcGraph::RIGHT ) ) )
+ {
+ $axisStart = clone $start;
+ $axisEnd = clone $end;
+
+ $axisStart->x += $boundings->width * $axis->axisSpace *
+ ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
+ $axisEnd->x -= $boundings->width * $axis->axisSpace *
+ ( $axis->position === ezcGraph::LEFT ? 1 : -1 );
+ }
+ else
+ {
+ $axisStart = $start;
+ $axisEnd = $end;
+ }
+
+ $axisPolygonCoordinates = array(
+ $this->get3dCoordinate( $axisStart, true ),
+ $this->get3dCoordinate( $axisEnd, true ),
+ $this->get3dCoordinate( $axisEnd, false ),
+ $this->get3dCoordinate( $axisStart, false ),
+ );
+
+ // Draw axis
+ if ( ( $this->options->fillAxis > 0 ) &&
+ ( $this->options->fillAxis < 1 ) )
+ {
+ $this->driver->drawPolygon(
+ $axisPolygonCoordinates,
+ $axis->border->transparent( $this->options->fillAxis ),
+ true
+ );
+ }
+ else
+ {
+ $this->driver->drawPolygon(
+ $axisPolygonCoordinates,
+ $axis->border,
+ ! (bool) $this->options->fillAxis
+ );
+ }
+
+ // Draw axis lines - scedule some for later to be drawn in front of
+ // the data
+ $this->driver->drawLine(
+ $axisPolygonCoordinates[0],
+ $axisPolygonCoordinates[1],
+ $axis->border,
+ 1
+ );
+
+ $this->frontLines[] = array(
+ $axisPolygonCoordinates[1],
+ $axisPolygonCoordinates[2],
+ $axis->border,
+ 1
+ );
+
+ $this->frontLines[] = array(
+ $axisPolygonCoordinates[2],
+ $axisPolygonCoordinates[3],
+ $axis->border,
+ 1
+ );
+
+ $this->frontLines[] = array(
+ $axisPolygonCoordinates[3],
+ $axisPolygonCoordinates[0],
+ $axis->border,
+ 1
+ );
+
+ // Draw small arrowhead
+ $this->drawAxisArrowHead(
+ $axisPolygonCoordinates[1],
+ $direction,
+ max(
+ $axis->minArrowHeadSize,
+ min(
+ $axis->maxArrowHeadSize,
+ abs( ceil( ( ( $end->x - $start->x ) + ( $end->y - $start->y ) ) * $axis->axisSpace / 4 ) )
+ )
+ ),
+ $axis->border
+ );
+
+ // Draw axis label
+ if ( $axis->label !== false )
+ {
+ $width = $this->dataBoundings->x1 - $this->dataBoundings->x0;
+ switch ( $axis->position )
+ {
+ case ezcGraph::TOP:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $axisPolygonCoordinates[2]->x + $axis->labelMargin - $width * ( 1 - $axis->axisSpace * 2 ),
+ $axisPolygonCoordinates[2]->y - $axis->labelMargin - $axis->labelSize
+ ),
+ $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::TOP | ezcGraph::RIGHT
+ );
+ break;
+ case ezcGraph::BOTTOM:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $axisPolygonCoordinates[1]->x + $axis->labelMargin,
+ $axisPolygonCoordinates[1]->y + $axis->labelMargin
+ ),
+ $width * ( 1 - $axis->axisSpace * 2 ) - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::TOP | ezcGraph::LEFT
+ );
+ break;
+ case ezcGraph::LEFT:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $axisPolygonCoordinates[1]->x - $width,
+ $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin
+ ),
+ $width - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::BOTTOM | ezcGraph::RIGHT
+ );
+ break;
+ case ezcGraph::RIGHT:
+ $this->driver->drawTextBox(
+ $axis->label,
+ new ezcGraphCoordinate(
+ $axisPolygonCoordinates[1]->x,
+ $axisPolygonCoordinates[1]->y - $axis->labelSize - $axis->labelMargin
+ ),
+ $width - $axis->labelMargin,
+ $axis->labelSize,
+ ezcGraph::BOTTOM | ezcGraph::LEFT
+ );
+ break;
+ }
+ }
+
+ // Collect axis labels and draw, when all axisSpaces are collected
+ $this->axisLabels[] = array(
+ 'object' => $labelClass,
+ 'boundings' => $boundings,
+ 'start' => clone $start,
+ 'end' => clone $end,
+ 'axis' => $axis,
+ );
+
+ if ( $this->xAxisSpace && $this->yAxisSpace )
+ {
+ foreach ( $this->axisLabels as $axisLabel )
+ {
+ // If font should not be synchronized, use font configuration from
+ // each axis
+ if ( $this->options->syncAxisFonts === false )
+ {
+ $this->driver->options->font = $axisLabel['axis']->font;
+ }
+
+ switch ( $axisLabel['axis']->position )
+ {
+ case ezcGraph::RIGHT:
+ case ezcGraph::LEFT:
+ $axisLabel['start']->x += $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
+ $axisLabel['end']->x -= $this->xAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
+ break;
+ case ezcGraph::TOP:
+ case ezcGraph::BOTTOM:
+ $axisLabel['start']->y += $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
+ $axisLabel['end']->y -= $this->yAxisSpace * ( $axisLabel['start'] > $axisLabel['end'] ? -1 : 1 );
+ break;
+ }
+
+ $axisLabel['object']->renderLabels(
+ $this,
+ $axisLabel['boundings'],
+ $axisLabel['start'],
+ $axisLabel['end'],
+ $axisLabel['axis']
+ );
+ }
+ }
+ }
+
+ /**
+ * Draw background image
+ *
+ * Draws a background image at the defined position. If repeat is set the
+ * background image will be repeated like any texture.
+ *
+ * @param ezcGraphBoundings $boundings Boundings for the background image
+ * @param string $file Filename of background image
+ * @param int $position Position of background image
+ * @param int $repeat Type of repetition
+ * @return void
+ */
+ public function drawBackgroundImage(
+ ezcGraphBoundings $boundings,
+ $file,
+ $position = 48, // ezcGraph::CENTER | ezcGraph::MIDDLE
+ $repeat = ezcGraph::NO_REPEAT )
+ {
+ $imageData = getimagesize( $file );
+ $imageWidth = $imageData[0];
+ $imageHeight = $imageData[1];
+
+ $imageWidth = min( $imageWidth, $boundings->x1 - $boundings->x0 );
+ $imageHeight = min( $imageHeight, $boundings->y1 - $boundings->y0 );
+
+ $imagePosition = new ezcGraphCoordinate(
+ $boundings->x0,
+ $boundings->y0
+ );
+
+ // Determine x position
+ switch ( true ) {
+ case ( $repeat & ezcGraph::HORIZONTAL ):
+ // If is repeated on this axis fall back to position zero
+ case ( $position & ezcGraph::LEFT ):
+ $imagePosition->x = $boundings->x0;
+ break;
+ case ( $position & ezcGraph::RIGHT ):
+ $imagePosition->x = max(
+ $boundings->x1 - $imageWidth,
+ $boundings->x0
+ );
+ break;
+ default:
+ $imagePosition->x = max(
+ $boundings->x0 + ( $boundings->x1 - $boundings->x0 - $imageWidth ) / 2,
+ $boundings->x0
+ );
+ break;
+ }
+
+ // Determine y position
+ switch ( true ) {
+ case ( $repeat & ezcGraph::VERTICAL ):
+ // If is repeated on this axis fall back to position zero
+ case ( $position & ezcGraph::TOP ):
+ $imagePosition->y = $boundings->y0;
+ break;
+ case ( $position & ezcGraph::BOTTOM ):
+ $imagePosition->y = max(
+ $boundings->y1 - $imageHeight,
+ $boundings->y0
+ );
+ break;
+ default:
+ $imagePosition->y = max(
+ $boundings->y0 + ( $boundings->y1 - $boundings->y0 - $imageHeight ) / 2,
+ $boundings->y0
+ );
+ break;
+ }
+
+ // Texturize backround based on position and repetition
+ $position = new ezcGraphCoordinate(
+ $imagePosition->x,
+ $imagePosition->y
+ );
+
+ do
+ {
+ $position->y = $imagePosition->y;
+
+ do
+ {
+ $this->driver->drawImage(
+ $file,
+ $position,
+ $imageWidth,
+ $imageHeight
+ );
+
+ $position->y += $imageHeight;
+ }
+ while ( ( $position->y < $boundings->y1 ) &&
+ ( $repeat & ezcGraph::VERTICAL ) );
+
+ $position->x += $imageWidth;
+ }
+ while ( ( $position->x < $boundings->x1 ) &&
+ ( $repeat & ezcGraph::HORIZONTAL ) );
+ }
+
+ /**
+ * Call all postprocessing functions
+ *
+ * @return void
+ */
+ protected function finish()
+ {
+ $this->finishCirleSectors();
+ $this->finishPieSegmentLabels();
+ $this->finishBars();
+ $this->finishLineSymbols();
+ $this->finishFrontLines();
+
+ return true;
+ }
+
+ /**
+ * Reset renderer properties
+ *
+ * Reset all renderer properties, which were calculated during the
+ * rendering process, to offer a clean environment for rerendering.
+ *
+ * @return void
+ */
+ protected function resetRenderer()
+ {
+ parent::resetRenderer();
+
+ // Also reset special 3D renderer options
+ $this->pieSegmentLabels = array(
+ 0 => array(),
+ 1 => array(),
+ );
+ $this->pieSegmentBoundings = false;
+ $this->linePostSymbols = array();
+ $this->frontLines = array();
+ $this->circleSectors = array();
+ $this->barPostProcessing = array();
+ $this->depth = false;
+ $this->xDepthFactor = false;
+ $this->yDepthFactor = false;
+ $this->dataBoundings = false;
+ $this->axisLabels = array();
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/axis_label_boxed.php b/src/TUnit/external/ezc/Graph/renderer/axis_label_boxed.php
new file mode 100644
index 0000000..ac15ebe
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/axis_label_boxed.php
@@ -0,0 +1,218 @@
+
+ * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisBoxedLabelRenderer();
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphAxisBoxedLabelRenderer extends ezcGraphAxisLabelRenderer
+{
+ /**
+ * Store step array for later coordinate modifications
+ *
+ * @var array(ezcGraphStep)
+ */
+ protected $steps;
+
+ /**
+ * Store direction for later coordinate modifications
+ *
+ * @var ezcGraphVector
+ */
+ protected $direction;
+
+ /**
+ * Store coordinate width modifier for later coordinate modifications
+ *
+ * @var float
+ */
+ protected $widthModifier;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ parent::__construct( $options );
+ $this->properties['outerStep'] = true;
+ }
+
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphBoundings $innerBoundings = null )
+ {
+ // receive rendering parameters from axis
+ $steps = $axis->getSteps();
+ $this->steps = $steps;
+
+ $axisBoundings = new ezcGraphBoundings(
+ $start->x, $start->y,
+ $end->x, $end->y
+ );
+
+ // Determine normalized axis direction
+ $this->direction = new ezcGraphVector(
+ $end->x - $start->x,
+ $end->y - $start->y
+ );
+ $this->direction->unify();
+
+ // Get axis space
+ $gridBoundings = null;
+ list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings );
+
+ // Determine additional required axis space by boxes
+ $firstStep = reset( $steps );
+ $lastStep = end( $steps );
+
+ $this->widthModifier = 1 + $firstStep->width / 2 + $lastStep->width / 2;
+
+ // Draw steps and grid
+ foreach ( $steps as $nr => $step )
+ {
+ $position = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * ( $step->position + $step->width ) / $this->widthModifier,
+ $start->y + ( $end->y - $start->y ) * ( $step->position + $step->width ) / $this->widthModifier
+ );
+
+ $stepWidth = $step->width / $this->widthModifier;
+
+ $stepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $stepWidth,
+ $axisBoundings->height * $stepWidth
+ );
+
+ if ( $this->showLabels )
+ {
+ // Calculate label boundings
+ switch ( true )
+ {
+ case ( abs( $this->direction->x ) > abs( $this->direction->y ) ) &&
+ ( $this->direction->x > 0 ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $stepSize->x + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y + $ySpace - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::CENTER | ezcGraph::TOP;
+ break;
+ case ( abs( $this->direction->x ) > abs( $this->direction->y ) ) &&
+ ( $this->direction->x < 0 ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x + $stepSize->x - $this->labelPadding,
+ $position->y + $ySpace - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::CENTER | ezcGraph::TOP;
+ break;
+ case ( $this->direction->y > 0 ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $xSpace + $this->labelPadding,
+ $position->y - $stepSize->y + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT;
+ break;
+ case ( $this->direction->y < 0 ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $xSpace + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y + $stepSize->y - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT;
+ break;
+ }
+
+ $renderer->drawText( $labelBoundings, $step->label, $alignement );
+ }
+
+ // major grid
+ if ( $axis->majorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $gridBoundings,
+ $position,
+ $stepSize,
+ $axis->majorGrid
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $position,
+ $this->direction,
+ $axis->position,
+ $this->majorStepSize,
+ $axis->border
+ );
+ }
+ }
+
+ /**
+ * Modify chart data position
+ *
+ * Optionally additionally modify the coodinate of a data point
+ *
+ * @param ezcGraphCoordinate $coordinate Data point coordinate
+ * @return ezcGraphCoordinate Modified coordinate
+ */
+ public function modifyChartDataPosition( ezcGraphCoordinate $coordinate )
+ {
+ $firstStep = reset( $this->steps );
+ $offset = $firstStep->width / 2 / $this->widthModifier;
+
+ return new ezcGraphCoordinate(
+ $coordinate->x * abs( $this->direction->y ) +
+ ( $coordinate->x / $this->widthModifier + $offset ) * abs( $this->direction->x ),
+ $coordinate->y * abs( $this->direction->x ) +
+ ( $coordinate->y / $this->widthModifier + $offset ) * abs( $this->direction->y )
+ );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/axis_label_centered.php b/src/TUnit/external/ezc/Graph/renderer/axis_label_centered.php
new file mode 100644
index 0000000..2b996fe
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/axis_label_centered.php
@@ -0,0 +1,266 @@
+
+ * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisCenteredLabelRenderer();
+ *
+ *
+ * @property bool $showZeroValue
+ * Show the value at the zero point of an axis. This value might be
+ * crossed by the other axis which would result in an unreadable
+ * label.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphAxisCenteredLabelRenderer extends ezcGraphAxisLabelRenderer
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['showZeroValue'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'showZeroValue':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties['showZeroValue'] = (bool) $propertyValue;
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphBoundings $innerBoundings = null )
+ {
+ // receive rendering parameters from axis
+ $steps = $axis->getSteps();
+
+ $axisBoundings = new ezcGraphBoundings(
+ $start->x, $start->y,
+ $end->x, $end->y
+ );
+
+ // Determine normalized axis direction
+ $direction = new ezcGraphVector(
+ $end->x - $start->x,
+ $end->y - $start->y
+ );
+ $direction->unify();
+
+ // Get axis space
+ $gridBoundings = null;
+ list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings );
+
+ // Draw steps and grid
+ foreach ( $steps as $nr => $step )
+ {
+ $position = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $step->position,
+ $start->y + ( $end->y - $start->y ) * $step->position
+ );
+ $stepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $step->width,
+ $axisBoundings->height * $step->width
+ );
+
+ if ( ! $step->isZero )
+ {
+ // major grid
+ if ( $axis->majorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $gridBoundings,
+ $position,
+ $stepSize,
+ $axis->majorGrid
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $position,
+ $direction,
+ $axis->position,
+ $this->majorStepSize,
+ $axis->border
+ );
+ }
+
+ // draw label
+ if ( $this->showLabels && ( $this->showZeroValue || ! $step->isZero ) )
+ {
+ // Calculate label boundings
+ if ( abs( $direction->x ) > abs( $direction->y ) )
+ {
+ // Horizontal labels
+ switch ( true )
+ {
+ case ( $nr === 0 ):
+ // First label
+ $labelSize = min(
+ $xSpace * 2,
+ $step->width * $axisBoundings->width
+ );
+ break;
+ case ( $step->isLast ):
+ // Last label
+ $labelSize = min(
+ $xSpace * 2,
+ $steps[$nr - 1]->width * $axisBoundings->width
+ );
+ break;
+ default:
+ $labelSize = min(
+ $step->width * $axisBoundings->width,
+ $steps[$nr - 1]->width * $axisBoundings->width
+ );
+ break;
+ }
+
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $labelSize / 2 + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x + $labelSize / 2 - $this->labelPadding,
+ $position->y + $ySpace - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::CENTER | ezcGraph::TOP;
+ }
+ else
+ {
+ // Vertical labels
+ switch ( true )
+ {
+ case ( $nr === 0 ):
+ // First label
+ $labelSize = min(
+ $ySpace * 2,
+ $step->width * $axisBoundings->height
+ );
+ break;
+ case ( $step->isLast ):
+ // Last label
+ $labelSize = min(
+ $ySpace * 2,
+ $steps[$nr - 1]->width * $axisBoundings->height
+ );
+ break;
+ default:
+ $labelSize = min(
+ $step->width * $axisBoundings->height,
+ $steps[$nr - 1]->width * $axisBoundings->height
+ );
+ break;
+ }
+
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $xSpace + $this->labelPadding,
+ $position->y - $labelSize / 2 + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y + $labelSize / 2 - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT;
+ }
+
+ $renderer->drawText( $labelBoundings, $step->label, $alignement );
+ }
+
+ // Iterate over minor steps
+ if ( !$step->isLast )
+ {
+ foreach ( $step->childs as $minorStep )
+ {
+ $minorStepPosition = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $minorStep->position,
+ $start->y + ( $end->y - $start->y ) * $minorStep->position
+ );
+ $minorStepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $minorStep->width,
+ $axisBoundings->height * $minorStep->width
+ );
+
+ if ( $axis->minorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $gridBoundings,
+ $minorStepPosition,
+ $minorStepSize,
+ $axis->minorGrid
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $minorStepPosition,
+ $direction,
+ $axis->position,
+ $this->minorStepSize,
+ $axis->border
+ );
+ }
+ }
+ }
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/axis_label_exact.php b/src/TUnit/external/ezc/Graph/renderer/axis_label_exact.php
new file mode 100644
index 0000000..4421b35
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/axis_label_exact.php
@@ -0,0 +1,304 @@
+
+ * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisExactLabelRenderer();
+ *
+ *
+ * @property bool $showLastValue
+ * Show the last value on the axis, which will be aligned different
+ * than all other values, to not interfere with the arrow head of
+ * the axis.
+ * @property bool $renderLastOutside
+ * Render the last label outside of the normal axis label boundings
+ * next to the chart boundings. This may interfere with axis labels
+ * or cause small font size with a low axisSpace.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphAxisExactLabelRenderer extends ezcGraphAxisLabelRenderer
+{
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['showLastValue'] = true;
+ $this->properties['renderLastOutside'] = false;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'showLastValue':
+ case 'renderLastOutside':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties[$propertyName] = (bool) $propertyValue;
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphBoundings $innerBoundings = null )
+ {
+ // receive rendering parameters from axis
+ $steps = $axis->getSteps();
+
+ $axisBoundings = new ezcGraphBoundings(
+ $start->x, $start->y,
+ $end->x, $end->y
+ );
+
+ // Determine normalized axis direction
+ $direction = new ezcGraphVector(
+ $end->x - $start->x,
+ $end->y - $start->y
+ );
+ $direction->unify();
+
+ // Get axis space
+ $gridBoundings = null;
+ list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings );
+
+ // Draw steps and grid
+ foreach ( $steps as $nr => $step )
+ {
+ $position = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $step->position,
+ $start->y + ( $end->y - $start->y ) * $step->position
+ );
+ $stepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $step->width,
+ $axisBoundings->height * $step->width
+ );
+
+ if ( ! $step->isZero )
+ {
+ // major grid
+ if ( $axis->majorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $gridBoundings,
+ $position,
+ $stepSize,
+ $axis->majorGrid
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $position,
+ $direction,
+ $axis->position,
+ $this->majorStepSize,
+ $axis->border
+ );
+ }
+
+ if ( $this->showLabels )
+ {
+ switch ( $axis->position )
+ {
+ case ezcGraph::RIGHT:
+ case ezcGraph::LEFT:
+ $labelWidth = $axisBoundings->width *
+ $steps[$nr - $step->isLast]->width /
+ ( $this->showLastValue + 1 );
+ $labelHeight = $ySpace;
+
+ if ( ( $this->renderLastOutside === true ) &&
+ ( $step->isLast === true ) )
+ {
+ $labelWidth = ( $boundings->width - $axisBoundings->width ) / 2;
+ }
+ break;
+
+ case ezcGraph::BOTTOM:
+ case ezcGraph::TOP:
+ $labelWidth = $xSpace;
+ $labelHeight = $axisBoundings->height *
+ $steps[$nr - $step->isLast]->width /
+ ( $this->showLastValue + 1 );
+
+ if ( ( $this->renderLastOutside === true ) &&
+ ( $step->isLast === true ) )
+ {
+ $labelHeight = ( $boundings->height - $axisBoundings->height ) / 2;
+ }
+ break;
+ }
+
+ $showLabel = true;
+ switch ( true )
+ {
+ case ( !$this->showLastValue && $step->isLast ):
+ // Skip last step if showLastValue is false
+ $showLabel = false;
+ break;
+ // Draw label at top left of step
+ case ( ( $axis->position === ezcGraph::BOTTOM ) &&
+ ( !$step->isLast ) ) ||
+ ( ( $axis->position === ezcGraph::BOTTOM ) &&
+ ( $step->isLast ) &&
+ ( $this->renderLastOutside ) ) ||
+ ( ( $axis->position === ezcGraph::TOP ) &&
+ ( $step->isLast ) &&
+ ( !$this->renderLastOutside ) ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $labelWidth + $this->labelPadding,
+ $position->y - $labelHeight + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y - $this->labelPadding
+ );
+ $alignement = ezcGraph::RIGHT | ezcGraph::BOTTOM;
+ break;
+ // Draw label at bottom right of step
+ case ( ( $axis->position === ezcGraph::LEFT ) &&
+ ( !$step->isLast ) ) ||
+ ( ( $axis->position === ezcGraph::LEFT ) &&
+ ( $step->isLast ) &&
+ ( $this->renderLastOutside ) ) ||
+ ( ( $axis->position === ezcGraph::RIGHT ) &&
+ ( $step->isLast ) &&
+ ( !$this->renderLastOutside ) ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x + $labelWidth - $this->labelPadding,
+ $position->y + $labelHeight - $this->labelPadding
+ );
+ $alignement = ezcGraph::LEFT | ezcGraph::TOP;
+ break;
+ // Draw label at bottom left of step
+ case ( ( $axis->position === ezcGraph::TOP ) &&
+ ( !$step->isLast ) ) ||
+ ( ( $axis->position === ezcGraph::TOP ) &&
+ ( $step->isLast ) &&
+ ( $this->renderLastOutside ) ) ||
+ ( ( $axis->position === ezcGraph::RIGHT ) &&
+ ( !$step->isLast ) ) ||
+ ( ( $axis->position === ezcGraph::RIGHT ) &&
+ ( $step->isLast ) &&
+ ( $this->renderLastOutside ) ) ||
+ ( ( $axis->position === ezcGraph::BOTTOM ) &&
+ ( $step->isLast ) &&
+ ( !$this->renderLastOutside ) ) ||
+ ( ( $axis->position === ezcGraph::LEFT ) &&
+ ( $step->isLast ) &&
+ ( !$this->renderLastOutside ) ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $labelWidth + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y + $labelHeight - $this->labelPadding
+ );
+ $alignement = ezcGraph::RIGHT | ezcGraph::TOP;
+ break;
+ }
+
+ if ( $showLabel )
+ {
+ $renderer->drawText(
+ $labelBoundings,
+ $step->label,
+ $alignement
+ );
+ }
+ }
+
+ if ( !$step->isLast )
+ {
+ // Iterate over minor steps
+ foreach ( $step->childs as $minorStep )
+ {
+ $minorStepPosition = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $minorStep->position,
+ $start->y + ( $end->y - $start->y ) * $minorStep->position
+ );
+ $minorStepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $minorStep->width,
+ $axisBoundings->height * $minorStep->width
+ );
+
+ if ( $axis->minorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $gridBoundings,
+ $minorStepPosition,
+ $minorStepSize,
+ $axis->minorGrid
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $minorStepPosition,
+ $direction,
+ $axis->position,
+ $this->minorStepSize,
+ $axis->border
+ );
+ }
+ }
+ }
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/axis_label_none.php b/src/TUnit/external/ezc/Graph/renderer/axis_label_none.php
new file mode 100644
index 0000000..120a554
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/axis_label_none.php
@@ -0,0 +1,44 @@
+
+ * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisNoLabelRenderer();
+ *
+ *
+ * @version 1.4.3
+ * @package Graph
+ */
+class ezcGraphAxisNoLabelRenderer extends ezcGraphAxisLabelRenderer
+{
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis )
+ {
+ return true;
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/axis_label_radar.php b/src/TUnit/external/ezc/Graph/renderer/axis_label_radar.php
new file mode 100644
index 0000000..f7d28b9
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/axis_label_radar.php
@@ -0,0 +1,322 @@
+
+ * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRadarLabelRenderer();
+ *
+ *
+ * @property float $lastStep
+ * Position of last step on the axis to calculate the grid.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphAxisRadarLabelRenderer extends ezcGraphAxisLabelRenderer
+{
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ $this->properties['lastStep'] = null;
+
+ parent::__construct( $options );
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'lastStep':
+ if ( !is_null( $propertyValue ) &&
+ ( !is_float( $propertyValue ) ||
+ ( $propertyValue < 0 ) ||
+ ( $propertyValue > 1 ) ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float <= 1' );
+ }
+
+ $this->properties['lastStep'] = $propertyValue;
+ break;
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis )
+ {
+ // receive rendering parameters from axis
+ $steps = $axis->getSteps();
+
+ $axisBoundings = new ezcGraphBoundings(
+ $start->x, $start->y,
+ $end->x, $end->y
+ );
+
+ // Determine normalized axis direction
+ $direction = new ezcGraphVector(
+ $start->x - $end->x,
+ $start->y - $end->y
+ );
+ $direction->unify();
+
+ // Draw steps and grid
+ foreach ( $steps as $nr => $step )
+ {
+ $position = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $step->position,
+ $start->y + ( $end->y - $start->y ) * $step->position
+ );
+ $stepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $step->width,
+ $axisBoundings->height * $step->width
+ );
+
+ // Draw major grid
+ if ( ( $this->lastStep !== null ) && $axis->majorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $boundings,
+ $position,
+ $stepSize,
+ $axis->majorGrid,
+ $step->position
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $position,
+ $direction,
+ $axis->position,
+ $this->majorStepSize,
+ $axis->border
+ );
+
+ // draw label
+ if ( $this->showLabels && ( $this->lastStep === null ) )
+ {
+ // Calculate label boundings
+ if ( abs( $direction->x ) > abs( $direction->y ) )
+ {
+ // Horizontal labels
+ switch ( true )
+ {
+ case ( $nr === 0 ):
+ // First label
+ $labelSize = min(
+ $renderer->xAxisSpace * 2,
+ $step->width * $axisBoundings->width
+ );
+ break;
+ case ( $step->isLast ):
+ // Last label
+ $labelSize = min(
+ $renderer->xAxisSpace * 2,
+ $steps[$nr - 1]->width * $axisBoundings->width
+ );
+ break;
+ default:
+ $labelSize = min(
+ $step->width * $axisBoundings->width,
+ $steps[$nr - 1]->width * $axisBoundings->width
+ );
+ break;
+ }
+
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $labelSize / 2 + $this->labelPadding,
+ $position->y + $this->labelPadding,
+ $position->x + $labelSize / 2 - $this->labelPadding,
+ $position->y + $renderer->yAxisSpace - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::CENTER | ezcGraph::TOP;
+ }
+ else
+ {
+ // Vertical labels
+ switch ( true )
+ {
+ case ( $nr === 0 ):
+ // First label
+ $labelSize = min(
+ $renderer->yAxisSpace * 2,
+ $step->width * $axisBoundings->height
+ );
+ break;
+ case ( $step->isLast ):
+ // Last label
+ $labelSize = min(
+ $renderer->yAxisSpace * 2,
+ $steps[$nr - 1]->width * $axisBoundings->height
+ );
+ break;
+ default:
+ $labelSize = min(
+ $step->width * $axisBoundings->height,
+ $steps[$nr - 1]->width * $axisBoundings->height
+ );
+ break;
+ }
+
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - $renderer->xAxisSpace + $this->labelPadding,
+ $position->y - $labelSize / 2 + $this->labelPadding,
+ $position->x - $this->labelPadding,
+ $position->y + $labelSize / 2 - $this->labelPadding
+ );
+
+ $alignement = ezcGraph::MIDDLE | ezcGraph::RIGHT;
+ }
+
+ $renderer->drawText( $labelBoundings, $step->label, $alignement );
+ }
+
+ // Iterate over minor steps
+ if ( !$step->isLast )
+ {
+ foreach ( $step->childs as $minorStep )
+ {
+ $minorStepPosition = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $minorStep->position,
+ $start->y + ( $end->y - $start->y ) * $minorStep->position
+ );
+ $minorStepSize = new ezcGraphCoordinate(
+ $axisBoundings->width * $minorStep->width,
+ $axisBoundings->height * $minorStep->width
+ );
+
+ if ( ( $this->lastStep !== null ) && $axis->minorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $boundings,
+ $minorStepPosition,
+ $minorStepSize,
+ $axis->minorGrid,
+ $minorStep->position
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $minorStepPosition,
+ $direction,
+ $axis->position,
+ $this->minorStepSize,
+ $axis->border
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Draw grid
+ *
+ * Draws a grid line at the current position
+ *
+ * @param ezcGraphRenderer $renderer Renderer to draw the grid with
+ * @param ezcGraphBoundings $boundings Boundings of axis
+ * @param ezcGraphCoordinate $position Position of step
+ * @param ezcGraphCoordinate $direction Direction of axis
+ * @param ezcGraphColor $color Color of axis
+ * @param int $stepPosition
+ * @return void
+ */
+ protected function drawGrid( ezcGraphRenderer $renderer, ezcGraphBoundings $boundings, ezcGraphCoordinate $position, ezcGraphCoordinate $direction, ezcGraphColor $color, $stepPosition = null )
+ {
+ // Calculate position on last axis
+ $start = new ezcGraphCoordinate(
+ $boundings->x0 + $width = ( $boundings->width / 2 ),
+ $boundings->y0 + $height = ( $boundings->height / 2 )
+ );
+
+ $lastAngle = $this->lastStep * 2 * M_PI;
+ $end = new ezcGraphCoordinate(
+ $start->x + sin( $lastAngle ) * $width,
+ $start->y - cos( $lastAngle ) * $height
+ );
+
+ $direction = new ezcGraphVector(
+ $end->x - $start->x,
+ $end->y - $start->y
+ );
+ $direction->unify();
+
+ // Convert elipse to circle for correct angle calculation
+ $direction->y *= ( $renderer->xAxisSpace / $renderer->yAxisSpace );
+ $angle = $direction->angle( new ezcGraphVector( 0, 1 ) );
+
+ $movement = new ezcGraphVector(
+ sin( $angle ) * $renderer->xAxisSpace
+ * ( $direction->x < 0 ? -1 : 1 ),
+ cos( $angle ) * $renderer->yAxisSpace
+ );
+
+ $start->x += $movement->x;
+ $start->y += $movement->y;
+ $end->x -= $movement->x;
+ $end->y -= $movement->y;
+
+ $lastPosition = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $stepPosition,
+ $start->y + ( $end->y - $start->y ) * $stepPosition
+ );
+
+ $renderer->drawGridLine(
+ $position,
+ $lastPosition,
+ $color
+ );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/renderer/axis_label_rotated.php b/src/TUnit/external/ezc/Graph/renderer/axis_label_rotated.php
new file mode 100644
index 0000000..a8f5d9a
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/renderer/axis_label_rotated.php
@@ -0,0 +1,433 @@
+
+ * $chart->xAxis->axisLabelRenderer = new ezcGraphAxisRotatedLabelRenderer();
+ *
+ * // Define angle manually in degree
+ * $chart->xAxis->axisLabelRenderer->angle = 45;
+ *
+ * // Increase axis space
+ * $chart->xAxis->axisSpace = .2;
+ *
+ *
+ * @property float $angle
+ * Angle of labels on axis in degrees.
+ *
+ * @version 1.4.3
+ * @package Graph
+ * @mainclass
+ */
+class ezcGraphAxisRotatedLabelRenderer extends ezcGraphAxisLabelRenderer
+{
+ /**
+ * Store step array for later coordinate modifications
+ *
+ * @var array(ezcGraphStep)
+ */
+ protected $steps;
+
+ /**
+ * Store direction for later coordinate modifications
+ *
+ * @var ezcGraphVector
+ */
+ protected $direction;
+
+ /**
+ * Store coordinate width modifier for later coordinate modifications
+ *
+ * @var float
+ */
+ protected $widthModifier;
+
+ /**
+ * Store coordinate offset for later coordinate modifications
+ *
+ * @var float
+ */
+ protected $offset;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Default option array
+ * @return void
+ * @ignore
+ */
+ public function __construct( array $options = array() )
+ {
+ parent::__construct( $options );
+ $this->properties['angle'] = null;
+ $this->properties['labelOffset'] = true;
+ }
+
+ /**
+ * __set
+ *
+ * @param mixed $propertyName
+ * @param mixed $propertyValue
+ * @throws ezcBaseValueException
+ * If a submitted parameter was out of range or type.
+ * @throws ezcBasePropertyNotFoundException
+ * If a the value for the property options is not an instance of
+ * @return void
+ * @ignore
+ */
+ public function __set( $propertyName, $propertyValue )
+ {
+ switch ( $propertyName )
+ {
+ case 'angle':
+ if ( !is_numeric( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, '0 <= float < 360' );
+ }
+
+ $reducement = (int) ( $propertyValue - $propertyValue % 360 );
+ $this->properties['angle'] = (float) $propertyValue - $reducement;
+ break;
+
+ case 'labelOffset':
+ if ( !is_bool( $propertyValue ) )
+ {
+ throw new ezcBaseValueException( $propertyName, $propertyValue, 'bool' );
+ }
+
+ $this->properties[$propertyName] = (bool) $propertyValue;
+ break;
+
+ default:
+ return parent::__set( $propertyName, $propertyValue );
+ }
+ }
+
+ /**
+ * Render Axis labels
+ *
+ * Render labels for an axis.
+ *
+ * @param ezcGraphRenderer $renderer Renderer used to draw the chart
+ * @param ezcGraphBoundings $boundings Boundings of the axis
+ * @param ezcGraphCoordinate $start Axis starting point
+ * @param ezcGraphCoordinate $end Axis ending point
+ * @param ezcGraphChartElementAxis $axis Axis instance
+ * @return void
+ */
+ public function renderLabels(
+ ezcGraphRenderer $renderer,
+ ezcGraphBoundings $boundings,
+ ezcGraphCoordinate $start,
+ ezcGraphCoordinate $end,
+ ezcGraphChartElementAxis $axis,
+ ezcGraphBoundings $innerBoundings = null )
+ {
+ // receive rendering parameters from axis
+ $steps = $axis->getSteps();
+ $this->steps = $steps;
+
+ $axisBoundings = new ezcGraphBoundings(
+ $start->x, $start->y,
+ $end->x, $end->y
+ );
+
+ // Determine normalized axis direction
+ $this->direction = new ezcGraphVector(
+ $end->x - $start->x,
+ $end->y - $start->y
+ );
+ $this->direction->unify();
+ $axisAngle = -$this->direction->angle( new ezcGraphVector( 1, 0 ) );
+
+ // Get axis space
+ $gridBoundings = null;
+ list( $xSpace, $ySpace ) = $this->getAxisSpace( $renderer, $boundings, $axis, $innerBoundings, $gridBoundings );
+
+ // Determine optimal angle if none specified
+ if ( $this->angle === null )
+ {
+ $minimumStepWidth = null;
+ foreach ( $steps as $nr => $step )
+ {
+ if ( ( $minimumStepWidth === null ) ||
+ ( $step->width < $minimumStepWidth ) )
+ {
+ $minimumStepWidth = $step->width;
+ }
+ }
+
+ $width = abs(
+ $axisBoundings->width * $minimumStepWidth * $this->direction->x +
+ $axisBoundings->height * $minimumStepWidth * $this->direction->y
+ );
+ $height = abs(
+ $ySpace * $this->direction->x +
+ $xSpace * $this->direction->y
+ );
+
+ $length = sqrt( pow( $width, 2 ) + pow( $height, 2 ) );
+ $this->angle = rad2deg( acos( $height / $length ) );
+ }
+
+ // Determine additional required axis space by boxes
+ $firstStep = reset( $steps );
+ $lastStep = end( $steps );
+
+ $textAngle = $axisAngle +
+ deg2rad( $this->angle ) +
+ ( $axis->position & ( ezcGraph::TOP | ezcGraph::BOTTOM ) ? deg2rad( 270 ) : deg2rad( 90 ) );
+
+ // Ensure angle between 0 and 360 degrees
+ $degTextAngle = rad2deg( $textAngle );
+ while ( $degTextAngle < 0 )
+ {
+ $degTextAngle += 360.;
+ }
+
+ if ( $this->properties['labelOffset'] )
+ {
+ $this->offset =
+ ( $this->angle < 0 ? -1 : 1 ) *
+ ( $axis->position & ( ezcGraph::TOP | ezcGraph::LEFT ) ? 1 : -1 ) *
+ ( 1 - cos( deg2rad( $this->angle * 2 ) ) );
+ }
+ else
+ {
+ $this->offset = 0;
+ }
+
+ $axisSpaceFactor = abs(
+ ( $this->direction->x == 0 ? 0 :
+ $this->direction->x * $ySpace / $axisBoundings->width ) +
+ ( $this->direction->y == 0 ? 0 :
+ $this->direction->y * $xSpace / $axisBoundings->height )
+ );
+
+ $start = new ezcGraphCoordinate(
+ $start->x + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ),
+ $start->y + max( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y )
+ );
+ $end = new ezcGraphCoordinate(
+ $end->x + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->x - $start->x ),
+ $end->y + min( 0., $axisSpaceFactor * $this->offset ) * ( $end->y - $start->y )
+ );
+
+ $labelLength = sqrt(
+ pow(
+ $xSpace * $this->direction->y +
+ $axisSpaceFactor * $this->offset * ( $end->x - $start->x ),
+ 2 ) +
+ pow(
+ $ySpace * $this->direction->x +
+ $axisSpaceFactor * $this->offset * ( $end->y - $start->y ),
+ 2 )
+ );
+
+ $this->offset *= $axisSpaceFactor;
+
+ // Draw steps and grid
+ foreach ( $steps as $nr => $step )
+ {
+ $position = new ezcGraphCoordinate(
+ $start->x + ( $end->x - $start->x ) * $step->position * abs( $this->direction->x ),
+ $start->y + ( $end->y - $start->y ) * $step->position * abs( $this->direction->y )
+ );
+
+ $stepSize = new ezcGraphCoordinate(
+ ( $end->x - $start->x ) * $step->width,
+ ( $end->y - $start->y ) * $step->width
+ );
+
+ // Calculate label boundings
+ switch ( true )
+ {
+ case ( $nr === 0 ):
+ $labelSize = min(
+ abs(
+ $xSpace * 2 * $this->direction->y +
+ $ySpace * 2 * $this->direction->x ),
+ abs(
+ $step->width * $axisBoundings->width * $this->direction->x +
+ $step->width * $axisBoundings->height * $this->direction->y )
+ );
+ break;
+ case ( $step->isLast ):
+ $labelSize = min(
+ abs(
+ $xSpace * 2 * $this->direction->y +
+ $ySpace * 2 * $this->direction->x ),
+ abs(
+ $steps[$nr - 1]->width * $axisBoundings->width * $this->direction->x +
+ $steps[$nr - 1]->width * $axisBoundings->height * $this->direction->y )
+ );
+ break;
+ default:
+ $labelSize = abs(
+ $step->width * $axisBoundings->width * $this->direction->x +
+ $step->width * $axisBoundings->height * $this->direction->y
+ );
+ break;
+ }
+
+ $labelSize = $labelSize * cos( deg2rad( $this->angle ) );
+ $lengthReducement = min(
+ abs( tan( deg2rad( $this->angle ) ) * ( $labelSize / 2 ) ),
+ abs( $labelLength / 2 )
+ );
+
+ switch ( true )
+ {
+ case ( ( ( $degTextAngle >= 0 ) &&
+ ( $degTextAngle < 90 ) &&
+ ( ( $axis->position === ezcGraph::LEFT ) ||
+ ( $axis->position === ezcGraph::RIGHT )
+ )
+ ) ||
+ ( ( $degTextAngle >= 270 ) &&
+ ( $degTextAngle < 360 ) &&
+ ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM )
+ )
+ )
+ ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x,
+ $position->y,
+ $position->x + abs( $labelLength ) - $lengthReducement,
+ $position->y + $labelSize
+ );
+ $labelAlignement = ezcGraph::LEFT | ezcGraph::TOP;
+ $labelRotation = $degTextAngle;
+ break;
+ case ( ( ( $degTextAngle >= 90 ) &&
+ ( $degTextAngle < 180 ) &&
+ ( ( $axis->position === ezcGraph::LEFT ) ||
+ ( $axis->position === ezcGraph::RIGHT )
+ )
+ ) ||
+ ( ( $degTextAngle >= 180 ) &&
+ ( $degTextAngle < 270 ) &&
+ ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM )
+ )
+ )
+ ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - abs( $labelLength ) + $lengthReducement,
+ $position->y,
+ $position->x,
+ $position->y + $labelSize
+ );
+ $labelAlignement = ezcGraph::RIGHT | ezcGraph::TOP;
+ $labelRotation = $degTextAngle - 180;
+ break;
+ case ( ( ( $degTextAngle >= 180 ) &&
+ ( $degTextAngle < 270 ) &&
+ ( ( $axis->position === ezcGraph::LEFT ) ||
+ ( $axis->position === ezcGraph::RIGHT )
+ )
+ ) ||
+ ( ( $degTextAngle >= 90 ) &&
+ ( $degTextAngle < 180 ) &&
+ ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM )
+ )
+ )
+ ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x - abs( $labelLength ) + $lengthReducement,
+ $position->y - $labelSize,
+ $position->x,
+ $position->y
+ );
+ $labelAlignement = ezcGraph::RIGHT | ezcGraph::BOTTOM;
+ $labelRotation = $degTextAngle - 180;
+ break;
+ case ( ( ( $degTextAngle >= 270 ) &&
+ ( $degTextAngle < 360 ) &&
+ ( ( $axis->position === ezcGraph::LEFT ) ||
+ ( $axis->position === ezcGraph::RIGHT )
+ )
+ ) ||
+ ( ( $degTextAngle >= 0 ) &&
+ ( $degTextAngle < 90 ) &&
+ ( ( $axis->position === ezcGraph::TOP ) ||
+ ( $axis->position === ezcGraph::BOTTOM )
+ )
+ )
+ ):
+ $labelBoundings = new ezcGraphBoundings(
+ $position->x,
+ $position->y + $labelSize,
+ $position->x + abs( $labelLength ) - $lengthReducement,
+ $position->y
+ );
+ $labelAlignement = ezcGraph::LEFT | ezcGraph::BOTTOM;
+ $labelRotation = $degTextAngle;
+ break;
+ }
+
+ $renderer->drawText(
+ $labelBoundings,
+ $step->label,
+ $labelAlignement,
+ new ezcGraphRotation(
+ $labelRotation,
+ $position
+ )
+ );
+
+ // major grid
+ if ( $axis->majorGrid )
+ {
+ $this->drawGrid(
+ $renderer,
+ $gridBoundings,
+ $position,
+ $stepSize,
+ $axis->majorGrid
+ );
+ }
+
+ // major step
+ $this->drawStep(
+ $renderer,
+ $position,
+ $this->direction,
+ $axis->position,
+ $this->majorStepSize,
+ $axis->border
+ );
+ }
+ }
+
+ /**
+ * Modify chart data position
+ *
+ * Optionally additionally modify the coodinate of a data point
+ *
+ * @param ezcGraphCoordinate $coordinate Data point coordinate
+ * @return ezcGraphCoordinate Modified coordinate
+ */
+ public function modifyChartDataPosition( ezcGraphCoordinate $coordinate )
+ {
+ return new ezcGraphCoordinate(
+ $coordinate->x * abs( $this->direction->y ) +
+ ( $coordinate->x * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->x ),
+ $coordinate->y * abs( $this->direction->x ) +
+ ( $coordinate->y * ( 1 - abs( $this->offset ) ) + max( 0, $this->offset ) ) * abs( $this->direction->y )
+ );
+ }
+}
+?>
diff --git a/src/TUnit/external/ezc/Graph/structs/context.php b/src/TUnit/external/ezc/Graph/structs/context.php
new file mode 100644
index 0000000..ab3fa72
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/structs/context.php
@@ -0,0 +1,92 @@
+dataset = $dataset;
+ $this->datapoint = $datapoint;
+ $this->url = $url;
+ }
+
+ /**
+ * __set_state
+ *
+ * @param array $properties Struct properties
+ * @return void
+ * @ignore
+ */
+ public function __set_state( array $properties )
+ {
+ $this->dataset = (string) $properties['dataset'];
+ $this->datapoint = (string) $properties['datapoint'];
+
+ // Check to keep BC
+ // @TODO: Remvove unnesecary check on next major version
+ if ( array_key_exists( 'url', $properties ) )
+ {
+ $this->url = (string) $properties['url'];
+ }
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/structs/coordinate.php b/src/TUnit/external/ezc/Graph/structs/coordinate.php
new file mode 100644
index 0000000..0af65b7
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/structs/coordinate.php
@@ -0,0 +1,76 @@
+x = $x;
+ $this->y = $y;
+ }
+
+ /**
+ * __set_state
+ *
+ * @param array $properties Struct properties
+ * @return void
+ * @ignore
+ */
+ public function __set_state( array $properties )
+ {
+ $this->x = $properties['x'];
+ $this->y = $properties['y'];
+ }
+
+ /**
+ * Returns simple string representation of coordinate
+ *
+ * @return string
+ * @ignore
+ */
+ public function __toString()
+ {
+ return sprintf( '( %.2f, %.2f )', $this->x, $this->y );
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/structs/step.php b/src/TUnit/external/ezc/Graph/structs/step.php
new file mode 100644
index 0000000..546e375
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/structs/step.php
@@ -0,0 +1,106 @@
+position = (float) $position;
+ $this->width = (float) $width;
+ $this->label = $label;
+ $this->childs = $childs;
+ $this->isZero = (bool) $isZero;
+ $this->isLast = (bool) $isLast;
+ }
+
+ /**
+ * __set_state
+ *
+ * @param array $properties Struct properties
+ * @return void
+ * @ignore
+ */
+ public function __set_state( array $properties )
+ {
+ $this->position = $properties['position'];
+ $this->width = $properties['width'];
+ $this->label = $properties['label'];
+ $this->childs = $properties['childs'];
+ $this->isZero = $properties['isZero'];
+ $this->isLast = $properties['isLast'];
+ }
+}
+
+?>
diff --git a/src/TUnit/external/ezc/Graph/tools.php b/src/TUnit/external/ezc/Graph/tools.php
new file mode 100644
index 0000000..8c34385
--- /dev/null
+++ b/src/TUnit/external/ezc/Graph/tools.php
@@ -0,0 +1,183 @@
+driver instanceof ezcGraphGdDriver ) )
+ {
+ throw new ezcGraphToolsIncompatibleDriverException( $chart->driver, 'ezcGraphGdDriver' );
+ }
+
+ $elements = $chart->renderer->getElementReferences();
+
+ if ( !count( $elements ) )
+ {
+ throw new ezcGraphToolsNotRenderedException( $chart );
+ }
+
+ $imageMap = sprintf( "\n";
+ }
+
+ /**
+ * Add links to clickable SVG elements in a chart with SVG driver.
+ *
+ * @param ezcGraphChart $chart
+ * @return void
+ */
+ public static function linkSvgElements( ezcGraphChart $chart )
+ {
+ if ( ! ( $chart->driver instanceof ezcGraphSvgDriver ) )
+ {
+ throw new ezcGraphToolsIncompatibleDriverException( $chart->driver, 'ezcGraphSvgDriver' );
+ }
+
+ $fileName = $chart->getRenderedFile();
+
+ if ( !$fileName )
+ {
+ throw new ezcGraphToolsNotRenderedException( $chart );
+ }
+
+ $dom = new DOMDocument();
+ $dom->load( $fileName );
+ $xpath = new DomXPath( $dom );
+
+ $elements = $chart->renderer->getElementReferences();
+
+ // Link chart elements
+ foreach ( $elements['data'] as $dataset => $datapoints )
+ {
+ foreach ( $datapoints as $datapoint => $ids )
+ {
+ $url = $chart->data[$dataset]->url[$datapoint];
+
+ if ( empty( $url ) )
+ {
+ continue;
+ }
+
+ foreach ( $ids as $id )
+ {
+ $element = $xpath->query( '//*[@id = \'' . $id . '\']' )->item( 0 );
+
+ $element->setAttribute( 'style', $element->getAttribute( 'style' ) . ' cursor: ' . $chart->driver->options->linkCursor . ';' );
+ $element->setAttribute( 'onclick', "top.location = '{$url}'" );
+ }
+ }
+ }
+
+ // Link legend elements
+ if ( isset( $elements['legend'] ) )
+ {
+ foreach ( $elements['legend'] as $objectName => $ids )
+ {
+ $url = $elements['legend_url'][$objectName];
+
+ if ( empty( $url ) )
+ {
+ continue;
+ }
+
+ foreach ( $ids as $id )
+ {
+ $element = $xpath->query( '//*[@id = \'' . $id . '\']' )->item( 0 );
+
+ $element->setAttribute( 'style', $element->getAttribute( 'style' ) . ' cursor: ' . $chart->driver->options->linkCursor . ';' );
+ $element->setAttribute( 'onclick', "top.location = '{$url}'" );
+ }
+ }
+ }
+
+ $dom->save( $fileName );
+ }
+}
+
+?>
diff --git a/src/TUnit/framework/reporting/template/style.css b/src/TUnit/framework/reporting/template/style.css
index f5a6c45..2c538eb 100644
--- a/src/TUnit/framework/reporting/template/style.css
+++ b/src/TUnit/framework/reporting/template/style.css
@@ -2,7 +2,7 @@ html {
overflow: auto;
}
body {
- background-color: #CCFFCC;
+ background-color: #EEEEEE;
color: #000000;
font-family: Consolas, "Courier New", monospace;
font-size: 16px;
diff --git a/src/TUnit/manifest.php b/src/TUnit/manifest.php
index 6dbf1eb..4734366 100644
--- a/src/TUnit/manifest.php
+++ b/src/TUnit/manifest.php
@@ -3,11 +3,11 @@
/**
* Autoload manifest
*
- * Autogenerated by manifester.php on 2009-06-27 18:04:06
+ * Autogenerated by manifester.php on 2009-06-30 01:05:55
*
* @package TUnit
* @version 0.5.0
- * @since 0.5.0
+ * @since 1.0
*/
return array(
diff --git a/tools/manifester.php b/tools/manifester.php
index 2188ba4..7309577 100644
--- a/tools/manifester.php
+++ b/tools/manifester.php
@@ -37,13 +37,14 @@
global $switches;
$switches = new CliSwitchCollection();
- $switches->addSwitch(new CliSwitch('directory', 'd', true, 'dir1,dir2,...', 'Comma-delimited list of directories'))
- ->addSwitch(new CliSwitch('version', 'v', true, 'version_number', 'Version number for use in @version tag'))
- ->addSwitch(new CliSwitch('package', 'p', true, 'package_name ', 'Name of the package for use in @package tag'))
- ->addSwitch(new CliSwitch('output', 'o', false, 'file', 'Name of the output file, defaults to stdout if empty'))
- ->addSwitch(new CliSwitch('quiet', 'q', false, null, 'Do not print progress messages'))
- ->addSwitch(new CliSwitch('recursive', 'r', false, null, 'Recursively walk the directories'))
- ->addSwitch(new CliSwitch('base-dir', 'b', true, 'dir', 'Base directory'));
+ $switches->addSwitch(new CliSwitch('directory', 'd', true, 'dir1,dir2,...', 'Comma-delimited list of directories'))
+ ->addSwitch(new CliSwitch('version', 'v', true, 'version_number', 'Version number for use in @version tag'))
+ ->addSwitch(new CliSwitch('package', 'p', true, 'package_name ', 'Name of the package for use in @package tag'))
+ ->addSwitch(new CliSwitch('output', 'o', false, 'file', 'Name of the output file, defaults to stdout if empty'))
+ ->addSwitch(new CliSwitch('quiet', 'q', false, null, 'Do not print progress messages'))
+ ->addSwitch(new CliSwitch('recursive', 'r', false, null, 'Recursively walk the directories'))
+ ->addSwitch(new CliSwitch('ignore', 'i', false, 'pattern1,pattern2...', 'File patterns to ignore'))
+ ->addSwitch(new CliSwitch('base-dir', 'b', true, 'dir', 'Base directory'));
array_shift($argv);
$args = Cli::parseArgs($argv, $switches);
@@ -77,7 +78,7 @@
*
* @package $args[package]
* @version $args[version]
- * @since $args[version]
+ * @since 1.0
*/
return array(
@@ -87,6 +88,11 @@ ENDDATA;
$classes = array();
$maxClassNameLength = 0;
+ $ignore = array(DIRECTORY_SEPARATOR . '.');
+ if (isset($args['ignore']) && !empty($args['ignore'])) {
+ $ignore = array_merge($ignore, explode(',', $args['ignore']));
+ }
+
foreach ($dirs as $dir) {
if (!is_dir($dir)) {
fwrite(STDERR, $dir . ' is not a directory... skipping');
@@ -96,7 +102,12 @@ ENDDATA;
$iterator = $args['recursive'] ? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) : new DirectoryIterator($dir);
foreach ($iterator as $file) {
- if ($file->isFile() && strpos($file->getPathName(), DIRECTORY_SEPARATOR . '.') === false && substr($file->getFileName(), -4) === '.php') {
+ if (
+ $file->isFile() &&
+ strpos($file->getPathName(), DIRECTORY_SEPARATOR . '.') === false &&
+ substr($file->getFileName(), -4) === '.php' &&
+ array_reduce($ignore, create_function('$old, $new', 'return $old && strpos(\'' . addslashes($file->getPathName()) . '\', $new) === false;'), true)
+ ) {
if (!$args['quiet']) {
echo 'Processing ' . $file->getPathName() . "\n";
}