Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use an <svg> tag when all I have is a base64 string for the image?

Tags:

html

svg

I would like to show an SVG image. I want to use CSS to modify the SVG directly, so I need to use an <svg> tag. However, I don't have the SVG source - all I have is the SVG encoded as a base64 string. In my HTML page, with an image tag, it looks something like this:

<img alt="" src="data:image/svg+xml;base64,lotsofunreadablestuff...">

How can I create an svg tag those shows the same image using the same base64 string? Every thread and piece of documentation I've found so far seems to assume that I'm either using the <use xlink:href=...></use> tag in my SVG or including my raw SVG inside my <svg> tags, neither of which apply in my situation.

I want to do something like this:

<svg>
  <magicalSourceTag>
    data:image/svg+xml;base64,lotsofunreadablestuff...
  </magicalSourceTag>
</svg>
like image 628
Kevin Avatar asked Sep 19 '16 22:09

Kevin


1 Answers

You can only apply CSS to SVGs within the same document. That means you'll either have to inline the SVG into your HTML, or embed your CSS into the SVG code itself.

Now, in order to inline an SVG, it depends on what you have.

For this answer I'm going to use the following data URL, which translates into (what is supposed to be) a camera icon:

data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgdmlld0JveD0iMCAtMTAgMTAwIDg1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPGRlZnM+CiAgICAgICAgPGNsaXBQYXRoIGlkPSJjbGlwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTAsMCBMMTAwLDAgTDEwMCw3NSBMMCw3NSBMMCwwCiAgICAgICAgICAgICAgICAgICAgIE01MCwxNSBBMjAsMjAgMCAxLDAgNTAsNjAgQTIwLDIwIDAgMSwwIDUwLDE1CiAgICAgICAgICAgICAgICAgICAgIE01MCwyNSBBMTIuNSwxMi41IDAgMSwxIDUwLDUwIEExMi41LDEyLjUgMCAxLDEgNTAsMjUiLz4KICAgICAgICA8L2NsaXBQYXRoPgogICAgPC9kZWZzPgogICAgPHJlY3QgeD0iMzAiIHk9Ii0xMCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjIwIiByeD0iMTAiIHJ5PSIxMCIvPgogICAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCIgaGVpZ2h0PSI3NSIgcng9IjE1IiByeT0iMTUiIGNsaXAtcGF0aD0idXJsKCNjbGlwKSIvPgo8L3N2Zz4K

1. Static data

If you have a hardcoded data URL that you want to use, you can simply decode it using any base64 decoder out there. Just paste the blob after base64, into the decoder and it should give your the raw XML back, which you can then embed into your HTML.

If you use the data URL from above, look up the first Google result for "base64 decode" and paste the part after base64, in there, you get:

<?xml version="1.0"?>
<svg viewBox="0 -10 100 85" xmlns="http://www.w3.org/2000/svg">
    <defs>
        <clipPath id="clip">
            <path d="M0,0 L100,0 L100,75 L0,75 L0,0
                     M50,15 A20,20 0 1,0 50,60 A20,20 0 1,0 50,15
                     M50,25 A12.5,12.5 0 1,1 50,50 A12.5,12.5 0 1,1 50,25"/>
        </clipPath>
    </defs>
    <rect x="30" y="-10" width="40" height="20" rx="10" ry="10"/>
    <rect x="0" y="0" width="100" height="75" rx="15" ry="15" clip-path="url(#clip)"/>
</svg>

You should strip the <?xml version="1.0"?> tag before inlining it into HTML to be 100% standard-compliant, but that's all you have to do, the rest should work just fine if pasted into your HTML.


2. Dynamic data

If the data URL is not fixed however, you need your application to do the decoding and inlining.

Again there are two ways to do this:

2.1 Server-side

I would consider server-side inlining the cleanest way to deal with dynamic data.
In theory any programming language can do this, and as long as it's got a base64 decoder and an XML parser, it shouldn't be too complicated.
Since you didn't say what (if any) server-side language you're using, I'm going to use PHP as an example, and I'm making use of the DOM extension, which is enabled and available by default.

First you'll want to get the XML from your data URL. You can (ab)use file_get_contents for that:

$xml = file_get_contents($dataURL);

Next you'll want to parse that XML into a DOM document:

$dom = new DOMDocument();
$dom->loadXML($xml);

And then you'll want to export just the root node (without the <?xml tag), so that it can be inlined into HTML. You can do this by calling $dom->saveXML with the root node as first argument, i.e.:

$svg = $dom->saveXML($dom->documentElement);

[ Online demo ]

This of course has the disadvantage of needing to have a server-side application run in the first place (other than the web server).

2.2 Client-side

If you need or want to do this in the browser, there is really only one option: JavaScript.

Similar to PHP's file_get_contents, you can (ab)use JavaScript's XMLHttpRequest to "load" the data from the URL:

var xhr = new XMLHttpRequest();
xhr.open('GET', dataURL);
xhr.addEventListener('load', function(ev)
{
    var xml = ev.target.response;
    // All following code here
});
xhr.send(null);

Equivalent to PHP's DOM library, JavaScript has DOMParser, which you can use to parse the entire string into a DOM document:

var dom = new DOMParser();
var svg = dom.parseFromString(xml, 'image/svg+xml');

And finally you can append the root element (i.e. svg.rootElement) anywhere you like in your DOM tree, e.g.

document.body.appendChild(svg.rootElement);

Demo:

var dataURL = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgdmlld0JveD0iMCAtMTAgMTAwIDg1IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPGRlZnM+CiAgICAgICAgPGNsaXBQYXRoIGlkPSJjbGlwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTAsMCBMMTAwLDAgTDEwMCw3NSBMMCw3NSBMMCwwCiAgICAgICAgICAgICAgICAgICAgIE01MCwxNSBBMjAsMjAgMCAxLDAgNTAsNjAgQTIwLDIwIDAgMSwwIDUwLDE1CiAgICAgICAgICAgICAgICAgICAgIE01MCwyNSBBMTIuNSwxMi41IDAgMSwxIDUwLDUwIEExMi41LDEyLjUgMCAxLDEgNTAsMjUiLz4KICAgICAgICA8L2NsaXBQYXRoPgogICAgPC9kZWZzPgogICAgPHJlY3QgeD0iMzAiIHk9Ii0xMCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjIwIiByeD0iMTAiIHJ5PSIxMCIvPgogICAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCIgaGVpZ2h0PSI3NSIgcng9IjE1IiByeT0iMTUiIGNsaXAtcGF0aD0idXJsKCNjbGlwKSIvPgo8L3N2Zz4K';
var xhr = new XMLHttpRequest();
xhr.open('GET', dataURL);
xhr.addEventListener('load', function(ev)
{
    var xml = ev.target.response;
    var dom = new DOMParser();
    var svg = dom.parseFromString(xml, 'image/svg+xml');
    document.body.appendChild(svg.rootElement);
});
xhr.send(null);
like image 161
Siguza Avatar answered Oct 05 '22 06:10

Siguza