diff options
-rw-r--r-- | CREDITS | 5 | ||||
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | src/driver/cairo_oo.php | 1006 | ||||
-rw-r--r-- | src/graph_autoload.php | 1 | ||||
-rw-r--r-- | tests/driver_cairo_oo_test.php | 1502 |
5 files changed, 2518 insertions, 2 deletions
@@ -25,3 +25,8 @@ Contributors - Elger Thiele * Custom highlight values in line and bar charts. + +- Michael Maclean + + * Contributing the Cairo-OO-driver + @@ -1,8 +1,10 @@ 1.?.? - [RELEASEDATE} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Implemented: #14294: Add option for axis label rotation -- Implemented: #13341: Vertical Bar Charts +- Implemented: #14294: Add option for axis label rotation. +- Implemented: #13341: Vertical Bar Charts. +- Implemented: #15135: Add support for pecl/cairo's object-oriented API, patch + by Michael Maclean. 1.4.3 - Monday 08 June 2009 diff --git a/src/driver/cairo_oo.php b/src/driver/cairo_oo.php new file mode 100644 index 0000000..21a9eac --- /dev/null +++ b/src/driver/cairo_oo.php @@ -0,0 +1,1006 @@ +<?php +/** + * File containing the ezcGraphCairoOODriver class + * + * @package Graph + * @version //autogentag// + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Extension of the basic driver package to utilize the cairo library. + * + * This drivers options are defined in the class + * {@link ezcGraphCairoDriverOptions} extending the basic driver options class + * {@link ezcGraphDriverOptions}. + * + * As this is the default driver you do not need to explicitely set anything to + * use it, but may use some of its advanced features. + * + * <code> + * $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 ezcGraphCairoOODriver(); + * + * $graph->render( 400, 200, 'tutorial_driver_cairo.png' ); + * </code> + * + * @version //autogentag// + * @package Graph + * @mainclass + */ +class ezcGraphCairoOODriver extends ezcGraphDriver +{ + /** + * Surface for cairo + * + * @var CairoImageSurface + */ + protected $surface; + + /** + * Current cairo context. + * + * @var CairoContext + */ + 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' ); + $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 = new CairoImageSurface( + CairoFormat::ARGB32, + $this->options->width, + $this->options->height + ); + + $this->context = new CairoContext( $this->surface ); + $this->context->setLineWidth( 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 = new CairoLinearGradient( + $color->startPoint->x, $color->startPoint->y, + $color->endPoint->x, $color->endPoint->y + ); + + $pattern->addColorStopRgba( + 0, + $color->startColor->red / 255, + $color->startColor->green / 255, + $color->startColor->blue / 255, + 1 - $color->startColor->alpha / 255 + ); + + $pattern->addColorStopRgba( + 1, + $color->endColor->red / 255, + $color->endColor->green / 255, + $color->endColor->blue / 255, + 1 - $color->endColor->alpha / 255 + ); + + $this->context->setSource( $pattern ); + $this->context->fill(); + break; + + case $color instanceof ezcGraphRadialGradient: + $pattern = new CairoRadialGradient( + 0, 0, 0, + 0, 0, 1 + ); + + $pattern->addColorStopRgba( + 0, + $color->startColor->red / 255, + $color->startColor->green / 255, + $color->startColor->blue / 255, + 1 - $color->startColor->alpha / 255 + ); + + $pattern->addColorStopRgba( + 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 = CairoMatrix::multiply( + $move = CairoMatrix::initTranslate( -$color->center->x, -$color->center->y ), + $scale = CairoMatrix::initScale( 1 / $color->width, 1 / $color->height ) + ); + $pattern->setMatrix( $matrix ); + + $this->context->setSource( $pattern ); + $this->context->fill(); + break; + default: + $this->context->setSourceRgba( + $color->red / 255, + $color->green / 255, + $color->blue / 255, + 1 - $color->alpha / 255 + ); + break; + } + + // Set line width + $this->context->setLineWidth( $thickness ); + + // Set requested fill state for context + if ( $filled ) + { + $this->context->fillPreserve(); + } + } + + /** + * 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(); + + $this->context->newPath(); + + $lastPoint = end( $points ); + $this->context->moveTo( $lastPoint->x, $lastPoint->y ); + + foreach ( $points as $point ) + { + $this->context->lineTo( $point->x, $point->y ); + } + + $this->context->closePath(); + + $this->getStyle( $color, $filled, $thickness ); + $this->context->stroke(); + + 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(); + + $this->context->newPath(); + + $this->context->moveTo( $start->x, $start->y ); + $this->context->lineTo( $end->x, $end->y ); + + $this->getStyle( $color, false, $thickness ); + $this->context->stroke(); + + 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 ) + { + $this->context->selectFontFace( $font->name, CairoFontSlant::NORMAL, CairoFontWeight::NORMAL ); + $this->context->setFontSize( $size ); + $extents = $this->context->textExtents( $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 ) + { + $this->context->selectFontFace( $font, CairoFontSlant::NORMAL, CairoFontWeight::NORMAL ); + $this->context->setFontSize( $size ); + + // Store current state of context + $this->context->save(); + $this->context->moveTo( 0, 0 ); + + if ( $rotation !== null ) + { + // Move to the center + $this->context->translate( + $rotation->getCenter()->x, + $rotation->getCenter()->y + ); + // Rotate around text center + $this->context->rotate( + deg2rad( $rotation->getRotation() ) + ); + // Center the text + $this->context->translate( + $position->x - $rotation->getCenter()->x, + $position->y - $rotation->getCenter()->y - $size * .15 + ); + } else { + $this->context->translate( + $position->x, + $position->y - $size * .15 + ); + } + + $this->context->newPath(); + $this->getStyle( $color, true ); + $this->context->showText( $text ); + $this->context->stroke(); + + // Restore state of context + $this->context->restore(); + } + + /** + * 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; + } + + $this->context->save(); + + // Draw circular arc path + $this->context->newPath(); + $this->context->translate( + $center->x, + $center->y + ); + $this->context->scale( + 1, $height / $width + ); + + $this->context->moveTo( 0, 0 ); + $this->context->arc( + 0., 0., + $width / 2, + deg2rad( $startAngle ), + deg2rad( $endAngle ) + ); + $this->context->lineTo( 0, 0 ); + + $this->context->restore(); + $this->getStyle( $color, $filled ); + $this->context->stroke(); + + // 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 ) ) + { + $this->context->newPath(); + $this->context->moveTo( + $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 ) ) + { + $this->context->lineTo( + $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 ) ) + { + $this->context->lineTo( + $center->x + cos( deg2rad( $angle ) ) * $width / 2, + $center->y + sin( deg2rad( $angle ) ) * $height / 2 + ); + + if ( $angle === $startAngle ) + { + break; + } + } + + $this->context->closePath(); + $this->getStyle( $color, $filled ); + $this->context->stroke( ); + + $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(); + + $this->context->save(); + + // Draw circular arc path + $this->context->newPath(); + $this->context->translate( + $center->x, + $center->y + ); + $this->context->scale( + 1, $height / $width + ); + + $this->context->arc( + 0., 0., + $width / 2, + 0, 2 * M_PI + ); + + $this->context->restore(); + $this->getStyle( $color, $filled ); + $this->context->stroke(); + + // 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 = CairoImageSurface::createFromPng( $file ); + + // Create pattern from source image to be able to transform it + $pattern = new CairoSurfacePattern( $imageSurface ); + + // Scale pattern to defined dimensions and move it to its destination position + $matrix = CairoMatrix::multiply( + $move = CairoMatrix::initTranslate( -$position->x, -$position->y ), + $scale = CairoMatrix::initScale( $data[0] / $width, $data[1] / $height ) + ); + $pattern->setMatrix( $matrix ); + + // Merge surfaces + $this->context->setSource( $pattern ); + $this->context->rectangle( $position->x, $position->y, $width, $height ); + $this->context->fill(); + } + + /** + * 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 ); + $this->surface->writeToPng( $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(); + $this->surface->writeToPng( $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: + * <code> + * array( + * 'surface' => resource, + * 'context' => resource, + * ) + * </code> + * + * @return array + */ + public function getResource() + { + return array( + 'surface' => $this->surface, + 'context' => $this->context, + ); + } +} + +?> diff --git a/src/graph_autoload.php b/src/graph_autoload.php index c66e1a2..f15e019 100644 --- a/src/graph_autoload.php +++ b/src/graph_autoload.php @@ -74,6 +74,7 @@ return array( 'ezcGraphBoundings' => 'Graph/math/boundings.php', 'ezcGraphCairoDriver' => 'Graph/driver/cairo.php', 'ezcGraphCairoDriverOptions' => 'Graph/options/cairo_driver.php', + 'ezcGraphCairoOODriver' => 'Graph/driver/cairo_oo.php', 'ezcGraphChartElementBackground' => 'Graph/element/background.php', 'ezcGraphChartElementDateAxis' => 'Graph/axis/date.php', 'ezcGraphChartElementLabeledAxis' => 'Graph/axis/labeled.php', diff --git a/tests/driver_cairo_oo_test.php b/tests/driver_cairo_oo_test.php new file mode 100644 index 0000000..cbd03a7 --- /dev/null +++ b/tests/driver_cairo_oo_test.php @@ -0,0 +1,1502 @@ +<?php +/** + * ezcGraphCairoOODriverTest + * + * @package Graph + * @version //autogen// + * @subpackage Tests + * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Tests for ezcGraph class. + * + * @package Graph + * @subpackage Tests + */ +class ezcGraphCairoOODriverTest extends ezcTestImageCase +{ + protected $driver; + + protected $tempDir; + + protected $basePath; + + protected $testFiles = array( + 'png' => 'png.png', + ); + + public static function suite() + { + return new PHPUnit_Framework_TestSuite( __CLASS__ ); + } + + protected function setUp() + { + if ( !ezcBaseFeatures::hasExtensionSupport( 'cairo' ) ) + { + $this->markTestSkipped( 'This test needs pecl/cairo support.' ); + } + + static $i = 0; + $this->tempDir = $this->createTempDir( 'ezcGraphCairoDriverTest' . sprintf( '_%03d_', ++$i ) ) . '/'; + $this->basePath = dirname( __FILE__ ) . '/data/'; + + $this->driver = new ezcGraphCairoOODriver(); + $this->driver->options->width = 200; + $this->driver->options->height = 100; + $this->driver->options->font->path = $this->basePath . 'font.ttf'; + } + + protected function tearDown() + { + unset( $this->driver ); + if ( !$this->hasFailed() ) + { + $this->removeTempDir(); + } + } + + public function testRenderPngToOutput() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawLine( + new ezcGraphCoordinate( 12, 45 ), + new ezcGraphCoordinate( 134, 12 ), + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->assertEquals( + $this->driver->getMimeType(), + 'image/png', + 'Wrong mime type returned.' + ); + + ob_start(); + // Suppress header already sent warning + @$this->driver->renderToOutput(); + file_put_contents( $filename, ob_get_clean() ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testGetResource() + { + $this->driver->drawLine( + new ezcGraphCoordinate( 12, 45 ), + new ezcGraphCoordinate( 134, 12 ), + ezcGraphColor::fromHex( '#3465A4' ) + ); + + ob_start(); + // Suppress header already sent warning + @$this->driver->renderToOutput(); + ob_end_clean(); + + $resource = $this->driver->getResource(); + $this->assertTrue( $resource['surface'] instanceof CairoImageSurface ); + $this->assertTrue( $resource['context'] instanceof CairoContext ); + } + + public function testDrawLine() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawLine( + new ezcGraphCoordinate( 12, 45 ), + new ezcGraphCoordinate( 134, 12 ), + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawLineWithDifferentWidths() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawLine( + new ezcGraphCoordinate( 12, 45 ), + new ezcGraphCoordinate( 134, 12 ), + ezcGraphColor::fromHex( '#3465A4' ), + 3 + ); + + $this->driver->drawLine( + new ezcGraphCoordinate( 12, 35 ), + new ezcGraphCoordinate( 134, 2 ), + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawPolygonThreePointsFilled() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $return = $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( 45, 12 ), + new ezcGraphCoordinate( 122, 34 ), + new ezcGraphCoordinate( 12, 71 ), + ), + ezcGraphColor::fromHex( '#3465A4' ), + true + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + + $this->assertEquals( + $return, + array( + new ezcGraphCoordinate( 45, 12 ), + new ezcGraphCoordinate( 122, 34 ), + new ezcGraphCoordinate( 12, 71 ), + ), + 'Expected point array as return value.' + ); + } + + public function testDrawTransparentPolygon() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( 45, 12 ), + new ezcGraphCoordinate( 122, 34 ), + new ezcGraphCoordinate( 12, 71 ), + ), + ezcGraphColor::fromHex( '#3465A47F' ), + true + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawPolygonThreePointsNotFilled() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( 45, 12 ), + new ezcGraphCoordinate( 122, 34 ), + new ezcGraphCoordinate( 12, 71 ), + ), + ezcGraphColor::fromHex( '#3465A4' ), + false + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawPolygonFivePoints() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( 45, 12 ), + new ezcGraphCoordinate( 122, 34 ), + new ezcGraphCoordinate( 12, 71 ), + new ezcGraphCoordinate( 3, 45 ), + new ezcGraphCoordinate( 60, 32 ), + ), + ezcGraphColor::fromHex( '#3465A4' ), + true + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircleSectorAcute() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $return = $this->driver->drawCircleSector( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + 12.5, + 25, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + + $this->assertEquals( + $return, + array( + new ezcGraphCoordinate( 100., 50. ), + new ezcGraphCoordinate( 139., 54. ), + new ezcGraphCoordinate( 137., 58. ), + new ezcGraphCoordinate( 136., 58.5 ), + ), + 'Expected point array as return value.', + 1. + ); + } + + public function testDrawCircleSectorAcuteNonFilled() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircleSector( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + 12.5, + 45, + ezcGraphColor::fromHex( '#3465A4' ), + false + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircleSectorAcuteReverse() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircleSector( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + 25, + 12.5, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircleSectorObtuse() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircleSector( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + 25, + 273, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircularArcAcute() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->imageMapResolution = 90; + $return = $this->driver->drawCircularArc( + new ezcGraphCoordinate( 100, 50 ), + 150, + 80, + 10, + 12.5, + 55, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + + $this->assertEquals( + $return, + array( + new ezcGraphCoordinate( 173., 59. ), + new ezcGraphCoordinate( 143., 83. ), + new ezcGraphCoordinate( 153., 83. ), + new ezcGraphCoordinate( 183., 59. ), + ), + 'Expected point array as return value.', + 1. + ); + } + + public function testDrawCircularArcAcuteReverse() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircularArc( + new ezcGraphCoordinate( 100, 50 ), + 150, + 80, + 10, + 55, + 12.5, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircularArcObtuse() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircularArc( + new ezcGraphCoordinate( 100, 50 ), + 150, + 70, + 10, + 25, + 300, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircularArcAcuteBorder() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircularArc( + new ezcGraphCoordinate( 100, 50 ), + 150, + 80, + 10, + 12.5, + 55, + ezcGraphColor::fromHex( '#3465A4' ), + false + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 0 + ); + } + + public function testDrawCircleFilled() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->imageMapResolution = 90; + $return = $this->driver->drawCircle( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + ezcGraphColor::fromHex( '#3465A4' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + + $this->assertEquals( + $return, + array( + new ezcGraphCoordinate( 140., 50. ), + new ezcGraphCoordinate( 100., 70. ), + new ezcGraphCoordinate( 60., 50. ), + new ezcGraphCoordinate( 100., 30. ), + ), + 'Expected point array as return value.', + 1. + ); + } + + public function testDrawCircleNonFilled() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawCircle( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + ezcGraphColor::fromHex( '#3465A4' ), + false + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawImagePng() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawImage( + $this->basePath . $this->testFiles['png'], + new ezcGraphCoordinate( 10, 10 ), + 100, + 50 + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircleRadialFill() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $return = $this->driver->drawCircle( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + new ezcGraphRadialGradient( + new ezcGraphCoordinate( 80, 40), + 80, + 40, + ezcGraphColor::fromHex( '#729FCF' ), + ezcGraphColor::fromHex( '#3465A4' ) + ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawCircleLinearFill() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $return = $this->driver->drawCircle( + new ezcGraphCoordinate( 100, 50 ), + 80, + 40, + new ezcGraphLinearGradient( + $start = new ezcGraphCoordinate( 80, 40 ), + $end = new ezcGraphCoordinate( 130, 55 ), + ezcGraphColor::fromHex( '#82BFFF' ), + ezcGraphColor::fromHex( '#3465A4' ) + ) + ); + + $this->driver->drawCircle( + $start, + 2, 2, ezcGraphColor::fromHex( '#CC0000' ) + ); + $this->driver->drawCircle( + $end, + 2, 2, ezcGraphColor::fromHex( '#CC0000' ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxShortString() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $return = $this->driver->drawTextBox( + 'Short', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + + $this->assertEquals( + $return, + array( + new ezcGraphCoordinate( 10., 10. ), + new ezcGraphCoordinate( 160., 10. ), + new ezcGraphCoordinate( 160., 80. ), + new ezcGraphCoordinate( 10., 80. ), + ), + 'Expected point array as return value.', + 1. + ); + } + + public function testDrawTextBoxShortStringRotated10Degrees() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $return = $this->driver->drawTextBox( + 'Short', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT, + new ezcGraphRotation( 10 ) + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxShortStringRotated45Degrees() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $return = $this->driver->drawTextBox( + 'Short', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT, + new ezcGraphRotation( 45, new ezcGraphCoordinate( 100, 50 ) ) + ); + + $this->driver->render( $filename ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxShortStringRotated340Degrees() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $return = $this->driver->drawTextBox( + 'Short', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT, + new ezcGraphRotation( 340, new ezcGraphCoordinate( 200, 100 ) ) + ); + + $this->driver->render( $filename ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxLongString() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'ThisIsAPrettyLongString', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxLongSpacedString() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'This Is A Pretty Long String', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxManualBreak() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + "New\nLine", + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxStringSampleRight() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( 20, 20 ), + new ezcGraphCoordinate( 110, 20 ), + new ezcGraphCoordinate( 110, 30 ), + new ezcGraphCoordinate( 20, 30 ), + ), + ezcGraphColor::fromHex( '#eeeeec' ), + true + ); + $this->driver->drawTextBox( + 'sample 4', + new ezcGraphCoordinate( 21, 21 ), + 88, + 8, + ezcGraph::RIGHT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxStringRight() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'ThisIsAPrettyLongString', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::RIGHT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxLongSpacedStringRight() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'This Is A Pretty Long String', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::RIGHT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxStringCenter() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'ThisIsAPrettyLongString', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::CENTER + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxLongSpacedStringCenter() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'This Is A Pretty Long String', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::CENTER + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxStringRightBottom() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'ThisIsAPrettyLongString', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::RIGHT | ezcGraph::BOTTOM + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxLongSpacedStringRightMiddle() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'This Is A Pretty Long String', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::RIGHT | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxStringCenterMiddle() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'ThisIsAPrettyLongString', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::CENTER | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextBoxLongSpacedStringCenterBottom() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'This Is A Pretty Long String', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::CENTER | ezcGraph::BOTTOM + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawSmallStringWithSpecialChars() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawPolygon( + array( + new ezcGraphCoordinate( 47, 54 ), + new ezcGraphCoordinate( 47, 84 ), + new ezcGraphCoordinate( 99, 84 ), + new ezcGraphCoordinate( 99, 54 ), + ), + ezcGraphColor::fromHex( '#DDDDDD' ), + true + ); + $this->driver->drawTextBox( + 'Safari (13.8%)', + new ezcGraphCoordinate( 47, 54 ), + 52, + 30, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 10 + ); + } + + public function testDrawTooLongTextException() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + try + { + $this->driver->drawTextBox( + 'This is very long text which is not supposed to fit in the bounding box.', + new ezcGraphCoordinate( 10, 10 ), + 1, + 20, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + } + catch ( ezcGraphFontRenderingException $e ) + { + return true; + } + + $this->fail( 'Expected ezcGraphFontRenderingException.' ); + } + + public function testDrawTextWithTextShadow() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->textShadow = true; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextWithCustomTextShadow() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->textShadow = true; + $this->driver->options->font->textShadowColor = '#888888'; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextWithBackground() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = false; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextWithBorder() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->minimizeBorder = false; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextWithMinimizedBorderAndBackgroundTopLeft() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT | ezcGraph::TOP + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawRotatedTextWithMinimizedBorderAndBackgroundTopLeft() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::LEFT | ezcGraph::TOP, + new ezcGraphRotation( 15 ) + ); + + $this->driver->render( $filename ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextWithMinimizedBorderAndBackgroundMiddleCenter() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::CENTER | ezcGraph::MIDDLE + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testDrawTextWithMinimizedBorderAndBackgroundBottomRight() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->options->font->border = ezcGraphColor::fromHex( '#555555' ); + $this->driver->options->font->background = ezcGraphColor::fromHex( '#DDDDDD' ); + $this->driver->options->font->minimizeBorder = true; + $this->driver->options->font->padding = 2; + + $this->driver->drawTextBox( + 'Some test string', + new ezcGraphCoordinate( 10, 10 ), + 150, + 70, + ezcGraph::RIGHT | ezcGraph::BOTTOM + ); + + $this->driver->render( $filename ); + + $this->assertTrue( + file_exists( $filename ), + 'No image was generated.' + ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } + + public function testShortenStringMoreChars() + { + $filename = $this->tempDir . __FUNCTION__ . '.png'; + + $this->driver->drawTextBox( + 'Teststring foo', + new ezcGraphCoordinate( 10, 10 ), + 24, + 6, + ezcGraph::LEFT + ); + + $this->driver->render( $filename ); + + $this->assertImageSimilar( + $filename, + $this->basePath . 'compare/' . 'ezcGraphCairoDriverTest' . '_' . __FUNCTION__ . '.png', + 'Image does not look as expected.', + 2000 + ); + } +} +?> |