Skip to content
Snippets Groups Projects
Commit f98cb4cb authored by Johannes Kreiner's avatar Johannes Kreiner
Browse files

[TASK] Update RenderSvgViewHelper

parent ef538968
No related branches found
No related tags found
No related merge requests found
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* All rights reserved * All rights reserved
* *
* This script is part of the AY project. The AY project is * This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify * free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
namespace SGalinski\SgYoutube\ViewHelpers; namespace SGalinski\SgYoutube\ViewHelpers;
use Exception;
use SimpleXMLElement;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/** /**
...@@ -39,7 +41,18 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -39,7 +41,18 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
* @api * @api
*/ */
protected $tagName = 'svg'; protected $tagName = 'svg';
protected $xmlInfo = '<?xml version="1.0"?>';
/**
* @var array
*/
protected static array $svgInstances = [];
/**
* The directory path where the SVGs are located
*
* @var string
*/
protected $directoryPath = __DIR__ . '/../../Resources/Public/Icons/'; protected $directoryPath = __DIR__ . '/../../Resources/Public/Icons/';
/** /**
...@@ -47,39 +60,77 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -47,39 +60,77 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
*/ */
public function initializeArguments(): void { public function initializeArguments(): void {
parent::initializeArguments(); parent::initializeArguments();
$this->registerArgument('name', 'string', 'The SVG name', TRUE); $this->registerArgument(
$this->registerArgument('color', 'string', 'The fill color', TRUE); 'name',
$this->registerArgument('id', 'string', 'The HTML id attribute', FALSE); 'string',
$this->registerArgument('path', 'string', 'Path to the SVG file if not in default', FALSE); 'The SVG name, also supports folders (e.g. fontawesome/solid/ad)',
$this->registerArgument('class', 'string', 'The HTML class attribute', FALSE); TRUE
$this->registerArgument('title', 'string', 'The HTML title attribute', FALSE); );
$this->registerArgument('width', 'string', 'The HTML width attribute', FALSE); $this->registerArgument(
$this->registerArgument('height', 'string', 'The HTML height attribute', FALSE); 'color',
$this->registerArgument('style', 'string', 'Inline CSS styles', FALSE); 'string',
$this->registerArgument('use', 'string', 'Inline CSS styles', FALSE); 'The color',
FALSE,
'currentColor'
);
$this->registerArgument(
'colorAttribute',
'string',
'The color target to affect: fill, stroke. Leave empty for both'
);
$this->registerArgument('id', 'string', 'The HTML id attribute');
$this->registerArgument('path', 'string', 'Path to the SVG file if not in default');
$this->registerArgument('class', 'string', 'The HTML class attribute');
$this->registerArgument('title', 'string', 'The HTML title attribute');
$this->registerArgument('width', 'string', 'The HTML width attribute');
$this->registerArgument('height', 'string', 'The HTML height attribute');
$this->registerArgument('viewBox', 'string', 'The HTML viewBox attribute');
$this->registerArgument(
'viewBoxOnly',
'boolean',
'If only the viewBox attribute should be used instead of width/height'
);
$this->registerArgument('stroke-width', 'string', 'The HTML stroke-width attribute');
$this->registerArgument('style', 'string', 'Inline CSS styles');
$this->registerArgument('use', 'string', 'Inline CSS styles');
$this->registerArgument(
'createNewInstance',
'boolean',
'Creates a new instance without reusing other ones'
);
$this->registerArgument('preserveColors', 'boolean', 'Preserves the original colors');
$this->registerArgument(
'createColorAttribute',
'boolean',
'Creates the color target for cases when it doesn\'t exist.',
FALSE,
FALSE
);
} }
/** /**
* Render the SVG file as an inline SVG element. * Render the SVG file as an inline SVG element.
* *
* @return string The rendered SVG element * @return string The rendered SVG element
* @throws \Exception * @throws Exception
*/ */
public function render(): string { public function render(): string {
global $tx_sgvideo_svgIds;
if (!$tx_sgvideo_svgIds) {
$tx_sgvideo_svgIds = [];
}
$name = $this->arguments['name']; $name = $this->arguments['name'];
$path = $this->arguments['path']; $path = $this->arguments['path'];
$width = $this->arguments['width']; $width = $this->arguments['width'];
$height = $this->arguments['height']; $height = $this->arguments['height'];
$viewBox = $this->arguments['viewBox'];
$viewBoxOnly = $this->arguments['viewBoxOnly'];
$strokeWidth = $this->arguments['stroke-width'];
$color = $this->arguments['color']; $color = $this->arguments['color'];
$colorAttribute = $this->arguments['colorAttribute'];
$id = $this->arguments['id']; $id = $this->arguments['id'];
$class = $this->arguments['class']; $class = $this->arguments['class'];
$style = $this->arguments['style']; $style = $this->arguments['style'];
$title = $this->arguments['title']; $title = $this->arguments['title'];
$createNewInstance = $this->arguments['createNewInstance'];
$preserveColors = $this->arguments['preserveColors'];
$createColorAttribute = $this->arguments['createColorAttribute'];
$src = $path ?? $this->directoryPath . '/' . $name . '.svg'; $src = $path ?? $this->directoryPath . '/' . $name . '.svg';
...@@ -92,7 +143,7 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -92,7 +143,7 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
} }
// Load the SVG into a SimpleXMLElement object // Load the SVG into a SimpleXMLElement object
$svg = new \SimpleXMLElement($content); $svg = new SimpleXMLElement($content);
// Set the attributes of the SVG element // Set the attributes of the SVG element
if ($width > 0) { if ($width > 0) {
...@@ -103,8 +154,30 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -103,8 +154,30 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
$this->addOrReplaceAttribute($svg, 'height', $height); $this->addOrReplaceAttribute($svg, 'height', $height);
} }
if (!empty($color)) { if (!empty($viewBox)) {
$this->setFill($svg, $color); $this->addOrReplaceAttribute($svg, 'viewBox', $viewBox);
}
if ($viewBoxOnly) {
unset($svg->attributes()->width, $svg->attributes()->height);
}
if ($strokeWidth > 0) {
$this->setStrokeWidth($svg, $strokeWidth);
}
if (!empty($color) && !$preserveColors) {
switch ($colorAttribute) {
case 'fill':
$this->setFill($svg, $color, $createColorAttribute);
break;
case 'stroke':
$this->setStroke($svg, $color, $createColorAttribute);
break;
default:
$this->setFill($svg, $color, $createColorAttribute);
$this->setStroke($svg, $color, $createColorAttribute);
}
} }
if ($style) { if ($style) {
...@@ -119,124 +192,106 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -119,124 +192,106 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
$this->addOrReplaceAttribute($svg, 'title', $title); $this->addOrReplaceAttribute($svg, 'title', $title);
} }
if ($createNewInstance) {
return str_replace($this->xmlInfo, '', $svg->asXML());
}
// Extract the SVG contents // Extract the SVG contents
$contents = $this->getContents($svg, TRUE); $contents = $this->getContents($svg, TRUE);
// Check if the SVG has already been rendered and use the <use> tag if possible // Check if the SVG has already been rendered and use the <use> tag if possible
if (!empty($tx_sgvideo_svgIds)) { if (isset(static::$svgInstances[$id])) {
if (isset($tx_sgvideo_svgIds[$id])) { $use = $svg->addChild('use');
$use = $svg->addChild('use'); // The boolean conversion of SimpleXMLElement is broken, therefore we MUST use instanceof
// The boolean conversion of SimpleXMLElement is broken, therefore we MUST use instanceof if ($use instanceof SimpleXMLElement) {
if ($use instanceof \SimpleXMLElement) { $use->addAttribute('href', '#' . $id);
$use->addAttribute('href', '#' . $id);
}
return $svg->asXML();
} }
} else {
$tx_sgvideo_svgIds = []; return str_replace($this->xmlInfo, '', $svg->asXML());
} }
// Add the unique ID to the list of rendered SVGs // Add the unique ID to the list of rendered SVGs
$tx_sgvideo_svgIds[$id] = $id; static::$svgInstances[$id] = $id;
$contentsElement = new \SimpleXMLElement($contents); $contentsElement = new SimpleXMLElement($contents);
$group = $svg->addChild('g'); $group = $svg->addChild('g');
// The boolean conversion of SimpleXMLElement is broken, therefore we MUST explicitly check against null // The boolean conversion of SimpleXMLElement is broken, therefore we MUST explicitly check against null
if ($group === NULL) { if ($group === NULL) {
return $svg->asXML(); return str_replace($this->xmlInfo, '', $svg->asXML());
} }
/** @noinspection NullPointerExceptionInspection */
$group->addAttribute('id', $id); $group->addAttribute('id', $id);
$this->xmlAdopt($group, $contentsElement); $this->xmlAdopt($group, $contentsElement);
return $svg->asXML(); return str_replace($this->xmlInfo, '', $svg->asXML());
//Todo: we don't really want this, do we?
// if (!empty($width) && !empty($height)) {
// $resultWithoutCssClass = str_replace($cssClass, '', $result);
// $cssClasses = $this->processWrapperClasses($cssClassPlain);
//
// return "<span $cssClasses style='width: $width; height: $height;'>$resultWithoutCssClass</span>";
// }
//
// if (!empty($width)) {
// $resultWithoutCssClass = str_replace($cssClass, '', $result);
// $cssClasses = $this->processWrapperClasses($cssClassPlain);
//
// return "<span $cssClasses style='width: $width;'>$resultWithoutCssClass</span>";
// }
//
// if (!empty($height)) {
// $resultWithoutCssClass = str_replace($cssClass, '', $result);
// $cssClasses = $this->processWrapperClasses($cssClassPlain);
//
// return "<span $cssClasses style='height: $height;'>$resultWithoutCssClass</span>";
// }
//
// return $result;
} }
/** /**
* Processes the common SVG wrapper's classes * Set the fill color of an SVG element.
* *
* @param string $cssClass * @param SimpleXMLElement $element The SVG element
* @return string * @param string $fill The fill color to set
* @param boolean $createColorAttribute If the attribute should be added
*/ */
public function processWrapperClasses(string $cssClass): string { protected function setFill(SimpleXMLElement $element, string $fill, bool $createColorAttribute): void {
return "class='$cssClass svg-wrapper d-inline-flex justify-content-center align-items-center'"; foreach ($element->children() as $child) {
$this->setFill($child, $fill, $createColorAttribute);
}
if ($createColorAttribute) {
$element->addAttribute('fill', $fill);
} elseif (isset($element->attributes()->fill)) {
$element->attributes()->fill = $fill;
}
} }
/** /**
* Processes the wrappers for the usage of the <use> SVG functionality * Set the stroke color of an SVG element.
* *
* @param string $svgContent * @param SimpleXMLElement $element The SVG element
* @param string $id * @param string $stroke The stroke color to set
* @param string $use * @param boolean $createColorAttribute If the attribute should be added
* @return string
*/ */
public function processUseWrappers(string $svgContent, string $id, string $use): string { protected function setStroke(SimpleXMLElement $element, string $stroke, bool $createColorAttribute): void {
$svgContent = "<g $id>$svgContent</g>"; foreach ($element->children() as $child) {
$this->setStroke($child, $stroke, $createColorAttribute);
if ($use) { }
$svgContent = "<use href='#$use' />"; if ($createColorAttribute) {
$element->addAttribute('stroke', $stroke);
} elseif (isset($element->attributes()->stroke)) {
$element->attributes()->stroke = $stroke;
} }
return $svgContent;
} }
/** /**
* Set the fill color of an SVG element. * Set the stroke width of an SVG element.
* *
* @param \SimpleXMLElement $element The SVG element * @param SimpleXMLElement $element The SVG element
* @param string $fill The fill color to set * @param string $strokeWidth The stroke width to set
*/ */
protected function setFill(\SimpleXMLElement $element, string $fill): void { protected function setStrokeWidth(SimpleXMLElement $element, string $strokeWidth): void {
foreach ($element->children() as $child) { foreach ($element->children() as $child) {
$this->setFill($child, $fill); $this->setStrokeWidth($child, $strokeWidth);
} }
if (isset($element->attributes()->fill)) { foreach ($element->path as $path) {
$element->attributes()->fill = $fill; $this->addOrReplaceAttribute($path, 'stroke-width', $strokeWidth);
} }
} }
/** /**
* Gets the contents of the SVG file * Gets the contents of the SVG file
* *
* @param \SimpleXMLElement $svg * @param SimpleXMLElement $svg
* @param bool $removeNode * @param bool $removeNode
* @return string * @return string
*/ */
private function getContents(\SimpleXMLElement $svg, bool $removeNode = FALSE): string { private function getContents(SimpleXMLElement $svg, bool $removeNode = FALSE): string {
$contents = ''; $contents = '';
foreach ($svg->children() as $child) { foreach ($svg->children() as $child) {
$contents .= $child->asXML() . "\n"; $contents .= $child->asXML() . "\n";
if ($removeNode) { if ($removeNode) {
$dom = dom_import_simplexml($child); $dom = dom_import_simplexml($child);
if ($dom) { $dom->parentNode->removeChild($dom);
$dom->parentNode->removeChild($dom);
}
} }
} }
...@@ -246,20 +301,21 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -246,20 +301,21 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
/** /**
* Inserts a new SimpleXMLElement at the given root * Inserts a new SimpleXMLElement at the given root
* *
* @param \SimpleXMLElement $root * @param SimpleXMLElement $root
* @param \SimpleXMLElement $newElement * @param SimpleXMLElement $newElement
* @return void * @return void
*/ */
private function xmlAdopt(\SimpleXMLElement $root, \SimpleXMLElement $newElement): void { private function xmlAdopt(SimpleXMLElement $root, SimpleXMLElement $newElement): void {
$node = $root->addChild($newElement->getName(), (string) $newElement); $node = $root->addChild($newElement->getName(), (string) $newElement);
if ($node === NULL) {
return;
}
foreach ($newElement->attributes() as $attr => $value) { foreach ($newElement->attributes() as $attr => $value) {
/** @noinspection NullPointerExceptionInspection */
$node->addAttribute($attr, $value); $node->addAttribute($attr, $value);
} }
foreach ($newElement->children() as $ch) { foreach ($newElement->children() as $ch) {
/** @noinspection NullPointerExceptionInspection */
$this->xmlAdopt($node, $ch); $this->xmlAdopt($node, $ch);
} }
} }
...@@ -267,20 +323,21 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper { ...@@ -267,20 +323,21 @@ class RenderSvgViewHelper extends AbstractTagBasedViewHelper {
/** /**
* Adds or replaces an attribute * Adds or replaces an attribute
* *
* @param \SimpleXMLElement $element * @param SimpleXMLElement $element
* @param string $attributeName * @param string $attributeName
* @param string $attributeValue * @param string $attributeValue
* @return void * @return void
*/ */
private function addOrReplaceAttribute( private function addOrReplaceAttribute(
\SimpleXMLElement $element, SimpleXMLElement $element,
string $attributeName, string $attributeName,
string $attributeValue string $attributeValue
): void { ): void {
if (isset($element[$attributeName])) { if (isset($element[$attributeName])) {
$element->$attributeName = $attributeValue; // Replace existing attribute value // Replace existing attribute value
} else { unset($element[$attributeName]);
$element->addAttribute($attributeName, $attributeValue); // Add new attribute
} }
$element->addAttribute($attributeName, $attributeValue);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment