Display SVG

Hi,
I’m trying to display an SVG in my streamlit report.
The SVG is a map from pygal: http://www.pygal.org/en/stable/documentation/types/maps/pygal_maps_fr.html

I got it as a raw html code, here is the beginning of the code:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="chart-97bfb953-d663-4781-bd5f-be3470799c95" class="pygal-chart" viewbox="0 0 800 600"><defs><style type="text/css">#chart-97bfb953-d663-

I can’t find a way to display it correctly.
Here is what I tried:

svg = chart.render().decode()  # this contain a string with the above code
st.write(svg, unsafe_allow_html=True))

but the html code is not interpreted correctly.
In the original code, there is a <defs> just after the closing of the first <svg>
but in my report, a closing </svg> somehow appear there are the image stays blank.

any suggestion?

1 Like

Hi @jeremie! Welcome to the Streamlit community! :hugs: :fr:

There is currently no way to embed SVGs into Streamlit, so I have created a feature request for you. (We would appreciate it if you commented on that feature request with you use case so that we could understand it better.)

If you would like to output geographic data specifically, please know that Streamlit supports several libraries that let you output maps such as Altair:

and Matplotlib:

and DeckGL, although I can’t find information on how to create a Choropleth chart in DeckGL after a quick Google search.

Thank you Adrien,
I’ll have a look at it.

Jérémie.

Le ven. 4 oct. 2019 à 19:57, Adrien Treuille via Streamlit streamlit@discoursemail.com a écrit :

I manage to include my SVG image, but I add to host it somewhere accessible by filename.
Here is my test dashboard with SVG and PNG: http://95.216.70.217:8501/
you can see the difference, the SVG allow to have “hoover” information for each location, which is neat.

Here is how I did that:
st.write("""<figure><embed type="image/svg+xml" src="https://froglabs.ai/media/mysvg.svg" /></figure>""", unsafe_allow_html=True)

How can I do that on the same machine by specifying a local path? (where the .py code is for instanceà

Hi @jeremie:

I believe what you’re looking for is:

def render_svg(svg):
    """Renders the given svg string."""
    b64 = base64.b64encode(svg.encode('utf-8')).decode("utf-8")
    html = r'<img src="data:image/svg+xml;base64,%s"/>' % b64
    st.write(html, unsafe_allow_html=True)

You can see an example of it’s use in this gist which you can try out by running:

streamlit run https://gist.githubusercontent.com/treuille/8b9cbfec270f7cda44c5fc398361b3b1/raw/19ce438121af7907d5d1527e36d715dcef755bd3/render_svg.py

Please note that this has only been tested in Python 3.6.

2 Likes

Here’s a full example for displaying matplotlib figures as svg, for anyone coming across this thread like me. Basically, you can see in the code that it’s easy to display any figure that can be saved as svg.

Funny thing is, it’s also twice as fast to render it as svg, so I’d definitely recommend this to be integrated in the official release at some point.

import base64
from io import StringIO

import matplotlib.pyplot as plt
import streamlit as st
from matplotlib import numpy as np


def svg_write(fig, center=True):
    """
    Renders a matplotlib figure object to SVG.
    Disable center to left-margin align like other objects.
    """
    # Save to stringIO instead of file
    imgdata = StringIO()
    fig.savefig(imgdata, format="svg")

    # Retrieve saved string
    imgdata.seek(0)
    svg_string = imgdata.getvalue()

    # Encode as base 64
    b64 = base64.b64encode(svg_string.encode("utf-8")).decode("utf-8")

    # Add some CSS on top
    css_justify = "center" if center else "left"
    css = '<p style="text-align:center; display: flex; justify-content: {};">'.format(css_justify)
    html = r'{}<img src="data:image/svg+xml;base64,{}"/>'.format(
        css, b64
    )

    # Write the HTML
    st.write(html, unsafe_allow_html=True)


if __name__ == "__main__":
    fig, ax = plt.subplots(nrows=5, ncols=5, figsize=(10, 10))
    ax = ax.ravel()

    for i in range(len(ax)):
        x = np.arange(0, np.pi * np.random.randint(2, 10), 0.1)
        y = np.sin(x)

        ax[i].plot(x, y)

    plt.tight_layout()

    st.subheader("Peasant pixels")
    st.write(fig)

    st.subheader("Glorious SVG")
    svg_write(fig, center = False)
2 Likes