Using Jinja Templates in Pelican Posts

When changing from Wordpress to Pelican, I found one thing that I did not like. When inserting images in a post, the image would be inserted as a simple img tag in the HTML output. I would like to use figures, like e.g. in scientific papers, with captions and a frame around.

After some research, I found that

  • HTML 5 supports the figure and figcaption tags. This is exactly what I want!
  • I can embed any HTML code in the markdown content for Pelican.
  • I do not like to mix Markdown and HTML in the content. It's error-prone and much to type.

After some talking to Google and browsing the webs, I found that there is the jinja2content plugin for Pelcian to support Jinja template code inside the post content. Around this plugin I set up a nice solution for adding figures to my posts. The idea is the following:

  • Use jinja2content to be able to use Jinja template code in posts
  • Import a file with Jinja macros into every post
  • Write a Jinja macro to generate HTML code for a figure with caption.

Set up the Jinja2Content Plugin

The plugin is part of the Pelican plugin repository on Github. It's easy to set up:

  • Clone the plugin repository
  • Copy the jinja2content folder into the plugins folder of your Pelican project.
  • Update pelicanconf.py to enable the plugin like this:
# pelicanconf.py
PLUGIN_PATHS = ['plugins']
PLUGINS = ['jinja2content']

Now you can use Jinja template code in your posts.

Adding Support for Figures

By default, the jinja2content plugin searches for templates in the theme template folder. So you can include or import templates from there. So I create a template file called macros.html with a macro to generate HTML code for a figure and put it into my theme templates folder.

Note: In the following code examples, all Jinja start and end tags {{ ... }}, {% ... %} and {# ... #} are replaced with square brackets [[ ... ]], [% ... %] and [# ... #]`. Otherwith the Jinja template engine would try to interpret them in this post. When copy & pasting this code, you need to replace the braces accordingly!

[# Macro to add a figure with caption to a post. #]
[% macro figure(src, caption='') -%]
<figure>
  <a href="[[ '{' ]]attach[[ '}' ]]images/[[ src ]]">
    <img src="[[ '{' ]]attach[[ '}' ]]images/[[ src ]]"
         title="[[ caption ]]"
         alt="[[ caption ]]">
  </a>
  [% if caption %]
  <figcaption>[[ caption ]]</figcaption>
  [% endif %]
</figure>
[%- endmacro %]

Now in every post, I need one line of Jinja code to import the macro:

[% import 'macros.html` as m %]

Then I can add figures in the post like this:

[[ m.figure('jinja-macro-for-figure.png', 'Screenshot of the original code of
the `figure` macro.') ]]

The result looks like this:

Screenshot of the original code of
the macro to embed figures in posts.
Screenshot of the original code of the macro to embed figures in posts.

Support for JINJA2_PREFIX setting

Now writing posts with figures is already easier. But it's still annoying to include the import statement at the top of every post.

So I have modified the code of jinja2content.py to support a post prefix setting. The read(...) method now gets the JINJA2CONTENT_PREFIX variable from the settings and prepends its content to every post.

def read(self, source_path):
    prefix = self.settings.get('JINJA2CONTENT_PREFIX', '')

    with pelican_open(source_path) as text:
        content = prefix + text
        content = self.env.from_string(content).render(**self.settings)

    with NamedTemporaryFile(delete=False) as f:
        f.write(content.encode())
        f.close()
        content, metadata = super().read(f.name)
        os.unlink(f.name)

        return content, metadata

Now I can add the following setting in pelicanconf.py to automatically import the macros.html file to all posts:

# pelicanconf.py
# prefix for all articles
JINJA2CONTENT_PREFIX="{% import 'macros.html' as m %}"

As this string is prepended to each post, it is prepended before the metadata section. So you can use jinja code to even generate metadata. On the other hand, you should not generate any other content, especially no blank lines, as this would terminate the metadata section.

LinkedIn logo mail logo