Search code examples
svgviewportscalingviewbox

SVG viewBox, rectangle, and polyline


The SVG attribute viewBox appears to be inconsistent. It seems it doesn't scale all SVG graphics primitives the same way. Here's a sample SVG file that has a rectangle, a circle, a polyline, and a polygon. The rectangle has been properly scaled and almost filled the viewPort (which has a width of 500 and a height of 500).

Please see the SVG code and image it produced below. As you will notice the polyline, polygon, and circle did not scale to fill the view port. They do (consistently) occupy the top-left quarter of the view port though (moved but retaining the original size). Can anyone please throw some light on what's going on with this? I will greatly appreciate your feedback.

<?xml version='1.0' encoding='utf-8'?>
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' 
    xmlns:xlink='http://www.w3.org/1999/xlink'
    height='499' width='501' viewBox='100 100 200 200'>
    <g stroke='BLACK' stroke-width='5' fill='none'>
        <rect x='105' y='105' width='193' height='193'/>
        <polygon points="150,100 200,200 100,200" style="stroke:purple" />
        <polyline points='115,180 155,127 180,180' stroke='red'/>
        <circle cx='150' cy='150' r='50' stroke='green'/>
    </g>
</svg>

SVG image converted to a JPG


Solution

  • Short answer:

    The SVG attribute viewBox on the sample code does scale all SVG graphics the same way; so the smaller object representations are actually smaller objects.


    Explanation:

    It's useful to look at he viewBox documentation to better understand the calculations. Let's try to go through you sample code step-by-step:

    1. the SVG viewport dimensions are set to 501 by 499 (width by height)
    2. the viewBox attributes are set as
      1. 100 for min-x and min-y, which will act like shifting the position of the viewport before its container top and left positions (which in the image it seems like irrelevant, since you also shifted all the coordinates by 100; see my note below)
      2. 200 for width and height, which will represent 100% of the viewport size (in this case ~500px); in other words, the 200 value in any children will be mapped (scaled) into ~500px
    3. the rect has 193 as width and height, which is nearly 200, this makes it occupy almost all of the ~500px by 500px viewport area
    4. the other items are scaled properly, but they seem smaller because, in fact, they are smaller
      1. e.g. the circle has r='50' which would fit an imaginary outer square of 100 by 100; 100 is 50% of 200, so it is scaled to ~250px by ~250px (250 = 50% of 500); that is why the circle seems to use 1/4 of the area
      2. the same idea is applied to the other graphic elements.

    NOTE:

    I found it was easier to understand the final results if there was no shift on the viewport and on the positioning coordinates. So, removing 100 from viewBox > min-x and min-y (step 2.1 above) and from all the positioning attributes would make this code easier to understand:

    <?xml version='1.0' encoding='utf-8'?>
        <svg version='1.1' xmlns='http://www.w3.org/2000/svg' 
            xmlns:xlink='http://www.w3.org/1999/xlink'
            height='499' width='501' viewBox='0 0 200 200'>
            <g stroke='BLACK' stroke-width='5' fill='none'>
                <rect x='5' y='5' width='193' height='193'/>
                <polygon points="50,0 100,100 0,100" style="stroke:purple" />
                <polyline points='15,80 55,27 80,80' stroke='red'/>
                <circle cx='50' cy='50' r='50' stroke='green'/>
            </g>
        </svg>