Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show full SVG with scaling intact

I have a d3 generated force layout diagram, I need to export it to png with the current (user-selected) scaling intact.

By my reasoning this will then grow the SVG width and height, so if the svg is 1920x1080 and it is 'zoomed' in, the exported svg should probably have a much larger width and height to accommodate this.

I have tried everything, and I am missing something, I just can't seem to dynamically calculate the correct values for the output I require.

Here is an example of my exported SVG, note that there is a lot more information, it is just not visible at that scale.

EDIT

Here is my basic exporting code, mostly adapted from highcharts:

    serializeSvg: function() {
        /**
         * serialize a xml object to string
         * @param {type} xmlNode the node to use
         * @returns {String|@exp;xmlNode@pro;xml|@exp;window@pro;XMLSerializer@call;@call;serializeToString}
         */
        function serializeXmlNode(xmlNode) {
            if (typeof window.XMLSerializer !== 'undefined') {
                return (new window.XMLSerializer()).serializeToString(xmlNode);
            } else if (typeof xmlNode.xml !== 'undefined') {
                return xmlNode.xml;
            }

            return '';
        }

        var svg = serializeXmlNode(document.getElementsByTagName('svg')[0]),
            factor = 2;
        svg = '<svg'
                + ' xmlns="http://www.w3.org/2000/svg"' // xml namespace
                + ' version="1.1"'
                + ' xmlns:xlink="http://www.w3.org/1999/xlink"' // for images
                + ' ' + svg.substring(svg.indexOf('<svg ') + 5);

        // highcharts svg sanitizer
        svg = svg.replace(/width="([^"]+)"/, function(m, width) {
                return 'width="' + (width * factor) + '"';
            }).replace(/height="([^"]+)"/, function(m, height) {
                return 'height="' + (height * factor) + '"';
            }).replace(/<rect class="drag"[^<]+<\/rect>/, '')

            // IE specific
            .replace(/<IMG /g, '<image ')
            .replace(/height=([^" ]+)/g, 'height="$1"')
            .replace(/width=([^" ]+)/g, 'width="$1"')
            .replace(/id=([^" >]+)/g, 'id="$1"')
            .replace(/class=([^" ]+)/g, 'class="$1"')
            .replace(/ transform /g, ' ')
            .replace(/:(path|rect)/g, '$1')
            .replace(/style="([^"]+)"/g, function(s) {
                    return s.toLowerCase();
            });

        return svg;
    }

and the main zoom/scaling startup for the d3 layout:

var layout = d3.layout.force();
var DEFAULT_SIZE = 64;
var GROWTH_SCALE = 1.15;
var SHRINK_SCALE = 1.05;

// creates a new force layout
var force = layout
    .size([w, h])
    .gravity(.06)
    .distance(110)
    //.friction(0.6)
    //.linkStrength(0.4)
    .charge(-((DEFAULT_SIZE * GROWTH_SCALE) * 10))
    .on('tick', tick);

// creates the svg context
var svg = d3.select('.la-container').append('svg:svg')
    .attr('width', w)
    .attr('height', h)
    .attr('pointer-events', 'all') // set for the pan/zooming
    .append('svg:g') // add a g element for capturing zoom and pan
      .call(d3.behavior.zoom().scaleExtent([0.6, 6.0]).on('zoom', redraw))
    .append('svg:g');

svg.append('svg:rect')
    .attr('class', 'drag')
    .attr('width', w)
    .attr('height', h)
    .attr('fill', 'white');
like image 387
epoch Avatar asked Mar 20 '13 12:03

epoch


2 Answers

So, in your d3 startup code, you call d3.behaviour.zoom.on() to bind the "redraw" function to the "zoom" action. To get the scale and translation level, you want to capture the d3.scale and d3.translate values in this function, something like this (as you haven't posted the code for the redraw function):

var scale = 1;
var xtranslate=0;
var ytranslate=0;

var redraw = function(){
    scale= d3.event.scale;
    xtranslate=d3.event.translate[0];
    ytranslate=d3.event.translate[1];

    //your actual code to redraw stufff
}

That will get you the scaling and translation values from d3: then you need to apply the appropriate changes to the exported svg: I haven't actually done this, but presumably, you scale the "height" and "width" attributes appropriately (I think this would be just by multiplying the attribute values by the scale value, but I can't be sure).

like image 100
RoryB Avatar answered Oct 23 '22 09:10

RoryB


Apply a view box and multiply it by scale value,

for example 1920x1080, when unzoomed then viewbox=0 0 1920 1080

at scale = 0.5 1920*0.5 1080*0.5 viewbox = 0 0 960 540

like image 1
Saad Ahmed Avatar answered Oct 23 '22 11:10

Saad Ahmed