Skip to content

Instantly share code, notes, and snippets.

@a-r-m-i-n
Created March 25, 2020 14:41
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-r-m-i-n/5779e15a114880c23685c268eac6bb12 to your computer and use it in GitHub Desktop.
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+
<?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;
}
}
@a-r-m-i-n
Copy link
Author

a-r-m-i-n commented Mar 25, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment