46

In SVG (and Canvas, Quartz, Postscript, ...), the transformation matrix affects both the path coordinates and the line width. Is there a way to make an adjustment so the line width is not affected? That is, in the following example, the scale is different for X and Y, which makes the square into a rectangle, which is OK, but it also makes the lines wider on two sides.

  <g transform="rotate(30) scale(5,1) ">
      <rect x="10" y="10" width="20" height="20" 
            stroke="blue" fill="none" stroke-width="2"/>
  </g>

rectangle width scaled pen

I can see that would be useful in many cases, but is there a way to opt out of it? I suppose I would like to have a separate pen TM or be able to set the pen to be an ellipse that the CTM converts into a circle, but I don't see anything like that.

Lacking that, I think I have to not tell SVG about my CTM and instead transform the coordinates myself, which means converting primitives like rect to their path equivalents.

Calmarius
  • 18,570
  • 18
  • 110
  • 157
xan
  • 7,511
  • 2
  • 32
  • 45

2 Answers2

56

Edit:

There is an attribute you can add to your rect to get exactly this behavior:

vector-effect="non-scaling-stroke"

This was wrong:

This will work if you apply the transform directly to the shape, not the group it is in. Of course, if you wanted to group several items and scale them all together, that approach won't work.

<rect x="10" y="10" width="20" height="20" 
            stroke="blue" fill="none" stroke-width="2"
            transform="rotate(30) scale(5,1)"/>

This may also depend on your SVG viewer; Inkscape renders your file the way you want (stroke width not affected by scale) but Chrome renders it as you have shown.

Russell Zahniser
  • 16,188
  • 39
  • 30
  • Interesting, but I think Chrome is correct. The SVG (1.1) spec says the transform attribute is applied first and that is equivalent to – xan Apr 27 '12 at 20:37
  • Yes, I think you're right - and that means my answer won't work, because I tested it in Inkscape and not Chrome. – Russell Zahniser Apr 27 '12 at 20:39
  • 5
    OK, I found a better answer [here](http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke). – Russell Zahniser Apr 27 '12 at 20:53
  • 1
    Note that Firefox does not currently (v12) support `vector-effect:non-scaling-stroke` – Phrogz Apr 27 '12 at 21:26
  • Thanks, @Russell. I hadn't checked SVG 1.2. – xan Apr 27 '12 at 23:38
  • non-scaling-stroke support should appear in Firefox 15. It's in the nightly builds right now. – Robert Longson May 19 '12 at 14:03
  • Any workarounds for the case where you are applying the transform to the group? – foobarbecue Oct 29 '14 at 07:31
  • Uh....I found this page because Inkscape is rendering my rotated shapes (ellipses) with exactly the kind of variable width that OP is talking about. Maybe see this: https://graphicdesign.stackexchange.com/questions/24736/how-can-i-preserve-border-width-when-scaling-an-object-with-inkscape – sh37211 Oct 17 '20 at 20:22
7

In postscript, describing the path and performing the stroke are separate events, so it's perfectly possible to have a separate "pen" TM.

%!PS
%A Funky Shape

matrix currentmatrix %save normal matrix for stroke pen
306 396 translate
72 72 scale
1 1 5 { pop
    360 5 div rotate
    1 0 translate
    0 0 moveto
    1 1 5 { pop
        360 5 div rotate
        1 0 translate
        1 0 lineto
        -1 0 translate
    } for
    -1 0 translate
    closepath
} for
setmatrix
[ 1 -3 4 2 0 0 ] concat %put some skew in the pen
10 rotate     %makes it look more "organic"
stroke
showpage
luser droog
  • 18,988
  • 3
  • 53
  • 105
  • Worked for my purpose in EPS. Looked up `setmatrix` in [redbook](https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf). (Removing **tour de force** coding art). – Owain Evans Sep 23 '20 at 03:06