From 8ec56fe45e6fd98565539d7798eee21c93b75e8b Mon Sep 17 00:00:00 2001 From: jmleroux Date: Thu, 17 Mar 2016 18:17:49 +0100 Subject: [PATCH 1/4] Extract duplicate code Extract SVG templates --- spec/Fixtures/flat-square.svg | 20 +++ spec/Fixtures/flat.svg | 20 +++ spec/Fixtures/license.svg | 25 ++-- spec/PUGX/Poser/Render/SvgFlatRenderSpec.php | 30 +--- .../Poser/Render/SvgFlatSquareRenderSpec.php | 27 +--- spec/PUGX/Poser/Render/SvgRenderSpec.php | 8 +- src/Render/LocalSvgRenderer.php | 130 +++++++++++++++++ src/Render/SvgFlatRender.php | 134 +----------------- src/Render/SvgFlatSquareRender.php | 134 +----------------- src/Render/SvgRender.php | 79 +---------- src/Resources/templates/flat-square.svg | 20 +++ src/Resources/templates/flat.svg | 20 +++ src/Resources/templates/plastic.svg | 18 +++ 13 files changed, 258 insertions(+), 407 deletions(-) create mode 100644 spec/Fixtures/flat-square.svg create mode 100644 spec/Fixtures/flat.svg create mode 100644 src/Render/LocalSvgRenderer.php create mode 100644 src/Resources/templates/flat-square.svg create mode 100644 src/Resources/templates/flat.svg create mode 100644 src/Resources/templates/plastic.svg diff --git a/spec/Fixtures/flat-square.svg b/spec/Fixtures/flat-square.svg new file mode 100644 index 0000000..6e7903c --- /dev/null +++ b/spec/Fixtures/flat-square.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + license + license + MIT + MIT + + diff --git a/spec/Fixtures/flat.svg b/spec/Fixtures/flat.svg new file mode 100644 index 0000000..a86cc24 --- /dev/null +++ b/spec/Fixtures/flat.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + license + license + MIT + MIT + + diff --git a/spec/Fixtures/license.svg b/spec/Fixtures/license.svg index df4ea43..4bfc082 100644 --- a/spec/Fixtures/license.svg +++ b/spec/Fixtures/license.svg @@ -1,13 +1,14 @@ - - - - - - -stable -stable -v2.0 -v2.0 - - \ No newline at end of file + + + + + + + + stable + stable + v2.0 + v2.0 + + diff --git a/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php b/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php index 3f79d2e..734957d 100644 --- a/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgFlatRenderSpec.php @@ -5,7 +5,6 @@ use PhpSpec\ObjectBehavior; use Prophecy\Argument; use PUGX\Poser\Badge; -use PUGX\Poser\Calculator\TextSizeCalculatorInterface; class SvgFlatRenderSpec extends ObjectBehavior { @@ -37,32 +36,9 @@ public function getMatchers() function it_should_render_a_license_mit_exactly_like_this_svg() { - $template = << - - - - - - - - - - - - - - license - license - MIT - MIT - - -EOF; - + $fixture = __DIR__ . '/../../../Fixtures/flat.svg'; + $template = file_get_contents($fixture); $badge = Badge::fromURI('license-MIT-blue.svg'); $this->render($badge)->__toString()->shouldBeLike($template); } - - -} +} diff --git a/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php b/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php index 6abc733..c476785 100644 --- a/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgFlatSquareRenderSpec.php @@ -24,7 +24,7 @@ function it_should_render_a_svg() public function getMatchers() { return array( - 'beAValidSVGImage' => function($subject) { + 'beAValidSVGImage' => function ($subject) { $regex = '/^$/'; $matches = array(); @@ -36,29 +36,8 @@ public function getMatchers() function it_should_render_a_license_mit_exactly_like_this_svg() { - $template = << - - - - - - - - - - - - - - license - license - MIT - MIT - - -EOF; - + $fixture = __DIR__ . '/../../../Fixtures/flat-square.svg'; + $template = file_get_contents($fixture); $badge = Badge::fromURI('license-MIT-blue.svg'); $this->render($badge)->__toString()->shouldBeLike($template); } diff --git a/spec/PUGX/Poser/Render/SvgRenderSpec.php b/spec/PUGX/Poser/Render/SvgRenderSpec.php index 2793036..34e54f9 100644 --- a/spec/PUGX/Poser/Render/SvgRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgRenderSpec.php @@ -24,12 +24,12 @@ function it_should_render_a_svg() public function getMatchers() { return array( - 'beAValidSVGImage' => function($subject) { + 'beAValidSVGImage' => function ($subject) { - $regex = '/^$/'; - $matches = array(); + $regex = '/^$/'; + $matches = array(); - return preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); + return preg_match($regex, (string) $subject, $matches, PREG_OFFSET_CAPTURE, 0); } ); } diff --git a/src/Render/LocalSvgRenderer.php b/src/Render/LocalSvgRenderer.php new file mode 100644 index 0000000..0cd5ae6 --- /dev/null +++ b/src/Render/LocalSvgRenderer.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PUGX\Poser\Render; + +use PUGX\Poser\Badge; +use PUGX\Poser\Calculator\GDTextSizeCalculator; +use PUGX\Poser\Calculator\TextSizeCalculatorInterface; +use PUGX\Poser\Image; + +/** + * Local SVG renderer. + * Generate SVG badges from local templates. + * + * @author JM Leroux + */ +abstract class LocalSvgRenderer implements RenderInterface +{ + const VENDOR_COLOR = '#555'; + + /** + * @var TextSizeCalculatorInterface + */ + protected $textSizeCalculator; + + /** + * @var string + */ + protected $templateName; + + /** + * Constructor. + * + * @param TextSizeCalculatorInterface $textSizeCalculator + */ + public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) + { + $this->textSizeCalculator = $textSizeCalculator; + + if (null === $this->textSizeCalculator) { + $this->textSizeCalculator = new GDTextSizeCalculator(); + } + } + + /** + * @param Badge $badge + * + * @return mixed + */ + public function render(Badge $badge) + { + $template = $this->getTemplate($this->templateName); + $parameters = $this->buildParameters($badge); + + return $this->renderSvg($template, $parameters, $badge->getFormat()); + } + + /** + * @param $format + * + * @return string SVG content of the template + */ + protected function getTemplate($format) + { + $templatesDirectory = __DIR__ . '/../Resources/templates'; + $filepath = sprintf('%s/%s.svg', $templatesDirectory, $format); + + if (!file_exists($filepath)) { + throw new \InvalidArgumentException(sprintf('No template for format %s', $format)); + } + + return file_get_contents($filepath); + } + + /** + * @param $text + * + * @return float + */ + protected function stringWidth($text) + { + return $this->textSizeCalculator->calculateWidth($text); + } + + /** + * @param string $render + * @param array $parameters + * @param string $format + * + * @return Image + */ + protected function renderSvg($render, $parameters, $format) + { + foreach ($parameters as $key => $variable) { + $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); + } + + return Image::createFromString($render, $format); + } + + /** + * @param Badge $badge + * + * @return array + */ + protected function buildParameters(Badge $badge) + { + $parameters = array(); + + $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); + $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); + $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; + $parameters['vendorColor'] = static::VENDOR_COLOR; + $parameters['valueColor'] = $badge->getHexColor(); + $parameters['vendor'] = $badge->getSubject(); + $parameters['value'] = $badge->getStatus(); + $parameters['vendorStartPosition'] = round($parameters['vendorWidth'] / 2, 1) + 1; + $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; + + return $parameters; + } +} diff --git a/src/Render/SvgFlatRender.php b/src/Render/SvgFlatRender.php index cd031c7..1fa2895 100644 --- a/src/Render/SvgFlatRender.php +++ b/src/Render/SvgFlatRender.php @@ -11,130 +11,14 @@ namespace PUGX\Poser\Render; -use PUGX\Poser\Badge; -use PUGX\Poser\Calculator\TextSizeCalculatorInterface; -use PUGX\Poser\Calculator\GDTextSizeCalculator; -use PUGX\Poser\Image; /** * Class SvgFlatGenerator * * @author Giulio De Donato */ -class SvgFlatRender implements RenderInterface +class SvgFlatRender extends LocalSvgRenderer { - const VENDOR_COLOR = '#555'; - private $textSizeCalculator; -/* - private static $template = << - - - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; - - -quasi ok -ù private static $template = << - - - - - - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; -*/ - -private static $template = << - - - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; - - /** - * Constructor. - * - * @param TextSizeCalculatorInterface $textSizeCalculator - */ - public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) - { - $this->textSizeCalculator = $textSizeCalculator; - - if (null === $this->textSizeCalculator) { - $this->textSizeCalculator = new GDTextSizeCalculator(); - } - } - - /** - * @param Badge $badge - * - * @return mixed - */ - public function render(Badge $badge) - { - $parameters = array(); - - $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); - $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); - $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; - $parameters['vendorColor'] = self::VENDOR_COLOR; - $parameters['valueColor'] = $badge->getHexColor(); - $parameters['vendor'] = $badge->getSubject(); - $parameters['value'] = $badge->getStatus(); - $parameters['vendorStartPosition'] = round($parameters['vendorWidth'] / 2, 1) + 1; - $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; - - return $this->renderSvg(self::$template, $parameters, $badge->getFormat()); - } + protected $templateName = 'flat'; /** * A list of all supported formats. @@ -145,18 +29,4 @@ public function supportedFormats() { return array('flat', 'svg'); } - - private function stringWidth($text) - { - return $this->textSizeCalculator->calculateWidth($text); - } - - private function renderSvg($render, $parameters, $format) - { - foreach ($parameters as $key => $variable) { - $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); - } - - return Image::createFromString($render, $format); - } } diff --git a/src/Render/SvgFlatSquareRender.php b/src/Render/SvgFlatSquareRender.php index 902c09f..2af5818 100644 --- a/src/Render/SvgFlatSquareRender.php +++ b/src/Render/SvgFlatSquareRender.php @@ -11,130 +11,14 @@ namespace PUGX\Poser\Render; -use PUGX\Poser\Badge; -use PUGX\Poser\Calculator\TextSizeCalculatorInterface; -use PUGX\Poser\Calculator\GDTextSizeCalculator; -use PUGX\Poser\Image; /** * Class SvgFlatGenerator * * @author Giulio De Donato */ -class SvgFlatSquareRender implements RenderInterface +class SvgFlatSquareRender extends LocalSvgRenderer { - const VENDOR_COLOR = '#555'; - private $textSizeCalculator; -/* - private static $template = << - - - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; - - -quasi ok -ù private static $template = << - - - - - - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; -*/ - -private static $template = << - - - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; - - /** - * Constructor. - * - * @param TextSizeCalculatorInterface $textSizeCalculator - */ - public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) - { - $this->textSizeCalculator = $textSizeCalculator; - - if (null === $this->textSizeCalculator) { - $this->textSizeCalculator = new GDTextSizeCalculator(); - } - } - - /** - * @param Badge $badge - * - * @return mixed - */ - public function render(Badge $badge) - { - $parameters = array(); - - $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); - $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); - $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; - $parameters['vendorColor'] = self::VENDOR_COLOR; - $parameters['valueColor'] = $badge->getHexColor(); - $parameters['vendor'] = $badge->getSubject(); - $parameters['value'] = $badge->getStatus(); - $parameters['vendorStartPosition'] = round($parameters['vendorWidth'] / 2, 1) + 1; - $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; - - return $this->renderSvg(self::$template, $parameters, $badge->getFormat()); - } + protected $templateName = 'flat-square'; /** * A list of all supported formats. @@ -145,18 +29,4 @@ public function supportedFormats() { return array('flat-square'); } - - private function stringWidth($text) - { - return $this->textSizeCalculator->calculateWidth($text); - } - - private function renderSvg($render, $parameters, $format) - { - foreach ($parameters as $key => $variable) { - $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); - } - - return Image::createFromString($render, $format); - } } diff --git a/src/Render/SvgRender.php b/src/Render/SvgRender.php index 7492301..a04fd21 100644 --- a/src/Render/SvgRender.php +++ b/src/Render/SvgRender.php @@ -12,75 +12,16 @@ namespace PUGX\Poser\Render; use PUGX\Poser\Badge; -use PUGX\Poser\Calculator\TextSizeCalculatorInterface; -use PUGX\Poser\Calculator\GDTextSizeCalculator; -use PUGX\Poser\Image; + /** * Class SvgGenerator * * @author Claudio D'Alicandro * @author Giulio De Donato */ -class SvgRender implements RenderInterface +class SvgRender extends LocalSvgRenderer { - const VENDOR_COLOR = '#555'; - private $textSizeCalculator; - private static $template = << - - - - - - - - - - - - {{ vendor }} - {{ vendor }} - {{ value }} - {{ value }} - - -EOF; - - /** - * Constructor. - * - * @param TextSizeCalculatorInterface $textSizeCalculator - */ - public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) - { - $this->textSizeCalculator = $textSizeCalculator; - - if (null === $this->textSizeCalculator) { - $this->textSizeCalculator = new GDTextSizeCalculator(); - } - } - - /** - * @param Badge $badge - * - * @return mixed - */ - public function render(Badge $badge) - { - $parameters = array(); - - $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); - $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); - $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; - $parameters['vendorColor'] = self::VENDOR_COLOR; - $parameters['valueColor'] = $badge->getHexColor(); - $parameters['vendor'] = $badge->getSubject(); - $parameters['value'] = $badge->getStatus(); - $parameters['vendorStartPosition'] = round($parameters['vendorWidth'] / 2, 1) + 1; - $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; - - return $this->renderSvg(self::$template, $parameters, $badge->getFormat()); - } + protected $templateName = 'plastic'; /** * A list of all supported formats. @@ -91,18 +32,4 @@ public function supportedFormats() { return array('plastic'); } - - private function stringWidth($text) - { - return $this->textSizeCalculator->calculateWidth($text); - } - - private function renderSvg($render, $parameters, $format) - { - foreach ($parameters as $key => $variable) { - $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); - } - - return Image::createFromString($render, $format); - } } diff --git a/src/Resources/templates/flat-square.svg b/src/Resources/templates/flat-square.svg new file mode 100644 index 0000000..8bb3772 --- /dev/null +++ b/src/Resources/templates/flat-square.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + {{ vendor }} + {{ vendor }} + {{ value }} + {{ value }} + + diff --git a/src/Resources/templates/flat.svg b/src/Resources/templates/flat.svg new file mode 100644 index 0000000..bf8eb64 --- /dev/null +++ b/src/Resources/templates/flat.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + {{ vendor }} + {{ vendor }} + {{ value }} + {{ value }} + + diff --git a/src/Resources/templates/plastic.svg b/src/Resources/templates/plastic.svg new file mode 100644 index 0000000..fc36097 --- /dev/null +++ b/src/Resources/templates/plastic.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + {{ vendor }} + {{ vendor }} + {{ value }} + {{ value }} + + From 1b70bbe62c543cadb0cc555a16200ad7bb16566b Mon Sep 17 00:00:00 2001 From: jmleroux Date: Wed, 23 Mar 2016 14:19:12 +0100 Subject: [PATCH 2/4] Code review by @stof --- src/Render/LocalSvgRenderer.php | 25 +++++++++++++++---------- src/Render/SvgFlatRender.php | 7 +++++-- src/Render/SvgFlatSquareRender.php | 7 +++++-- src/Render/SvgRender.php | 7 +++++-- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/Render/LocalSvgRenderer.php b/src/Render/LocalSvgRenderer.php index 0cd5ae6..a7130d1 100644 --- a/src/Render/LocalSvgRenderer.php +++ b/src/Render/LocalSvgRenderer.php @@ -29,16 +29,9 @@ abstract class LocalSvgRenderer implements RenderInterface /** * @var TextSizeCalculatorInterface */ - protected $textSizeCalculator; + private $textSizeCalculator; /** - * @var string - */ - protected $templateName; - - /** - * Constructor. - * * @param TextSizeCalculatorInterface $textSizeCalculator */ public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) @@ -57,12 +50,17 @@ public function __construct(TextSizeCalculatorInterface $textSizeCalculator = nu */ public function render(Badge $badge) { - $template = $this->getTemplate($this->templateName); + $template = $this->getTemplate($this->getTemplateName()); $parameters = $this->buildParameters($badge); return $this->renderSvg($template, $parameters, $badge->getFormat()); } + /** + * @return string + */ + abstract protected function getTemplateName(); + /** * @param $format * @@ -103,6 +101,13 @@ protected function renderSvg($render, $parameters, $format) $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); } + // validate svg + libxml_use_internal_errors(true); + $xml = simplexml_load_string($render); + if (false === $xml) { + throw new \RuntimeException('Generated string is not a valid SVG'); + } + return Image::createFromString($render, $format); } @@ -111,7 +116,7 @@ protected function renderSvg($render, $parameters, $format) * * @return array */ - protected function buildParameters(Badge $badge) + private function buildParameters(Badge $badge) { $parameters = array(); diff --git a/src/Render/SvgFlatRender.php b/src/Render/SvgFlatRender.php index 1fa2895..9ef9e93 100644 --- a/src/Render/SvgFlatRender.php +++ b/src/Render/SvgFlatRender.php @@ -18,8 +18,6 @@ */ class SvgFlatRender extends LocalSvgRenderer { - protected $templateName = 'flat'; - /** * A list of all supported formats. * @@ -29,4 +27,9 @@ public function supportedFormats() { return array('flat', 'svg'); } + + protected function getTemplateName() + { + return 'flat'; + } } diff --git a/src/Render/SvgFlatSquareRender.php b/src/Render/SvgFlatSquareRender.php index 2af5818..439fbb2 100644 --- a/src/Render/SvgFlatSquareRender.php +++ b/src/Render/SvgFlatSquareRender.php @@ -18,8 +18,6 @@ */ class SvgFlatSquareRender extends LocalSvgRenderer { - protected $templateName = 'flat-square'; - /** * A list of all supported formats. * @@ -29,4 +27,9 @@ public function supportedFormats() { return array('flat-square'); } + + protected function getTemplateName() + { + return 'flat-square'; + } } diff --git a/src/Render/SvgRender.php b/src/Render/SvgRender.php index a04fd21..64812ea 100644 --- a/src/Render/SvgRender.php +++ b/src/Render/SvgRender.php @@ -21,8 +21,6 @@ */ class SvgRender extends LocalSvgRenderer { - protected $templateName = 'plastic'; - /** * A list of all supported formats. * @@ -32,4 +30,9 @@ public function supportedFormats() { return array('plastic'); } + + protected function getTemplateName() + { + return 'plastic'; + } } From b58426088d69edd6b5d84019722e11e03cbee36b Mon Sep 17 00:00:00 2001 From: jmleroux Date: Wed, 23 Mar 2016 14:55:02 +0100 Subject: [PATCH 3/4] LocalSvgRenderer private methods --- src/Render/LocalSvgRenderer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Render/LocalSvgRenderer.php b/src/Render/LocalSvgRenderer.php index a7130d1..bb0b039 100644 --- a/src/Render/LocalSvgRenderer.php +++ b/src/Render/LocalSvgRenderer.php @@ -66,7 +66,7 @@ abstract protected function getTemplateName(); * * @return string SVG content of the template */ - protected function getTemplate($format) + private function getTemplate($format) { $templatesDirectory = __DIR__ . '/../Resources/templates'; $filepath = sprintf('%s/%s.svg', $templatesDirectory, $format); @@ -83,7 +83,7 @@ protected function getTemplate($format) * * @return float */ - protected function stringWidth($text) + private function stringWidth($text) { return $this->textSizeCalculator->calculateWidth($text); } @@ -95,7 +95,7 @@ protected function stringWidth($text) * * @return Image */ - protected function renderSvg($render, $parameters, $format) + private function renderSvg($render, $parameters, $format) { foreach ($parameters as $key => $variable) { $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); From 1bf3f010fa191e61b92e44c4ceac2602919855c8 Mon Sep 17 00:00:00 2001 From: jmleroux Date: Wed, 23 Mar 2016 15:06:42 +0100 Subject: [PATCH 4/4] LocalSvgRenderer check for correct svg file --- spec/Fixtures/invalid_template/plastic.svg | 13 ++++++ spec/Fixtures/xml_template/plastic.svg | 26 +++++++++++ spec/PUGX/Poser/Render/SvgRenderSpec.php | 16 +++++++ src/Render/LocalSvgRenderer.php | 50 ++++++++++++++-------- 4 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 spec/Fixtures/invalid_template/plastic.svg create mode 100644 spec/Fixtures/xml_template/plastic.svg diff --git a/spec/Fixtures/invalid_template/plastic.svg b/spec/Fixtures/invalid_template/plastic.svg new file mode 100644 index 0000000..94b2a65 --- /dev/null +++ b/spec/Fixtures/invalid_template/plastic.svg @@ -0,0 +1,13 @@ + + + + + + + + + stable + stable + v2.0 + v2.0 + diff --git a/spec/Fixtures/xml_template/plastic.svg b/spec/Fixtures/xml_template/plastic.svg new file mode 100644 index 0000000..0ec2225 --- /dev/null +++ b/spec/Fixtures/xml_template/plastic.svg @@ -0,0 +1,26 @@ + + + + PHP: Behind the Parser + + + Ms. Coder + Onlivia Actora + + + Mr. Coder + El ActÓr + + + + So, this language. It's like, a programming language. Or is it a + scripting language? All is revealed in this thrilling horror spoof + of a documentary. + + + PHP solves all my web problems + + 7 + 5 + + diff --git a/spec/PUGX/Poser/Render/SvgRenderSpec.php b/spec/PUGX/Poser/Render/SvgRenderSpec.php index 34e54f9..50dc13c 100644 --- a/spec/PUGX/Poser/Render/SvgRenderSpec.php +++ b/spec/PUGX/Poser/Render/SvgRenderSpec.php @@ -21,6 +21,22 @@ function it_should_render_a_svg() $this->render($badge)->shouldBeAValidSVGImage(); } + function it_should_not_render_an_invalid_svg($calculator) + { + $templatesDir = __DIR__ . '/../../../Fixtures/invalid_template'; + $this->beConstructedWith($calculator, $templatesDir); + $badge = Badge::fromURI('version-stable-97CA00.svg'); + $this->shouldThrow(new \RuntimeException('Generated string is not a valid XML'))->duringRender($badge); + } + + function it_should_not_render_non_svg_xml($calculator) + { + $templatesDir = __DIR__ . '/../../../Fixtures/xml_template'; + $this->beConstructedWith($calculator, $templatesDir); + $badge = Badge::fromURI('version-stable-97CA00.svg'); + $this->shouldThrow(new \RuntimeException('Generated xml is not a SVG'))->duringRender($badge); + } + public function getMatchers() { return array( diff --git a/src/Render/LocalSvgRenderer.php b/src/Render/LocalSvgRenderer.php index bb0b039..5d37ec3 100644 --- a/src/Render/LocalSvgRenderer.php +++ b/src/Render/LocalSvgRenderer.php @@ -15,6 +15,7 @@ use PUGX\Poser\Calculator\GDTextSizeCalculator; use PUGX\Poser\Calculator\TextSizeCalculatorInterface; use PUGX\Poser\Image; +use SimpleXMLElement; /** * Local SVG renderer. @@ -31,16 +32,26 @@ abstract class LocalSvgRenderer implements RenderInterface */ private $textSizeCalculator; + /** + * @var string + */ + private $templatesDirectory; + /** * @param TextSizeCalculatorInterface $textSizeCalculator + * @param null|string $templatesDirectory */ - public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null) + public function __construct(TextSizeCalculatorInterface $textSizeCalculator = null, $templatesDirectory = null) { $this->textSizeCalculator = $textSizeCalculator; - if (null === $this->textSizeCalculator) { $this->textSizeCalculator = new GDTextSizeCalculator(); } + + $this->templatesDirectory = $templatesDirectory; + if (null === $this->templatesDirectory) { + $this->templatesDirectory = __DIR__ . '/../Resources/templates';; + } } /** @@ -50,7 +61,7 @@ public function __construct(TextSizeCalculatorInterface $textSizeCalculator = nu */ public function render(Badge $badge) { - $template = $this->getTemplate($this->getTemplateName()); + $template = $this->getTemplate($this->getTemplateName()); $parameters = $this->buildParameters($badge); return $this->renderSvg($template, $parameters, $badge->getFormat()); @@ -68,8 +79,7 @@ abstract protected function getTemplateName(); */ private function getTemplate($format) { - $templatesDirectory = __DIR__ . '/../Resources/templates'; - $filepath = sprintf('%s/%s.svg', $templatesDirectory, $format); + $filepath = sprintf('%s/%s.svg', $this->templatesDirectory, $format); if (!file_exists($filepath)) { throw new \InvalidArgumentException(sprintf('No template for format %s', $format)); @@ -90,7 +100,7 @@ private function stringWidth($text) /** * @param string $render - * @param array $parameters + * @param array $parameters * @param string $format * * @return Image @@ -101,11 +111,13 @@ private function renderSvg($render, $parameters, $format) $render = str_replace(sprintf('{{ %s }}', $key), $variable, $render); } - // validate svg - libxml_use_internal_errors(true); - $xml = simplexml_load_string($render); - if (false === $xml) { - throw new \RuntimeException('Generated string is not a valid SVG'); + try { + $xml = new SimpleXMLElement($render); + } catch (\Exception $e) { + throw new \RuntimeException('Generated string is not a valid XML'); + } + if ('svg' !== $xml->getName()) { + throw new \RuntimeException('Generated xml is not a SVG'); } return Image::createFromString($render, $format); @@ -120,15 +132,15 @@ private function buildParameters(Badge $badge) { $parameters = array(); - $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); - $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); - $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; - $parameters['vendorColor'] = static::VENDOR_COLOR; - $parameters['valueColor'] = $badge->getHexColor(); - $parameters['vendor'] = $badge->getSubject(); - $parameters['value'] = $badge->getStatus(); + $parameters['vendorWidth'] = $this->stringWidth($badge->getSubject()); + $parameters['valueWidth'] = $this->stringWidth($badge->getStatus()); + $parameters['totalWidth'] = $parameters['valueWidth'] + $parameters['vendorWidth']; + $parameters['vendorColor'] = static::VENDOR_COLOR; + $parameters['valueColor'] = $badge->getHexColor(); + $parameters['vendor'] = $badge->getSubject(); + $parameters['value'] = $badge->getStatus(); $parameters['vendorStartPosition'] = round($parameters['vendorWidth'] / 2, 1) + 1; - $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; + $parameters['valueStartPosition'] = $parameters['vendorWidth'] + round($parameters['valueWidth'] / 2, 1) - 1; return $parameters; }