SVG clipPaths clipping objects more than expected. What could be the reason? - svg

I've recently found out that the clipPaths in SVG clip a bigger area than what's specified in the commands.
Below is my code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<rect id="r1" x="100.85" y="100.39" width="200.51" height="100.72" fill="black"/>
<rect id="r2" x="100.85" y="100.39" width="200.51" height="100.72" fill="yellow"/>
<clipPath id="clip">
<use xlink:href="#r2"/>
</clipPath>
</defs>
<use xlink:href="#r1"/>
<use xlink:href="#r2" clip-path="url(#clip)"/>
</svg>
According to the code, the rect "r1" should be completely overlapped by the clipped rect "r2" and only r2 should be seen. But that's not the case. There is a black border seen on the output.
What could be the reason? As all browsers (Chrome, Firefox, Safari) seem to show the exact behavior, I was wondering if I made a mistake when understanding the specification.
I have also tried applying shape-rendering="crispEdges" attribute, wondering that Smoothing could be the culprit, but it made no difference in this case.
Further, I have found out that if the values are all integers (or maps to pixels exactly), this border goes away.
Would appreciate any input regarding this problem.
Thanks in advance.

The clip is working fine. What you are seeing is the effects of anti-aliasing.
Because your rectangles are not perfectly aligned with the pixels of the screen, some border pixels of the rectangles are being drawn with less than 100% opacity.
So when the black rectangle is drawn, it has some border pixels that are draw as a mix of black and the background colour (white).
+-----+-----+- -
|grey |grey |
+-----+-----+ ..etc..
|black|black|
+-----+-----+- -
Then the yellow rectangle is being drawn exactly on top of it. This time, the rectangle border pixels are being drawn as a mix of yellow and those black-white (ie. grey) pixels drawn earlier underneath.
+-----------+-----------+- -
|yellow/grey|yellow/grey|
+-----------+-----------+ ..etc..
| yellow | yellow |
+-----------+-----------+- -
If you draw your rectangles at exact pixel boundaries, that anti-alias bleed won't be visible.
Demo here

Related

SVG Scaling and viewBox

Here is a simple SVG file:
<svg xmlns="http://www.w3.org/2000/svg" width="100mm" height="100mm" version="1.1" viewBox="0 0 377.95 377.95">
<rect x="0" width="189" height="189" stroke="black" stroke-width="6" fill="red"/>
</svg>
This renders a 100mm x 100mm box with the top left corner at the origin as expected.
If I change the viewbox to:
viewBox="0 0 177.95 177.95"
then the box is scaled up, still with the top left corner at the origin, as expected.
However, if I change only the width of the view box like so:
viewBox="0 0 177.95 377.95"
then then box is not scaled but is moved along the X axis.
I thought only the first two parameters of the viewbox affected the translation? Also why isn't the box stretched in the X direction?
Does the viewbox scaling only work correctly if the scaling is the same in both X and Y directions?
Thanks!
How the viewport is scaled in actual dimensions (embed frame) of the SVG is governed by preserveAspectRatio attribute.
See MDN entry and/or refer too Understanding SVG Coordinate Systems and Transformations by Sara Soueidan.

SVG stroke changes colour when overlaid

I have a board where I highlight a square using the following:
<g id="marker1" transform="translate(1.5000, 1.500000)" fill="#000000" fill-opacity="0.2" >
<rect width="37" height="37"
stroke="yellow" stroke-width="3" stroke-opacity="1" />
</g>
But the stroke highlight seems to change colour depending on if it is over a dark square or a light square. It is as though the opacity of the stroke is not 100%.
How do I ensure that is doesn't change colour, regardless of which square it is over?
I've noticed that in a css file there is an entry referring to the highlighter, and in there it has opacity of 0.5. I changed it to 1 and all is good.

Mixing stroke width units in SVG

This seemed to be working before I added a viewBox (which was required since paths are in user units).
<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="7in" height="7in" viewBox="0 0 7 7">
<rect x="2" y="2" width="1" height="1" style="fill:none;stroke-width:1px;stroke:rgb(0,0,0)" />
</svg>
Now, if I look in inkscape, the stroke-width is 90px (1in). Also, if I specify the rect size or position in inches, it gets a lot bigger (or moves to a bigger coordinate) than I would think it should. I know I'm missing something but reading through the viewbox and viewport docs are not leading me anywhere enlightening (they mostly discuss in terms of pixels). Could someone steer me in the right direction here?
Without the viewBox you have a viewport of 7in square. In CSS 1in is always 96px so your viewport would be 96 pixels across. If you drew a rect 1in across it would therefore occupy 96 pixels of the screen. (Inkscape may do something different but all browsers use 96px = 1in).
When you add a viewBox you add scaling into the mix. 1px on your drawing may no longer represent 1px on the screen. I.e. 1in on your drawing is still 96px on your drawing but no longer 96 pixels on the screen.
Your current viewBox says that 7px on the screen is now 7in or 7 x 96px so everything on the drawing is magnified by a factor of 96. 1in on the drawing is now 96 x 96 pixels on the screen.

SVG dilate/erode filter vs. Illustrator Offset Path

Below is a screen capture of SVG image which is rendered on Chrome 22.0.1229.79 Mac. The original svg is on jsfiddle:
http://jsfiddle.net/LGBk5/
The left image is made using SVG:s dilate and erode filters. The right one is made using Illustrator's Offset Path effect.
The left one has problems: the border at the bottom is distorted and curves are not as smooth. Meanwhile the thick black border is the same in both.
Has my SVG some parameter wrong or are the dilate and erode filters so seemingly buggy?
EDIT: The purpose is to make paths thinner or thicker in SVG, but according to this example, the erode/dilate is not stable enough to rely.
Filter effects are done on pixel data (the rasterized path), while the path offset operation in Illustrator (similar in Inkscape) is done using the original path data (or vector data if you wish).
The former is like using photoshop filters, the latter is creating new paths by using the existing path. They're both stable, but they're not the same operation.
Illustrator's path offset and SVG filters erode/dilate are different operations.
This erode filter is working as designed - there is no bug here. For every pixel in the input image, the filter looks at the maximum RGBA values in a rectangle around it (the radius). In a normal image this tends to generate "rectangular highlights" for want of a better term. And results in weird artifacts when applied to curved draw paths. From the spec:
The dilation (or erosion) kernel is a rectangle with a width of
2*x-radius and a height of 2*y-radius. In dilation, the output pixel
is the individual component-wise maximum of the corresponding R,G,B,A
values in the input image's kernel rectangle. In erosion, the output
pixel is the individual component-wise minimum of the corresponding
R,G,B,A values in the input image's kernel rectangle.
So, imagine that that single pixel at the pointy end of your shape. With a 10 pixel "radius" in your filter (and remember that radius is an incredibly misleading term because it's using a rectangle not a circle!). Let's say it's at 100,100, for arguments sake. When the filter processes values for pixels in the range 90,110 to 110,110, its dilation radius is going to detect that pixel at 100,100 and paint all pixels in that range black. And just like that, your nice pointy end has been dilated into a straight line.
Note that you can achieve most offset path effects using nested strokes (some of which have masks to trim the inside or outside of the path.
For example, here is the OP's path reimplemented this way:
<!-- Left drawing is made using erode and dilate -->
<!-- Right one is made by Illustrator's Offset Path -->
<svg width="612" height="792" viewBox="0 0 612 792" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="curve" d="M21.552,74.438c2.531-28.879,73.668-52.734,102.629-53.971
c32.164-1.373,74.764,23.746,61.766,53.197c-32,72.5-84.236-59.594-109.5-29.5c-23.367,27.833,55.4,142.969,55.4,142.969
S18.109,113.708,21.552,74.438z"/>
<mask id="inner">
<use xlink:href="#curve" fill="white"/>
</mask>
</defs>
<!-- this black outermost line -->
<use x="10" y="10" xlink:href="#curve" style="stroke:black;stroke-width:26;stroke-linejoin:miter;stroke-miterlimit:10"></use>
<!-- thick red outer line -->
<use x="10" y="10" xlink:href="#curve" style="stroke:#f00;stroke-width:24;stroke-linejoin:miter;stroke-miterlimit:10"></use>
<!-- innermost black thin line, with green fill -->
<use x="10" y="10" xlink:href="#curve" style="fill:#1CFF00;stroke:black;stroke-width:32;stroke-linejoin:miter;stroke-miterlimit:10" mask="url(#inner)"></use>
<!-- blue inner stroke -->
<use x="10" y="10" xlink:href="#curve" style="fill:none;stroke:#5555FF;stroke-width:30;stroke-linejoin:miter;stroke-miterlimit:10" mask="url(#inner)"></use>
<!-- lastly, the black line -->
<use x="10" y="10" xlink:href="#curve" style="fill:none;stroke:black;stroke-width:10;stroke-linejoin:miter;stroke-miterlimit:10"></use>
<g transform="translate(210,10)">
<path fill="#FF0000" stroke="#231F20" d="M126.273,201.917c-1.188-0.766-29.407-19.044-57.679-42.532c-41.739-34.676-60.31-60.754-58.441-82.068
c1.575-17.974,18.042-34.105,48.943-47.945c21.673-9.707,48.782-16.997,65.925-17.729c1.023-0.043,2.057-0.065,3.096-0.065
c26.722,0,55.103,13.789,67.484,32.787c7.866,12.07,9.101,25.736,3.476,38.482c-8.697,19.704-20.608,29.697-35.403,29.702
c-0.002,0-0.007,0-0.01,0C144.382,112.551,127.62,95,111.407,78.028c-7.054-7.385-18.575-19.446-23.912-21.338
c-1.086,2.002-6.186,15.821,20.666,67.477c16.226,31.214,35.475,59.438,35.668,59.72l35.977,52.589L126.273,201.917z"/>
<path fill="#5555FF" stroke="#231F20" stroke-width="10" stroke-miterlimit="10" d="M22.939,78.438
c2.531-28.879,73.668-52.734,102.629-53.971c32.164-1.373,74.764,23.746,61.766,53.197c-32,72.5-84.237-59.594-109.5-29.5
c-23.366,27.833,55.401,142.969,55.401,142.969S19.497,117.709,22.939,78.438z"/>
<path fill="#00FF00" stroke="#231F20" d="M79.986,131.678C38.498,95.796,38.41,81.397,38.549,79.807c0.289-3.29,5.843-10.151,19.371-17.933
C57.676,78.899,64.972,101.816,79.986,131.678L79.986,131.678z M163.665,84.044c-7.09,0-22.461-16.091-31.646-25.706
c-5.867-6.143-11.433-11.969-16.966-16.846c4.324-0.776,8.128-1.238,11.184-1.368c0.621-0.027,1.249-0.04,1.88-0.04
c16.911,0,36.471,8.903,43.603,19.846c3.317,5.089,2.508,8.623,1.278,11.408C168.884,80.659,165.163,84.043,163.665,84.044
L163.665,84.044z"/>
</g>
</svg>

Constant border in a dynamic SVG graphic

I want to have a rectangle that takes all the place in a SVG file. It should also have a border (3px stroke width). The size of the graphic should be easy changeable (by changing attributes "width" and "height" of the "svg" node). I came up with following construction:
<svg width="150" height="35" >
<g>
<rect
id="rect6648"
style="fill:#ffffff; fill-opacity:1; stroke:#000000; stroke-width:3;"
x="0"
y="0"
width="100%"
height="100%" />
</g>
</svg>
But it produces following image with dirty border:
I need something like this:
Is it possible at all? As mentioned before it must work for any size of the graphic.
Thanks in advance!
Alas, no, at least not with purely declarative SVG. The stroke on a shape is painted on both sides of the geometric line that defines that shape (in your case, there's 1.5 on either side). Because of that, it will get clipped for a shape that fills the whole viewbox.
In which context are you using this? You should be able to script it: get the size of the viewbow, on rect set x and y to stroke-width/2, width to width - stroke-width and height to height - stroke-width. If in a dynamic context you will need to detect resizes, but that's often possible.
You need to place the ractangle at half pixel coordinates like x="0.5" y="0.5", then the borders won't be blurry. Also add vector-effect:non-scaling-stroke to the rectangle's CSS to be sure that the border is always 3px wide regardless of zoom level.

Resources