Search code examples
svgrotationblendercss-transformsinkscape

SVG transform-origin broken in Inkscape


I have this SVG file: xml

<svg xmlns="http://www.w3.org/2000/svg" width="300mm" height="300mm" id="image">
  <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

  <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865" transform="rotate(51)"
    transform-origin="113.38582677299999 113.38582677299999"></rect>

  <path d="
        M 170.0787401595,113.38582677299999
        Q 109.97448081636158,111.75869821811467 149.06383319514674,157.4444954545483
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  <g transform="rotate(1.6451612903225807)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(47.70967741935484)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.9291728801154,111.85730708558926 151.53378395847778,155.32411227472434
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(3.2903225806451615)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(44.41935483870968)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.88671463478482,111.95717607594642 153.8779628220461,153.06546067295844
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(4.935483870967742)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(41.12903225806451)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.84714108325986,112.0582228564553 156.08864114581934,150.6759873101943
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(6.580645161290323)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(37.83870967741935)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.81048485026771,112.16036412340621 158.158530434343,148.16357016015428
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(8.225806451612904)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(34.54838709677419)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.7767761554767,112.26351567078673 160.08080636654395,145.5364925360554
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(9.870967741935484)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(31.258064516129032)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.74604278858297,112.36759245970185 161.84913129517457,142.80341578094155
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(11.516129032258066)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(27.96774193548387)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.71831008640042,112.47250868848064 163.45767514171737,139.97335071167026
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(13.161290322580646)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(24.677419354838708)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.6936009119729,112.57817786341175 164.90113461786132,137.05562791070196
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(14.806451612903226)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(21.387096774193548)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.67193563572567,112.68451287004946 166.1747507101772,134.0598669636379
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(16.451612903225808)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(18.096774193548384)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.65333211867201,112.79142604503151 167.27432437034577,130.99594474392939
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(18.096774193548388)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(14.806451612903224)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.6378056976883,112.89882924834941 168.19623035920955,127.87396284932184
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(19.741935483870968)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(11.516129032258064)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.6253691728703,113.0066339360117 168.9374291990041,124.70421429739422
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(21.387096774193548)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(8.225806451612904)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.61603279698062,113.11475123304022 169.49547719436387,121.49714958999664
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(23.03225806451613)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(4.935483870967737)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.60980426699628,113.22309200673944 169.86853448906282,118.26334225847053
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>

  <g transform="rotate(24.67741935483871)" transform-origin="30mm 30mm">

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"></rect>

    <rect x="170.0787401595" y="112.81889763913499" width="555.5905511877" height="0.566929133865"
      transform="rotate(1.6451612903225765)" transform-origin="113.38582677299999 113.38582677299999"></rect>

    <path d="
        M 170.0787401595,113.38582677299999
        Q 109.60668871776325,113.33156694017794 170.05537113192838,115.01345400324652
      " fill="none" stroke-width="0.566929133865" stroke="#000000"></path>

  </g>
</svg>

Here is how it renders in Chrome and Firefox (and how I want it to look): browsers

(the screenshot is cropped by hand, this is not the actual rendered size)

And here is how it is rendered in GIMP, Inkscape, Blender, Gwenview and probably many other apps: inkscape

Using some geometry I found out that browsers interpret transform-origin as coordinates relative to the document edges (as I expect), while in GIMP and others the rotation pivot is located in the upper left corner regardless of transform-origin.

This SVG is generated by a script. I could rewrite it to avoid using transforms, but I wonder if there is an easier way to make transform-origin work.


Solution

  • You are using transform and transform-origin as defined in the CSS Transforms Level 1 Candidate Recommendation, and you are using them as presentation attributes. transform-origin as a presentation attribute was only introduced in the 2017 working draft. While browser vendors have the capacity to bring up their products to new spec standards even before they reach recommendation status, most open source projects have not. For Inkscape, you have to live with the implementation according to the SVG 1.1 attribute standard, which is incompatible to CSS.

    But that is not really a problem in your case. You only use transform-origin in conjunction with the rotate() function. And for that there is a helpful difference between the SVG attribute syntax and the CSS property functional notation: you can define the center of rotation inside the function. (This syntax only exists for rotate(), but not for scale(), skewX() or skewY().)

    A CSS rule (note the obligatory units) of

    style="transform:rotate(51deg);transform-origin:113.38582677299999px 113.38582677299999px;"
    

    can be written as a SVG attribute

    transform="rotate(51 113.38582677299999 113.38582677299999)"
    

    But take care that

    The second condition means you cannot use mm units, but need to recompute them to px units.