Merging Page/Site Data into a Template SVG

I have an SVG that I want to customize for each content post. Something like this:

<svg xmlns="http://www.w3.org/2000/svg">
  <g>
    <rect x="0" y="0" width="100" height="100" fill="red"></rect>
    <text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">
Hello
  </text>
  </g>
</svg>

It produces an SVG image like this:
image

It’s easy enough to get that to display in my shortcode with an <img src="hello_world.svg"> tag.

However, I am trying to alter the message in each instance of the SVG based on Page data or Site data. So instead of the SVG code above which hard-codes “Hello”, I might create one “template” copy of the SVG (I’m not sure what folder makes the most sense; assets?) like this:

<svg xmlns="http://www.w3.org/2000/svg">
  <g>
    <rect x="0" y="0" width="100" height="100" fill="red"></rect>
    <text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">
{{$.Site.Data.stringlist.string1}}
     </text>
  </g>
</svg>

And then have that merged with data within my shortcode where that shortcode is referenced.

I think I need to use something like this:

{{ $originalSvgPath := "hello_world.svg" }}
{{ $originalSvg := resources.Get $originalSvgPath | resources.ExecuteAsTemplate $originalSvgPath .}}
<img src="{{$originalSvg.Permalink}}">

If I use static functions like {{add 3 5}}, it seems to be working. But if I try to reference variables from the shortcode context like $.Page.Params.mystring, the SVG gets populated with a blank value.

  1. Are there some examples I could follow to make this work?
  2. What directory(ies) should I be using to achieve this?
  3. Is there a way to generate the outputted SVG file to the content’s page bundle folder?
  4. Can someone explain the parameters for resources.ExecuteAsTemplate function? The official docs say:

"The function takes three arguments: the resource target path, the template context, and the resource object.

But I’m struggling to understand what those conceptually represent, in part because the official example doesn’t show what the referenced main.scss looks like, nor what the output ultimately would look like based on the example parameters.

Thanks.

Which one?

Site data. But the Page data identifies the Site data by way of an index function, i.e.:

{{ $data := (index $.Site.Data.words $.Page.Params.specificWordIdentifier }}

So actually both.

content/post/post-1.md

+++
title = 'Post 1'
date = 2022-10-03T19:21:46-07:00
draft = false
specificWordIdentifier = 'a'
+++

{{< svg >}}

data/words.toml

a = 'Foo'
b = 'Bar'

layouts/shortcodes/svg.html

{{ with $specificWordIdentifier := .Page.Params.specificWordIdentifier }}
  {{ with index site.Data.words . }}
    <svg xmlns="http://www.w3.org/2000/svg">
      <g>
        <rect x="0" y="0" width="100" height="100" fill="red"></rect>
        <text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">
          {{ . }}
        </text>
      </g>
    </svg>
  {{ else }}
    {{ errorf "The %q shortcode was unable to find a key named %q in data/words. See %s" $.Name $specificWordIdentifier $.Position }}
  {{ end }}
{{ else }}
  {{ errorf "The %q shortcode requires a front matter field named %q. See %s" .Name "specificWordIdentifier" $specificWordIdentifier .Position }}
{{ end }}

Thank you, @jmooring. That produces inline SVG elements. I was previously able to get something like that to work. But my goal is to generate the resulting SVG markup to an external image file rather than an inline SVG element. That is what I assumed I’d need to use resources.ExecuteAsTemplate for. Generating SVG into an external file provides advantages based on how images are rendered (responsiveness, unselectable text, etc.) vs. inline SVG, which creates all of those individual elements in the page.

1 Like
content/post/post-1.md
+++
title = 'Post 1'
date = 2022-10-03T19:21:46-07:00
draft = false
specificWordIdentifier = 'a'
+++

{{< svg >}}

data/words.toml
a = 'Foo'
b = 'Bar'

assets/templates/svg.html
<svg xmlns="http://www.w3.org/2000/svg">
  <g>
    <rect x="0" y="0" width="100" height="100" fill="red"></rect>
    <text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">
      {{ . }}
    </text>
  </g>
</svg>

layouts/shortcodes/svg.html
{{ with $specificWordIdentifier := .Page.Params.specificWordIdentifier }}
  {{ with index site.Data.words . }}
    {{ $targetPath := printf "/images/%s.svg" $specificWordIdentifier }}
    {{ $r := resources.Get "templates/svg.html" }}
    {{ $i := $r | resources.ExecuteAsTemplate $targetPath . }}
    <img src="{{ $i.RelPermalink }}" alt="">
  {{ else }}
    {{ errorf "The %q shortcode was unable to find a key named %q in data/words. See %s" $.Name $specificWordIdentifier $.Position }}
  {{ end }}
{{ else }}
  {{ errorf "The %q shortcode requires a front matter field named %q. See %s" .Name "specificWordIdentifier" $specificWordIdentifier .Position }}
{{ end }}

In layouts/shortcodes/svg.html, set $targetPath to whatever you want, as long as it is unique.

Alternatively, you might consider embedding the image using a data URL.

layouts/shortcodes/svg.html
{{ with $specificWordIdentifier := .Page.Params.specificWordIdentifier }}
  {{ with index site.Data.words . }}
    {{ $r := resources.Get "templates/svg.html" }}
    {{ with $r | resources.ExecuteAsTemplate "" . }}
      <img src="data:image/svg+xml;base64,{{ .Content | base64Encode }}">
    {{ end }}
  {{ else }}
    {{ errorf "The %q shortcode was unable to find a key named %q in data/words. See %s" $.Name $specificWordIdentifier $.Position }}
  {{ end }}
{{ else }}
  {{ errorf "The %q shortcode requires a front matter field named %q. See %s" .Name "specificWordIdentifier" $specificWordIdentifier .Position }}
{{ end }}
2 Likes

That worked! Once again, thanks for your help!

One more question: I am trying to generate the resulting file, but I don’t necessarily want to use it in the spot where I’m generating it. I’m sort of using it as a function. The theme I’m using-- Clarity-- takes a filename as a parameter in the front matter for specifying the “featureImage,” so I’m just trying to generate this feature image to a file so the theme can pick it up.

This seems NOT to generate the image file if I don’t explicitly include the <img src="{{ $i.RelPermalink }}" alt=""> in the shortcode.

So if I just do this (<img> commented out), the image doesn’t seem to get generated:

    {{ $targetPath := printf "/images/%s.svg" $specificWordIdentifier }}
    {{ $r := resources.Get "templates/svg.html" }}
    {{ $i := $r | resources.ExecuteAsTemplate $targetPath . }}
    {{/* <img src="{{ $i.RelPermalink }}" alt=""> */}}

Does that make sense? Is there a way to force it to generate the file even if the file is not immediately referenced?

https://gohugo.io/hugo-pipes/introduction/#asset-publishing

Hugo publishes assets to the to the publishDir (typically public) when you invoke .Permalink, .RelPermalink, or .Publish.

So this will write to disk:

{{ $i.Publish }}

Yessir, that did it. Thank you.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.