Reputation:
I'm using Two.js, I want to allow the user to import an svg file and display it on my scene.
Two.interpret() can take any dom of tag that has SVG data in it, so I've created this function to simulate tag from a file:
this.m_Two.interpretFromFile = function (two, fname) {
//Create an object and load an svg from 'fname'
var object = document.createElement("object");
//Set the id to the filename without path or extension
object.id = fname;
object.id = object.id.replace(/\\/g, '/');
object.id = object.id.substring(object.id.lastIndexOf('/')+1, object.id.lastIndexOf('.'));
console.log("Importing " + object.id);
object.data = fname; //HTML takes care of the rest
object.type = "image/svg+xml";
object.width = 64; //Import from file maybe?
object.height = 64;
object.style = "visibility:hidden;position:absolute;left:0px;top:0px;";
document.body.appendChild(object); //Add it to the body (invis) that way it loads
//When it loads, finish up
object.onload = function () {
if (!object.contentDocument || object.contentDocument.getElementsByTagName("svg").length < 1) return;
var svgObj = object.contentDocument.getElementsByTagName('svg')[0];
var ret = two.interpret(svgObj);
document.body.removeChild(object); //Remove it. Use path.clone to duplicate stuff..
return ret; //Return our new svg path
}
};
This function takes a Two instance, and a file url, and returns a two.path.
This all works fine and dandy when use the console with twoInstance.interpretFromFile (twoInstance, "images/Test.svg");
However, I am running these files locally for testing, and I certainly don't expect anyone to go into their browser's console and use the function, already knowing the file's exact path, and also know to type "file://" before that path... Yeesh
document.getElementById("dWorkSpace").ondrop = function(e) {
e.preventDefault(); //Default event loads the file itself.. KILL IT WITH FIRE.
if (!e.dataTransfer || !e.dataTransfer.items.length > 0 || !e.dataTransfer.files[0]) return;
var file = e.dataTransfer.files[0];
twoInstance.interpretFromFile(twoInstance, "file://" + file.name);
}
'file.name' only gives the file, not the path (yes, I know this is a securety thing in browsers. I've already looked at all of the other questions about that..)
So NOW, I could use a FileReader to read the svg file itself, but how do I get that data into an tag? I need it in this format at some point:
<object data="Test.svg" type="image/svg+xml"></object>
Because Two.js seems to not like 'svg' tag.. Just 'object' tag..
I am SO sorry for the enlongated question.. I'm pretty deep down this rabbit hole, as you can see. I tried to only use relevant code, but honestly the whole thing is monstrously large, and I'd hate to post it all.
Upvotes: 1
Views: 5575
Reputation: 1
If you want to include the external svg file as inline SVG, you can use FileReader.readAsText()
function to get the content of SVG as a text string.
Reference: https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText
const reader = new FileReader();
reader.addEventListener("load", (event) => {
console.log(event.target.result);
});
reader.readAsText(file);
Or if you want to embed it as Object or Img, you can use FileReader.readAsDataURL()
to get the SVG as encoded data string, refer to this article
https://web.dev/read-files/
and its code example:
https://glitch.com/~read-image-file
const reader = new FileReader();
reader.addEventListener('load', event => {
document.getElementById('output').src = event.target.result;
});
reader.readAsDataURL(file);
Upvotes: 0
Reputation: 18973
This is my way to upload svg image and append to div, use atob to convert base64 string to svg string: var svg = atob(event.target.result.replace(/data:image\/svg\+xml;base64,/, ''));
I use it in angular application. I share for whom concern.
handleFileInput(file: FileList) {
this.fileToUpload = file.item(0);
var xlinkNS = "http://www.w3.org/1999/xlink";
//Show image preview
let reader = new FileReader();
reader.onload = (event: any) => {
var svg = atob(event.target.result.replace(/data:image\/svg\+xml;base64,/, ''));
console.log(svg);
var element = document.createElement("div");
element.id = "svg";
document.getElementsByClassName('svg-container')[0].innerHTML = '';
document.getElementsByClassName('svg-container')[0].appendChild(element);
var newSvg = document.getElementById('svg');
newSvg.outerHTML = svg;
}
reader.readAsDataURL(this.fileToUpload);
}
HTML
<div class="svg-container">
<div id="svg"></div>
</div>
<div class="image-upload">
<label (click)='inputClick(Image)'>
<img src="image/upload.png" />
</label>
<input #Image type="file" (change)="handleFileInput($event.target.files)"/>
<button type="submit" class="btn-large btn-submit" [disabled]="Image.value=='' || !imageForm.valid"><i class="material-icons">save</i></button>
</div>
Upvotes: 2
Reputation:
What a quick answer.. @guest271314 (I'll link this if I can figure it out later).
This worked for me:
document.getElementById("dWorkSpace").ondrop = function(e) {
e.preventDefault(); //Default event loads the file itself.. KILL IT WITH FIRE.
if (!e.dataTransfer || !e.dataTransfer.items.length > 0 || !e.dataTransfer.files[0]) return;
var file = e.dataTransfer.files[0];
var reader = new FileReader(); //Make a reader
reader.onloadend = function (evt) { //When the file is actually loaded
Application.m_Two.interpretFromFile(Application.m_Two, reader.result);
}
reader.readAsDataURL(file);
}
The changed bit is with the FileReader object, and I used readAsDataUrl like guest271314 was talking about in the first comment. Can you tell that I'm new? xD
Demonstration: http://jonathancrowder.com/AnimatorProject/Application.html -- Drag an SVG file onto the page to try it out.
Upvotes: 1
Reputation: 137133
You will have to create an BlobURL from your File object (which is just a direct pointer to the file on the user's disk), and then set your <object>
's data
to this BlobURL.
Here I will just use an <input>
element, but its basically the same with any Blob/File, where-ever they came (note though that IIRC you need to call getAsFile()
method from dataTransfer
's Files.
f.onchange = e =>{
var file = f.files[0];
if(file.type === 'image/svg+xml'){
var obj = document.createElement('object');
obj.data = URL.createObjectURL(file);
obj.onload = e => URL.revokeObjectURL(obj.data);
document.body.appendChild(obj);
}
};
<input type="file" id="f"/>
Upvotes: 3