I have a SVG file (an exported Gliffy diagram) that I want to open and edit in Inkscape. When viewing the code of the file using the developer options of Chrome, it looks like:
<svg xmlns="...>
<g transform="...>
<image xlink:href="data:image/svg+xml,%0A%20...></image>
</g>
... (about 20 more <g>...</g> tags)
</svg>
When decoding the part starting with %0A%20..., it translates to something like
data:image/svg xml,
<svg xmlns="http://www.w3.org/2000/svg" height="50000" width="50000">
<style>
.gliffy-rte-text {
...
The issue is, that in Inkscape those parts will be replaced by a placeholder telling me "Linked image not found" and as speculated here, Inkscape most likely is not able to read the CSS styling correctly or probably at all.
I would very much appreciate any thoughts or ideas on how to convert the file such that it can be edited and displayed correctly in Inkscape.
You might convert all styling to element attributes using SVGOMG:
replace your embedded <image> element by the decoded data url content. Your parent svg should look something like this:
<!-- parent svg -->
<svg xmlns="http://www.w3.org/2000/svg" >
<!-- embedded svg decoded data url -->
<svg xmlns="http://www.w3.org/2000/svg" height="50000" width="50000">
<style>
.gliffy-rte-text {
}
</style>
</svg>
</svg>
Use SVGOMG "inline styles" and "style to attribute"parameter:
You should disable all other optimizing parameter, since they might strip to many other attributes.
Expected before result
<svg viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<style>
circle{
fill:none;
stroke: orange;
stroke-width:10;
}
</style>
<circle cx="128" cy="128" r="100"/>
</svg>
After
<svg viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<circle cx="128" cy="128" r="100" fill="none" stroke="orange" stroke-width="10"/>
</svg>
Open the wrapper file in a browser. Right-click on the area containing the embedded SVG, and choose "Save (image) as...". If the wrapper contains multiple <image> tags, you will have to save them to separate files, but at least they will be in a form Inkscape can handle.
If you want to get them all together in one SVG file again, you will have to re-import them via the Inkscape import function. Take care to select 'Include as editable object', or you will end up right where you started:
The speculations above about CSS are unsubstantiated, btw. Inkscape will convert the content of a <style> element into inline style attributes, but otherwise handle them correctly. What happened is stated quite clearly in the above screenshot: data URLs embeded via an <image> tag will not be editable in Inkscape.
I'm trying to hack together a sprite sheet from a set of icons. I know almost nothing about SVG. I can get the simple icons to work, but an icon with a clip path isn't displaying properly. From what I can tell it seems like it's not using the clip path.
The sprite works in jsfilddle and it works if I just load the svg on it's own and include a < use > statement in the SVG. But if I have a separate < use > it doesn't work.
All my testing has been done in Chrome (50.0.2661.94)
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<clipPath id="folder-clip-0">
<path d="..." />
</clipPath>
<symbol id="folder" viewBox="0 0 32 32">
<g class="container" data-width="32" data-height="27" transform="translate(0 2)">
<path d="..." class="..." />
<path class="..." d="..." />
<path clip-path="url(#folder-clip-0)" d="..." class="..." />
</g>
</symbol>
</defs>
</svg>
I'm using it like so:
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/img/path/sprite.svg#folder">
</use>
</svg>
When I use the separate statement it looks like this:
But it should look like this:
The color difference is not relevant, it's just the background when the image was taken.
Edit:
I just discovered that if I dump the whole sprite sheet into the page HTML and reference it locally instead of an external file it works. So I don't know what's wrong with my external reference.
e.g.
<svg>
<use xlinkHref={"/img/path/not/work/sprite.svg#folder"}></use>
</svg>
vs.
<svg>
<symbol id="folder"></symbol>
</svg>
<svg>
<use xlinkHref={"#folder"}></use>
</svg>
This works for me as a fallback, but I'd rather have an external SVG file instead of embedding it in my HTML.
Edit 2:
If the SVG sprite sheet is embeded in the HTML directly using the external link shows the icon correctly.
This seems to be a browser support issue. Using the external reference works as expected in Firefox. Chrome doesn't handle clip paths and some other functions in external references. There's an outstanding bug report filed. Safari also doesn't support it.
Related StackOverflow ticket: Why can't I reference an SVG linear gradient defined in an external file (paint server)?
Open bugs:
https://code.google.com/p/chromium/issues/detail?id=109212
https://bugs.webkit.org/show_bug.cgi?id=105904
I came accross a strange behaviour when using SVG with AngularJS. I'm using the $routeProvider service to configure my routes. When I put this simple SVG in my templates, everything is fine:
<div id="my-template">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect fill="red" height="200" width="300" />
</svg>
// ...
</div>
But when I add a filter, with this code for instance:
<div id="my-template">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="blurred">
<feGaussianBlur stdDeviation="5"/>
</filter>
</defs>
<rect style="filter:url(#blurred)" fill="red" height="200" width="300" />
</svg>
</div>
Then:
It works on my homepage.
With Firefox, the SVG isn't visible anymore on the other pages, but it still leaves space where it would have been. With Chrome, the SVG is visible, but not blurred at all.
The SVG is visible again when I remove manually (with Firebug) the filter style.
Here is the routes configuration:
$routeProvider
.when('/site/other-page/', {
templateUrl : 'view/Site/OtherPage.html',
controller : 'Site.OtherPage'
})
.when('/', {
templateUrl : 'view/Site/Home.html',
controller : 'Site.Home'
})
.otherwise({
redirectTo : '/'
})
;
Fiddle
Please notice that I've failed to reproduce the problem with Chrome in a Fiddle, although it "works" with Firefox.
I've tried to no avail to create my whole SVG with document.createElementNS().
Does someone has an idea of what is happening?
The problem
The problem is that there is a <base> tag in my HTML page. Therefore, the IRI used to identify the filter is not anymore relative to the current page, but to the URL indicated in the <base> tag.
This URL was also the URL of my home page, http://example.com/my-folder/ for instance.
For the pages other than the home page, http://example.com/my-folder/site/other-page/ for example, #blurred was computed to the absolute URL http://example.com/my-folder/#blurred. But for a simple GET request, without JavaScript, and therefore without AngularJS, this is simply my base page, with no template loaded. Thus, the #blurred filter doesn't exist on this pages.
In such cases, Firefox doesn't render the <rect> (which is the normal behaviour, see the W3C recommandation). Chrome simply doesn't apply the filter.
For the home page, #blurred is also computed to the absolute URL http://example.com/my-folder/#blurred. But this time, this is also the current URL. There is no need to send a GET request, and thus the #blurred filter exists.
I should have seen the additional request to http://example.com/my-folder/, but in my defense, it was lost in a plethora of other requests to JavaScript files.
The solution
If the <base> tag is mandatory, the solution is to use an absolute IRI to identify the filter. With the help of AngularJS, this is pretty simple. In the controller or in the directive that is linked to the SVG, inject the $location service and use the absUrl() getter:
$scope.absUrl = $location.absUrl();
Now, in the SVG, just use this property:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="blurred">
<feGaussianBlur stdDeviation="5"/>
</filter>
</defs>
<rect style="filter:url({{absUrl}}#blurred)" fill="red" height="200" width="300" />
</svg>
Related: SVG Gradient turns black when there is a BASE tag in HTML page?
It looks like a behaviour I observed before. The root cause is that you end up having multiple elements (filters) with the same id (blurred). Different browsers handle it differently...
Here is what I did to reproduce your case: http://jsfiddle.net/z5cwZ/
It has two svg and one is hidden, firefox shows none.
There are two possibilities to avoid conflicting ids. First you can generate unique ids from your template (I can't help in doing it with angularjs tough). Here is an example: http://jsfiddle.net/KbCLB/1/
Second possibility, and it may be easier with angularjs, is to put the filter outside of the individual svgs (http://jsfiddle.net/zAbgr/1/):
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="0" height="0">
<defs>
<filter id="blurred">
<feGaussianBlur stdDeviation="10" />
</filter>
</defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="display:none">
<rect style="filter:url(#blurred)" fill="red" height="200" width="300" />
</svg>
<br/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect style="filter:url(#blurred)" fill="red" height="200" width="300" />
</svg>
Just ran into this problem. Expanding from #Blackhole's answer, my solution was to add a directive that changes the value of every fill attribute.
angular.module('myApp').directive('fill', function(
$location
) { 'use strict';
var absUrl = 'url(' + $location.absUrl() + '#';
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
$attrs.$set('fill', $attrs.fill.replace(/url\(#/g, absUrl));
}
};
});
I recently ran into this issue myself, if the svg is used on different routes you will have to add the $location to each path.
A better way to implement this is to just use a window.location.href instead of the $location service.
Also had issues with svg link:hrefs, and yes the problem is because of the <base> - I had errors being thrown when concatenating the absolute url because of the Strict Contextual Escaping restrictions.
My solution was to write a filter that added the absolute url and also trusted the concatenation.
angular.module('myApp').filter('absUrl', ['$location', '$sce', function ($location, $sce) {
return function (id) {
return $sce.trustAsResourceUrl($location.absUrl() + id);
};
}]);
And then use it like this: {{'#SVGID_67_' | absUrl}
Result : http://www.example.com/deep/url/to/page#SVGID_67_ and no $sce errors
I'm trying to hack together a sprite sheet from a set of icons. I know almost nothing about SVG. I can get the simple icons to work, but an icon with a clip path isn't displaying properly. From what I can tell it seems like it's not using the clip path.
The sprite works in jsfilddle and it works if I just load the svg on it's own and include a < use > statement in the SVG. But if I have a separate < use > it doesn't work.
All my testing has been done in Chrome (50.0.2661.94)
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<clipPath id="folder-clip-0">
<path d="..." />
</clipPath>
<symbol id="folder" viewBox="0 0 32 32">
<g class="container" data-width="32" data-height="27" transform="translate(0 2)">
<path d="..." class="..." />
<path class="..." d="..." />
<path clip-path="url(#folder-clip-0)" d="..." class="..." />
</g>
</symbol>
</defs>
</svg>
I'm using it like so:
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="/img/path/sprite.svg#folder">
</use>
</svg>
When I use the separate statement it looks like this:
But it should look like this:
The color difference is not relevant, it's just the background when the image was taken.
Edit:
I just discovered that if I dump the whole sprite sheet into the page HTML and reference it locally instead of an external file it works. So I don't know what's wrong with my external reference.
e.g.
<svg>
<use xlinkHref={"/img/path/not/work/sprite.svg#folder"}></use>
</svg>
vs.
<svg>
<symbol id="folder"></symbol>
</svg>
<svg>
<use xlinkHref={"#folder"}></use>
</svg>
This works for me as a fallback, but I'd rather have an external SVG file instead of embedding it in my HTML.
Edit 2:
If the SVG sprite sheet is embeded in the HTML directly using the external link shows the icon correctly.
This seems to be a browser support issue. Using the external reference works as expected in Firefox. Chrome doesn't handle clip paths and some other functions in external references. There's an outstanding bug report filed. Safari also doesn't support it.
Related StackOverflow ticket: Why can't I reference an SVG linear gradient defined in an external file (paint server)?
Open bugs:
https://code.google.com/p/chromium/issues/detail?id=109212
https://bugs.webkit.org/show_bug.cgi?id=105904
I'm trying to define my iconset-svg using a symbol tag instead of a g tag but it isn't working as the icon isn't rendered. If I define some icon using it works.
The code:
<iron-iconset-svg name="br-icons">
<svg style="display: none">
<defs>
<symbol id="icon-menu" viewBox="0 0 1024 1024">
<title>menu</title>
<path class="path1" d="M64 192h896v192h-896zM64 448h896v192h-896zM64 704h896v192h-896z"></path>
</symbol>
</defs>
</svg>
</iron-iconset-svg>
I am using it like this:
<iron-icon icon="br-icons:icon-menu"></iron-icon>
Does anyone know how I can get this working?
The response I got from the Polymer team was "you need to use a <use> element with a <symbol>, which doesn't let you style it". So I think for now you'll want to stick with <g>. Apologies if I'm mistaken here, I am not an SVG expert by any means :)