| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
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 ?>
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 |