Search code examples
svgcross-browsertext-alignment

Cross-browser textPath horizontal centre alignment for multi-line text


I would like to horizontally centre align more than one line of text.

The following approach works in Firefox 68.0.2

path { stroke: #000; }
text { fill: #000; } 
<svg width="400" height="120">
  <path id="p1" d="M10,40l250,0" />
  <text text-anchor="middle">
    <textPath href="#p1" startOffset="50%">
      <tspan x="0" dy="1.4em">this is the first line</tspan>
      <tspan x="0" dy="1.4em">and this is the second</tspan>
    </textPath>
  </text>
</svg>

However, in Chrome Version 77.0.3865.90, both lines are rendered to the left. It looks as though the combined length of the two <tspan> is used to calculate the centre point.

screenshot from Chrome showing the incorrectly aligned lines

I can make it work in both browsers by using two separate <text> elements:

path { stroke: #000; }
text { fill: #000; }
<svg width="400" height="120">
  <path id="p1" d="M10,40l250,0" />
  <text text-anchor="middle">
    <textPath href="#p1" startOffset="50%">
      <tspan x="0" dy="1.4em">this is the first line</tspan>          
    </textPath>
  </text>
  <text text-anchor="middle">
    <textPath href="#p1" startOffset="50%">
      <tspan x="0" dy="2.8em">and this is the second</tspan>
    </textPath>
  </text>
</svg>

(I also tried using two <textPath> within the same <text> parent, but the behaviour of dy is also different between the two browsers)

My questions are:

  1. Which browser is behaving correctly (is this a bug in the other one)?
  2. Is there a cross-browser way to achieve the same result without rendering two separate <text> elements?

Solution

  • I believe Chrome is doing the correct thing here - other than the missing "a". :-/

    In SVG 1.1, the interaction of the startOffset and x attributes wasn't well defined, and browsers did all different things.

    However, in SVG 2, an attempt to fully describe the text layout algorithm for <textPath> elements has been made. You can find it here:

    https://svgwg.org/svg2-draft/single-page.html#text-TextpathLayoutRules

    In there it says:

    For text-anchor:middle, startpoint-on-the-path is the point on the path which represents the point on the path which is [ ‘startOffset’ minus half of the total advance values for all of the glyphs in the ‘textPath’ element ] distance along the path from the start of the path

    Then later, it says:

    When the inline-base direction is horizontal, then any ‘x’ attributes on ‘text’ or ‘tspan’ elements represent new absolute offsets along the path, thus providing explicit new values for startpoint-on-the-path.

    In other words, your x="0" attributes are overriding the startOffset and text-anchor settings.

    So being more simple and explicit, as you are in your second SVG, is the way to go.