From ec3bafa026b2a38f985a94965df390f0a4df50db Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 9 Jul 2014 18:00:02 +0200 Subject: [PATCH] Fix number formatting in chart classes Use explizit number formatting instead of implicit type conversions when rendering SVGs, to prevent a change of the locale from breaking the charts. fixes #6585 --- library/Icinga/Chart/Format.php | 46 +++++++++++++++++++ library/Icinga/Chart/Primitive/Circle.php | 4 +- library/Icinga/Chart/Primitive/Line.php | 8 ++-- library/Icinga/Chart/Primitive/Path.php | 7 ++- library/Icinga/Chart/Primitive/PieSlice.php | 11 +++-- library/Icinga/Chart/Primitive/Rect.php | 9 ++-- library/Icinga/Chart/Primitive/Text.php | 11 +++-- library/Icinga/Chart/Render/LayoutBox.php | 10 +++- library/Icinga/Chart/Render/RenderContext.php | 3 +- library/Icinga/Chart/SVGRenderer.php | 4 +- library/Icinga/Chart/Unit/AxisUnit.php | 2 +- 11 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 library/Icinga/Chart/Format.php diff --git a/library/Icinga/Chart/Format.php b/library/Icinga/Chart/Format.php new file mode 100644 index 000000000..d55d58b26 --- /dev/null +++ b/library/Icinga/Chart/Format.php @@ -0,0 +1,46 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Chart; + +class Format +{ + /** + * Format a number into a number-string as defined by the SVG-Standard + * + * @see http://www.w3.org/TR/SVG/types.html#DataTypeNumber + * + * @param $number + * + * @return string + */ + public static function formatSVGNumber($number) + { + return number_format($number, 1, '.', ''); + } +} diff --git a/library/Icinga/Chart/Primitive/Circle.php b/library/Icinga/Chart/Primitive/Circle.php index 3f8cdd568..853eef11a 100644 --- a/library/Icinga/Chart/Primitive/Circle.php +++ b/library/Icinga/Chart/Primitive/Circle.php @@ -83,8 +83,8 @@ class Circle extends Styleable implements Drawable { $coords = $ctx->toAbsolute($this->x, $this->y); $circle = $ctx->getDocument()->createElement('circle'); - $circle->setAttribute('cx', $coords[0]); - $circle->setAttribute('cy', $coords[1]); + $circle->setAttribute('cx', Format::formatSVGNumber($coords[0])); + $circle->setAttribute('cy', Format::formatSVGNumber($coords[1])); $circle->setAttribute('r', 5); $circle->setAttribute('style', $this->getStyle()); $this->applyAttributes($circle); diff --git a/library/Icinga/Chart/Primitive/Line.php b/library/Icinga/Chart/Primitive/Line.php index 5f9d85ecd..47aca0316 100644 --- a/library/Icinga/Chart/Primitive/Line.php +++ b/library/Icinga/Chart/Primitive/Line.php @@ -101,10 +101,10 @@ class Line extends Styleable implements Drawable list($x1, $y1) = $ctx->toAbsolute($this->xStart, $this->yStart); list($x2, $y2) = $ctx->toAbsolute($this->xEnd, $this->yEnd); $line = $doc->createElement('line'); - $line->setAttribute('x1', $x1); - $line->setAttribute('x2', $x2); - $line->setAttribute('y1', $y1); - $line->setAttribute('y2', $y2); + $line->setAttribute('x1', Format::formatSVGNumber($x1)); + $line->setAttribute('x2', Format::formatSVGNumber($x2)); + $line->setAttribute('y1', Format::formatSVGNumber($y1)); + $line->setAttribute('y2', Format::formatSVGNumber($y2)); $line->setAttribute('style', $this->getStyle()); $this->applyAttributes($line); return $line; diff --git a/library/Icinga/Chart/Primitive/Path.php b/library/Icinga/Chart/Primitive/Path.php index 240726349..ea42c58f7 100644 --- a/library/Icinga/Chart/Primitive/Path.php +++ b/library/Icinga/Chart/Primitive/Path.php @@ -30,8 +30,9 @@ namespace Icinga\Chart\Primitive; -use \DOMElement; -use \Icinga\Chart\Render\RenderContext; +use DOMElement; +use Icinga\Chart\Render\RenderContext; +use Icinga\Chart\Format; /** * Drawable for creating a svg path element @@ -177,6 +178,8 @@ class Path extends Styleable implements Drawable if (!$this->isAbsolute) { $point = $ctx->toAbsolute($point[0], $point[1]); } + $point[0] = Format::formatSVGNumber($point[0]); + $point[1] = Format::formatSVGNumber($point[1]); if ($lastPoint && $this->discrete) { $pathDescription .= sprintf($tpl, $point[0], $lastPoint[1]); } diff --git a/library/Icinga/Chart/Primitive/PieSlice.php b/library/Icinga/Chart/Primitive/PieSlice.php index 272145edb..e930390a1 100644 --- a/library/Icinga/Chart/Primitive/PieSlice.php +++ b/library/Icinga/Chart/Primitive/PieSlice.php @@ -29,8 +29,9 @@ namespace Icinga\Chart\Primitive; -use \DOMElement; -use \Icinga\Chart\Render\RenderContext; +use DOMElement; +use Icinga\Chart\Render\RenderContext; +use Icinga\Chart\Format; /** * Component for drawing a pie slice @@ -138,15 +139,15 @@ class PieSlice extends Animatable implements Drawable $yEnd = $y - ($r * cos($this->endRadian)); // Draw a straight line to the upper part of the arc - $pathString .= 'L ' . $xStart . ' ' . $yStart; + $pathString .= 'L ' . Format::formatSVGNumber($xStart) . ' ' . Format::formatSVGNumber($yStart); // Instead of directly connecting the upper part of the arc (leaving a triangle), draw a bow with the radius - $pathString .= ' A ' . $r . ' ' . $r ; + $pathString .= ' A ' . Format::formatSVGNumber($r) . ' ' . Format::formatSVGNumber($r); // These are the flags for the bow, see the SVG path documentation for details // http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands $pathString .= ' 0 ' . (($this->endRadian - $this->startRadian > M_PI) ? '1' : '0 ') . ' 1'; // xEnd and yEnd are the lower point of the arc - $pathString .= ' '.$xEnd . ' ' . $yEnd; + $pathString .= ' ' . Format::formatSVGNumber($xEnd) . ' ' . Format::formatSVGNumber($yEnd); return $pathString; } diff --git a/library/Icinga/Chart/Primitive/Rect.php b/library/Icinga/Chart/Primitive/Rect.php index e66849598..8b86b35fb 100644 --- a/library/Icinga/Chart/Primitive/Rect.php +++ b/library/Icinga/Chart/Primitive/Rect.php @@ -31,6 +31,7 @@ namespace Icinga\Chart\Primitive; use \DomElement; use \Icinga\Chart\Render\RenderContext; +use Icinga\Chart\Format; /** * Drawable representing the SVG rect element @@ -116,10 +117,10 @@ class Rect extends Animatable implements Drawable if ($this->keepRatio) { $ctx->ignoreRatio(); } - $rect->setAttribute('x', $x); - $rect->setAttribute('y', $y); - $rect->setAttribute('width', $width); - $rect->setAttribute('height', $height); + $rect->setAttribute('x', Format::formatSVGNumber($x)); + $rect->setAttribute('y', Format::formatSVGNumber($y)); + $rect->setAttribute('width', Format::formatSVGNumber($width)); + $rect->setAttribute('height', Format::formatSVGNumber($height)); $rect->setAttribute('style', $this->getStyle()); $this->applyAttributes($rect); diff --git a/library/Icinga/Chart/Primitive/Text.php b/library/Icinga/Chart/Primitive/Text.php index ad6190e27..5cd47e56b 100644 --- a/library/Icinga/Chart/Primitive/Text.php +++ b/library/Icinga/Chart/Primitive/Text.php @@ -30,9 +30,10 @@ namespace Icinga\Chart\Primitive; -use \DOMElement; -use \DOMText; -use \Icinga\Chart\Render\RenderContext; +use DOMElement; +use DOMText; +use Icinga\Chart\Render\RenderContext; +use Icinga\Chart\Format; /** * Wrapper for the SVG text element @@ -184,7 +185,7 @@ class Text extends Styleable implements Drawable { list($x, $y) = $ctx->toAbsolute($this->x, $this->y); $text = $ctx->getDocument()->createElement('text'); - $text->setAttribute('x', $x - 15); + $text->setAttribute('x', Format::formatSVGNumber($x - 15)); $text->setAttribute( 'style', $this->getStyle() @@ -196,7 +197,7 @@ class Text extends Styleable implements Drawable . 'text-anchor: ' . $this->alignment ); - $text->setAttribute('y', $y); + $text->setAttribute('y', Format::formatSVGNumber($y)); $text->appendChild(new DOMText($this->text)); return $text; } diff --git a/library/Icinga/Chart/Render/LayoutBox.php b/library/Icinga/Chart/Render/LayoutBox.php index 3cbe893d4..02d99376d 100644 --- a/library/Icinga/Chart/Render/LayoutBox.php +++ b/library/Icinga/Chart/Render/LayoutBox.php @@ -29,6 +29,8 @@ namespace Icinga\Chart\Render; +use Icinga\Chart\Format; + /** * Layout class encapsulating size, padding and margin information */ @@ -147,7 +149,13 @@ class LayoutBox $scaleX *= $this->getWidth()/100; $scaleY *= $this->getHeight()/100; - return sprintf('translate(%s, %s) scale(%s, %s)', $translateX, $translateY, $scaleX, $scaleY); + return sprintf( + 'translate(%s, %s) scale(%s, %s)', + Format::formatSVGNumber($translateX), + Format::formatSVGNumber($translateY), + Format::formatSVGNumber($scaleX), + Format::formatSVGNumber($scaleY) + ); } /** diff --git a/library/Icinga/Chart/Render/RenderContext.php b/library/Icinga/Chart/Render/RenderContext.php index 5337c2b89..62150a42a 100644 --- a/library/Icinga/Chart/Render/RenderContext.php +++ b/library/Icinga/Chart/Render/RenderContext.php @@ -30,8 +30,7 @@ namespace Icinga\Chart\Render; -use \DOMDocument; -use \Icinga\Util\Dimension; +use DOMDocument; /** * Context for rendering, handles ratio based coordinate calculations. diff --git a/library/Icinga/Chart/SVGRenderer.php b/library/Icinga/Chart/SVGRenderer.php index c3840d561..bd9b8d47b 100644 --- a/library/Icinga/Chart/SVGRenderer.php +++ b/library/Icinga/Chart/SVGRenderer.php @@ -107,9 +107,9 @@ class SVGRenderer $ctx = $this->createRenderContext(); $svg = $this->document->createElement('svg'); $svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg'); - $svg->setATtribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); + $svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink'); $svg->setAttribute('width', $this->width . '%'); - $svg->setAttribute('height', $this->width . '%'); + $svg->setAttribute('height', $this->height . '%'); $svg->setAttribute( 'viewBox', sprintf( diff --git a/library/Icinga/Chart/Unit/AxisUnit.php b/library/Icinga/Chart/Unit/AxisUnit.php index a34de8268..9e77bb8dc 100644 --- a/library/Icinga/Chart/Unit/AxisUnit.php +++ b/library/Icinga/Chart/Unit/AxisUnit.php @@ -29,7 +29,7 @@ namespace Icinga\Chart\Unit; -use \Iterator; +use Iterator; /** * Base class for Axis Units