[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

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

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


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