0

I have an svg path with a text and a character like this:

<svg id="svg1" style="overflow:visible">

  <path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
  <text text-anchor="middle"  dy="-30" >
    <textPath href="#path1" startOffset="50%" fill="red">Shape</textPath>
    <textPath href="#path1" startOffset="50%" fill="red" dominant-baseline="central" >
      <tspan font-size="30" dy="-5">⬤</tspan>
    </textPath>
  </text>
</svg>

And I want the circle (or any character in general) to align perfectly at the center of the line regardless of font-size or any other attribute

I tried changing dy but it's really isn't a universal solution.
Here's what it looks like for different browsers:

for font-size="50" in firefox
enter image description here

for font-size="50" in Brave
enter image description here

for font-size="10" in firefox & Brave
enter image description here

So how do I universally align the character to the vertical center for any given style and/or attribute?

Edit
as mentioned in the comments, I tried this solution but it really doesn't solve the problem for varying font-size nor does it perfectly align to the center cross browser

<svg id="svg1" style="overflow:visible">

  <path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
  <text text-anchor="middle"  dy="-30" >
    <textPath href="#path1" startOffset="50%" fill="red">Shape</textPath>
    <textPath href="#path1" startOffset="50%" fill="red" dominant-baseline="central"
    alignment-baseline="central">
      <tspan font-size="30" dy="-5">⬤</tspan>
    </textPath>
  </text>
</svg>
cak3_lover
  • 1,440
  • 5
  • 26

2 Answers2

1

Unfortunately, there is no reliable way to center text vertically.

Firefox and chrome will interpret dominant-baseline quite differently.

For a consistent rendering, you could use some javaScript.
This approach also requires to measure some character/glyph proportions to calculate an ideal baseline shift.

So we need to get a ratio between font size and character height.

E.g write a capital in Arial at 100 points in inkscape, Illustrator etc. and convert it to paths/outlines and check it's height: 71.582 pt So the capital to font-size ratio is: 100/71.582 = 0.71582

Example 1: emulate dominant-baseline:central; font-size 10 and 20

<style>
svg{
  height:90vmin;
  border:1px solid #ccc
}
</style>
<svg  viewBox="0 0 50 20">
  <line x1="0" x2="100%" y1="50%" y2="50%" stroke="#000" stroke-width="10" /></line>
   <text  font-size="10" style="font-family:Arial" x="0" y="50%" fill="red" dy="3.5791">I⬤</text>
   <text  font-size="20" style="font-family:Arial" x="20" y="50%" fill="red" dy="7.1582">I⬤</text>
</svg>

The dy values are calculated like so:

10 (1. font-size) * 0.71582 (capheight ratio) / 2 = 3.5791
20 (2. font-size) * 0.71582 (capheight ratio) / 2 = 7.1582

The bad news: you need to get different ratios for each type of character e.g lowercase letters.

enter image description here

Albeit, it might be enough to check the x-height for lowercase characters.

Example 2: save ratios to data-attribute; calculate dy via js

let texts = document.querySelectorAll('text');
texts.forEach(function(text){
  let offsetRatio = parseFloat(text.getAttribute('data-offsetratio'));
  let style = window.getComputedStyle(text);
  let fontSize = parseFloat(style.fontSize);
  let dy = fontSize * offsetRatio /2;
  text.setAttribute('dy', dy );
  
})
svg{
  width:100%;
  border:1px solid #ccc
}
<svg id="svg1" style="overflow:visible" viewBox="0 30 100 40">
  <path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
  <path id="path2" stroke="#fff" d="M 0 50 L 100 50" stroke-width="0.25" />
  <g font-size="10" style="font-family:Arial,Georgia,'Segoe UI'">
    <text data-offsetratio="0.71582" x="0" y="50" fill="red">I⬤</text>
    <text data-offsetratio="0.51855" x="15" y="50" fill="red">x</text>
  </g>
  
  <g font-size="25" style="font-family:Arial,Georgia,'Segoe UI'">
    <text data-offsetratio="0.71582" x="30" y="50" fill="red">I⬤</text>
    <text data-offsetratio="0.56409" x="60" y="50" fill="red">●</text>
    <text data-offsetratio="0.70081" x="75" y="50" fill="red">•</text>
  </g>
</svg>
herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
0

Vertically aligning a text is done using dominant-baseline, but you can see from this example that it is not easy to do. Here I align ⬤Gga using the values middle and central.

I added a viewBox to <svg> so that it is easier to control the position. But I guess it all depends on the font used.

<svg overflow="hidden" viewBox="0 0 100 65" width="250">
  <rect width="100" height="65" fill="#eee" />
  <path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
  <text text-anchor="middle" dy="-30" fill="red"> 
    <textPath href="#path1" startOffset="50%">Shape</textPath>
    <textPath href="#path1" startOffset="50%" dominant-baseline="middle" font-size="30">⬤Gga</textPath>
  </text>
</svg>

<svg overflow="hidden" viewBox="0 0 100 65" width="250">
  <rect width="100" height="65" fill="#eee" />
  <path id="path1" stroke="black" d="M 0 50 L 100 50" stroke-width="10" />
  <text text-anchor="middle" dy="-30" fill="red"> 
    <textPath href="#path1" startOffset="50%">Shape</textPath>
    <textPath href="#path1" startOffset="50%" dominant-baseline="central" font-size="30">⬤Gga</textPath>
  </text>
</svg>
chrwahl
  • 8,675
  • 2
  • 20
  • 30