0xCC, 'g' => 0x00, 'b' => 0x00 ]; case 'green': return [ 'r' => 0x00, 'g' => 0xCC, 'b' => 0x00 ]; case 'blue': return [ 'r' => 0x00, 'g' => 0x00, 'b' => 0xCC ]; case 'yellow': return [ 'r' => 0xFF, 'g' => 0xFF, 'b' => 0x00 ]; case 'orange': return [ 'r' => 0xFF, 'g' => 0x99, 'b' => 0x00 ]; case 'lightgreen': return [ 'r' => 0x99, 'g' => 0xFF, 'b' => 0x99 ]; case 'lightblue': return [ 'r' => 0x66, 'g' => 0x99, 'b' => 0xFF ]; case 'black': return [ 'r' => 0x00, 'g' => 0x00, 'b' => 0x00 ]; case 'white': return [ 'r' => 0xFF, 'g' => 0xFF, 'b' => 0xFF ]; case 'gray': return [ 'r' => 0xDD, 'g' => 0xDD, 'b' => 0xDD ]; default: return [ 'r' => 0x00, 'g' => 0x00, 'b' => 0x00 ]; } } } class Fake_GRenderer extends GRenderer // {{{1 { public $renderer; public $left; public $top; public $width; public $height; public function __construct(&$renderer, $left, $top, $right, $bottom) { $this->renderer = &$renderer; $this->left = $left; $this->top = $top; $this->width = $right - $left; $this->height = $bottom - $top; } public function addLink($target, $left, $top, $right, $bottom, $title = null) { $this->renderer->addLink( $target, $left * $this->width + $this->left, $top * $this->height + $this->top, $right * $this->width + $this->left, $bottom * $this->height + $this->top, $title ); } public function drawLine($x1, $y1, $x2, $y2, $style) { $this->renderer->drawLine( $x1 * $this->width + $this->left, $y1 * $this->height + $this->top, $x2 * $this->width + $this->left, $y2 * $this->height + $this->top, $style ); } public function drawRectangle($left, $top, $right, $bottom, $style) { $this->renderer->drawRectangle( $left * $this->width + $this->left, $top * $this->height + $this->top, $right * $this->width + $this->left, $bottom * $this->height + $this->top, $style ); } public function drawPie($centerX, $centerY, $radius, $begin, $end, $style) { $this->renderer->drawPie( $centerX * $this->width + $this->left, $centerY * $this->height + $this->top, $radius * $this->width, $begin, $end, $style ); } public function drawText($text, $left, $right, $height, $style) { $this->renderer->drawText( $text, $left * $this->width + $this->left, $right * $this->width + $this->left, $height * $this->height + $this->top, $style ); } public function getTextWidth($text, $style) { // Make sure the font size does not get smaller with scale return $this->renderer->getTextWidth($text, $style) / $this->width; } public function getTextHeight($style) { // Make sure the font size does not get smaller with scale return $this->renderer->getTextHeight($style) / $this->height; } public function getStyle($name) { return $this->renderer->getStyle($name); } public function httpOutput($filename) { $this->renderer->httpOutput($filename); } public function writeToStream($stream) { $this->renderer->writeToStream($stream); } } class DataHandler { /** * Provides means to hook into the data display sequence to add decorations * or external behaviour. * * Called every time a data entry is displayed by the engine. Handlers are * registered by Graphic::addDataHandler(). No implementations are provided * with the distribution. * * @param renderer The renderer instance on which the rendering is performed. * This instance may not be cached for other purposes. * @param positionData Indicates the coordinate of the data to be rendered. * Coordinates are renderer-specific. This value is provided * as an associative array. (x,y) is provided for single-point * data. (top,left,bottom,right) is provided for regions. * @param series The key of the source series of the data. * @param entryIndex The zero-based index of the entry in the series. */ public function handle($renderer, $positionData, $series, $entryIndex) { die("Abstract Function Call"); } } class Graphic { public $legend; public $title; public $parameters; public $dataHandlers; public function __construct() { $this->legend = []; $this->parameters = []; $this->dataHandlers = []; } public function setTitle($title) { $this->title = $title; } public function addLegend($color, $value, $url = null) { // $color name // $value label $this->legend[] = [ $color, $value, $url ]; } public function addDataHandler($handler) { if (is_a($handler, 'DataHandler')) { $this->dataHandlers[] = $handler; } } public function getRequiredSeries() { // Returns an associative array with series name as key // Value has to be true if the series is required. die("Abstract Function Call"); } public function draw(&$renderer) { $top = 0; $left = 0; $bottom = 1; $right = 1; $layout = $this->_layout(); if ($layout['title-active'] && ! empty($this->title)) { $top += 0.1; $renderer->drawText($this->title, 0, 1, 0.04, $renderer->getStyle($layout['title-font'])); } if ($layout['legend-active'] && count($this->legend) > 0 && $layout['legend-location'] != 'static') { $this->_drawLegend($renderer, $left, $top, $right, $bottom, $layout); } $left += $layout['content-margin']; $right -= $layout['content-margin']; $top += $layout['content-margin']; $bottom -= $layout['content-margin']; $this->_drawContent(new Fake_GRenderer($renderer, $left, $top, $right, $bottom)); if ($layout['legend-active'] && count($this->legend) > 0 && $layout['legend-location'] == 'static') { $this->_drawLegend($renderer, $left, $top, $right, $bottom, $layout); } } public function setData($data) { if (! is_array($data)) { return false; } foreach ($this->getRequiredSeries() as $key => $value) { if ($value && ( ! array_key_exists($key, $data) || ! is_array($data[$key]) )) { return false; } } foreach ($data as $key => $values) { if (! is_array($values)) { return false; } $data[$key] = array_values($values); } return $this->_handleData($data); } public function setParam($name, $value) { $this->parameters[$name] = $value; } public function _getColor() { static $index = 0; $colors = [ 'Red', 'Green', 'Blue', 'Yellow', 'Orange', 'LightGreen', 'LightBlue' ]; return $colors[$index++ % count($colors)]; } public function _drawLegend(&$renderer, &$left, &$top, &$right, &$bottom, $layout) { $box_size = $layout['legend-box-size']; $padding = $layout['legend-padding']; $margin = $layout['legend-margin']; $legend_font = $renderer->getStyle($layout['legend-font']); $font_height = $renderer->getTextHeight($legend_font); // Calculate size {{{3 $item_size = []; foreach ($this->legend as $key => $value) { $item_size[$key] = $renderer->getTextWidth($value[1], $legend_font); } $width = 0; $height = 0; $single_height = max($font_height, $box_size); switch ($layout['legend-orientation']) { case 'horizontal': $height = $single_height + 2 * $padding; $width = array_sum($item_size) // text width + (1 + count($item_size) ) * $padding // padding between items + ( $box_size + $padding ) * count($item_size); // box and box padding break; case 'vertical': $height = $single_height * count($item_size) + (1 + count($item_size) ) * $padding; $width = max($item_size) + 3 * $padding + $box_size; break; } // Calculate position {{{3 $x = 0; $y = 0; switch ($layout['legend-location']) { case 'left': $y = $top + ( $bottom - $top ) / 2 - $height / 2; $x = $left + $margin; $left += 2 * $margin + $width; break; case 'right': $y = $top + ( $bottom - $top ) / 2 - $height / 2; $x = $right - $margin - $width; $right -= 2 * $margin + $width; break; case 'bottom': $x = $left + ( $right - $left ) / 2 - $width / 2; $y = $bottom - $margin - $height; $bottom -= 2 * $margin + $height; break; case 'top': $x = $left + ( $right - $left ) / 2 - $width / 2; $y = $top + $margin; $top += 2 * $margin + $height; break; case 'static': switch ($layout['legend-location-rel']) { case 'top-left': $x = $layout['legend-location-x'] + $margin; $y = $layout['legend-location-y'] + $margin; break; case 'top-right': $x = $layout['legend-location-x'] - $width - $margin; $y = $layout['legend-location-y'] + $margin; break; case 'bottom-left': $x = $layout['legend-location-x'] + $margin; $y = $layout['legend-location-y'] - $height - $margin; break; case 'bottom-right': $x = $layout['legend-location-x'] - $width - $margin; $y = $layout['legend-location-y'] - $height - $margin; break; case 'center': $x = $layout['legend-location-x'] - $width / 2; $y = $layout['legend-location-y'] - $height / 2; break; } break; } // Draw the thing {{{3 $renderer->drawRectangle($x, $y, $x + $width, $y + $height, $renderer->getStyle($layout['legend-style'])); $box_offset = 0; $text_offset = 0; if ($box_size > $font_height) { $text_offset = ( $box_size - $font_height ) / 2; } else { $box_offset = ( $font_height - $box_size ) / 2; } $x += $padding; $y += $padding; foreach ($this->legend as $key => $info) { list( $color, $value, $url ) = $info; $this->_drawLegendBox( new Fake_GRenderer( $renderer, $x, $y + $box_offset, $x + $box_size, $y + $box_size + $box_offset ), $color ); switch ($layout['legend-orientation']) { case 'horizontal': $renderer->drawText( $value, $x + $box_size + $padding, $x + $box_size + $padding + $item_size[$key], $y + $text_offset, $legend_font ); if (! empty($url)) { $renderer->addLink( $url, $x + $padding, $y + $box_offset, $x + $box_size + $padding + $item_size[$key], $y + $box_offset + $box_size, $value ); } $x += $box_size + $padding * 2 + $item_size[$key]; break; case 'vertical': $renderer->drawText( $value, $x + $box_size + $padding, $x - $padding + $width, $y + $text_offset, $legend_font ); if (! empty($url)) { $renderer->addLink( $url, $x, $y + $box_offset, $x + $width, $y + $box_offset + $box_size, $value ); } $y += $padding + $single_height; break; } } // }}}3 } public function _drawContent(&$renderer) { die("Abstract Function Call"); } public function _drawLegendBox(&$renderer, $color) { die("Abstract Function Call"); } public function _handleData($data) { die("Abstract Function Call"); } public function _default() { return [ 'title-active' => true, 'title-font' => 'Normal-Text-Center', // Any Text style name 'legend-active' => true, 'legend-font' => 'Large-Text', // Any Text style name 'legend-location' => 'right', // left, right, bottom, top, static 'legend-location-x' => 1, // static only, x location 'legend-location-y' => 1, // static only, y location 'legend-location-rel' => 'bottom-right', // static only, relative postion (top-left, top-right, bottom-left, bottom-right, center 'legend-orientation' => 'vertical', // horizontal, vertical 'legend-style' => 'FillStroke-White', // Any FillStroke style name 'legend-box-size' => 0.03, 'legend-margin' => 0.01, 'legend-padding' => 0.01, 'content-margin' => 0.02 ]; } public function _layout() { // Planning some user-preferences, until then, defaults. return array_merge($this->_default(), $this->parameters); } public function _notify($renderer, $positionData, $series, $index) { foreach ($this->dataHandlers as $handler) { $handler->handle($renderer, $positionData, $series, $index); } } }