Export style with <svg> to .svg - javascript

I have an HTML file, that has a link to export all the <svg> inside to one .SVG. That's perfect, it's what I need and the community helped me a lot.
Now I have a problem that all the SVG inside the file, are clogged on top of each other.
I need them to keep the same 'display' they have on the webpage. I tried using internal, inline and external style... But nothing worked.
The X and Y I have in the <svg> is executed when I open the .SVG file in any program, but it doesn't affect anything on the HTML document.
I'm using the svg-converter.js to get all the .svg inside the <img> and convert to <svg>.
For some reason, inside that JS, something don't let me group all the <svg> inside another <svg>, only inside <div>. I have no idea if that is somehow causing the problem of the style not being exported.
Here is the working link.
Any help will be much appreciated.
Ps: I'd love to know why my question is downvoted.

You need to use x and y attributes to arrange your inner <svg> elements inside your root <svg> element.
Something like this (I've replaced the SVG contents with circles for brevity):
<svg xmlns="http://www.w3.org/3000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1224 1224">
<svg x="0" y="0" width="50%" height="50%" viewBox="0 0 612 612">
<circle fill="#7AB344" cx="306" cy="306" r="250"/>
</svg>
<svg x="50%" y="0" width="50%" height="50%" viewBox="0 0 612 612">
<circle fill="#8DD4F0" cx="306" cy="306" r="250"/>
</svg>
<svg x="0" y="50%" width="50%" height="50%" viewBox="0 0 612 612">
<circle fill="orange" cx="306" cy="306" r="250"/>
</svg>
<svg x="50%" y="50%" width="50%" height="50%" viewBox="0 0 612 612">
<circle fill="rebeccapurple" cx="306" cy="306" r="250"/>
</svg>
</svg>

THIS IS THE FINAL WORKING CODE:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/3000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://brand.express/projects/teste/svg-converter.js"></script>
</head>
<body>
<main id="content">
<div id="mySVG" style="width:100%;padding:0;margin:0;">
<img src='img/leafers-sapling.svg' class='svg-convert' x="0" y="0" width="286px">
<img src='img/aqualine-sapling.svg' class='svg-convert' x="286px" y="0" width="286px">
<img src='img/leafers-sapling.svg' class='svg-convert' x="572px" y="0" width="286px">
<img src='img/aqualine-sapling.svg' class='svg-convert' x="858px" y="0" width="286px">
</div>
</main>
<script>
//transforma os .svg para <svg>
jQuery(document).ready(function($) {
$('.svg-convert').svgConvert({
onComplete: function() {
exportSVG(document.getElementById('mySVG'));
},
svgCleanupAttr: []
});
});
</script>
<script>
var exportSVG = function(divContainer) {
var svgContainer = document.createElementNS("http://www.w3.org/1999/xhtml", "svg");
svgContainer.setAttribute('width','1200px');
$('#mySVG').find("svg").clone().appendTo(svgContainer);
var svgData = svgContainer.outerHTML;
var a = document.createElement('a');
a.href = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
a.download = 'finalSVG.svg';
a.innerHTML = 'download the .SVG file';
document.body.appendChild(a);
};
</script>
</body>
</html>

Related

Combine two SVG elements into one using Javascript?

Let's say I have the following html context, which I don't have access to its creation :
<div id="outer">
<div id="chart-div">
<svg id="chart"></svg>
</div>
<div id="legend-div">
<svg id="legend"></svg>
</div>
</div>
What I'm trying to do is export this SVG to an image using canvg library, the problem here is that they are separated and so I get two canvas, and canvg library accepts a SVG definition string as input.
How can I modify the html elements above so that they are only one SVG? Using Javascript as this is a browser extension.
I have tried just switching the DIVs tags to SVG but it just broke everything and the SVG became blank
This is my solution to your problem: I make the outer div display:none, I'm creating another svg element (you may do it dynamicaly) end inside the #new svg I'm using the #chart and the #legend. I hope it helps.
svg{border:1px solid;}
#outer {display:none}
#outer div{position:absolute; width:500px;}
<div id="outer">
<div id="chart-div">
<svg id="chart" viewBox="0 0 300 150">
<circle stroke="gold" fill="none" cx="100" cy="75" stroke-width="40" stroke-dasharray="124.85" stroke-dashoffset="20" r="20" />
</svg>
</div>
<div id="legend-div">
<svg id="legend" viewBox="0 0 300 150">
<rect fill="skyBlue" x="200" y="100" width="80" height ="30" />
</svg>
</div>
</div>
<svg id="new" viewBox="0 0 300 150" width="500">
<use xlink:href="#chart" />
<use xlink:href="#legend" />
</svg>
This is a Javascript solution for merging two svg accessibles in the document through DOM manipulation.
var svgNS = "http://www.w3.org/2000/svg";
var outer = document.getElementById('outer');
// get chart content
var chart = document.getElementById('chart-div');
var chartSvg = chart.getElementsByTagName('svg')[0];
var chartContent = Array.from(chartSvg.childNodes);
// get legend content
var legend = document.getElementById('legend-div');
var legendSvg = legend.getElementsByTagName('svg')[0];
var legendContent = Array.from(legendSvg.childNodes);
// create a merged-div where we are going to merge the svgs
var merged = document.createElement('div');
merged.setAttribute('id', 'merged-div');
outer.appendChild(merged);
// createElementNS for svg
var mergedSvg = document.createElementNS(svgNS, 'svg');
mergedSvg.setAttribute('id', 'merged');
// keep the viewBox of the chart
mergedSvg.setAttribute('viewBox', chartSvg.getAttribute('viewBox'));
merged.appendChild(mergedSvg);
// adding the content of both svgs
for (let i = 0; i < chartContent.length; i++) {
mergedSvg.appendChild(chartContent[i]);
}
for (let i = 0; i < legendContent.length; i++) {
mergedSvg.appendChild(legendContent[i]);
}
// the unmerged svgs can be removed
chart.remove();
legend.remove();
<div id="outer">
<div id="chart-div">
<svg id="chart" viewBox="0 0 300 150">
<circle stroke="gold" fill="none" cx="100" cy="75" stroke-width="40" stroke-dasharray="124.85" stroke-dashoffset="20" r="20" />
</svg>
</div>
<div id="legend-div">
<svg id="legend" viewBox="0 0 300 150">
<rect fill="skyBlue" x="200" y="100" width="80" height ="30" />
</svg>
</div>
</div>
Resulting markup:
<div id="outer">
<div id="merged-div">
<svg id="merged" viewBox="0 0 300 150">
<circle stroke="gold" fill="none" cx="100" cy="75" stroke-width="40" stroke-dasharray="124.85" stroke-dashoffset="20" r="20"></circle>
<rect fill="skyBlue" x="200" y="100" width="80" height="30"></rect>
</svg>
</div>
</div>

Clone certain group of elements from external SVG and use that group to create another SVG

I'm trying to use only certain group of elements from external SVG file(icons.svg) and use the selected group to create another SVG tag. The reason for trying to have it this way is that I don't want to have separate SVG file for every single icon as there will be tons of them eventually.
I prefer using D3 and currently my HTML code is following (which doesn't work):
<!DOCTYPE html>
<meta charset="utf-8">
<head><script src="https://d3js.org/d3.v3.js"></script></head>
<body>
<script>
d3.xml("icons.svg").mimeType("image/svg+xml").get(function(error, xml) {
if (error) throw error;
iconSvg = xml.getElementsByTagName("svg")[0];
sel = d3.select(iconSvg).selectAll("#icon2");
var newSvg = d3.select("body").append("svg")
.attr({"id":"newicon", "width":50, "height":50
}).append(sel);
});
</script>
The "icons.svg" file content is following:
<?xml version="1.0" encoding="utf-8"?>
<svg width="100%" version="1.1" id="icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<g id="icon1" width="16" height="16" >
<circle cx="7.5" cy="7.5" r="6.0" stroke="black" stroke-width="1" fill="#00AD5A" />
</g>
<g id="icon2" width="16" height="16" >
<circle cx="7.5" cy="7.5" r="6.0" stroke="black" stroke-width="1" fill="#FF0000" />
</g>
<g id="icon3" width="16" height="16" >
<circle cx="7.5" cy="7.5" r="6.0" stroke="black" stroke-width="1" fill="#FF00FF" />
</g>
</svg>
I'd be thankful for any help!
I found one solution to the problem. I changed lines
sel = d3.select(iconSvg).selectAll("#icon2");
to this:
sel = d3.select(iconSvg).select("#icon2").html();
and
var newSvg = d3.select("body").append("svg")
.attr({"id":"newicon", "width":50, "height":50
}).append(sel);
to this:
var newSvg = d3.select("body").append("svg")
.attr({"id":"newicon", "width":50, "height":50
}).append("g").html(sel);
and the end result is what I want.

Displaying and changing an external SVG background image

I'm trying to display and an external image inside an svg and then change it on button click. I can't get the background to show nor can I get it to change.
here is my fiddle:
https://jsfiddle.net/bsp9601z/
<head><meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<div id="slider"></div>
<button onclick="go()">change background</button>
<g id="backgroundImage">
<image width="100%" height="100%" xlink:href="http://www.menucool.com/slider/jsImgSlider/images/image-slider-2.jpg" /></g>
<svg width="792pt" height="612pt" viewBox="0 0 792 612" enable-background="new 0 0 792 612"
version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
</svg>
</svg>
function go(){
var im = document.getElementById('backgroundImage');
im.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'http://demo.elmanawy.info/moraco/layout1/assets/images/portfolio/8.jpg');
im.setAttributeNS('http://www.w3.org/1999/xlink','width', "100%");
im.setAttributeNS('http://www.w3.org/1999/xlink','height', "100%");
}
You are missing <svg> tags and selecting <g> rather than <image>
<button onclick="go()">change background</button>
<svg>
<g id="backgroundImage">
<image width="100%" height="100%" xlink:href="http://www.menucool.com/slider/jsImgSlider/images/image-slider-2.jpg" />
</g>
</svg>
<script>
function go() {
var im = document.getElementById('backgroundImage').querySelector("image");
im.setAttributeNS('http://www.w3.org/1999/xlink'
, 'xlink:href'
, 'http://demo.elmanawy.info/moraco/layout1/assets/images/portfolio/8.jpg');
}
</script>

Adding Javascript events to SVG <def> elements instead of directly on the children?

I know that it is possible to attach plain-old Javascript event handlers to elements within an SVG tag. I am looking to duplicate very many instances of an object, so I am planning to use the <def> and <use> tags to simplify things a bit. However each of my children items will need to handle "click" and other events. I could define those events for each child, but it would be nice if I could somehow define them ONCE in the initial items, and then just "reuse" that click event. Is this possible?
https://jsfiddle.net/khyp1o0w/
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<rect id="someDef" x="0" y="0" width="60" height="30" fill="#f00" />
<!-- ^^ would like to define an event here... -->
</defs>
<use x="150" y="150" xlink:href="#someDef" />
<use x="250" y="250" xlink:href="#someDef" />
<!-- ^^ ... that is used by these guys -->
</svg>
EDIT: Adding example SVG.
IIRC elements targeted by a use element should be internally copied into the <use> element just like if the <use> were an iframe, and the content were cloned.
So this means that events attached on the original node won't be copied onto the copies, unless this event is part of the node markup (inlined). But chrome don't really follow specs here and won't even make it work... So here is an example for FF :
// this won't work
document.getElementById('someDef').addEventListener('click', function() {
this.setAttribute('stroke', randomColor());
});
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<script type="application/javascript">
function randomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[~~(Math.random() * 15)];
}
return color;
}
</script>
<!-- this will work in FF -->
<rect id="someDef" onclick="this.setAttribute('fill',randomColor())" x="0" y="0" width="60" height="30" fill="#f00" />
</defs>
<use x="150" y="150" xlink:href="#someDef" />
<use x="250" y="250" xlink:href="#someDef" />
</svg>
You could of course use event delegation by listening for click events on your root svg element, but this would work only for direct target elements, not for nested ones :
var svg = document.querySelector('svg');
svg.addEventListener('click', function(e) {
var usedTargetID = e.target.getAttributeNS("http://www.w3.org/1999/xlink", 'href');
switch (usedTargetID) {
case '#direct':
clickDirect(e);
break;
case '#nested':
clickNested(e);
break;
default:
return;
}
});
function clickDirect(e) {
// what you seem to want
e.target.setAttribute('fill', randomColor());
}
function clickNested(e) {
// will set both nested...
e.target.setAttribute('fill', randomColor());
}
function randomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[~~(Math.random() * 15)];
}
return color;
}
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<rect id="direct" x="0" y="0" width="60" height="30" />
<g id="nested">
<!-- you can only access this -->
<!-- not its content individually -->
<rect x="80" y="0" width="60" height="30" />
<rect x="20" y="70" width="60" height="30" />
</g>
</defs>
<use x="150" y="50" xlink:href="#direct" />
<use x="250" y="150" xlink:href="#nested" />
</svg>
Or you could also add an event per element, but this would have the same limitations as delegation...

How to pack text inside svg rect

I wish to fit svg text inside a rect. I could use an approach to compare the widths and add line breaks to the text, but it's not tedious.
Is there a more elegant way than this? Maybe by using CSS or d3?
UPDATE:the following code appends foreignObject using d3 but the div is not displayed. (it is there in the code inspecter)
var group = d3.select("#package");
var fo = group.append("foreignObject").attr("x", 15).attr("y", 15).attr("width", 190).attr("height", 90);
fo.append("div").attr("xmlns", "http://www.w3.org/1999/xhtml").attr("style", "width:190px; height:90px; overflow-y:auto").text("Thiggfis the dgdexsgsggs wish to fit insidegssgsgs");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="p"></p>
<svg width="220" height="120" viewBox="0 0 220 120" id="package">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black"/>
</svg>
A namespace cannot be assigned by attr, it's a side effect of element creation. You need an html div so you need to tell d3 that by calling the element xhtml:div, once you do that, d3 will do the rest.
var group = d3.select("#package");
var fo = group.append("foreignObject").attr("x", 15).attr("y", 15).attr("width", 190).attr("height", 90);
fo.append("xhtml:div").attr("style", "width:190px; height:90px; overflow-y:auto").text("Thiggfis the dgdexsgsggs wish to fit insidegssgsgs");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="p"></p>
<svg width="220" height="120" viewBox="0 0 220 120" id="package">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black"/>
</svg>
Here's a simple example of a foreignObject used to insert HTML markup into an SVG:
<svg width="220" height="120" viewBox="0 0 220 120">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black" />
<foreignObject x="15" y="15" width="190" height="90">
<div xmlns="http://www.w3.org/1999/xhtml" style="width:190px; height:90px; overflow-y:auto"><b>This</b> is the <i>text</i> I wish to fit inside <code>rect</code></div>
</foreignObject>
</svg>

Categories

Resources