Episerver - Handling an SVG image type and displaying it using a Path tag

In the current project that I'm on, there was an interesting request that they wanted to use SVG images for some of their things, such as logos and icons. This meant that they needed them rendered using the <Path> DOM element, not as an <Img>.

This was a tricky one and there wasn't very much information about how to accomplish this, as its not necessarily a common request. That being said, it does have its advantages, such as recoloring the vector using CSS.


In this post, we're going to go through the following:
  • Creating a new Media Descriptor that will handle the SVG file format
  • Looking at how SVGs are stored
  • AzureBlob (DXP) vs FileBlob (Local)
  • Extracting the XML and returning it

Creating a Media Descriptor

A media descriptor simply defines a set of properties for a specific file type.

These models are typically inherited from the ImageData class.

For instance, we typically create a model that covers the bases for a majority of the image types, and add things such as AltText and/or Title.

Our media descriptor looks like this:
[MediaDescriptor(ExtensionString = "jpg,jpeg,jpe,ico,gif,bmp,png,webp")]

We call this class "ImageMedia", which we will use as our base for our SVG Media Descriptor.

Now, just as we did above, if we wanted to specifically create a Media Descriptor that will handle SVG files, we will do the following:
[MediaDescriptor(ExtensionString = "svg")]

So, again, this says that any media uploaded with the file extension of ".svg" will be created of this type. We named this "SvgMedia."


If you would like to learn more about Media Descriptors, you can view the Episerver documentation here: https://world.episerver.com/documentation/developer-guides/CMS/Content/assets-and-media/Media-types-and-templates/

How are SVGs stored? Cloud? Local?

When a media item gets uploaded into Episerver, it is stored in blobs.

There are a few different kinds of blobs, such as FileBlob, AzureBlob, SqlBlob.

The types that we will be looking at in this blog are the FileBlob and AzureBlob.

Locally, the type is of FileBlob. This is stored in the filesystem.

Once this gets into the Azure DXP environment, these are no longer stored as FileBlob, but are stored as an AzureBlob.


Just to summarize:
  • Locally, media items are stored as FileBlob
  • In DXP, media items are stored as AzureBlob

Using the Blob object to pull out the XML

Due to this changing of file types, it was a bit difficult to figure out the best and most efficient way to get the path XML out of these types of files.

My first try to get the XML Path out of the SVG was using the type of FileBlob, since I was developing in my local instance.

Within my SvgMedia class, I created a string property with only a getter property. It looked like this:

public virtual string XML
        {
            get
            {
                var fileBlob = BinaryData as FileBlob;

                var xmlDoc = new XmlDocument();
                xmlDoc.Load(fileBlob.FilePath);

                return xmlDoc.InnerXml;
            }
        }

What this is doing is casting the BinaryData of the file as a FileBlob. This gives us access to the FilePath property, which we can use to load the file as an XmlDocument, and then load the file contents (which is the XML Path).

This works perfectly fine when we use the XML property on a view, but only if you're local. (or on an on-prem environment)

Lets take a look for if we go into a DXP Azure environment, since we know these files are stored as AzureBlob, casting them as FileBlob is not going to work.

The problem here is that the AzureBlob has no property of FilePath, so we cannot load it that way.

We could do something like cast it as an AzureBlob and open it using OpenRead and use that to load it as an XmlDocument. The problem is that how do we debug any of this locally if we use this as an AzureBlob type? (Other than use AzureBlob storage, but who wants to do that when we're developing locally?)

What we COULD do though, is look at a type that both of them inherit from, such as "Blob."

Obviously since we don't have a FilePath property we can't use that, but we can use the same method that I just mentioned for AzureBlob. 

Once we cast the BinaryData as type of Blob, we can create an XmlDocument object, and then load the blob using OpenRead.

[MediaDescriptor(ExtensionString = "svg")]
    public class SvgMedia : ImageMedia
    {
        public virtual string XML
        {
            get
            {
                try
                {
                    var blob = BinaryData as Blob;
                    var xmlDoc = new XmlDocument();
                    xmlDoc.Load(blob.OpenRead());

                    return xmlDoc.InnerXml;
                }
                catch
                {
                    //If this fails, dont cause a 5xx error, fail gracefully.
                    return "";
                }
            }
        }
    }

Conclusion

So far, this is working great! If we allow the type of ImageMedia on a ContentArea property, this will allow either of the above classes (ImageMedia and SvgMedia), which will mean either a <img> tag will render, or the <path> tag.

Just because the above works, it does not mean it is the best way to do it.

I would love to know if any of you have found a better way to do this.

Comments

Popular posts from this blog

EpiServer DXP Deployment API PowerShell Scripts for CI/CD in Azure Devops - Part 2

EpiServer DXP Deployment API PowerShell Scripts for CI/CD in Azure Devops - Part 1

EpiServer DXP Deployment API PowerShell Scripts for CI/CD in Azure Devops - Part 3