EZ-Components

This commit is contained in:
Andreas Österreicher
2009-02-17 15:27:43 +00:00
parent 359c3c55c9
commit 0a8e90a8b3
572 changed files with 64490 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,972 @@
<?php
/**
* File containing the ezcGraphFlashDriver class
*
* @package Graph
* @version 1.3
* @copyright Copyright (C) 2005-2008 eZ systems as. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Driver to create Flash4 (SWF) files as graph output. The options of this
* class are defined in The options of this class are defined in the option
* class {@link ezcGraphFlashDriverOptions} extending the basic
* {@link ezcGraphDriverOptions}.
*
* <code>
* $graph = new ezcGraphPieChart();
* $graph->title = 'Access statistics';
* $graph->legend = false;
*
* $graph->driver = new ezcGraphFlashDriver();
* $graph->options->font = 'tutorial_font.fdb';
*
* $graph->driver->options->compression = 7;
*
* $graph->data['Access statistics'] = new ezcGraphArrayDataSet( array(
* 'Mozilla' => 19113,
* 'Explorer' => 10917,
* 'Opera' => 1464,
* 'Safari' => 652,
* 'Konqueror' => 474,
* ) );
*
* $graph->render( 400, 200, 'tutorial_driver_flash.swf' );
* </code>
*
*
* @version 1.3
* @package Graph
* @mainclass
*/
class ezcGraphFlashDriver extends ezcGraphDriver
{
/**
* Flash movie
*
* @var SWFMovie
*/
protected $movie;
/**
* Unique element id
*
* @var int
*/
protected $id = 1;
/**
* Array with strings to draw later
*
* @var array
*/
protected $strings = array();
/**
* Constructor
*
* @param array $options Default option array
* @return void
* @ignore
*/
public function __construct( array $options = array() )
{
ezcBase::checkDependency( 'Graph', ezcBase::DEP_PHP_EXTENSION, 'ming' );
$this->options = new ezcGraphFlashDriverOptions( $options );
}
/**
* Returns unique movie object as a parent canvas for all swf objects.
*
* @return SWFMovie
*/
public function getDocument()
{
if ( $this->movie === null )
{
ming_setscale( 1.0 );
$this->movie = new SWFMovie();
$this->movie->setDimension( $this->modifyCoordinate( $this->options->width ), $this->modifyCoordinate( $this->options->height ) );
$this->movie->setRate( 1 );
$this->movie->setBackground( 255, 255, 255 );
}
return $this->movie;
}
/**
* Set the fill and line properties for a SWWFShape according to the
* given parameters.
*
* @param SWFShape $shape
* @param ezcGraphColor $color
* @param mixed $thickness
* @param mixed $filled
* @return void
*/
protected function setShapeColor( SWFShape $shape, ezcGraphColor $color, $thickness, $filled )
{
if ( $filled )
{
switch ( true )
{
case ( $color instanceof ezcGraphLinearGradient ):
$gradient = new SWFGradient();
$gradient->addEntry(
0,
$color->startColor->red,
$color->startColor->green,
$color->startColor->blue,
255 - $color->startColor->alpha
);
$gradient->addEntry(
1,
$color->endColor->red,
$color->endColor->green,
$color->endColor->blue,
255 - $color->endColor->alpha
);
$fill = $shape->addFill( $gradient, SWFFILL_LINEAR_GRADIENT );
// Calculate desired length of gradient
$length = sqrt(
pow( $color->endPoint->x - $color->startPoint->x, 2 ) +
pow( $color->endPoint->y - $color->startPoint->y, 2 )
);
$fill->scaleTo( $this->modifyCoordinate( $length ) / 32768 , $this->modifyCoordinate( $length ) / 32768 );
$fill->rotateTo(
rad2deg( asin(
( $color->endPoint->x - $color->startPoint->x ) / $length
) + 180 )
);
$fill->moveTo(
$this->modifyCoordinate(
( $color->startPoint->x + $color->endPoint->x ) / 2
),
$this->modifyCoordinate(
( $color->startPoint->y + $color->endPoint->y ) / 2
)
);
$shape->setLeftFill( $fill );
break;
case ( $color instanceof ezcGraphRadialGradient ):
$gradient = new SWFGradient();
$gradient->addEntry(
0,
$color->startColor->red,
$color->startColor->green,
$color->startColor->blue,
255 - $color->startColor->alpha
);
$gradient->addEntry(
1,
$color->endColor->red,
$color->endColor->green,
$color->endColor->blue,
255 - $color->endColor->alpha
);
$fill = $shape->addFill( $gradient, SWFFILL_RADIAL_GRADIENT );
$fill->scaleTo( $this->modifyCoordinate( $color->width ) / 32768, $this->modifyCoordinate( $color->height ) / 32768 );
$fill->moveTo( $this->modifyCoordinate( $color->center->x ), $this->modifyCoordinate( $color->center->y ) );
$shape->setLeftFill( $fill );
break;
default:
$fill = $shape->addFill( $color->red, $color->green, $color->blue, 255 - $color->alpha );
$shape->setLeftFill( $fill );
break;
}
}
else
{
$shape->setLine( $this->modifyCoordinate( $thickness ), $color->red, $color->green, $color->blue, 255 - $color->alpha );
}
}
/**
* Modifies a coordinate value, as flash usally uses twips instead of
* pixels for a higher solution, as it only accepts integer values.
*
* @param float $pointValue
* @return float
*/
protected function modifyCoordinate( $pointValue )
{
return $pointValue * 10;
}
/**
* Demodifies a coordinate value, as flash usally uses twips instead of
* pixels for a higher solution, as it only accepts integer values.
*
* @param float $pointValue
* @return float
*/
protected function deModifyCoordinate( $pointValue )
{
return $pointValue / 10;
}
/**
* Draws a single polygon.
*
* @param array $points Point array
* @param ezcGraphColor $color Polygon color
* @param mixed $filled Filled
* @param float $thickness Line thickness
* @return void
*/
public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
{
$movie = $this->getDocument();
if ( !$filled )
{
// The middle of the border is on the outline of a polygon in ming,
// fix that:
try
{
$points = $this->reducePolygonSize( $points, $thickness / 2 );
}
catch ( ezcGraphReducementFailedException $e )
{
return false;
}
}
$shape = new SWFShape();
$this->setShapeColor( $shape, $color, $thickness, $filled );
$lastPoint = end( $points );
$shape->movePenTo( $this->modifyCoordinate( $lastPoint->x ), $this->modifyCoordinate( $lastPoint->y ) );
foreach ( $points as $point )
{
$shape->drawLineTo( $this->modifyCoordinate( $point->x ), $this->modifyCoordinate( $point->y ) );
}
$object = $movie->add( $shape );
$object->setName( $id = 'ezcGraphPolygon_' . $this->id++ );
return $id;
}
/**
* Draws a line
*
* @param ezcGraphCoordinate $start Start point
* @param ezcGraphCoordinate $end End point
* @param ezcGraphColor $color Line color
* @param float $thickness Line thickness
* @return void
*/
public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
{
$movie = $this->getDocument();
$shape = new SWFShape();
$this->setShapeColor( $shape, $color, $thickness, false );
$shape->movePenTo( $this->modifyCoordinate( $start->x ), $this->modifyCoordinate( $start->y ) );
$shape->drawLineTo( $this->modifyCoordinate( $end->x ), $this->modifyCoordinate( $end->y ) );
$object = $movie->add( $shape );
$object->setName( $id = 'ezcGraphLine_' . $this->id++ );
return $id;
}
/**
* Returns boundings of text depending on the available font extension
*
* @param float $size Textsize
* @param ezcGraphFontOptions $font Font
* @param string $text Text
* @return ezcGraphBoundings Boundings of text
*/
protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
{
$t = new SWFText();
$t->setFont( new SWFFont( $font->path ) );
$t->setHeight( $size );
$boundings = new ezcGraphBoundings( 0, 0, $t->getWidth( $text ), $size );
return $boundings;
}
/**
* Writes text in a box of desired size
*
* @param string $string Text
* @param ezcGraphCoordinate $position Top left position
* @param float $width Width of text box
* @param float $height Height of text box
* @param int $align Alignement of text
* @param ezcGraphRotation $rotation
* @return void
*/
public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
{
$padding = $this->options->font->padding + ( $this->options->font->border !== false ? $this->options->font->borderWidth : 0 );
$width = $this->modifyCoordinate( $width - $padding * 2 );
$height = $this->modifyCoordinate( $height - $padding * 2 );
$position = new ezcGraphCoordinate(
$this->modifyCoordinate( $position->x + $padding ),
$this->modifyCoordinate( $position->y + $padding )
);
// Try to get a font size for the text to fit into the box
$maxSize = $this->modifyCoordinate( min( $height, $this->options->font->maxFontSize ) );
$minSize = $this->modifyCoordinate( $this->options->font->minFontSize );
$result = false;
for ( $size = $maxSize; $size >= $minSize; )
{
$result = $this->testFitStringInTextBox( $string, $position, $width, $height, $size );
if ( is_array( $result ) )
{
break;
}
$size = $this->deModifyCoordinate( $size );
$size = $this->modifyCoordinate( floor( ( $newsize = $size * ( $result ) ) >= $size ? $size - 1 : $newsize ) );
}
if ( !is_array( $result ) )
{
if ( ( $height >= $this->options->font->minFontSize ) &&
( $this->options->autoShortenString ) )
{
$result = $this->tryFitShortenedString( $string, $position, $width, $height, $size = $this->modifyCoordinate( $this->options->font->minFontSize ) );
}
else
{
throw new ezcGraphFontRenderingException( $string, $this->options->font->minFontSize, $width, $height );
}
}
$this->options->font->minimalUsedFont = $this->deModifyCoordinate( $size );
$this->strings[] = array(
'text' => $result,
'id' => $id = 'ezcGraphTextBox_' . $this->id++,
'position' => $position,
'width' => $width,
'height' => $height,
'align' => $align,
'font' => $this->options->font,
'rotation' => $rotation,
);
return $id;
}
/**
* Render text depending of font type and available font extensions
*
* @param string $id
* @param string $text
* @param string $chars
* @param int $type
* @param string $path
* @param ezcGraphColor $color
* @param ezcGraphCoordinate $position
* @param float $size
* @param float $rotation
* @return void
*/
protected function renderText( $id, $text, $chars, $type, $path, ezcGraphColor $color, ezcGraphCoordinate $position, $size, $rotation = null )
{
$movie = $this->getDocument();
$tb = new SWFTextField( SWFTEXTFIELD_NOEDIT );
$tb->setFont( new SWFFont( $path ) );
$tb->setHeight( $size );
$tb->setColor( $color->red, $color->green, $color->blue, 255 - $color->alpha );
$tb->addString( $text );
$tb->addChars( $chars );
$object = $movie->add( $tb );
$object->rotate(
( $rotation !== null ? -$rotation->getRotation() : 0 )
);
$object->moveTo(
$position->x +
( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 0, 2 ) ) ),
$position->y -
$size * ( 1 + $this->options->lineSpacing ) +
( $rotation === null ? 0 : $this->modifyCoordinate( $rotation->get( 1, 2 ) ) )
);
$object->setName( $id );
}
/**
* Draw all collected texts
*
* The texts are collected and their maximum possible font size is
* calculated. This function finally draws the texts on the image, this
* delayed drawing has two reasons:
*
* 1) This way the text strings are always on top of the image, what
* results in better readable texts
* 2) The maximum possible font size can be calculated for a set of texts
* with the same font configuration. Strings belonging to one chart
* element normally have the same font configuration, so that all texts
* belonging to one element will have the same font size.
*
* @access protected
* @return void
*/
protected function drawAllTexts()
{
// Iterate over all strings to collect used chars per font
$chars = array();
foreach ( $this->strings as $text )
{
$completeString = '';
foreach ( $text['text'] as $line )
{
$completeString .= implode( ' ', $line );
}
// Collect chars for each font
if ( !isset( $chars[$text['font']->path] ) )
{
$chars[$text['font']->path] = $completeString;
}
else
{
$chars[$text['font']->path] .= $completeString;
}
}
foreach ( $this->strings as $text )
{
$size = $this->modifyCoordinate( $text['font']->minimalUsedFont );
$completeHeight = count( $text['text'] ) * $size + ( count( $text['text'] ) - 1 ) * $this->options->lineSpacing;
// Calculate y offset for vertical alignement
switch ( true )
{
case ( $text['align'] & ezcGraph::BOTTOM ):
$yOffset = $text['height'] - $completeHeight;
break;
case ( $text['align'] & ezcGraph::MIDDLE ):
$yOffset = ( $text['height'] - $completeHeight ) / 2;
break;
case ( $text['align'] & ezcGraph::TOP ):
default:
$yOffset = 0;
break;
}
$padding = $text['font']->padding + $text['font']->borderWidth / 2;
if ( $this->options->font->minimizeBorder === true )
{
// Calculate maximum width of text rows
$width = false;
foreach ( $text['text'] as $line )
{
$string = implode( ' ', $line );
$boundings = $this->getTextBoundings( $size, $text['font'], $string );
if ( ( $width === false) || ( $boundings->width > $width ) )
{
$width = $boundings->width;
}
}
switch ( true )
{
case ( $text['align'] & ezcGraph::LEFT ):
$xOffset = 0;
break;
case ( $text['align'] & ezcGraph::CENTER ):
$xOffset = ( $text['width'] - $width ) / 2;
break;
case ( $text['align'] & ezcGraph::RIGHT ):
$xOffset = $text['width'] - $width;
break;
}
$borderPolygonArray = array(
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ),
$this->deModifyCoordinate( $text['position']->y - $padding + $yOffset )
),
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ),
$this->deModifyCoordinate( $text['position']->y - $padding + $yOffset )
),
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x + $padding * 2 + $xOffset + $width ),
$this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight )
),
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x - $padding + $xOffset ),
$this->deModifyCoordinate( $text['position']->y + $padding * 2 + $yOffset + $completeHeight )
),
);
}
else
{
$borderPolygonArray = array(
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x - $padding ),
$this->deModifyCoordinate( $text['position']->y - $padding )
),
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ),
$this->deModifyCoordinate( $text['position']->y - $padding )
),
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x + $padding * 2 + $text['width'] ),
$this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] )
),
new ezcGraphCoordinate(
$this->deModifyCoordinate( $text['position']->x - $padding ),
$this->deModifyCoordinate( $text['position']->y + $padding * 2 + $text['height'] )
),
);
}
if ( $text['rotation'] !== null )
{
foreach ( $borderPolygonArray as $nr => $point )
{
$borderPolygonArray[$nr] = $text['rotation']->transformCoordinate( $point );
}
}
if ( $text['font']->background !== false )
{
$this->drawPolygon(
$borderPolygonArray,
$text['font']->background,
true
);
}
if ( $text['font']->border !== false )
{
$this->drawPolygon(
$borderPolygonArray,
$text['font']->border,
false,
$text['font']->borderWidth
);
}
// Render text with evaluated font size
$completeString = '';
foreach ( $text['text'] as $line )
{
$string = implode( ' ', $line );
$completeString .= $string;
$boundings = $this->getTextBoundings( $size, $text['font'], $string );
$text['position']->y += $size;
switch ( true )
{
case ( $text['align'] & ezcGraph::LEFT ):
$position = new ezcGraphCoordinate(
$text['position']->x,
$text['position']->y + $yOffset
);
break;
case ( $text['align'] & ezcGraph::RIGHT ):
$position = new ezcGraphCoordinate(
$text['position']->x + ( $text['width'] - $boundings->width ),
$text['position']->y + $yOffset
);
break;
case ( $text['align'] & ezcGraph::CENTER ):
$position = new ezcGraphCoordinate(
$text['position']->x + ( ( $text['width'] - $boundings->width ) / 2 ),
$text['position']->y + $yOffset
);
break;
}
// Optionally draw text shadow
if ( $text['font']->textShadow === true )
{
$this->renderText(
$text['id'],
$string,
$chars[$text['font']->path],
$text['font']->type,
$text['font']->path,
$text['font']->textShadowColor,
new ezcGraphCoordinate(
$position->x + $this->modifyCoordinate( $text['font']->textShadowOffset ),
$position->y + $this->modifyCoordinate( $text['font']->textShadowOffset )
),
$size,
$text['rotation']
);
}
// Finally draw text
$this->renderText(
$text['id'],
$string,
$chars[$text['font']->path],
$text['font']->type,
$text['font']->path,
$text['font']->color,
$position,
$size,
$text['rotation']
);
$text['position']->y += $size * $this->options->lineSpacing;
}
}
}
/**
* Draws a sector of cirlce
*
* @param ezcGraphCoordinate $center Center of circle
* @param mixed $width Width
* @param mixed $height Height
* @param mixed $startAngle Start angle of circle sector
* @param mixed $endAngle End angle of circle sector
* @param ezcGraphColor $color Color
* @param mixed $filled Filled
* @return void
*/
public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
{
if ( $startAngle > $endAngle )
{
$tmp = $startAngle;
$startAngle = $endAngle;
$endAngle = $tmp;
}
$movie = $this->getDocument();
$shape = new SWFShape();
$this->setShapeColor( $shape, $color, 1, $filled );
if ( !$filled )
{
try
{
$reduced = $this->reduceEllipseSize( $center, $width, $height, $startAngle, $endAngle, .5 );
}
catch ( ezcGraphReducementFailedException $e )
{
return false;
}
$startAngle = $reduced['startAngle'];
$endAngle = $reduced['endAngle'];
$width -= 1;
$height -= 1;
}
$shape->movePenTo( $this->modifyCoordinate( $center->x ), $this->modifyCoordinate( $center->y ) );
// @TODO: User SWFShape::curveTo
for(
$angle = $startAngle;
$angle <= $endAngle;
$angle = min( $angle + $this->options->circleResolution, $endAngle ) )
{
$shape->drawLineTo(
$this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
$this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 )
);
if ( $angle === $endAngle )
{
break;
}
}
$shape->drawLineTo(
$this->modifyCoordinate( $center->x ),
$this->modifyCoordinate( $center->y )
);
$object = $movie->add( $shape );
$object->setName( $id = 'ezcGraphCircleSector_' . $this->id++ );
return $id;
}
/**
* Draws a circular arc consisting of several minor steps on the bounding
* lines.
*
* @param ezcGraphCoordinate $center
* @param mixed $width
* @param mixed $height
* @param mixed $size
* @param mixed $startAngle
* @param mixed $endAngle
* @param ezcGraphColor $color
* @param bool $filled
* @return string Element id
*/
protected function simulateCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled )
{
$movie = $this->getDocument();
$id = 'ezcGraphCircularArc_' . $this->id++;
for (
$tmpAngle = min( ceil ( $startAngle / 180 ) * 180, $endAngle );
$tmpAngle <= $endAngle;
$tmpAngle = min( ceil ( $startAngle / 180 + 1 ) * 180, $endAngle ) )
{
$shape = new SWFShape();
$this->setShapeColor( $shape, $color, 1, $filled );
$shape->movePenTo(
$this->modifyCoordinate( $center->x + cos( deg2rad( $startAngle ) ) * $width / 2 ),
$this->modifyCoordinate( $center->y + sin( deg2rad( $startAngle ) ) * $height / 2 )
);
// @TODO: Use SWFShape::curveTo
for(
$angle = $startAngle;
$angle <= $tmpAngle;
$angle = min( $angle + $this->options->circleResolution, $tmpAngle ) )
{
$shape->drawLineTo(
$this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
$this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 + $size )
);
if ( $angle === $tmpAngle )
{
break;
}
}
for(
$angle = $tmpAngle;
$angle >= $startAngle;
$angle = max( $angle - $this->options->circleResolution, $startAngle ) )
{
$shape->drawLineTo(
$this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
$this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 )
);
if ( $angle === $startAngle )
{
break;
}
}
$object = $movie->add( $shape );
$object->setName( $id );
$startAngle = $tmpAngle;
if ( $tmpAngle === $endAngle )
{
break;
}
}
return $id;
}
/**
* Draws a circular arc
*
* @param ezcGraphCoordinate $center Center of ellipse
* @param integer $width Width of ellipse
* @param integer $height Height of ellipse
* @param integer $size Height of border
* @param float $startAngle Starting angle of circle sector
* @param float $endAngle Ending angle of circle sector
* @param ezcGraphColor $color Color of Border
* @param bool $filled
* @return void
*/
public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
{
if ( $startAngle > $endAngle )
{
$tmp = $startAngle;
$startAngle = $endAngle;
$endAngle = $tmp;
}
$id = $this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $color, $filled );
if ( ( $this->options->shadeCircularArc !== false ) &&
$filled )
{
$gradient = new ezcGraphLinearGradient(
new ezcGraphCoordinate(
$center->x - $width,
$center->y
),
new ezcGraphCoordinate(
$center->x + $width,
$center->y
),
ezcGraphColor::fromHex( '#FFFFFF' )->transparent( $this->options->shadeCircularArc * 1.5 ),
ezcGraphColor::fromHex( '#000000' )->transparent( $this->options->shadeCircularArc * 1.5 )
);
$this->simulateCircularArc( $center, $width, $height, $size, $startAngle, $endAngle, $gradient, $filled );
}
return $id;
}
/**
* Draw circle
*
* @param ezcGraphCoordinate $center Center of ellipse
* @param mixed $width Width of ellipse
* @param mixed $height height of ellipse
* @param ezcGraphColor $color Color
* @param mixed $filled Filled
* @return void
*/
public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
{
$movie = $this->getDocument();
$shape = new SWFShape();
$this->setShapeColor( $shape, $color, 1, $filled );
// Reduce size
if ( !$filled )
{
$width -= 1;
$height -= 1;
}
$shape->movePenTo(
$this->modifyCoordinate( $center->x + $width / 2 ),
$this->modifyCoordinate( $center->y )
);
// @TODO: User SWFShape::curveTo
for ( $angle = $this->options->circleResolution; $angle < 360; $angle += $this->options->circleResolution )
{
$shape->drawLineTo(
$this->modifyCoordinate( $center->x + cos( deg2rad( $angle ) ) * $width / 2 ),
$this->modifyCoordinate( $center->y + sin( deg2rad( $angle ) ) * $height / 2 )
);
}
$shape->drawLineTo(
$this->modifyCoordinate( $center->x + $width / 2 ),
$this->modifyCoordinate( $center->y )
);
$object = $movie->add( $shape );
$object->setName( $id = 'ezcGraphCircle_' . $this->id++ );
return $id;
}
/**
* Draw an image
*
* The image will be inlined in the SVG document using data URL scheme. For
* this the mime type and base64 encoded file content will be merged to
* URL.
*
* @param mixed $file Image file
* @param ezcGraphCoordinate $position Top left position
* @param float $width Width of image in destination image
* @param float $height Height of image in destination image
* @return void
*/
public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
{
$movie = $this->getDocument();
$imageData = getimagesize( $file );
if ( ( $imageData[2] !== IMAGETYPE_JPEG ) && ( $imageData[2] !== IMAGETYPE_PNG ) )
{
throw new ezcGraphFlashBitmapTypeException( $imageData[2] );
}
// Try to create a new SWFBitmap object from provided file
$bitmap = new SWFBitmap( fopen( $file, 'rb' ) );
// Add the image to the movie
$object = $this->movie->add( $bitmap );
// Image size is calculated on the base of a tick size of 20, so
// that we need to transform this, to our tick size.
$factor = $this->modifyCoordinate( 1 ) / 20;
$object->scale( $factor, $factor );
// Scale by ratio of requested and original image size
$object->scale(
$width / $imageData[0],
$height / $imageData[1]
);
// Move object to the right position
$object->moveTo(
$this->modifyCoordinate( $position->x ),
$this->modifyCoordinate( $position->y )
);
// Create, set and return unique ID
$object->setName( $id = 'ezcGraphImage_'. $this->id++ );
return $id;
}
/**
* Return mime type for current image format
*
* @return string
*/
public function getMimeType()
{
return 'application/x-shockwave-flash';
}
/**
* Finally save image
*
* @param string $file Destination filename
* @return void
*/
public function render( $file )
{
$this->drawAllTexts();
$movie = $this->getDocument();
$movie->save( $file, $this->options->compression );
}
/**
* Get resource of rendered result
*
* Return the resource of the rendered result. You should not use this
* method before you called either renderToOutput() or render(), as the
* image may not be completely rendered until then.
*
* @return SWFMovie
*/
public function getResource()
{
return $this->movie;
}
}
?>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,299 @@
<?php
/**
* File containing the ezcGraphSVGDriver class
*
* @package Graph
* @version 1.3
* @copyright Copyright ( C ) 2005-2008 eZ systems as. All rights reserved.
* @author Freddie Witherden
* @license http://ez.no/licenses/new_bsd New BSD License
*/
/**
* Helper class, offering requrired calculation basics and font metrics to use
* SVG fonts with the SVG driver.
*
* You may convert any ttf font into a SVG font using the `ttf2svg` bianry from
* the batik package. Depending on the distribution it may only be available as
* `batik-ttf2svg-<version>`.
*
* Usage:
* <code>
* $font = new ezcGraphSvgFont();
* var_dump(
* $font->calculateStringWidth( '../tests/data/font.svg', 'Just a test string.' ),
* $font->calculateStringWidth( '../tests/data/font2.svg', 'Just a test string.' )
* );
* </code>
*
* @version 1.3
* @package Graph
* @mainclass
*/
class ezcGraphSvgFont
{
/**
* Units per EM
*
* @var float
*/
protected $unitsPerEm;
/**
* Used glyphs
*
* @var array
*/
protected $usedGlyphs = array();
/**
* Cache for glyph size to save XPath lookups.
*
* @var array
*/
protected $glyphCache = array();
/**
* Used kernings
*
* @var array
*/
protected $usedKerns = array();
/**
* Path to font
*
* @var string
*/
protected $fonts = array();
/**
* Initialize SVG font
*
* Loads the SVG font $filename. This should be the path to the file
* generated by ttf2svg.
*
* Returns the (normlized) name of the initilized font.
*
* @param string $fontPath
* @return string
*/
protected function initializeFont( $fontPath )
{
$fontPath = realpath( $fontPath );
if ( isset( $this->fonts[$fontPath] ) )
{
return $fontPath;
}
// Check for existance of font file
if ( !is_file( $fontPath ) || !is_readable( $fontPath ) )
{
throw new ezcBaseFileNotFoundException( $fontPath );
}
$this->fonts[$fontPath] = simplexml_load_file( $fontPath )->defs->font;
// SimpleXML requires us to register a namespace for XPath to work
$this->fonts[$fontPath]->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
// Extract the number of units per Em
$this->unitsPerEm[$fontPath] = (int) $this->fonts[$fontPath]->{'font-face'}['units-per-em'];
return $fontPath;
}
/**
* Get name of font
*
* Get the name of the given font, by extracting its font family from the
* SVG font file.
*
* @param string $fontPath
* @return string
*/
public static function getFontName( $fontPath )
{
$font = simplexml_load_file( $fontPath )->defs->font;
// SimpleXML requires us to register a namespace for XPath to work
$font->registerXPathNamespace( 'svg', 'http://www.w3.org/2000/svg' );
// Extract the font family name
return (string) $font->{'font-face'}['font-family'];
}
/**
* XPath has no standard means of escaping ' and ", with the only solution
* being to delimit your string with the opposite type of quote. ( And if
* your string contains both concat( ) it ).
*
* This method will correctly delimit $char with the appropriate quote type
* so that it can be used in an XPath expression.
*
* @param string $char
* @return string
*/
protected static function xpathEscape( $char )
{
return "'" . str_replace(
array( '\'', '\\' ),
array( '\\\'', '\\\\' ),
$char ) . "'";
}
/**
* Returns the <glyph> associated with $char.
*
* @param string $fontPath
* @param string $char
* @return float
*/
protected function getGlyph( $fontPath, $char )
{
// Check if glyphwidth has already been calculated.
if ( isset( $this->glyphCache[$fontPath][$char] ) )
{
return $this->glyphCache[$fontPath][$char];
}
$matches = $this->fonts[$fontPath]->xpath(
$query = "glyph[@unicode=" . self::xpathEscape( $char ) . "]"
);
if ( count( $matches ) === 0 )
{
// Just ignore missing glyphs. The client will still render them
// using a default font. We try to estimate some width by using a
// common other character.
return $this->glyphCache[$fontPath][$char] =
( $char === 'o' ? false : $this->getGlyph( $fontPath, 'o' ) );
}
$glyph = $matches[0];
if ( !in_array( $glyph, $this->usedGlyphs ) )
{
$this->usedGlyphs[$fontPath][] = $glyph;
}
// There should only ever be one match
return $this->glyphCache[$fontPath][$char] = $glyph;
}
/**
* Returns the amount of kerning to apply for glyphs $g1 and $g2. If no
* valid kerning pair can be found 0 is returned.
*
* @param string $fontPath
* @param SimpleXMLElement $g1
* @param SimpleXMLElement $g2
* @return int
*/
public function getKerning( $fontPath, SimpleXMLElement $glyph1, SimpleXMLElement $glyph2 )
{
// Get the glyph names
$g1Name = self::xpathEscape( ( string ) $glyph1['glyph-name'] );
$g2Name = self::xpathEscape( ( string ) $glyph2['glyph-name'] );
// Get the unicode character names
$g1Uni = self::xpathEscape( ( string ) $glyph1['unicode'] );
$g2Uni = self::xpathEscape( ( string ) $glyph2['unicode'] );
// Search for kerning pairs
$pair = $this->fonts[$fontPath]->xpath(
"svg:hkern[( @g1=$g1Name and @g2=$g2Name )
or
( @u1=$g1Uni and @g2=$g2Uni )]"
);
// If we found anything return it
if ( count( $pair ) )
{
if ( !in_array( $pair[0], $this->usedKerns ) )
{
$this->usedKerns[$fontPath][] = $pair[0];
}
return ( int ) $pair[0]['k'];
}
else
{
return 0;
}
}
/**
* Calculates the width of $string in the current font in Em's.
*
* @param string $fontPath
* @param string $string
* @return float
*/
public function calculateStringWidth( $fontPath, $string )
{
// Ensure font is properly initilized
$fontPath = $this->initializeFont( $fontPath );
$strlen = strlen( $string );
$prevCharInfo = null;
$length = 0;
// @TODO: Add UTF-8 support here - iterating over the bytes does not
// really help.
for ( $i = 0; $i < $strlen; ++$i )
{
// Find the font information for the character
$charInfo = $this->getGlyph( $fontPath, $string[$i] );
// Handle missing glyphs
if ( $charInfo === false )
{
$prevCharInfo = null;
$length .= .5 * $this->unitsPerEm[$fontPath];
continue;
}
// Add the horizontal advance for the character to the length
$length += (float) $charInfo['horiz-adv-x'];
// If we are not the first character, look for kerning pairs
if ( $prevCharInfo !== null )
{
// Apply kerning (if any)
$length -= $this->getKerning( $fontPath, $prevCharInfo, $charInfo );
}
$prevCharInfo = clone $charInfo;
}
// Divide by _unitsPerEm to get the length in Em
return (float) $length / $this->unitsPerEm[$fontPath];
}
/**
* Add font definitions to SVG document
*
* Add the SVG font definition paths for all used glyphs and kernings to
* the given SVG document.
*
* @param DOMDocument $document
* @return void
*/
public function addFontToDocument( DOMDocument $document )
{
$defs = $document->getElementsByTagName( 'defs' )->item( 0 );
$fontNr = 0;
foreach ( $this->fonts as $path => $definition )
{
// Just import complete font for now.
// @TODO: Only import used characters.
$font = dom_import_simplexml( $definition );
$font = $document->importNode( $font, true );
$font->setAttribute( 'id', 'Font' . ++$fontNr );
$defs->appendChild( $font );
}
}
}
?>
@@ -0,0 +1,242 @@
<?php
/**
* File containing the ezcGraphSVGDriver class
*
* @package Graph
* @version 1.3
* @copyright Copyright (C) 2005-2008 eZ systems as. All rights reserved.
* @license http://ez.no/licenses/new_bsd New BSD License
* @access private
*/
/**
* Simple output driver for debuggin purposes. Just outputs shapes as text on
* CLI.
*
* @version 1.3
* @package Graph
* @access private
*/
class ezcGraphVerboseDriver extends ezcGraphDriver
{
/**
* Number of call on driver
*
* @var int
*/
protected $call = 0;
/**
* Constructor
*
* @param array $options
* @return void
* @ignore
*/
public function __construct( array $options = array() )
{
$this->options = new ezcGraphSvgDriverOptions( $options );
echo "\n";
}
/**
* Draws a single polygon
*
* @param array $points
* @param ezcGraphColor $color
* @param bool $filled
* @param float $thickness
* @return void
*/
public function drawPolygon( array $points, ezcGraphColor $color, $filled = true, $thickness = 1. )
{
$pointString = '';
foreach ( $points as $point )
{
$pointString .= sprintf( "\t( %.2f, %.2f )\n", $point->x, $point->y );
}
printf( "% 4d: Draw %spolygon:\n%s",
$this->call++,
( $filled ? 'filled ' : '' ),
$pointString
);
}
/**
* Draws a single line
*
* @param ezcGraphCoordinate $start
* @param ezcGraphCoordinate $end
* @param ezcGraphColor $color
* @param float $thickness
* @return void
*/
public function drawLine( ezcGraphCoordinate $start, ezcGraphCoordinate $end, ezcGraphColor $color, $thickness = 1. )
{
printf( "% 4d: Draw line from ( %.2f, %.2f ) to ( %.2f, %.2f ) with thickness %d.\n",
$this->call++,
$start->x,
$start->y,
$end->x,
$end->y,
$thickness
);
}
/**
* Returns boundings of text depending on the available font extension
*
* @param float $size Textsize
* @param ezcGraphFontOptions $font Font
* @param string $text Text
* @return ezcGraphBoundings Boundings of text
*/
protected function getTextBoundings( $size, ezcGraphFontOptions $font, $text )
{
return null;
}
/**
* Wrties text in a box of desired size
*
* @param mixed $string
* @param ezcGraphCoordinate $position
* @param mixed $width
* @param mixed $height
* @param int $align
* @param ezcGraphRotation $rotation
* @return void
*/
public function drawTextBox( $string, ezcGraphCoordinate $position, $width, $height, $align, ezcGraphRotation $rotation = null )
{
printf( "% 4d: Draw text '%s' at ( %.2f, %.2f ) with dimensions ( %d, %d ) and alignement %d.\n",
$this->call++,
$string,
$position->x,
$position->y,
$width,
$height,
$align
);
}
/**
* Draws a sector of cirlce
*
* @param ezcGraphCoordinate $center
* @param mixed $width
* @param mixed $height
* @param mixed $startAngle
* @param mixed $endAngle
* @param ezcGraphColor $color
* @param bool $filled
* @return void
*/
public function drawCircleSector( ezcGraphCoordinate $center, $width, $height, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
{
printf( "% 4d: Draw %scicle sector at ( %.2f, %.2f ) with dimensions ( %d, %d ) from %.2f to %.2f.\n",
$this->call++,
( $filled ? 'filled ' : '' ),
$center->x,
$center->y,
$width,
$height,
$startAngle,
$endAngle
);
}
/**
* Draws a circular arc
*
* @param ezcGraphCoordinate $center Center of ellipse
* @param integer $width Width of ellipse
* @param integer $height Height of ellipse
* @param integer $size Height of border
* @param float $startAngle Starting angle of circle sector
* @param float $endAngle Ending angle of circle sector
* @param ezcGraphColor $color Color of Border
* @param bool $filled
* @return void
*/
public function drawCircularArc( ezcGraphCoordinate $center, $width, $height, $size, $startAngle, $endAngle, ezcGraphColor $color, $filled = true )
{
printf( "% 4d: Draw circular arc at ( %.2f, %.2f ) with dimensions ( %d, %d ) and size %.2f from %.2f to %.2f.\n",
$this->call++,
$center->x,
$center->y,
$width,
$height,
$size,
$startAngle,
$endAngle
);
}
/**
* Draws a circle
*
* @param ezcGraphCoordinate $center
* @param mixed $width
* @param mixed $height
* @param ezcGraphColor $color
* @param bool $filled
*
* @return void
*/
public function drawCircle( ezcGraphCoordinate $center, $width, $height, ezcGraphColor $color, $filled = true )
{
printf( "% 4d: Draw %scircle at ( %.2f, %.2f ) with dimensions ( %d, %d ).\n",
$this->call++,
( $filled ? 'filled ' : '' ),
$center->x,
$center->y,
$width,
$height
);
}
/**
* Draws a imagemap of desired size
*
* @param mixed $file
* @param ezcGraphCoordinate $position
* @param mixed $width
* @param mixed $height
* @return void
*/
public function drawImage( $file, ezcGraphCoordinate $position, $width, $height )
{
printf( "% 4d: Draw image '%s' at ( %.2f, %.2f ) with dimensions ( %d, %d ).\n",
$this->call++,
$file,
$position->x,
$position->y,
$width,
$height
);
}
/**
* Return mime type for current image format
*
* @return string
*/
public function getMimeType()
{
return 'text/plain';
}
/**
* Finally save image
*
* @param mixed $file
* @return void
*/
public function render ( $file )
{
printf( "Render image.\n" );
}
}
?>