Monday, May 13, 2024
 Popular · Latest · Hot · Upcoming
177
rated 0 times [  181] [ 4]  / answers: 1 / hits: 6548  / 10 Years ago, sat, may 10, 2014, 12:00:00

I am writing a little HTML5+JS tool to generate an SVG image. I have encountered a number of issues in doing so, and while I have workarounds for most of them, in at least one case I feel like there must be a better way. And then there are a couple of things that still just aren't working.



At present, this is for my own use, so cross-browser compatibility isn't a concern; as long as it works in Firefox (first preference) or Chromium, it's all good. I would like to stick it online once it's working right, though, so compatibility caveats would be appreciated.



Goals




  1. All processing should be done client-side; in fact, at this stage everything is a local file://, no web server involved.

  2. Add text and elements to an SVG image (inline in the HTML) using scripted form elements.

  3. Click on the SVG (which is shrunk down to a preview size) to open it, as modified, in a new window/tab.

  4. Use some easy-to-access method (i.e. not DOM inspector, copy to text file, save) to save the SVG to disk.



Test case



https://gist.github.com/perey/1d352a790f749aa05a8b (see it in action)



<!DOCTYPE html>
<html xmlns=http://www.w3.org/1999/xhtml lang=en xml:lang=en>
<head>
<meta charset=utf-8/>
<title>SVG Generator</title>
<style type=text/css>
figure {
width: 45%;
float: right;
}
#output-pic {
border: thin solid green;
cursor: pointer;
}
form {
width: 45%;
float: left;
}
</style>
<script>
window.onload = function() {
document.getElementById(input-box).oninput = update_text;
document.getElementById(output-pic).onclick = show_svg;
update_text();
}
function update_text() {
var input_elem = document.getElementById(input-box);
var output_elem = document.getElementById(changeable-text);
output_elem.textContent = input_elem.value;
}
function show_svg() {
var svg_win = window.open(, svg_win);
var embedded_svg = document.getElementById(output-pic);
var transplanted_svg = svg_win.document.importNode(embedded_svg, true);
var blank_root = svg_win.document.documentElement;
svg_win.document.removeChild(blank_root);
svg_win.document.appendChild(transplanted_svg);
}
</script>
</head>
<body>
<figure role=img aria-labelledby=preview-caption>
<figcaption id=preview-caption>Preview <small>(click for full
size)</small></figcaption>
<svg id=output-pic
xmlns=http://www.w3.org/2000/svg
xmlns:xlink=http://www.w3.org/1999/xlink
version=1.1 width=640px height=480px
viewBox=0 0 640 480 preserveAspectRatio=xMinYMin>
<title>A test SVG file</title>
<defs>
<style type=text/css>
text {
font-family: serif;
stroke: none;
fill: red;
}
.underline {
stroke: blue;
stroke-width: 1;
fill: none;
marker-mid: url(#arrow);
}
</style>
<marker id=arrow
viewBox=-3 -3 6 6 orient=auto
markerUnits=strokeWidth
refX=0 refY=0
markerWidth=6 markerHeight=6>
<path d=M0,0 -3,-3 3,0 -3,3 Z/>
</marker>
</defs>
<text id=changeable-text text-anchor=middle font-size=40
x=320 y=240></text>
<path class=underline d=M10,250 h310 310/>
</svg>
</figure>
<form>
<label>Text: <input id=input-box/></label>
</form>
</body>
</html>


Issues



Opening the SVG



Opening about:blank, deleting its document element, and adding the SVG element, feels really hacky. However, nothing else has worked I have only found a slightly better way of constructing a document in a new window (see below).



In particular, I've tried loading a barebones SVG file and adding all the child nodes of the preview SVG, like so:



function show_svg() {
var svg_win = window.open(blank.svg, svg_win);
var embedded_svg = document.getElementById(output-pic);
var transplanted_svg = svg_win.document.importNode(embedded_svg, true);
var blank_root = svg_win.document.documentElement;

while (transplanted_svg.hasChildNodes()) {
blank_root.appendChild(transplanted_svg.firstChild);
}
svg_win.alert(Done!);
}


However, after this function does its thing, the loaded file then wipes all changes made to it and reverts to its pristine state. (The alert is there to highlight this fact: in Firefox, the alert box itself disappears without user action when the page is wiped. In Chromium, the alert box hangs about, but the wipe happens after it's dismissed.)



It's not a matter of tying the node reparenting to the new window's onload handler. Yes it is. I made a mistake when I first tried that. Here's what I did:



function show_svg() {
var svg_win = window.open(blank.svg, svg_win);
var embedded_svg = document.getElementById(output-pic);
var transplanted_svg = svg_win.document.importNode(embedded_svg, true);
var blank_root = svg_win.document.documentElement;

svg_win.onload = function () {
while (transplanted_svg.hasChildNodes()) {
blank_root.appendChild(transplanted_svg.firstChild);
}
svg_win.alert(Done!);
}
}


What I should've done is put the definition of blank_root inside the onload handler. That works.



Still feels like there should be a way to construct a new document from scratch, though. Modifying a blank SVG is better than modifying the about:blank HTML, but is that really the best way?



Missing markers



(This only seems to be an issue with Firefox, not with Chromium.)



The marker-mid styling works fine in the preview image, but not in the opened SVG. I have no idea why. Edit: Modifying an SVG file instead of about:blank doesn't have this issue. I'm off to file a bug, but I already suspect they're going to say don't try and dynamically convert a HTML file into an SVG file.



Saving the generated SVG



I have no idea how to do this. A few tantalising hints seem to say that it's something to do with Blobs, but I've found nothing that addresses saving a generated SVG file client-side, and I don't understand what they're doing well enough to make it work for me.



Any help, suggestions, advice, or corrections?


More From » html

 Answers
2

I have solved my own problems, using modern HTML5 APIs.



The new show_svg() function looks like this:



function show_svg(evt) {
var svg = document.getElementById(output-pic);
var serializer = new XMLSerializer();
var svg_blob = new Blob([serializer.serializeToString(svg)],
{'type': image/svg+xml});
var url = URL.createObjectURL(svg_blob);

var svg_win = window.open(url, svg_win);
}


The browser's own Save functionality will work on this new window, and it doesn't involve any modifications to other files that feel weird or hacky. (It does seem a bit odd to serialise the SVG only to view it in the browser again, but this nonetheless seems to be The Right Thing under HTML5.)



The only unresolved problem is the disappearing markers—in fact, the problem gets worse, as now <use> elements don't work either! However, they're still there and functional in the code, so once the SVG is saved to a file, everything in the file works fine. And I've filed a bug with Mozilla, too.


[#45402] Friday, May 9, 2014, 10 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
clarkulisesa

Total Points: 422
Total Questions: 93
Total Answers: 112

Location: Austria
Member since Thu, Jan 7, 2021
3 Years ago
clarkulisesa questions
Mon, Feb 24, 20, 00:00, 4 Years ago
Mon, Aug 12, 19, 00:00, 5 Years ago
;