5
votes

I have trouble applying all transformations matrices in svg file. If file contains only paths, and without gradients - all works fine. But after adding and applying gradientTransform to linearGradient causes rendering errors.

Algorithm:

  1. multiply group and path matrices
  2. multiply path transform whith linked linearGradient gradientTransform
  3. apply transform to path
  4. apply gradientTransform to linearGradient

Input file:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="linearGradient3755">
      <stop offset="0" />
      <stop offset="1" stop-opacity="0" />
    </linearGradient>
    <linearGradient id="linearGradient3761" xlink:href="#linearGradient3755" x1="16.162441" y1="66.128159" x2="117.17769" y2="66.128159" gradientUnits="userSpaceOnUse" />
  </defs>
  <g transform="translate(0,-924.36218)">
    <g transform="matrix(0.95516166,-0.46694301,0.71994792,0.61949768,-706.90347,408.6637)">
      <path d="M 2.1428571,3 L 126.07143,3 L 126.07143,123 L 2.1428571,123 z" transform="translate(0,924.36218)" style="fill:#ff0000;fill-opacity:1;stroke:none" />
      <path d="M 16.162441,21.428905 L 117.17769,21.428905 L 117.17769,110.8274 L 16.162441,110.8274 z" transform="matrix(0.96592583,-0.25881905,0.25881905,0.96592583,-17.36888,938.82017)" style="fill:url(#linearGradient3761);fill-opacity:1;stroke:none" />
    </g>
  </g>
</svg>

Output file:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="linearGradient3755">
      <stop offset="0" />
      <stop offset="1" stop-opacity="0" />
    </linearGradient>
    <linearGradient id="linearGradient3761" xlink:href="#linearGradient3755" y1="95.70844949469" x1="26.6443734054997" y2="33.95075671356" x2="101.020294143975" gradientUnits="userSpaceOnUse" />
  </defs>
  <path fill="#f00" d="M -37.2023,57.8018 C -37.2023,57.8018 81.1699,-.0654 81.1699,-.0654 81.1699,-.0654 167.5631,74.2746 167.5631,74.2746 167.5631,74.2746 49.1909,132.1418 49.1909,132.1418 z" />
  <path fill="url(#linearGradient3761)" d="M -15.4903,74.3628 C -15.4903,74.3628 58.8856,12.6051 58.8856,12.6051 58.8856,12.6051 143.155,55.2964 143.155,55.2964 143.155,55.2964 68.7791,117.0541 68.7791,117.0541 z" />
</svg>

My question is why gradients are different, when vectors are equal? And how can I fix it?

1
There's no gradientTransform in either your input or output files. Should there be? - Robert Longson
Transform apply to path from g element and then copied as gradientTransform to linked linearGradient. So gradientTransform added to linearGradient during file proccesing. Then I apply gradientTransform to linearGradient. Is this correct way to get rid of any transform in the file? - RazrFalcon
As I said the linearGradient in the question has no gradientTransform attribute, so I don't understand the question or your comment. - Robert Longson
If really simplify: how to apply transform in this element: <linearGradient gradientTransform="matrix(.73628 -.61137 .94263 .47754 -47.58995 74.01084)" id="linearGradient3761" xlink:href="#linearGradient3755" y1="66.128159" x1="16.162441" y2="66.128159" x2="117.17769" gradientUnits="userSpaceOnUse"/> Before apply all looks good, as in first picture, after apply it looks like at second picture. <linearGradient id="linearGradient3761" xlink:href="#linearGradient3755" y1="95.70844949469" x1="26.6443734054997" y2="33.95075671356" x2="101.020294143975" gradientUnits="userSpaceOnUse"/> - RazrFalcon

1 Answers

2
votes

Your algorithm is correct as long as your transform matrix doesn't skew the gradient.

In your case, the final transformation that is applied to the gradient is

translate(0,-924.36218) matrix(0.95516166,-0.46694301,0.71994792,0.61949768,-706.90347,408.6637) matrix(0.96592583,-0.25881905,0.25881905,0.96592583,-17.36888,938.82017)

which is the same as

matrix(0.7363,-0.6113,0.9426,0.4775,-47.6376,74.0101)

which is equivalent to

translate(-47.6376, 74.0101) rotate(-39.7) scale(0.957, 0.9695) skewX(23.4337)

If the last skew(23.4337) weren't there, everything should work as you desired.

It's pretty hard, or more accurately I should say impossible, to achieve the skew effect without using gradientTransform attribute. According to the SVG 1.1 Specification:

gradientTransform contains the definition of an optional additional transformation from the gradient coordinate system onto the target coordinate system (i.e., userSpaceOnUse or objectBoundingBox). This allows for things such as skewing the gradient.

It is designed to achieve the skew effect.

If you are still curious why it's impossible, I've made a simple example shown in the image below.

an example explaining skew effect

On the left, we have a horizontal linear gradient applied to a square. If we skew the gradient on X axis for 20 degrees, the effect is rendered like a combination of 2 shapes:

  • a triangle on the left side whose fill is a solid color
  • the same square filled with the same gradient, but the square itself is skewed 20 degrees, and then clipped to its original form.

Now you can't simply use a line to describe the gradient, which is why we have to use gradientTransform attribute.