50

Is there a way to cut one shape out of another in SVG? For example, I have a rect and an ellipse and I want to make a rect with a transparent hole in the middle. I would imagine it would look something like this:

<set operation="difference" fill="black">
    <rect x="10" y="10" width="50" height="50/>
    <ellipse cx="35" cy="35" rx=10 ry=10/>
</set>

The closest thing I can find is clipping, which will give me the intersection of two shapes. In my example that would result in just the hole being solid and the rest of the rect being transparent.

I looked through Inkscape and there is a difference option in the path menu, but this converts the shapes to paths and then creates a new path. The identity of the shapes is lost so there is no easy way to, for example go into the svg file and change the radius of the ellipse.

Is there any ideas for how I might do this?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
user4891
  • 897
  • 2
  • 9
  • 14

4 Answers4

51

You must use path element to cut a hole.

See the example from the SVG specification: (you can click this link or the following image to view the real svg file)

svg hole example

<g fill-rule="evenodd" fill="red" stroke="black" stroke-width="3">
    <path d="M 250,75 L 323,301 131,161 369,161 177,301 z"/>

    <path d="M 600,81 A 107,107 0 0,1 600,295 A 107,107 0 0,1 600,81 z
             M 600,139 A 49,49 0 0,1 600,237 A 49,49 0 0,1 600,139 z"/>

    <path d="M 950,81 A 107,107 0 0,1 950,295 A 107,107 0 0,1 950,81 z
             M 950,139 A 49,49 0 0,0 950,237 A 49,49 0 0,0 950,139 z"/>
</g>

For your case:

<path d="M10 10h50v50h-50z M23 35a14 10 0 1 1 0 0.0001 z"
    stroke="blue" stroke-width="2" fill="red" fill-rule="evenodd" />

M10 10h50v50h-50z will draw a rect.

M25 35a10 10 0 1 1 0 0.0001 z will draw a ellipse.

fill-rule="evenodd" will make the hole.


The key point is draw outer shape and inner shapes(holes) in different direction (clockwise vs anti-clockwise).

  • Draw the outer shape clockwise and draw the inner(holes) shapes anti-clockwise.
  • Or conversely, draw the outer shape(holes) anti-clockwise and draw the inner shapes clockwise.
  • Concat the path datas of outer shape and inner shapes(holes).

You can cut more holes by concat more hole path data.

This image explain how to cut a hole:

This image explain how to cut a hole

See w3c docs: SVG Arc Commands and SVG fill-rule Property.

Community
  • 1
  • 1
cuixiping
  • 24,167
  • 8
  • 82
  • 93
  • 7
    Direction doesn't matter. The key is the `fill-rule="evenodd"`. Both paths can be in the same direction and it still works. Where an odd number of shapes overlap, pixels are drawn, where an even number overlap, pixels are not drawn. – Octopus Dec 11 '17 at 19:31
  • 1
    I want to note here (since I suffered from this mistake) that you just set the fill rule evenodd, but then you don't need all the use and defs and xlink, you just need the move command. For example, shape1, move, shape2, all in one path with evenodd fill, and it will cut shape2 out of shape1, no need for anything else. I can post an answer here if that would help. – Justin Sep 21 '21 at 05:46
  • 1
    Do note that polygons can also achieve the same effect, not just paths. So long as evenodd is its fill rule, and the element has edges that can intersect, it will work. – Jamesthe1 Mar 29 '22 at 00:15
35

You should be able to use the fill-rule: evenodd property to achieve this effect, if you want to prevent the fill of the rectangle from painting where the circle is. See this example from the SVG specification (image below will only display if your browser supports SVG):

A pentagram and two circles, filled in red, with the centers cut showing the white background

For some reason, I can't seem to get this to work with shapes like the rect and ellipse that you provided in the question. Here's my attempt:

A blue square with a circle inside

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
2

You need to use a <path> if you want to use fill-rule. But it's certainly possible to change the radius of a circle that is a subpath of the <path>, you just need to master the arc path command.

Erik Dahlström
  • 59,452
  • 12
  • 120
  • 139
-2

Unfortunately it seems that there's no way to do this. Neither SVG 1.0 nor SVG 1.1 do not support boolean shape operations, clipping is supported for other reasons. The closest you can get is trying to get a "inverted" shape to do the clipping.

Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • I don't understand why this is downvoted. This answer just states the reality. The other posts don't answer the question, as the OP asked specifically they wanted to keep shapes, not to use paths. – PhiLho Sep 01 '23 at 10:41