svgdotjs / svgdom

Straightforward DOM implementation to make SVG.js run headless on Node.js
MIT License
269 stars 53 forks source link

Different transform results than browser #113

Open cyberwombat opened 12 months ago

cyberwombat commented 12 months ago

I am migrating a complex browser based SVG generator to use SVGDOM and have it working 99%. There is one spot however where the transform calculations are different and wondering if you think it might be a bug or if there is something I am missing.

I have an element and applying the following action:[]

this.el.transform({
  scaleX: 0.25,
  scaleY:  0.25,
})

The "coordinates" data before the transform is the same but the result is different - For ex notice the "f" and "translateY" parameters.

Browser:

Before transform:

{
  "scaleX": 0.25,
  "scaleY": 0.25,
  "shear": 0,
  "rotate": 0,
  "translateX": 987,
  "translateY": 182,
  "originX": 0,
  "originY": 0,
  "a": 0.25,
  "b": 0,
  "c": 0,
  "d": 0.25,
  "e": 987,
  "f": 182
}

After:

{
  "scaleX": 0.25,
  "scaleY": 0.25,
  "shear": 0,
  "rotate": 0,
  "translateX": 0,
  "translateY": 0,
  "originX": 0,
  "originY": 0,
  "a": 0.25,
  "b": 0,
  "c": 0,
  "d": 0.25,
  "e": 0,
  "f": 0
}

SVGDom: Before transform:

{
  scaleX: 0.25,
  scaleY: 0.25,
  shear: 0,
  rotate: 0,
  translateX: 987,
  translateY: 182,
  originX: 0,
  originY: 0,
  a: 0.25,
  b: 0,
  c: 0,
  d: 0.25,
  e: 987,
  f: 182
}

After transform:

{
  scaleX: 0.25,
  scaleY: 0.25,
  shear: 0,
  rotate: 0,
  translateX: 0,
  translateY: -20.9765625,
  originX: 0,
  originY: 0,
  a: 0.25,
  b: 0,
  c: 0,
  d: 0.25,
  e: 0,
  f: -20.9765625
}

I have tried this with 0.1.14 as well as current master with same result.

Happy to provide additional data. Do you have an idea what issue could be?

Fuzzyma commented 12 months ago

Can you check the bounding box in both versions? I have the gut feeling that it is wrong on svgdom

cyberwombat commented 12 months ago

Hmm so the bbox before the transform on both is:

 {
  x: 0,
  y: 0,
  w: 0,
  width: 0,
  h: 0,
  height: 0,
  x2: 0,
  y2: 0,
  cx: 0,
  cy: 0
}

After the transform the browser bbox is unchanged but the svgdom becomes:

{
  x: -534.1015625,
  y: -73.3203125,
  w: 1068.203125,
  width: 1068.203125,
  h: 90.703125,
  height: 90.703125,
  x2: 534.1015625,
  y2: 17.3828125,
  cx: 0,
  cy: -27.96875
}

What is happening here?

Fuzzyma commented 12 months ago

The bbox should have some dimension before transformation. Can you give me the code of the svg? Without a bbox, the element is either not in the Dom or doesn't have any dimensions. In both cases its kinda pointless to transform the element.

cyberwombat commented 12 months ago

I believe the issue resides in the font loading. On my browser I create the svg element and set the text to an empty string using plain() and then load assign the font. Interestingly the bbox stays at zero for all coordinates throughout usage (even after adding a string and a font). The svgdom on the other hand tries to load OpenSans by default. I will do more experiments and report back.

cyberwombat commented 12 months ago

I haven't quite pinpointed where issue is and unfortunately my codebase is way to complex to use as an example but the gist of what is happening is that the order in which I add elements matters on svgdom as far as transforms.

Mock example

const el = SVG(...)

el.rect()

// This 
el.plain('foo').translate(...)
el.plain('bar').translate(...)

// Is not the same as this when transforms happen 
el.plain('bar').translate(...)
el.plain('foo').translate(...)

Basically I set an initial rectangle box in which I write text. I set the initial transforms to be relative to that box and on browser it does not matter what order I do the plain() calls in. But on svgdom the second call seems to use the first one to affect its position. So in my initial transform on browser - bar is sitting at bottom of rectangle but on svgdom its sitting at bottom of the foo bbox.

Fuzzyma commented 12 months ago

That is wild. Without reproduction this is really hard to pin down. I assume your mock example doesnt have that issue, right?