Space between two svg text elements - javascript

I am currently working with SVG element of JavaScript.
Here is my scenario.
I have an svg element in which I have two text element like this
<svg>
<g transform = "translate(0, 20)">
<text style="font-size: 0.5em;" x="0" y="-4"> text </text>
<text style="font-size: 0.5em;" x="70" y="-4"> value </text>
</g>
<svg>
It's appearing fine like this
text value
I have used font-size 0.5em because I want to my text to resize when I resize my svg.(by using jquery resizable)
Its working fine, but the problem is that when I resize my svg the space between text decreases and at some point the text becomes so big that both texts overlap with each other because of the fixed x and y attributes.
Is there a way to avoid this problem. A method by which I can keep the space between the two texts constant or the text can be relatively placed.

Converting to a single text value would seem to meet the use case you've outlined above. If you need text relatively placed you can convert one to a tspan and then use dx and dy e.g. <text>text <tspan dx="1em" dy="1em">value</tspan></text>

Related

Getting text from text element that has overflowing content only returns the visible segment of the text

I have an SVG containing several groups with a text element and just a bit of text. That text element is set to match the size of its parent, which can sometimes be somewhat small. This occasionally results in a text element that contains more text than it can fit, so it naturally overflows.
I have a loop that cycles through all of these groups to read the text. When it comes across a text element that’s overflown, it only returns the text that’s visible.
In the below example, theTile is the group. Also, I’m using SVG.js.
// If there is a text node in the SVG, collect the details from
// the content and delete it. If there isn't, move on.
const textNode = theTile.select('text');
// Check for a text node. If it exists, collect the content
// for use in positioning for the group.
let groupCoordinates;
const textNodeCount = textNode.members.length;
if (textNodeCount) {
const tileTextRaw = textNode.last().text();
… do some stuff with this text …
}
tileTextRaw ends up returning “name:Peristyle_5”. The full text inside of that text element is actually “name:Peristyle_5-top:162-left:1349-width:50-height:75”
For a visual example, here is the text element I’m trying to read within the SVG:
This is the SVG I’m working with:
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="75" viewBox="0 0 50 75">
<title>Peristyle_5</title>
<g id="Peristyle_5">
<g>
<rect id="background-2" data-name="background" width="50" height="75" fill="#7facc7"/>
</g>
<text transform="translate(9.726 21.76)" font-size="15" font-family="OperatorMonoSSm-Book, Operator Mono SSm">nam<tspan x="0" y="18">e:P</tspan><tspan x="0" y="36">eri</tspan></text>
</g>
</svg>
What am I missing?

SVG text not aligned correctly on edge

I have created an SVG with a base64 background image and two text areas (top and bottom text). These text areas can be updated via two input forms.
This functions correctly apart from on Internet Explorer and Edge, whereby the bottom text is aligned to the left instead of the center and the position of the bottom text is also incorrect. Top text is displaying correctly.
I am currently only trying to resolve this issue for Edge. If you view the link below I have a demo of the SVG and two fields.
https://jsfiddle.net/znhs955p/1/
For excepted behavior view on chrome or firefox.
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.58850098px;line-height:1.25;font-family:sans-serif;letter-spacing:2px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.43085253"
id="bottom"
transform="matrix(0.91651557,0,0,0.91221872,8.953221,18.767226)"
x="1.5895385"
y="3.9430504">
<textPath
xlink:href="#ellipse4592"
startOffset="50%"
id="bottom-text"
style="font-size:21px;letter-spacing:2px;stroke-width:1.43085253">
Bottom Text
</textPath>
</text>
<text
id="text4630"
style="font-style:normal;font-weight:normal;font-size:13.35012245px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66707253"
xml:space="preserve"
transform="matrix(-0.90287605,0,0,-0.90624359,513.44619,329.63062)">
<textPath
xlink:href="#path5204"
startOffset="50%"
id="top-text"
style="font-size:18px;stroke-width:1.66707253">
Top Text
</textPath>
</text>
Remove the x and y attributes from the <text> element that wraps your <textPath>.
I.e. change:
<text ...
x="1.5895385"
y="3.9430504">
<textPath ... id="bottom-text" ...>Bottom Text</textPath>
</text>
to
<text ...>
<textPath ... id="bottom-text" ...>Bottom Text</textPath>
</text>
Those x and y attributes seem to be confusing IE.
https://jsfiddle.net/znhs955p/2/

Using javascript, how do I copy svg tspan elements inside an svg text element into a different text element?

I have an svg text element with two tspan elements. It looks like this.
<text id="majNote5" x="25" y="70">
<tspan>D</tspan>
<tspan font-family="'Opus Text'" font-size="25">b</tspan>
</text>
I have a 2nd empty text element which looks like this.
<text id="majChord1" x="425" y="70" ></text>
I want to copy both tspan elements and their respective texts in the first text element into the 2nd text element.
I tried the following:
var majNote5 = document.getElementById('majNote5');
var majChord1 = document.getElementById('majChord1');
majChord1.textContent = majNote5.textContent;
This does copy the text contents of the tspan elements and places it in the other container, but without any of the tspan elements. So afterward it looks like this:
<text id="majChord1" x="25" y="70">Db</text>
How do I get the second text container to look exactly like the first container?
Thanks,
--christopher

Get the coordinates of a letter in a SVG text element

Say I have a very basic SVG:
<svg viewBox="0 0 100 50">
<path transform="translate(X,Y)" fill="none" stroke="red" d="M11,11.1c-3.9,1.2-12.4,9-10.4,12.2s14.9-0.8,15.5-2.1s-4-2.7-4-2.7"></path>
<text transform="translate(10,25)">Hello World.</text>
</svg>
I know the height and width of the <path> (from some other algorithm) and the position of the <text> (from the transform attribute shown above).
How can I know the X and Y coordinates of the letter W?
My goal here is to set the translation of the path so that it appears just below the letter W.
To give you a better idea of the end goal, here's what I'm trying to do: I want to automatically replace all <text> in a svg by paths that I've drawn to match the font (Permanent Marker). This way I can create a realistic handwriting effect (see here for a live demo: whiteboard-comics.com/good_time_for_drinking=f(week_day))
So far, I start with a <text> and my images look like this:
Then I replace by hand, letter by letter, in Illustrator, the entire text. And it end up looking like this:
And when I do it algorithmically (just based on the coordinates of the <text> element and the width of each hand drawn character as a <path>), it becomes this:

Internet Explorer and tspan vertical alignment

To vertically align a tspan element inside a text element in SVG, the CSS properties alignment-baseline and dominant-baseline work great in Chrome and in FF, respectively. So far so good.
With Internet Explorer it gets a bit crazy:
an open bug report asserts that these properties do not work for IE9-11 ...
... but the official documentation states that alignment-baseline is supported
CSS feature-sniffing in IE9 & IE11 reports that they support alignment-baseline as well as dominant-baseline for tspan, but they do not work with any values
to add confusion to frustration, this MSDN dev page simply says both properties are currently unsupported
This wouldn't be such an issue for IE9 (one could simply hack the desired alignment), but since I want to get away from browser detection, I would like to know:
is there a workable cross-browser solution?
how come even IE11 doesn't support this basic SVG styling property and how to work around that?
Thanks!
I have no idea why IE doesn't support alignment-baseline, let alone why you're getting such mixed information.
You can sort of hack the same behaviour using the dy attribute and font-based units ("em" and "ex"). It works pretty well if you just want to center a specific text element on a point.
<text x="50%" y="50%" dy="0.5ex" text-anchor="middle">
This text will be centered in your coordinate system!
</text>
But the problem with dy is that -- unless y is also set explicitly for the same element -- it is calculated relative to the position of the previous character. So if you want to center a text span relative to the surrounding spans, you have to first adjust for any previous offset and then set the new offset. The resulting code isn't pretty:
<text x="50%" y="25%" font-size="150%">
<tspan dy="0.5ex">Large font with</tspan><tspan
dy="-0.5ex"> <tspan
font-size="50%" dy="0.5ex">small font<tspan
dy="-0.5ex"> </tspan></tspan></tspan><tspan
dy="0.5ex">embedded.</tspan>
</text>
<text x="50%" y="75%" font-size="75%">
<tspan dy="0.5ex">Small font with</tspan><tspan
dy="-0.5ex"> <tspan
font-size="200%" dy="0.5ex">large font<tspan
dy="-0.5ex"> </tspan></tspan></tspan><tspan
dy="0.5ex">embedded.</tspan>
http://fiddle.jshell.net/awj49/
Rendering in IE11:
(gray lines mark the reference y coordinate)
If you can, it makes much cleaner code to just explicitly set the y attribute on each tspan:
<text x="50%" font-size="150%">
<tspan y="25%" dy="0.5ex">Large font with</tspan>
<tspan font-size="50%" y="25%" dy="0.5ex">small font</tspan>
<tspan y="25%" dy="0.5ex">embedded.</tspan>
</text>
<text x="50%" y="75%" font-size="75%">
<tspan y="75%" dy="0.5ex">Small font with</tspan>
<tspan font-size="200%" y="75%" dy="0.5ex">large font</tspan>
<tspan y="75%" dy="0.5ex">embedded.</tspan>
</text>
http://fiddle.jshell.net/awj49/1/
The final rendering is the same:
IE doesn't seem to support any vertical alignment CSS properties. A tspan is rendered with a default of what approximates alignment-baseline: central. Therefore, browser detection and polyfilling seems to be the only option just now to have your elements render the same across all browsers.
I adapted AmeliaBR's brilliant answer by manually adding to the y attribute of the parent text element to the desired difference of the font height in pixels (+50% of the font height = hanging, -50% = before-edge ...). Like so:
d3.selectAll('text').each( function()
var text = d3.select(this);
var dy = text.attr('y') || 0;
var h = text.style('font-size').replace('px', '') * 1;
var nh = (dy * 1) + (h / 2);
text.attr('y', nh);
});
I've used d3 here, but obviously this works with any JS library or pure JS DOM-selection.

Categories

Resources