nytimes / svg-crowbar

Extracts an SVG node and accompanying styles from an HTML document and allows you to download it all as an SVG file.
https://nytimes.github.io/svg-crowbar/
MIT License
839 stars 153 forks source link

Replace xlink:href="file.jpg" by src="<data URI>" so raster image are embeded. #16

Open hugolpz opened 9 years ago

hugolpz commented 9 years ago

Currently, d3js generated SVG with linked images via :

svg.append("image")
  .attr("xlink:href", "file.jpg")
  .attr("width", width)
  .attr("height", height)
  .attr("class", "bg");

producing :

<image xlink:href="file.jpg" width="960" height="500" class="bg"></image>

, when dowloaded by v2.0, result into svg file with missing rasters.

The solution would be 1/ to convert file.jpg into a data URI string (var str) using JS. 2/ to replace

   <image xlink:href="file.jpg" ...

by

   <image href="data:image/png;base64,<dataURI>" ... 

3/ to repeat for each <image xlink:href= found.

The JS code to do 1. have been discussed here while 1 & 2 are in demo there (v/96/). The script successfully replace the href link by an href with valid and working data uri, so the image is really embedded within the svg. Then, crowbar naturally download both the SVG code and the (embedded) images :)

Note: as my stack appears to require server side base64 convertion and my d3js skills are limited, I will not push further in this way. But most of it is there, and it should be good to add to crowbar.

Note: I didn't tested if the data_uri type extension should really match the image's extension.

hugolpz commented 9 years ago
var converterEngine = function (input) { // fn BLOB => Binary => Base64 ?
    var uInt8Array = new Uint8Array(input),
        i = uInt8Array.length;
    var biStr = []; //new Array(i);
    while (i--) {
        biStr[i] = String.fromCharCode(uInt8Array[i]);
    }
    var base64 = window.btoa(biStr.join(''));
    console.log("2. base64 produced >>> " + base64); // print-check conversion result
    return base64;
};

var getImageBase64 = function (url, callback) {
    // 1. Loading file from url:
    var xhr = new XMLHttpRequest(url);
    xhr.open('GET', url, true); // url is the url of a PNG image.
    xhr.responseType = 'arraybuffer';
    xhr.callback = callback;
    xhr.onload = function (e) {
        if (this.status == 200) { // 2. When loaded, do:
            console.log("1:Loaded response >>> " + this.response); // print-check xhr response 
            var imgBase64 = converterEngine(this.response); // convert BLOB to base64
            this.callback(imgBase64); //execute callback function with data
        }
    };
    xhr.send();
};

//SVG DOM injection
getImageBase64('http://fiddle.jshell.net/img/logo.png', function (data) {
    d3.selectAll("image")
      .attr("href", "data:image/png;base64," + data); // replace link by data URI
})
hugolpz commented 9 years ago

Issue moved to d3js project.

DDDgfx commented 8 years ago

Is there any chance this will be incorporated into SVG crowbar? It could be very useful - particularly if your graphic is using some external SVG icons.