Including SVG in Twig template
Asked Answered
A

3

6

I am changing my assets from PNGs to SVGs, where appropriate, however I am having difficultly including these SVGs in my Twig templates.

I am trying to include the SVG like this:

{{ source('/assets/img/crmpicco-horizontal-logo.svg') }}

However, this results in the following error:

Unable to find template "/assets/img/crmpicco-horizontal-logo.svg" (looked into: /Library/WebServer/Documents/crmpicco/symfony/app/Resources/views, /Library/WebServer/Documents/crmpicco/symfony/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form).

Why can't I include this SVG in the same directory as my other assets? Specifically, i'm interested in finding out why a SVG can't be treated as an asset with Assetic.

Arica answered 25/10, 2017 at 13:40 Comment(1)
Possible duplicate of Twig: Include external Code from an SVG fileBayberry
B
6

You have to rename your svg file with a twig extension crmpicco-horizontal-logo.svg.twig and do this :

{% include ":svg:crmpicco-horizontal-logo.svg.twig" %}

The folder svg is in app/Ressources/views in this example

Bayberry answered 25/10, 2017 at 13:44 Comment(4)
Thanks, is there a reason it can't sit in the filesystem with other assets?Arica
Because you use include and the include twig function|tag can only be use with ressources in app/Ressources/views or bundles/Ressources/views. But I don't know why we have to use this solution with Symfony. By my side when static integration differ from the project, I have to create a script to copy/past svg assets in other folders and rename the files. I don't very like that but it's worksBayberry
I find this Include a template which is in the web folder. Thanks also I didn't know that was possible :)Bayberry
Sure. However, why can't I use asset to point to an SVG? Is it simply because it's not an image?Arica
P
10

Here is the solution I use (Twig with Symfony 4 and Webpack Encore) :

  1. Inside your twig config, declare your public path :
twig:
    paths:
        "%kernel.project_dir%/public": public_path
  1. Load your svg asset like that :
{{ source('@public_path'~asset('build/images/your_file.svg')) }}
Periphery answered 16/2, 2019 at 14:18 Comment(1)
Brilliant! This should be the correct answer.Tuneless
B
6

You have to rename your svg file with a twig extension crmpicco-horizontal-logo.svg.twig and do this :

{% include ":svg:crmpicco-horizontal-logo.svg.twig" %}

The folder svg is in app/Ressources/views in this example

Bayberry answered 25/10, 2017 at 13:44 Comment(4)
Thanks, is there a reason it can't sit in the filesystem with other assets?Arica
Because you use include and the include twig function|tag can only be use with ressources in app/Ressources/views or bundles/Ressources/views. But I don't know why we have to use this solution with Symfony. By my side when static integration differ from the project, I have to create a script to copy/past svg assets in other folders and rename the files. I don't very like that but it's worksBayberry
I find this Include a template which is in the web folder. Thanks also I didn't know that was possible :)Bayberry
Sure. However, why can't I use asset to point to an SVG? Is it simply because it's not an image?Arica
F
0

The @adrien-lamotte solution was working for inlining svg tag. However I also needed to dynamically add class(es) on the tag.

I ended up coding a specialized Twig extension for loading SVG and adding attributes, so you do:

{{ source(svg_asset('/images/front/connexion.svg'))|add_class('svg-picto') }}

The svg_asset function is just to limit boilerplate; whereas the add_class and add_html_attr filters allows for the SVG DOM manipulation.

The extension class:

<?php

namespace App\Twig\Extension;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Twig\TwigFilter;

class SvgExtension extends AbstractExtension
{
    private $assetExt;

    public function __construct(
        \Symfony\Bridge\Twig\Extension\AssetExtension $assetExt
    ) {
        $this->assetExt = $assetExt;
    }

    public function getFunctions(): array
    {
        // We could use a macro also, but this would need to be imported
        // each time.
        // https://twig.symfony.com/doc/3.x/tags/macro.html
        return array(
            new TwigFunction('svg_asset', [$this, 'svg_asset_url']),
        );
    }

    public function getFilters(): array
    {
        return array(
            new TwigFilter('add_html_attr', array($this, 'add_html_attr'), [
                'is_safe' => ['html'],
            ]),
            new TwigFilter('add_class', array($this, 'add_class'), [
                'is_safe' => ['html'],
            ]),
        );
    }

    public function svg_asset_url($path): string
    {
        $assetUrl = $this->assetExt->getAssetUrl($path);
        return '@public_path/' . $assetUrl;
    }

    public function add_html_attr(string $html, $attr_name, $attr_value): string
    {
        // We assume a top level tag, on which will add an attribute
        // value (without checking for a prior existence of the attribute key).
        // We could use a naive Regex or a DOM library.
        // https://github.com/scotteh/php-dom-wrapper#attr
        // Or just the plain PHP DOMDocument() https://www.php.net/domdocument.loadhtml.
        $doc = new \DOMWrap\Document();
        $doc->setLibxmlOptions(LIBXML_HTML_NOIMPLIED);
        return $doc
            ->html($html)
            ->children()->first()
            ->attr($attr_name, $attr_value);
    }

    public function add_class(string $html, $class_value): string
    {
        return $this->add_html_attr($html, 'class', $class_value);
    }

    public function getName()
    {
        return 'svg.extension';
    }
}

@public_path is declared in config/packages/twig.yaml:

twig:
    paths:
        "%kernel.project_dir%/public": public_path

It is important to note you can have a near equivalent solution to target the SVG with CSS, without the overhead: just add an HTML tag wrapper. For eg.

<div class="svg-wrapper">
  {{ source('@public_path'~asset('build/images/your_file.svg')) }}
</div>
Formerly answered 4/11, 2022 at 14:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.