Description
Version
5.1.0
Test Case
https://jsfiddle.net/58hje9ar/5
Information about environment
Browser, latest chrome
Steps To Reproduce
I have an issue with shadow offset when exporting fabric object / group to SVG.
In the first fiddle, https://jsfiddle.net/58hje9ar/5, I have created an rectangle and placed it in a group, then applying an orange shadow to the group. I have also applied 2x scale and 45 degree rotation.
The exported SVG ( can be found in the browsers console ):
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1000" height="1000" viewBox="0 0 1000 1000" xml:space="preserve">
<desc>Created with Fabric.js 5.1.0</desc>
<defs>
</defs>
<g transform="matrix(1.41 -1.41 0.71 0.71 398.64 152.98)">
<filter id="SVGID_0" y="-118%" height="336%" x="-90%" width="280%">
<feGaussianBlur in="SourceAlpha" stdDeviation="0.5"/>
<feOffset dx="-70.71" dy="70.71" result="oBlur"/>
<feFlood flood-color="rgb(255,165,0)" flood-opacity="1"/>
<feComposite in2="oBlur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<g style="filter: url(#SVGID_0);">
<g transform="matrix(1 0 0 1 0 0)">
<rect style="stroke: rgb(0,0,0); stroke-width: 3; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,0,0); fill-rule: nonzero; opacity: 1;" x="-50" y="-35" rx="0" ry="0" width="100" height="70"/>
</g>
</g>
</g>
</svg>
The shadows transform in the svg is a bit off compared to what the canvas render. This issue becomes clear when both rotation and scaling is applied and the shadow offset is quite large.
In the second fiddle, https://jsfiddle.net/58hje9ar/7/, we have the same rectangle and shadow inserted into a group. This time the transformation is applied on the rectangle instead of the group.
The exported SVG:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1000" height="1000" viewBox="0 0 1000 1000" xml:space="preserve">
<desc>Created with Fabric.js 5.1.0</desc>
<defs>
</defs>
<g transform="matrix(1 0 0 1 398.64 198.64)">
<filter id="SVGID_0" y="-71%" height="242%" x="-21%" width="142%">
<feGaussianBlur in="SourceAlpha" stdDeviation="0.5"/>
<feOffset dx="0" dy="100" result="oBlur"/>
<feFlood flood-color="rgb(255,165,0)" flood-opacity="1"/>
<feComposite in2="oBlur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<g style="filter: url(#SVGID_0);">
<g transform="matrix(1.41 -1.41 0.71 0.71 0 0)">
<rect style="stroke: rgb(0,0,0); stroke-width: 3; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,0,0); fill-rule: nonzero; opacity: 1;" x="-50" y="-35" rx="0" ry="0" width="100" height="70"/>
</g>
</g>
</g>
</svg>
The transformation of the shadow is now same in canvas and SVG.
This becomes an issue when loading SVG data into fabric using loadSVGFromString since we would like to group the incomming svg by using groupSVGElements. If the latter creates an Fabric.Group which we then apply a shadow and transformation, the exported SVG is not rendering shadow with the same transform as the canvas: https://jsfiddle.net/48rtpkhn/2/
The difference is more subtle but its clearly visible.
It seems to me that the transform of outer SVG group in the exported SVG ( containing the filter and the content ) is applied on the feOffset filter ( which is expected given the hierarchy ).
Looking at the 'toSVG' function in shadow.class.js it seems that there is a compensation for the objects rotation, however not the scale:
offset = fabric.util.rotateVector( { x: this.offsetX, y: this.offsetY }, fabric.util.degreesToRadians(-object.angle))
I tried to override the function and change that to:
t = Fabric.util.invertTransform(object.calcTransformMatrix());
t[5] = 0; t[4] = 0;
offset = Fabric.util.transformPoint({ x: this.offsetX, y: this.offsetY }, t),
The idea was to remove the objects entire transform from the offset before exporting to SVG except translation. Then feOffset would recieve the same transform from the parent group when rendering the SVG.
This seems to be working for the first fiddle, https://jsfiddle.net/sr64avj2/4/ but not when loading content from SVG into a group.
I just started using Fabric so it's safe to say that my assumptions can be incorrect.
Expected Behavior
Shadow in exported SVG should align with shadow on canvas
Actual Behavior
Shadow in exported SVG is slightly off compared to canvas when applying transformations to a group with objects.
Activity