[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/ezc/Graph/src/driver/ -> cairo_oo.php (source)

   1  <?php
   2  /**
   3   * File containing the ezcGraphCairoOODriver class
   4   *
   5   * @package Graph
   6   * @version 1.5
   7   * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
   8   * @license http://ez.no/licenses/new_bsd New BSD License
   9   */
  10  
  11  /**
  12   * Extension of the basic driver package to utilize the cairo library.
  13   *
  14   * This drivers options are defined in the class 
  15   * {@link ezcGraphCairoDriverOptions} extending the basic driver options class
  16   * {@link ezcGraphDriverOptions}. 
  17   *
  18   * As this is the default driver you do not need to explicitely set anything to
  19   * use it, but may use some of its advanced features.
  20   *
  21   * <code>
  22   *   $graph = new ezcGraphPieChart();
  23   *   $graph->background->color = '#FFFFFFFF';
  24   *   $graph->title = 'Access statistics';
  25   *   $graph->legend = false;
  26   *   
  27   *   $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
  28   *       'Mozilla' => 19113,
  29   *       'Explorer' => 10917,
  30   *       'Opera' => 1464,
  31   *       'Safari' => 652,
  32   *       'Konqueror' => 474,
  33   *   ) );
  34   *   
  35   *   $graph->renderer = new ezcGraphRenderer3d();
  36   *   $graph->renderer->options->pieChartShadowSize = 10;
  37   *   $graph->renderer->options->pieChartGleam = .5;
  38   *   $graph->renderer->options->dataBorder = false;
  39   *   $graph->renderer->options->pieChartHeight = 16;
  40   *   $graph->renderer->options->legendSymbolGleam = .5;
  41   * 
  42   *   // Use cairo driver
  43   *   $graph->driver = new ezcGraphCairoOODriver();
  44   *   
  45   *   $graph->render( 400, 200, 'tutorial_driver_cairo.png' );
  46   * </code>
  47   *
  48   * @version 1.5
  49   * @package Graph
  50   * @mainclass
  51   */
  52  class ezcGraphCairoOODriver extends ezcGraphDriver
  53  {
  54      /**
  55       * Surface for cairo
  56       * 
  57       * @var CairoImageSurface
  58       */
  59      protected $surface;
  60  
  61      /**
  62       * Current cairo context.
  63       * 
  64       * @var CairoContext
  65       */
  66      protected $context;
  67  
  68      /**
  69       * List of strings to draw
  70       * array ( array(
  71       *          'text' => array( 'strings' ),
  72       *          'options' => ezcGraphFontOptions,
  73       *      )
  74       * 
  75       * @var array
  76       */
  77      protected $strings = array();
  78  
  79      /**
  80       * Constructor
  81       * 
  82       * @param array $options Default option array
  83       * @return void
  84       * @ignore
  85       */
  86      public function __construct( array $options = array() )
  87      {
  88          ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'cairo' );
  89          $this->options = new ezcGraphCairoDriverOptions( $options );
  90      }
  91  
  92      /**
  93       * Initilize cairo surface
  94       *
  95       * Initilize cairo surface from values provided in the options object, if
  96       * is has not been already initlized.
  97       * 
  98       * @return void
  99       */
 100      protected function initiliazeSurface()
 101      {
 102          // Immediatly exit, if surface already exists
 103          if ( $this->surface !== null )
 104          {
 105              return;
 106          }
 107  
 108          $this->surface = new CairoImageSurface(
 109              CairoFormat::ARGB32, 
 110              $this->options->width, 
 111              $this->options->height
 112          );
 113  
 114          $this->context = new CairoContext( $this->surface );
 115          $this->context->setLineWidth( 1 );
 116      }
 117  
 118      /**
 119       * Get SVG style definition
 120       *
 121       * Returns a string with SVG style definitions created from color, 
 122       * fillstatus and line thickness.
 123       * 
 124       * @param ezcGraphColor $color Color
 125       * @param mixed $filled Filled
 126       * @param float $thickness Line thickness.
 127       * @return string Formatstring
 128       */
 129      protected function getStyle( ezcGraphColor $color, $filled = true, $thickness = 1. )
 130      {
 131          switch ( true )
 132          {
 133              case $color instanceof ezcGraphLinearGradient:
 134                  $pattern = new CairoLinearGradient(
 135                      $color->startPoint->x, $color->startPoint->y,
 136                      $color->endPoint->x, $color->endPoint->y
 137                  );
 138  
 139                  $pattern->addColorStopRgba(
 140                      0,
 141                      $color->startColor->red / 255,
 142                      $color->startColor->green / 255,
 143                      $color->startColor->blue / 255,
 144                      1 - $color->startColor->alpha / 255
 145                  );
 146  
 147                  $pattern->addColorStopRgba(
 148                      1,
 149                      $color->endColor->red / 255,
 150                      $color->endColor->green / 255,
 151                      $color->endColor->blue / 255,
 152                      1 - $color->endColor->alpha / 255
 153                  );
 154  
 155                  $this->context->setSource( $pattern );
 156                  $this->context->fill();
 157                  break;
 158  
 159              case $color instanceof ezcGraphRadialGradient:
 160                  $pattern = new CairoRadialGradient(
 161                      0, 0, 0,
 162                      0, 0, 1
 163                  );
 164  
 165                  $pattern->addColorStopRgba(
 166                      0,
 167                      $color->startColor->red / 255,
 168                      $color->startColor->green / 255,
 169                      $color->startColor->blue / 255,
 170                      1 - $color->startColor->alpha / 255
 171                  );
 172  
 173                  $pattern->addColorStopRgba(
 174                      1,
 175                      $color->endColor->red / 255,
 176                      $color->endColor->green / 255,
 177                      $color->endColor->blue / 255,
 178                      1 - $color->endColor->alpha / 255
 179                  );
 180  
 181                  // Scale pattern, and move it to the correct position
 182                  $matrix = CairoMatrix::multiply(
 183                      $move = CairoMatrix::initTranslate( -$color->center->x, -$color->center->y ),
 184                      $scale = CairoMatrix::initScale( 1 / $color->width, 1 / $color->height )
 185                  );
 186                  $pattern->setMatrix( $matrix );
 187  
 188                  $this->context->setSource( $pattern );
 189                  $this->context->fill();
 190                  break;
 191              default:
 192                  $this->context->setSourceRgba(
 193                      $color->red / 255,
 194                      $color->green / 255,
 195                      $color->blue / 255,
 196                      1 - $color->alpha / 255
 197                  );
 198                  break;
 199          }
 200  
 201          // Set line width
 202           $this->context->setLineWidth( $thickness );
 203  
 204          // Set requested fill state for context
 205          if ( $filled )
 206          {
 207              $this->context->fillPreserve();
 208          }
 209      }
 210  
 211      /**
 212       * Draws a single polygon. 
 213       * 
 214       * @param array $points Point array
 215       * @param ezcGraphColor $color Polygon color
 216       * @param mixed $filled Filled
 217       * @param float $thickness Line thickness
 218       * @return void
 219       */
 220      public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
 221      {
 222          $this->initiliazeSurface();
 223  
 224          $this->context->newPath();
 225  
 226          $lastPoint = end( $points );
 227          $this->context->moveTo( $lastPoint->x, $lastPoint->y );
 228  
 229          foreach ( $points as $point )
 230          {
 231              $this->context->lineTo( $point->x, $point->y );
 232          }
 233  
 234          $this->context->closePath();
 235  
 236          $this->getStyle( $color, $filled, $thickness );
 237          $this->context->stroke();
 238  
 239          return $points;
 240      }
 241      
 242      /**
 243       * Draws a line 
 244       * 
 245       * @param ezcGraphCoordinate $start Start point
 246       * @param ezcGraphCoordinate $end End point
 247       * @param ezcGraphColor $color Line color
 248       * @param float $thickness Line thickness
 249       * @return void
 250       */
 251      public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
 252      {
 253          $this->initiliazeSurface();
 254  
 255          $this->context->newPath();
 256  
 257          $this->context->moveTo( $start->x, $start->y );
 258          $this->context->lineTo( $end->x, $end->y );
 259  
 260          $this->getStyle( $color, false, $thickness );
 261          $this->context->stroke();
 262  
 263          return array( $start, $end );
 264      }
 265  
 266      /**
 267       * Returns boundings of text depending on the available font extension
 268       * 
 269       * @param float $size Textsize
 270       * @param ezcGraphFontOptions $font Font
 271       * @param string $text Text
 272       * @return ezcGraphBoundings Boundings of text
 273       */
 274      protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
 275      {
 276          $this->context->selectFontFace( $font->name, CairoFontSlant::NORMAL, CairoFontWeight::NORMAL );
 277          $this->context->setFontSize( $size );
 278          $extents = $this->context->textExtents( $text );
 279  
 280          return new ezcGraphBoundings(
 281              0,
 282              0,
 283              $extents['width'],
 284              $extents['height']
 285          );
 286      }
 287  
 288      /**
 289       * Writes text in a box of desired size
 290       * 
 291       * @param string $string Text
 292       * @param ezcGraphCoordinate $position Top left position
 293       * @param float $width Width of text box
 294       * @param float $height Height of text box
 295       * @param int $align Alignement of text
 296       * @param ezcGraphRotation $rotation
 297       * @return void
 298       */
 299      public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
 300      {
 301          $this->initiliazeSurface();
 302  
 303          $padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 );
 304  
 305          $width -= $padding * 2;
 306          $height -= $padding * 2;
 307          $textPosition = new ezcGraphCoordinate(
 308              $position->x + $padding,
 309              $position->y + $padding
 310          );
 311  
 312          // Try to get a font size for the text to fit into the box
 313          $maxSize = min( $height, $this->options->font->maxFontSize );
 314          $result = false;
 315          for ( $size = $maxSize; $size >= $this->options->font->minFontSize; )
 316          {
 317              $result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size );
 318              if ( is_array( $result ) )
 319              {
 320                  break;
 321              }
 322              $size = ( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : floor( $newsize ) );
 323          }
 324          
 325          if ( !is_array( $result ) )
 326          {
 327              if ( ( $height >= $this->options->font->minFontSize ) &&
 328                   ( $this->options->autoShortenString ) )
 329              {
 330                  $result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->options->font->minFontSize );
 331              } 
 332              else
 333              {
 334                  throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height );
 335              }
 336          }
 337  
 338          $this->options->font->minimalUsedFont = $size;
 339          $this->strings[] = array(
 340              'text' => $result,
 341              'position' => $textPosition,
 342              'width' => $width,
 343              'height' => $height,
 344              'align' => $align,
 345              'font' => $this->options->font,
 346              'rotation' => $rotation,
 347          );
 348  
 349          return array(
 350              clone $position,
 351              new ezcGraphCoordinate( $position->x + $width, $position->y ),
 352              new ezcGraphCoordinate( $position->x + $width, $position->y + $height ),
 353              new ezcGraphCoordinate( $position->x, $position->y + $height ),
 354          );
 355      }
 356      
 357      /**
 358       * Render text depending of font type and available font extensions
 359       * 
 360       * @param string $id 
 361       * @param string $text 
 362       * @param string $font 
 363       * @param ezcGraphColor $color 
 364       * @param ezcGraphCoordinate $position 
 365       * @param float $size 
 366       * @param float $rotation 
 367       * @return void
 368       */
 369      protected function renderText( $text, $font, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null )
 370      {
 371          $this->context->selectFontFace( $font, CairoFontSlant::NORMAL, CairoFontWeight::NORMAL );
 372          $this->context->setFontSize( $size );
 373          
 374          // Store current state of context
 375          $this->context->save();
 376          $this->context->moveTo( 0, 0 );
 377  
 378          if ( $rotation !== null )
 379          {
 380              // Move to the center
 381              $this->context->translate(
 382                  $rotation->getCenter()->x, 
 383                  $rotation->getCenter()->y
 384              );
 385              // Rotate around text center
 386              $this->context->rotate(
 387                  deg2rad( $rotation->getRotation() ) 
 388              );
 389              // Center the text
 390              $this->context->translate(
 391                  $position->x - $rotation->getCenter()->x,
 392                  $position->y - $rotation->getCenter()->y - $size * .15
 393              );
 394          } else {
 395              $this->context->translate(
 396                  $position->x,
 397                  $position->y - $size * .15
 398              );
 399          }
 400  
 401          $this->context->newPath();
 402          $this->getStyle( $color, true );
 403          $this->context->showText( $text );
 404          $this->context->stroke();
 405  
 406          // Restore state of context
 407          $this->context->restore();
 408      }
 409  
 410      /**
 411       * Draw all collected texts
 412       *
 413       * The texts are collected and their maximum possible font size is 
 414       * calculated. This function finally draws the texts on the image, this
 415       * delayed drawing has two reasons:
 416       *
 417       * 1) This way the text strings are always on top of the image, what 
 418       *    results in better readable texts
 419       * 2) The maximum possible font size can be calculated for a set of texts
 420       *    with the same font configuration. Strings belonging to one chart 
 421       *    element normally have the same font configuration, so that all texts
 422       *    belonging to one element will have the same font size.
 423       * 
 424       * @access protected
 425       * @return void
 426       */
 427      protected function drawAllTexts()
 428      {
 429          $this->initiliazeSurface();
 430  
 431          foreach ( $this->strings as $text )
 432          {
 433              $size = $text['font']->minimalUsedFont;
 434  
 435              $completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing;
 436  
 437              // Calculate y offset for vertical alignement
 438              switch ( true )
 439              {
 440                  case ( $text['align'] & ezcGraph::BOTTOM ):
 441                      $yOffset = $text['height'] - $completeHeight;
 442                      break;
 443                  case ( $text['align'] & ezcGraph::MIDDLE ):
 444                      $yOffset = ( $text['height'] - $completeHeight ) / 2;
 445                      break;
 446                  case ( $text['align'] & ezcGraph::TOP ):
 447                  default:
 448                      $yOffset = 0;
 449                      break;
 450              }
 451  
 452              $padding = $text['font']->padding + $text['font']->borderWidth / 2;
 453              if ( $this->options->font->minimizeBorder === true )
 454              {
 455                  // Calculate maximum width of text rows
 456                  $width = false;
 457                  foreach ( $text['text'] as $line )
 458                  {
 459                      $string = implode( ' ', $line );
 460                      $boundings = $this->getTextBoundings( $size, $text['font'], $string );
 461                      if ( ( $width === false) || ( $boundings->width > $width ) )
 462                      {
 463                          $width = $boundings->width;
 464                      }
 465                  }
 466  
 467                  switch ( true )
 468                  {
 469                      case ( $text['align'] & ezcGraph::CENTER ):
 470                          $xOffset = ( $text['width'] - $width ) / 2;
 471                          break;
 472                      case ( $text['align'] & ezcGraph::RIGHT ):
 473                          $xOffset = $text['width'] - $width;
 474                          break;
 475                      case ( $text['align'] & ezcGraph::LEFT ):
 476                      default:
 477                          $xOffset = 0;
 478                          break;
 479                  }
 480  
 481                  $borderPolygonArray = array(
 482                      new ezcGraphCoordinate(
 483                          $text['position']->x - $padding + $xOffset,
 484                          $text['position']->y - $padding + $yOffset
 485                      ),
 486                      new ezcGraphCoordinate(
 487                          $text['position']->x + $padding * 2 + $xOffset + $width,
 488                          $text['position']->y - $padding + $yOffset
 489                      ),
 490                      new ezcGraphCoordinate(
 491                          $text['position']->x + $padding * 2 + $xOffset + $width,
 492                          $text['position']->y + $padding * 2 + $yOffset + $completeHeight
 493                      ),
 494                      new ezcGraphCoordinate(
 495                          $text['position']->x - $padding + $xOffset,
 496                          $text['position']->y + $padding * 2 + $yOffset + $completeHeight
 497                      ),
 498                  );
 499              }
 500              else
 501              {
 502                  $borderPolygonArray = array(
 503                      new ezcGraphCoordinate(
 504                          $text['position']->x - $padding,
 505                          $text['position']->y - $padding
 506                      ),
 507                      new ezcGraphCoordinate(
 508                          $text['position']->x + $padding * 2 + $text['width'],
 509                          $text['position']->y - $padding
 510                      ),
 511                      new ezcGraphCoordinate(
 512                          $text['position']->x + $padding * 2 + $text['width'],
 513                          $text['position']->y + $padding * 2 + $text['height']
 514                      ),
 515                      new ezcGraphCoordinate(
 516                          $text['position']->x - $padding,
 517                          $text['position']->y + $padding * 2 + $text['height']
 518                      ),
 519                  );
 520              }
 521  
 522              if ( $text['rotation'] !==  null )
 523              {
 524                  foreach ( $borderPolygonArray as $nr => $point )
 525                  {
 526                      $borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point );
 527                  }
 528              }
 529  
 530              if ( $text['font']->background !== false )
 531              {
 532                  $this->drawPolygon( 
 533                      $borderPolygonArray, 
 534                      $text['font']->background,
 535                      true
 536                  );
 537              }
 538  
 539              if ( $text['font']->border !== false )
 540              {
 541                  $this->drawPolygon( 
 542                      $borderPolygonArray, 
 543                      $text['font']->border,
 544                      false,
 545                      $text['font']->borderWidth
 546                  );
 547              }
 548  
 549              // Render text with evaluated font size
 550              $completeString = '';
 551              foreach ( $text['text'] as $line )
 552              {
 553                  $string = implode( ' ', $line );
 554                  $completeString .= $string;
 555                  $boundings = $this->getTextBoundings( $size, $text['font'], $string );
 556                  $text['position']->y += $size;
 557  
 558                  switch ( true )
 559                  {
 560                      case ( $text['align'] & ezcGraph::LEFT ):
 561                          $position = new ezcGraphCoordinate( 
 562                              $text['position']->x, 
 563                              $text['position']->y + $yOffset
 564                          );
 565                          break;
 566                      case ( $text['align'] & ezcGraph::RIGHT ):
 567                          $position = new ezcGraphCoordinate( 
 568                              $text['position']->x + ( $text['width'] - $boundings->width ), 
 569                              $text['position']->y + $yOffset
 570                          );
 571                          break;
 572                      case ( $text['align'] & ezcGraph::CENTER ):
 573                          $position = new ezcGraphCoordinate( 
 574                              $text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ), 
 575                              $text['position']->y + $yOffset
 576                          );
 577                          break;
 578                  }
 579  
 580                  // Optionally draw text shadow
 581                  if ( $text['font']->textShadow === true )
 582                  {
 583                      $this->renderText( 
 584                          $string,
 585                          $text['font']->name, 
 586                          $text['font']->textShadowColor,
 587                          new ezcGraphCoordinate(
 588                              $position->x + $text['font']->textShadowOffset,
 589                              $position->y + $text['font']->textShadowOffset
 590                          ),
 591                          $size,
 592                          $text['rotation']
 593                      );
 594                  }
 595                  
 596                  // Finally draw text
 597                  $this->renderText( 
 598                      $string,
 599                      $text['font']->name, 
 600                      $text['font']->color, 
 601                      $position,
 602                      $size,
 603                      $text['rotation']
 604                  );
 605  
 606                  $text['position']->y += $size * $this->options->lineSpacing;
 607              }
 608          }
 609      }
 610  
 611      /**
 612       * Draws a sector of cirlce
 613       * 
 614       * @param ezcGraphCoordinate $center Center of circle
 615       * @param mixed $width Width
 616       * @param mixed $height Height
 617       * @param mixed $startAngle Start angle of circle sector
 618       * @param mixed $endAngle End angle of circle sector
 619       * @param ezcGraphColor $color Color
 620       * @param mixed $filled Filled;
 621       * @return void
 622       */
 623      public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
 624      {
 625          $this->initiliazeSurface();
 626  
 627          // Normalize angles
 628          if ( $startAngle > $endAngle )
 629          {
 630              $tmp = $startAngle;
 631              $startAngle = $endAngle;
 632              $endAngle = $tmp;
 633          }
 634          
 635          $this->context->save();
 636          
 637          // Draw circular arc path
 638          $this->context->newPath();
 639          $this->context->translate(
 640              $center->x,
 641              $center->y
 642          );
 643          $this->context->scale(
 644              1, $height / $width
 645          );
 646  
 647          $this->context->moveTo( 0, 0 );
 648          $this->context->arc(
 649              0., 0., 
 650              $width / 2, 
 651              deg2rad( $startAngle ),
 652              deg2rad( $endAngle )
 653          );
 654          $this->context->lineTo( 0, 0 );
 655  
 656          $this->context->restore();
 657          $this->getStyle( $color, $filled );
 658          $this->context->stroke();
 659  
 660          // Create polygon array to return
 661          $polygonArray = array( $center );
 662          for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution )
 663          {
 664              $polygonArray[] = new ezcGraphCoordinate(
 665                  $center->x + 
 666                      ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
 667                  $center->y + 
 668                      ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
 669              );
 670          }
 671          $polygonArray[] = new ezcGraphCoordinate(
 672              $center->x + 
 673                  ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
 674              $center->y + 
 675                  ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
 676          );
 677  
 678          return $polygonArray;
 679      }
 680  
 681      /**
 682       * Draws a circular arc consisting of several minor steps on the bounding 
 683       * lines.
 684       * 
 685       * @param ezcGraphCoordinate $center 
 686       * @param mixed $width 
 687       * @param mixed $height 
 688       * @param mixed $size 
 689       * @param mixed $startAngle 
 690       * @param mixed $endAngle 
 691       * @param ezcGraphColor $color 
 692       * @param bool $filled 
 693       * @return string Element id
 694       */
 695      protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled )
 696      {
 697          for ( 
 698              $tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle ); 
 699              $tmpAngle <= $endAngle; 
 700              $tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) )
 701          {
 702              $this->context->newPath();
 703              $this->context->moveTo(
 704                  $center->x + cos( deg2rad( $startAngle ) ) * $width / 2, 
 705                  $center->y + sin( deg2rad( $startAngle ) ) * $height / 2
 706              );
 707  
 708              // @TODO: Use cairo_curve_to()
 709              for(
 710                  $angle = $startAngle;
 711                  $angle <= $tmpAngle;
 712                  $angle = min( $angle + $this->options->circleResolution, $tmpAngle ) )
 713              {
 714                  $this->context->lineTo(
 715                      $center->x + cos( deg2rad( $angle ) ) * $width / 2, 
 716                      $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size
 717                  );
 718  
 719                  if ( $angle === $tmpAngle )
 720                  {
 721                      break;
 722                  }
 723              }
 724  
 725              for(
 726                  $angle = $tmpAngle;
 727                  $angle >= $startAngle;
 728                  $angle = max( $angle - $this->options->circleResolution, $startAngle ) )
 729              {
 730                  $this->context->lineTo(
 731                      $center->x + cos( deg2rad( $angle ) ) * $width / 2, 
 732                      $center->y + sin( deg2rad( $angle ) ) * $height / 2
 733                  );
 734  
 735                  if ( $angle === $startAngle )
 736                  {
 737                      break;
 738                  }
 739              }
 740  
 741              $this->context->closePath();
 742              $this->getStyle( $color, $filled );
 743              $this->context->stroke( );
 744  
 745              $startAngle = $tmpAngle;
 746              if ( $tmpAngle === $endAngle ) 
 747              {
 748                  break;
 749              }
 750          }
 751      }
 752  
 753      /**
 754       * Draws a circular arc
 755       * 
 756       * @param ezcGraphCoordinate $center Center of ellipse
 757       * @param integer $width Width of ellipse
 758       * @param integer $height Height of ellipse
 759       * @param integer $size Height of border
 760       * @param float $startAngle Starting angle of circle sector
 761       * @param float $endAngle Ending angle of circle sector
 762       * @param ezcGraphColor $color Color of Border
 763       * @param bool $filled
 764       * @return void
 765       */
 766      public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
 767      {
 768          $this->initiliazeSurface();
 769  
 770          // Normalize angles
 771          if ( $startAngle > $endAngle )
 772          {
 773              $tmp = $startAngle;
 774              $startAngle = $endAngle;
 775              $endAngle = $tmp;
 776          }
 777  
 778          $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled );
 779  
 780          if ( ( $this->options->shadeCircularArc !== false ) &&
 781               $filled )
 782          {
 783              $gradient = new ezcGraphLinearGradient(
 784                  new ezcGraphCoordinate(
 785                      $center->x - $width,
 786                      $center->y
 787                  ),
 788                  new ezcGraphCoordinate(
 789                      $center->x + $width,
 790                      $center->y
 791                  ),
 792                  ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ),
 793                  ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 )
 794              );
 795          
 796              $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled );
 797          }
 798  
 799          // Create polygon array to return
 800          $polygonArray = array();
 801          for ( $angle = $startAngle; $angle < $endAngle; $angle += $this->options->imageMapResolution )
 802          {
 803              $polygonArray[] = new ezcGraphCoordinate(
 804                  $center->x + 
 805                      ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ),
 806                  $center->y + 
 807                      ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
 808              );
 809          }
 810          $polygonArray[] = new ezcGraphCoordinate(
 811              $center->x + 
 812                  ( ( cos( deg2rad( $endAngle ) ) * $width ) / 2 ),
 813              $center->y + 
 814                  ( ( sin( deg2rad( $endAngle ) ) * $height ) / 2 )
 815          );
 816  
 817          for ( $angle = $endAngle; $angle > $startAngle; $angle -= $this->options->imageMapResolution )
 818          {
 819              $polygonArray[] = new ezcGraphCoordinate(
 820                  $center->x + 
 821                      ( ( cos( deg2rad( $angle ) ) * $width ) / 2 ) + $size,
 822                  $center->y + 
 823                      ( ( sin( deg2rad( $angle ) ) * $height ) / 2 )
 824              );
 825          }
 826          $polygonArray[] = new ezcGraphCoordinate(
 827              $center->x + 
 828                  ( ( cos( deg2rad( $startAngle ) ) * $width ) / 2 ) + $size,
 829              $center->y + 
 830                  ( ( sin( deg2rad( $startAngle ) ) * $height ) / 2 )
 831          );
 832  
 833          return $polygonArray;
 834      }
 835  
 836      /**
 837       * Draw circle 
 838       * 
 839       * @param ezcGraphCoordinate $center Center of ellipse
 840       * @param mixed $width Width of ellipse
 841       * @param mixed $height height of ellipse
 842       * @param ezcGraphColor $color Color
 843       * @param mixed $filled Filled
 844       * @return void
 845       */
 846      public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
 847      {
 848          $this->initiliazeSurface();
 849          
 850          $this->context->save();
 851          
 852          // Draw circular arc path
 853          $this->context->newPath();
 854          $this->context->translate( 
 855              $center->x,
 856              $center->y
 857          );
 858          $this->context->scale( 
 859              1, $height / $width
 860          );
 861  
 862          $this->context->arc( 
 863              0., 0., 
 864              $width / 2, 
 865              0, 2 * M_PI
 866          );
 867  
 868          $this->context->restore();
 869          $this->getStyle( $color, $filled );
 870          $this->context->stroke();
 871  
 872          // Create polygon array to return
 873          $polygonArray = array();
 874          for ( $angle = 0; $angle < ( 2 * M_PI ); $angle += deg2rad( $this->options->imageMapResolution ) )
 875          {
 876              $polygonArray[] = new ezcGraphCoordinate(
 877                  $center->x + 
 878                      ( ( cos( $angle ) * $width ) / 2 ),
 879                  $center->y + 
 880                      ( ( sin( $angle ) * $height ) / 2 )
 881              );
 882          }
 883  
 884          return $polygonArray;
 885      }
 886  
 887      /**
 888       * Draw an image 
 889       *
 890       * The image will be inlined in the SVG document using data URL scheme. For
 891       * this the mime type and base64 encoded file content will be merged to 
 892       * URL.
 893       * 
 894       * @param mixed $file Image file
 895       * @param ezcGraphCoordinate $position Top left position
 896       * @param mixed $width Width of image in destination image
 897       * @param mixed $height Height of image in destination image
 898       * @return void
 899       */
 900      public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
 901      {
 902          $this->initiliazeSurface();
 903  
 904          // Ensure given bitmap is a PNG image
 905          $data = getimagesize( $file );
 906          if ( $data[2] !== IMAGETYPE_PNG )
 907          {
 908              throw new Exception( 'Cairo only has support for PNGs.' );
 909          }
 910  
 911          // Create new surface from given bitmap
 912          $imageSurface = CairoImageSurface::createFromPng( $file );
 913  
 914          // Create pattern from source image to be able to transform it
 915          $pattern = new CairoSurfacePattern( $imageSurface );
 916  
 917          // Scale pattern to defined dimensions and move it to its destination position
 918          $matrix = CairoMatrix::multiply(
 919              $move = CairoMatrix::initTranslate( -$position->x, -$position->y ),
 920              $scale = CairoMatrix::initScale( $data[0] / $width, $data[1] / $height )
 921          );
 922          $pattern->setMatrix( $matrix );
 923  
 924          // Merge surfaces
 925          $this->context->setSource( $pattern );
 926          $this->context->rectangle( $position->x, $position->y, $width, $height );
 927          $this->context->fill();
 928      }
 929  
 930      /**
 931       * Return mime type for current image format
 932       * 
 933       * @return string
 934       */
 935      public function getMimeType()
 936      {
 937          return 'image/png';
 938      }
 939  
 940      /**
 941       * Render image directly to output
 942       *
 943       * The method renders the image directly to the standard output. You 
 944       * normally do not want to use this function, because it makes it harder 
 945       * to proper cache the generated graphs.
 946       * 
 947       * @return void
 948       */
 949      public function renderToOutput()
 950      {
 951          $this->drawAllTexts();
 952  
 953          header( 'Content-Type: ' . $this->getMimeType() );
 954  
 955          // Write to tmp file, echo and remove tmp file again.
 956          $fileName = tempnam( '/tmp', 'ezc' );
 957  
 958          // cairo_surface_write_to_png( $this->surface, $file );
 959          $this->surface->writeToPng( $fileName );
 960          $contents = file_get_contents( $fileName );
 961          unlink( $fileName );
 962  
 963          // Directly echo contents
 964          echo $contents;
 965      }
 966  
 967      /**
 968       * Finally save image
 969       * 
 970       * @param string $file Destination filename
 971       * @return void
 972       */
 973      public function render( $file )
 974      {
 975          $this->drawAllTexts();
 976          $this->surface->writeToPng( $file );
 977      }
 978  
 979      /**
 980       * Get resource of rendered result
 981       *
 982       * Return the resource of the rendered result. You should not use this
 983       * method before you called either renderToOutput() or render(), as the
 984       * image may not be completely rendered until then.
 985       *
 986       * This method returns an array, containing the surface and the context in
 987       * a structure like:
 988       * <code>
 989       *  array(
 990       *      'surface' => resource,
 991       *      'context' => resource,
 992       *  )
 993       * </code>
 994       * 
 995       * @return array
 996       */
 997      public function getResource()
 998      {
 999          return array( 
1000              'surface' => $this->surface,
1001              'context' => $this->context,
1002          );
1003      }
1004  }
1005  
1006  ?>


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7