Created
March 25, 2020 14:41
-
-
Save a-r-m-i-n/5779e15a114880c23685c268eac6bb12 to your computer and use it in GitHub Desktop.
Font Awesome ViewHelper (using SVG sprites) for TYPO3 CMS 9+
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php declare(strict_types=1); | |
namespace Armin\Vieweg\ViewHelpers; | |
use TYPO3\CMS\Core\Core\Environment; | |
use TYPO3\CMS\Core\Utility\GeneralUtility; | |
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; | |
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; | |
/** | |
* Font Awesome SVG ViewHelper | |
* | |
* Usage examples: | |
* | |
* <av:fa icon="fab typo3" /> | |
* <av:fa icon="fas file" /> == <av:fa icon="file" /> | |
* <av:fa icon="far file" /> | |
*/ | |
class FaViewHelper extends AbstractViewHelper | |
{ | |
protected $escapeOutput = false; | |
protected static $cache = []; | |
public function initializeArguments() | |
{ | |
$this->registerArgument('icon', 'string', 'Icon name', true); | |
$this->registerArgument('size', 'string', 'Icon size in pixel', false, ''); | |
$this->registerArgument('class', 'string', 'Optional class name(s)', false, ''); | |
$this->registerArgument('color', 'string', 'Optional color', false, ''); | |
} | |
public static function renderStatic( | |
array $arguments, | |
\Closure $renderChildrenClosure, | |
RenderingContextInterface $renderingContext | |
) { | |
// Prepare requested icon (type and icon (name)) | |
$iconParts = GeneralUtility::trimExplode(' ', $arguments['icon'], true); | |
$type = 'solid'; | |
if (count($iconParts) === 2) { | |
$firstPart = reset($iconParts); | |
if ($firstPart === 'far') { | |
$type = 'regular'; | |
} elseif ($firstPart === 'fab') { | |
$type = 'brands'; | |
} elseif ($firstPart === 'fas') { | |
$type = 'solid'; | |
} else { | |
throw new \InvalidArgumentException( | |
'Font Awesome icon prefix "' . $firstPart . '" not allowed! Allowed are: "far", "fas" or "fab"' | |
); | |
} | |
$icon = end($iconParts); | |
} elseif (count($iconParts) === 1) { | |
$icon = reset($iconParts); | |
} else { | |
throw new \InvalidArgumentException( | |
'Invalid icon name given. Valid name would be: "fas fa-file" or "fa-file" or just "file"' | |
); | |
} | |
if (strpos($icon, 'fa-') === 0) { | |
$icon = substr($icon, 3); | |
} | |
// Check if icon exists | |
$path = Environment::getProjectPath() . '/vendor/fortawesome/font-awesome/svgs/'; | |
$path .= $type . '/'; | |
$path .= $icon . '.svg'; | |
if (!file_exists($path)) { | |
throw new \RuntimeException('Given Font Awesome Icon "' . $arguments['icon'] . '" not found!'); | |
} | |
// Prepare view helper arguments | |
$size = (string) $arguments['size']; | |
$color = (string) $arguments['color']; | |
$class = 'svg-icon'; | |
if (!empty($arguments['class'])) { | |
$class .= ' ' . $arguments['class']; | |
} | |
// If the same icon is requested a second time, use a reference to symbol instead | |
if (array_key_exists($path, self::$cache)) { | |
$id = self::$cache[$path]; | |
return self::buildSvgSymbolReference($id, $size, $class, $color); | |
} | |
// Create and cache symbol | |
return self::createSvgSymbol($type, $icon, $path, $size, $class, $color); | |
} | |
protected static function buildSvgSymbolReference(string $id, string $size, string $class, string $color): string | |
{ | |
if (!empty($size)) { | |
$size = ' width="' . $size . '" height="' . $size . '" '; | |
} | |
if (!empty($color)) { | |
$color = ' fill="' . $color . '"'; | |
} | |
return '<svg' . $size . $color . ' class="' . $class . '">' | |
. '<use xlink:href="#' . $id . '"></use>' . | |
'</svg>'; | |
} | |
/** | |
* @param string $type | |
* @param string $icon | |
* @param string $path | |
* @param string $size | |
* @param string $class | |
* @param string $color | |
* @return string | |
*/ | |
protected static function createSvgSymbol( | |
string $type, | |
string $icon, | |
string $path, | |
string $size, | |
string $class, | |
string $color | |
) : string { | |
$svgContents = file_get_contents($path); | |
$svgDocument = new \DOMDocument(); | |
$svgDocument->loadXML($svgContents); | |
// Used for caching and symbol identifier | |
$id = 'fa-' . $type . '-' . $icon; | |
// Create the symbol | |
$symbolDocument = new \DOMDocument(); | |
$symbol = $symbolDocument->createElement('symbol'); | |
$symbol->setAttribute('id', $id); | |
$symbol->setAttribute('viewBox', $svgDocument->documentElement->getAttribute('viewBox')); | |
$symbolDocument->appendChild($symbol); | |
// Get paths of font awesome SVG | |
foreach ($svgDocument->documentElement->childNodes as $svgpath) { | |
$iconPathsFragment = $symbolDocument->createDocumentFragment(); | |
$iconPathsFragment->appendXML($svgDocument->saveXML($svgpath)); | |
$symbol->appendChild($iconPathsFragment); | |
} | |
// Prepare symbol output | |
$result = '<svg class="d-none">'; | |
foreach ($symbolDocument->childNodes as $childNode) { | |
$result .= $symbolDocument->saveXML($childNode); | |
} | |
$result .= '</svg>'; | |
// Directly append a reference to this symbol (which is never displayed) | |
$result .= self::buildSvgSymbolReference($id, (string)$size, $class, $color); | |
// Add cache item and return the output | |
self::$cache[$path] = $id; | |
return $result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's required to fetch the "fortawesome/font-awesome" package, with Composer. And it's assumed that Bootstrap or at least a d-none class, is existing.