For coders TYPO3 Tech Corner

[TYPO3] Render svg-files inline in Fluid

[TYPO3] Render svg-files inline in Fluid

With a simple ViewHelper you can render SVG better in the future. This may save the server from a lot of unnecessary requests if you use a lot of these icons.

Before: <img src="/typo3conf/ext/in2template/Resources/Public/Logo.svg" width="200" height="100" title="Logo" id="logo"/> After: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 487.56 340.16" id="logo" width="200" height="100" title="Logo"> <g class="cls-1"> ... </g> </svg>

The integration into Fluid could then look like this:

<in2template:resource.svgInline src="EXT:in2template/Resources/Public/Logo-small.svg" width="200"/> or with all attributes: <in2template:resource.svgInline src="EXT:in2template/Resources/Public/Logo-small.svg" width="200" height="100" id="logo" title="Logo" class="className" viewBox="0 0 100 100" data="{foo:'bar'}" additionalAttributes="{onclick:'anything()'}"/>

The ViewHelper for this could be in your site package (here EXT:in2template/Classes/ViewHelpers/Resource/SvgInlineViewHelper.php):

<?php declare(strict_types=1); namespace In2code\In2template\ViewHelpers\Resource; use Closure; use DOMDocument; use DOMElement; use In2code\In2template\Exception\FileException; use SimpleXMLElement; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Service\ImageService; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; class SvgInlineViewHelper extends AbstractViewHelper { /** * @var bool */ protected $escapeOutput = false; /** * Initializing arguments * * @return void */ public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('src', 'string', 'e.g. EXT:in2template/Resources/Public/Images/any.svg', true); $this->registerArgument('id', 'string', 'Id to set in the svg'); $this->registerArgument('class', 'string', 'Css class(es) for the svg'); $this->registerArgument('width', 'string', 'Width of the svg.'); $this->registerArgument('height', 'string', 'Height of the svg.'); $this->registerArgument('viewBox', 'string', 'Specifies the view box for the svg'); $this->registerArgument('data', 'array', 'Array of data-attributes'); $this->registerArgument('additionalAttributes', 'array', 'any attributes', false, []); } /** * @param array $arguments * @param Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return string * @SuppressWarnings(PHPMD) * @throws FileException */ public static function renderStatic( array $arguments, Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ) { $imageService = GeneralUtility::makeInstance(ImageService::class); if ((string)$arguments['src'] === '') { throw new FileException('You must specify a string src.', 1666087832); } $image = $imageService->getImage($arguments['src'], null, false); if ($image->getExtension() !== 'svg') { throw new FileException('You must provide a svg file.', 1666087835); } $svgContent = $image->getContents(); if ($svgContent === '') { throw new FileException('The svg file must not be empty.', 1666087837); } $attributes = [ 'id' => $arguments['id'], 'class' => $arguments['class'], 'width' => $arguments['width'], 'height' => $arguments['height'], 'viewBox' => $arguments['viewBox'], 'data' => $arguments['data'], ] + $arguments['additionalAttributes']; return self::getInlineSvg($svgContent, $attributes); } /** * @param string $svgContent * @param array $attributes * @return string */ protected static function getInlineSvg( string $svgContent, array $attributes = [] ): string { $svgElement = simplexml_load_string($svgContent); if (!$svgElement instanceof SimpleXMLElement) { return ''; } $domXml = dom_import_simplexml($svgElement); if (!$domXml instanceof DOMElement || !$domXml->ownerDocument instanceof DOMDocument) { return ''; } foreach (self::updateAttributes($attributes) as $attributeKey => $attributeValue) { if ($attributeValue !== null) { $domXml->setAttribute($attributeKey, htmlspecialchars((string)$attributeValue)); } } return (string)$domXml->ownerDocument->saveXML($domXml->ownerDocument->documentElement); } /** * @param array $attributes * @return array */ protected static function updateAttributes(array $attributes): array { if ($attributes['id'] !== null) { $attributes['id'] = htmlspecialchars(trim((string)$attributes['id'])); } if (is_array($attributes['data'])) { foreach ($attributes['data'] as $attributeDataKey => $attributeDataValue) { $attributes['data-' . (string)$attributeDataKey] = htmlspecialchars((string)$attributeDataValue); } unset($attributes['data']); } return $attributes; } }

As always, this example assumes you are using a sitepackage. I didn't include the empty FileException file.

Inspired by review.typo3.org/c/Packages/TYPO3.CMS/+/70798/10/typo3/sysext/fluid/Classes/ViewHelpers/SvgInlineViewHelper.php by Marcus Schwemer.

Back

"Code faster, look at the time" - does this sound familiar to you?

How about time and respect for code quality? Working in a team? Automated tests?

Join us