Skip to main content

You Just Got Vectored! SVG Image Formats

 If you're reading this, then you've come across a need that nearly all Opti developers encounter in their careers; You need to display a vector image format (SVGs and the like) properly, the <IMG> tag just isn't going to cut it anymore. Post like this are a right-of-passage for Opti bloggers.

"So," you think "if there are so many other blogs out there on the topic, why should I read yours?" Firstly, you enjoy my familiar and conversational tone of writing.

Cary Elwes does the English accent, I do the nerd stuff.

Second, you've already come this far, you might as well finish as this point, it's not long, I promise. To that point, and most importantly, I've seen some complex solutions out there, this one is a quick and simple implementation. 100% Guarantee!

The VectorFile class:
[MediaDescriptor(ExtensionString = "svg")]
    public class VectorFile : ImageData
    {
        public override Blob Thumbnail { get => BinaryData; }

        public virtual string XML
        {
            get
            {
                try
                {
                    var blob = BinaryData;
                    var xmlDoc = new XmlDocument();
                    xmlDoc.Load(blob.OpenRead());

                    return xmlDoc.InnerXml;
                }
                catch
                {
                    return "";
                }
            }
        }
    }

This delightful little class will extract the thumbnail for previews and place the image in an XML document that can be inserted into a view as an <SVG> tag. You can see from the MediaDescriptor attribute that we're only allowing .SVG files here. This list can be expanded as needed. This is where you can also supply further properties regarding your SVG in the class, it's just content after all! 

The VectorFileController class:


    public class VectorFileController : AsyncPartialContentComponent<VectorFile>
    {
        protected override async Task<IViewComponentResult> InvokeComponentAsync(VectorFile currentBlock)
        {
            return await Task.FromResult(View("~/Features/Media/_vectorFile.cshtml", currentBlock));
        }
    }

The should look familiar to CMS12 block development, just your run-of-the-mill controller. No tricks, no fills. 

The VectorFile view:

@model VectorFile

@if (Model != null)
{
    @((MarkupString)Model.XML)
}

A lot of devs would have you use @Html.Raw to put the SVG code on the screen, but with the MarkupString struct. We on less shaky ground here because MarkupString is parsed in an HTML or SVG format, rather than trusting that the silly SVG you grabbed off Pinterest for that company party doesn't have some nasty embedded code. 

Now, on the next upload to your Media folder, any SVG file should appear as a VectorFile instead of an ImageFile! (You did take the .svg extension out the MediaDescriptor of your raster image class, right?)

Told you it would be short and simple! Happy coding Opti-mists!

Comments

Popular posts from this blog

Optimizely Gets More (Case-)Sensitive

As Windows developers, we don't always have to pay attention to capitalization when dealing with paths and URIs. With CMS12, Optimizely has started deploying to a Linux container for hosting sites. This means that deployed sites (and developers!) will have to pay attention to capitals in references. Inconsistent capitalization can cause 404 errors in unexpected places. Thankfully there's a few ways to handle that!  The worst part of this is that developers won't be able to find these issues until deployed to Azure, with Windows, being case in-sensitive and all. Once named, files and folders can be difficult to change in git. Below are some ways to help rename your files so that they'll play nicely in Linux. Rename via Git mv Command   One way to update capitalization is by the git mv command. In you git command shell:  git mv <source> <target> This command will rename a file or folder, however it still runs in the context of Windows. If you want to simply ch

The 1001st Piece in your 1000 Piece Puzzle: .NET Default Interface Functions

I was recently working with a client who wanted a reasonably large subsystem added to Optimizely that would add automated management to their content. While cutting the code for this, I found myself writing similar code across multiple classes. The reason why I had to write it that way was: 1) The client was currently on CMS11 and didn't have access to newer language features; 2) The hierarchy of the classes prevented me from inserting a common ancestor. Thankfully, .NET has expanded the functionality of interfaces, so we can take advantage of those within Optimizely. With .NET 5, Microsoft introduced default implementations on interfaces. Now interfaces can bring along a default implementation. Resulting in all classes that implement the interface can use the default implementation or override it with custom logic. Enough text! Let's code! Original Interface The following code is something that we'd create for an Optimizely experiment: using OptimizelySDK; using Optimizely

Config-Per-Site in Multi-Tenant Environments

Recently, a task was given to me at work where we needed a multisite configuration. We all know that multi-environment is as easy as appsettings.<environment_name>.config. What about in multi-tenant environments? You can't have appsettings.site1.config and appsettings.site2.config in your site! Well, not without a little extra work... Allow me to introduce  AddKeyPerFile ; this handy little function that you set up in your Program.cs will enumerate the files in a directory and add them to your Configuration.  "Alright!" you must be thinking "Show me how this miracle function works..." Well, let's get to it! First, you'll want to identify the config sections that will be unique to each site and put them in their own folder. I do this by site name because it makes the most sense by our conventions, if something else works for your practice, these names aren't set in stone.  Now, in order to suck those values into your config, there's one simp