Why are the SVG Text elements too high? - svg

I noticed the the root coordinates for a text element are not at the top left corner like a rect element:
Is there a way to set it such that when a text element is at (0,0), it fits inside the parent element?

If I understood you well, you can use this:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline
A) Chromium browsers
svg {
dominant-baseline: hanging;
}
https://jsfiddle.net/e7vc4bqj/
B) Chromium and Firefox
.text {
dominant-baseline: hanging;
}
https://jsfiddle.net/3zskd148/
SVG text coordinates are used to define its left bottom corner by default:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor
Hope this help :)

Why are the SVG Text elements too high?
The x and y coordinates of a <text> element specify the start of the baseline of the text. This makes complete sense. You wouldn't want it to be the top left of the first character - because it would then be a difficult job to get text of different sizes and styles to line up.
There is no global option in SVG that changes that behaviour. However see below for alternatives)
Is there a way to set it such that when a text element is at (0,0), it fits inside the parent element?
Normally you would just adjust the y coordinate based on the font size.
However there are a couple of alternatives you can use:
One is the xxx-baseline properties (as #gengns has pointed out), that can alter how the character glyphs are positioned relative to the baseline. Note however, that those attributes are not entirely reliable, due to mixed browser support. Plus they depend on the font containing the correct data tables. Not all fonts have those tables.
A better option IMO is to use the dy attribute. This adds a relative offset to the text position. Meaning the text is actually positioned at (x, y + dy). And it is supported by all browsers.
<svg width="200" height="150">
<rect x="0" y="0" width="200" height="150" fill="skyblue"/>
<text x="0" y="0"
font-size="25px" dy="1em">asd</text>
</svg>

Related

Shift all objects in an SVG down and to the right by a given number of pixels

Is there some kind of a wrapper object in SVG that I can use to shift all objects (lines, polygons, circles, text etc) down and to the right in a simple easy fashion? I realize now that I have not left enough space in the top left corner of my SVG definition.
Maybe there is a margin or padding element in SVG that I can use. Please note that I do not desire to wrap this inside HTML or use CSS trickery to achieve this, but ideally I would like to do this with pure SVG if possible.
You can either alter the viewBox attribute to reveal different portion of the infinite plane, i.e. changing e.g.
<svg viewBox="0 0 100 100">
to
<svg viewBox="-10 -10 110 110">
will bring extra 10 point rows and columns to top an left, effectively shifting content to bottom right (and shrinking it).
Or you can wrap all root elements of your SVG into <g> element and apply (add) single transform to all its children through it:
<g transform="translate(10, 10)">
<!-- content -->
</g>

How do I get svg vmin right?

According to the standards I'm reading -- for example https://www.w3.org/TR/css-values-4/#viewport-relative-lengths -- a vmin unit should be 1% of the smallest dimension of the containing viewport.
Going for a minimal example illustrating my dilemma, this is square in my current instance of chrome:
<svg><rect height="30vmin" width="30vmin" fill="red">
But this is not:
<svg><rect height="50vmin" width="50vmin" fill="red">
Playing with variations on this theme (closing tags, adding width and height to the svg element, etc.) suggests that the rect is not using the svg viewport as its reference, but instead is using some containing browser context as its reference viewport.
So, my question is: how do I specify to the browser that I want vmin units to refer to the innermost containing svg viewport? (Specifically when working with svg elements embedded in html documents.)
Browser support for those units that were added in CSS3 may still be spotty. I haven't checked recently.
But the rule is that these units are resolved relative to the whole document. So in a browser, that will be the whole browser window.
This SVGWG issue may help clarify things.
https://github.com/w3c/svgwg/issues/207
how do I specify to the browser that I want vmin units to refer to the innermost containing svg viewport?
You can use percentage values for coordinates,
<rect height="50%" width="50%" fill="red">
However in SVG, percentage values are always relative to their associated axis. So percentage width values are relative to the X axis, and percentage height values are relative to the Y axis.
Alternatively you could use a suitable viewBox and appropriate coordinate values relative to that viewBox. For example, if your viewBox has a width and height of 100:
viewBox="0 0 100 100"
All coordinates values in the SVG would effectively be percentage values. However the same axis rule applies as described above.
svg {
width: 200px;
background-color: linen;
}
<svg viewBox="0 0 100 100">
<!-- rectnagle 50% x 33.3% -->
<rect width="50" height="33.3"/>
</svg>

Re-using Tooltip text

I have a number of shapes that I want to display browser tooltips when I hover over them. However, because they contain images and caption texts, it means duplicating the same tooltip on each of their contents because they are higher in the Z-Order than the shape.
I was hoping to put them in a <defs> so that I could re-use them. For instance:
<defs>
<title id='t1'>This is my tooltip</title>
</defs>
<image ...etc...>
<use xlink:href="#t1"/>
</image>
but this doesn't work. Although it sounds like a fairly obvious use-case, I'm guessing that defs only helps with graphic elements. Is that true? Is there another way I can do this?
Having the same tooltip for my rectangle captions (or any text) is unnecessary if I include the CSS text { pointer-events: none; }.
Duplicating the tooltip across the combined shapes can be done by putting a transparent rectangle over them all and giving that a (single instance) tooltip.

Space between hexagons in SVG

I've tried to make some hexagon-based map in SVG. Unfortunately, there are white spaces between fields.
I've disabled fields' borders (stroke="none stroke-width="0"), rounded all floating points to integers and made sure that hexagons have common points (no space between them). It didn't help.
Two screenshots shows same SVG in different magnifications http://imgur.com/GLiJs,gi3pt
Source code is here: http://pastebin.com/hqwTKW4M (remember to change extension to svg, after download).
Setting shape-rendering property to 'crispEdges' for all hexagons (or group of them) solves this issue. E.g.
<polygon
points="0,90 45,12 135,12 180,90 135,168 45,168"
fill="green" stroke="none" stroke-width="0"
shape-rendering="crispEdges" />
http://www.w3.org/TR/SVG/painting.html#ShapeRenderingProperty

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