user8112809
user8112809

Reputation:

(JavaScript) Import external <object> svg from File or FileReader Object

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

Answers (4)

Wenting Zhang
Wenting Zhang

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

Hien Nguyen
Hien Nguyen

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

user8112809
user8112809

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

Kaiido
Kaiido

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

Related Questions