svgdotjs / svg.import.js

A plugin for importing raw SVG into the svg.js library
97 stars 44 forks source link

scale to fit #5

Closed redon closed 11 years ago

redon commented 11 years ago

I try to import an svg into a div with given dimensions

   /** create a div */
   var d = document.createElement('div');
   d.id = 'myid';
   document.body.appendChild(d);

   /** size of the div */
   $('#myid').css('width', '150px');
   $('#myid').css('height', '100px');
   $('#myid').css('background-color', '#fcc');

   /** the svg (circle with r=250) */
   var rawSvg = '<svg version="1.2" baseProfile="tiny" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="250px" height="250px" viewBox="0 0 250 250" xml:space="preserve"> <circle fill="#0080FF" cx="125" cy="125" r="125"/> </svg>';

   var draw = new SVG('myid');
   draw.svg(rawSvg);

the svg is not scaled to the div as intended but keeps its original dimension

Bildschirmfoto 2013-03-20 um 10 10 02

same thing if I pass the dimension to the svg instead to the div

   draw.svg(rawSvg).size(150,100);

the result is correct when I also pass the viewbox of the svg

   draw.svg(rawSvg).size(150,100).viewbox(0,0,250,250);

Bildschirmfoto 2013-03-20 um 10 19 20

My problem is, that I have hundreds of svg stored in an external json and I don't know the viewbox of them in advance.

So how can i get the original viewbox before I draw the svg (or draw it, get the original viewbox and aply these values)?

When I place the svg without size and viewbox and try to get the viewbox with

   var box = draw.viewbox();

I get box = 0 0 100% 100% same with width = 100% and height = 100% that doesn't help

When I place the svg with size but without viewbox and try to get the viewbox I get box = 0 0 150 100 and for the width = 150 and height = 100 that doesn't help either

Seems to me as if the original viewbox is overwritten here by the new size because the actual shown dimensions are 0 0 250 250 (see first example)

Any help? Thank you

redon commented 11 years ago

because the rawSvg is a string, I can do some string manipulation to get the original width and height ;-)

   var a = [];
   a = rawSvg.split('width="');
   a = a[1].split('px');
   var vbw = a[0];

   a = [];
   a = rawSvg.split('height="');
   a = a[1].split('px');
   var vbh = a[0];

I can deal with that but I am not shure, if the svg should scale to fit without the need to repeat the viewbox

wout commented 11 years ago

Ok, I see your problem. Fortunately this is easy to achieve. The best thing is to import the svg data into a group:

var draw = SVG('canvas')
var group = draw.group()
group.svg(rawSvg)

Then you can get the bounding box of that group:

var box = group.bbox()

Now you know the exact size of all the imported elements together and you can define the viewbox to fit that group:

draw.viewbox(box.x, box.y, box.width, box.height)
redon commented 11 years ago

Thank you, that works

but if the element of the svg does not fill the viewbox completely (empty space arround the shapes) I get the inner dimensions (dimensions of the shape) and not the width and hight defined in the svg.

for

rawSvg = '<svg version="1.2" baseProfile="tiny" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="250px" height="250px" viewBox="0 0 250 250" xml:space="preserve"> <circle fill="#0080FF" cx="125" cy="125" r="105"/> </svg>';

I get box.width = 210 (2 * r="105") instead of 250

and it will look like Bildschirmfoto 2013-03-20 um 12 12 59

instead of Bildschirmfoto 2013-03-20 um 12 13 27

wout commented 11 years ago

That is because the <svg> tag in your rawSvg data has a width, height and viewbox defined:

<svg ... width="250px" height="250px" viewBox="0 0 250 250" ... />

The best solution here is to remove those attributes during the import process by passing a block in the svg() method:

var draw = SVG('canvas')
var group = draw.group()

group.svg(rawSvg, function() {
  if (this.type == 'svg') {
    this.attr('width', null)
    this.attr('height', null)
    this.attr('viewBox', null)
  }
})
redon commented 11 years ago

but I need the empty space, therefore it makes no sense to block the attributes during import

Thank you anyhow - I will stay with my »string-hack«

wout commented 11 years ago

I'm sorry, I wrongly interpreted your message. The bbox() method only calculates the total size of the elements inside a container element (e.g. <g> or <svg>) and not the size of the container element itself. The bbox() method gets its data directly from the native getBBox() method so it works correctly.

What you can do is apply the viewbox of the imported svg element to the svg parent you are importing to:

var draw = SVG('canvas')

draw.svg(rawSvg, function() {
  if (this.type == 'svg') {
    var box = this.viewbox()
    draw.viewbox(box.x, box.y, box.width, box.height)
  }
})

Note: to do this you will need the latest version (v0.5) of the svg.import.js plugin.

redon commented 11 years ago

had to make some changes because I actually have

   /** @type {Object} */ this.svgDraw = this.svgDraw || {};
   /** @type {Object} */ this.svgStore = this.svgStore || {};

   /** @type {Object} */ var box;
   this.svgDraw = new SVG('myid');
   this.svgStore = this.svgDraw(rawSvg, function() {
      if (this.type === 'svg') {
         box = this.viewbox();
      }
   });
   this.svgDraw.viewbox(box.x, box.y, box.width, box.height);

but this works! Thanks

wout commented 11 years ago

Great! I modified the viewbox() method on containers to also accept an object as the first argument:

this.svgDraw.viewbox(box)

This will be possible in the next release of svg.js (v0.11).