svg: why does y="0" start outside the viewport instead of in the top edge for text? - svg

This is the only thing preventing me from understanding how the coordination system works...
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="400" x="0" y="0">
<text x="0" y="0">
<tspan>✉</tspan>
</text>
</svg>
If the y-axis points down why does y="0" start from the outside of the viewport? Shouldn't it start right at the top edge? It blows my logic away...
The x-axis points to the right and it starts right in the left edge of the viewport, now this is logic and normal behavior.
Why does the y-axis behave like this? Or why make one start from the outside and the other not? What is the logic behind this? Unless I'm misunderstanding how it works...

It's all in the SVG specification
the initial coordinate system has the origin at the top/left with the x-axis pointing to the right and the y-axis pointing down
The origin for text is basically the bottom left corner of the glyph for left-to-right text.
For most uses of Latin text (i.e., writing-mode:lr, text-anchor:start and alignment-baseline:baseline) the alignment-point in the glyph will be the intersection of left edge of the glyph cell (or some other glyph-specific x-axis coordinate indicating a left-side origin point) with the Latin baseline of the glyph.

default baseline is in text downside, can use attr dominant-baseline change baseline, here is mdn example
<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg">
<path d="M20,20 L180,20 M20,50 L180,50 M20,80 L180,80" stroke="grey" />
<text dominant-baseline="auto" x="30" y="20">Auto</text>
<text dominant-baseline="middle" x="30" y="50">Middle</text>
<text dominant-baseline="hanging" x="30" y="80">Hanging</text>
</svg>

Related

Clipping an image with a preserved aspect ratio using a polygon that doesn't need to have its aspect ratio preserved

I'd like to create a responsive SVG that consists of an image clipped by a polygon.
The image needs to preserve its aspect ratio and get cropped, while the polygon needs to adapt in width to its container but preserve the same height.
I don't want the whole thing to scale homothetically.
visual explanation of the issue
If I set preserveAspectRatio="none" to the svg, the polygon stretches like planned, but the image is then distorted. I've tried setting preserveAspectRatio="xMidYmid slice" to the image, but since the svg is set not to have its aspect ratio preserved it doesn't work as planned.
Here's my current code:
<svg viewBox="0 0 1440 810" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<clipPath id="clip">
<polygon points="0 750,1440 810,1440 60,0 0" />
</clipPath>
</defs>
<g aria-hidden="true" clip-path="url(#clip)">
<image
opacity="0.75"
preserveAspectRatio="xMinYMid slice"
xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Monasterio_Khor_Virap%2C_Armenia%2C_2016-10-01%2C_DD_25.jpg/2880px-Monasterio_Khor_Virap%2C_Armenia%2C_2016-10-01%2C_DD_25.jpg"
/>
</g>
</svg>
Thanks in advance for your help.

SVG text slightly off center

I have some text on a blue rectangle, centered horizontally and vertically. It is nearly perfectly in the center, however it is slightly too high. How can I fix this so that it is perfectly centered?
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<g>
<rect fill="#8080ff" height="400" width="400" y="0" x="0"/>
<text font-weight="bold" stroke="black" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" stroke-width="0" fill="#000000">Go</text>
</g>
</svg>
Changing dominant-baseline: middle to dominant-baseline: central fixed the problem. I had to look this up but if I understand it correctly, central prioritises the ideographic baseline whereas middle prioritises the alphabetic baseline. The alphabetic baseline hugs the bottom of the text, whereas the ideographic baseline rests just below the text. This extra space also sits above the text (regardless of the baseline being used), so you have to use the ideographic baseline to accomodate for this extra space, to centre it perfectly.
I think that's how it works anyway. These are the links I used to figure it out, if you want to understand it more.
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline
What is the difference between alphabetic and ideographic in Flutter's TextBaseline enum

Zero stroke-width in SVG

Having used Postscript for years, I am now learning SVG. There is a feature of PS that I have not been able to replicate so far: zero-width lines. In PS, a line with zero width is always visible: PostScript converts zero line width to the smallest printable width. On the screen, when zooming they never get any thinkness, yet are visible no matter the scale. I have used them when I wanted to render very thin lines, without worring about the final resolution I was going to use, and they turned out really useful.
However, in the official SVG docs (https://www.w3.org/TR/svg-strokes/) it says that:
A zero value causes no stroke to be painted. A negative value is invalid.
Is there a way in SVG to build zero-width lines in the sense of PostScript?
As Robert said, the nearest thing to what you want in SVG is vector-effect="non-scaling-stroke". This fixes the stroke width at 1 no matter how the SVG is scaled.
This works on Chrome and Firefox (and probably Opera - haven't checked), but AFAIK not IE/Edge.
<svg viewBox="0 0 100 100">
<rect x="10" y="10" width="80" height="80"
fill="none" stroke="black" stroke-width="1"
vector-effect="non-scaling-stroke"/>
</svg>
Note that antialiasing will come into play depending on the position of the lines. The position will be affected by the scale.
If your lines are rectilinear (horizontal or vertical), you might also want to use shape-rendering="crispEdges". This will turn off antialiasing for the shape on which it is used, resulting in sharp one-pixel lines.
<svg viewBox="0 0 100 100">
<rect x="10" y="10" width="80" height="80"
fill="none" stroke="black" stroke-width="1"
vector-effect="non-scaling-stroke" shape-rendering="crispEdges"/>
</svg>

SVG viewBox scales some elements differently than others

The client asked to scale the drawing down so I added width/height and viewBox to the SVG element. The problem is that some elements are scaled differently than others. The structure of the svg is like this:
<svg width="100%" height="100%" viewBox="0 50 700 200">
<defs>...</defs>
<g id="group-ab">
Here are the boxes (drawn with polyline) that are scaled correctly, both in their own group (group-a, group-b)
</g>
<g id="group-a-id">
<text>A</text>
<circle id="group-a-id-bg-circle"></circle>
</g>
<g id="group-B-id">
<text>B</text>
<circle id="group-b-id-bg-circle"></circle>
</g>
<path id="group-a-pattern" d="..."></path>
<path id="group-b-pattern" d="..."></path>
</svg>
The path is the pattern for the pieces. It seems that everything that isn't under the group "group-ab" isn't scaled correctly, see the image below. Normally (when not using viewBox) the pattern fills up the boxes and the A/B are centered with the boxes (same translates are done to them).
Why is this happening? One would think that it doesn't matter if elements are within groups or not if svg is scaled.

Programmatically centering svg path

I'm working on a PHP script that generates a jpg wallpaper from an SVG-file according to the screen resolution of the visitor. The wallpaper consists of a circular gradient (rectangle) background and a path on top of it. How would you go about centering the path horizontally and vertically to the rectangle? Remember that the rectangle's size and proportions are not a constant. Should I separate the background and path to different svg files or is there an easy way to center paths? Maybe a framework?
This is easilly achieved by using nested <svg> elements and the preserveAspectRatio attribute. Put your background in the outer svg and your path in the inner one.
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%">
<rect id="background" width="100%" height="100%" fill="grey"/>
<svg preserveAspectRatio="xMidYMid meet" viewBox="0 0 30 40" width="100%" height="100%">
<g>
<circle cx="15" cy="20" r="10" fill="yellow"/>
<circle cx="12" cy="17" r="1.5" fill="black"/>
<circle cx="18" cy="17" r="1.5" fill="black"/>
<path d="M 10 23 A 8 13 0 0 0 20 23" stroke="black" stroke-width="2" fill="none"/>
</g>
</svg>
</svg>
Run this snippet and try resizing the window.
To get this to work, all you need to ensure is that the viewBox attribute on the inner <svg> element is correctly set.
If you know the coordinates of the paths, you could take the total of the x/y coordinates and divide by the number of coordinates, this will give you the average position for the coordinate set. Then, offset each coordinate by the coordinates for half the width/height of the square, plus any offset, minus the difference between the center of the coordinate set and half the width/height of the square.
This should result in your coordinates being centered within the square, I think (it is rather early here, and I've just started my first coffee, so I could be wrong). This is of course assuming you know all the variables in play (the width/height of the square, any offset applied and the coordinates of the path).

Resources