| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Jul 28 15:48:31 2011 | Cross-referenced by PHPXref 0.7 |