7

I have got the below code to display text along a path. I am planning to make sort of dynamic where I can just type in what i want and it displays it along the path. Haven't worked out how to do that yet, any suggestions would be mostly welcome.

However my question is, how do I find out exactly at what point the text goes beyond the end of the path and no longer display. The idea is when I have it working dynamically, if the user types a sentence longer than what the path can handle, it will tell you that the text will be cut off from a certain point. in this case the user only sees the words "The quick brown fox jum", the fore I want the error message to say "ps over the lazy dog" could not be displayed or at least at a minimum to the say "The sentence is too long, and is not displayed in full"

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 
<svg viewBox = "0 0 500 300" version = "1.1">
    <defs>
      <path id = "s3" d = "M 10,90 Q 100,15 200,70 "/>
    </defs>
    <g>
        <text font-size = "20">
            <textPath xlink:href = "#s3">
                The quick brown fox jumps over the lazy dog                
            </textPath>
        </text>
        <use x = "0" y = "0" xlink:href = "#s3" stroke = "black" fill = "none"/>
    </g>
</svg>
Dinesh
  • 71
  • 1
  • 2

4 Answers4

11

You can query the computed lengths of the string that should go on the path, and the length of the path. Then compare the two, if the string length is longer than the path length then text will fall off the path.

You can also use the knowledge of the path length to squeeze the string to fit, like this:

<svg viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
      <path id="s3" d="M 10,90 Q 100,15 200,70 "/>
    </defs>
    <g>
        <text font-size="20">
            <textPath xlink:href="#s3" textLength="204" lengthAdjust="spacingAndGlyphs">
                The quick brown fox jumps over the lazy dog                
            </textPath>
        </text>
        <use x="0" y="0" xlink:href="#s3" stroke="black" fill="none"/>
    </g>
</svg>

Here's an example where the string length is manipulated by decreasing the font-size:

<svg viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
      <path id="s3" d="M 10,90 Q 100,15 200,70 "/>
    </defs>
    <g>
        <text font-size="20" font-family="Arial,Helvetica,sans-serif">
            <textPath id="tp" xlink:href="#s3" lengthAdjust="spacingAndGlyphs">
                The quick brown fox jumps over the lazy dog
            </textPath>
        </text>
        <use x="0" y="0" xlink:href="#s3" stroke="black" fill="none"/>
    </g>
    <script><![CDATA[
        var textpath = document.getElementById("tp");
        var path = document.getElementById("s3");
        var fontsize = 20;
        while (textpath.getComputedTextLength() > path.getTotalLength())
        {
            fontsize -= 0.01;
            textpath.setAttribute("font-size", fontsize);
        }
    ]]></script>
</svg>
Erik Dahlström
  • 59,452
  • 12
  • 120
  • 139
  • 2
    +1, This isn't *exactly* true, though, since the last character will only disappear if more than half of it falls off the path. So `getComputedTextLength()` can be slightly larger than `getTotalLength()` before anything actually disappears. And it gets even more complicated when you start messing with `text-anchor` or `startOffset`, or start moving `tspan`s around on the path. – mercator Jul 30 '11 at 02:41
  • @mercator: you're right, it is however possible to measure each character separately and be more precise. startOffset just sets the alignment point on the path (in percent along the path), and text-anchor sets how to align around the alignment point. It's a bit more complicated, but it's still possible to calculate in script I think. – Erik Dahlström Aug 02 '11 at 05:55
  • Hi everyone! One question... What if what I want something like text overflow through the path? Sorry if it's a dumb question but I know very little of SVG and the overflow property is not so obvious using paths... :S – Beto Aveiga Feb 03 '12 at 23:28
  • @beto: that should probably be a new question instead. – Erik Dahlström Feb 04 '12 at 10:47
  • **note**: `getComputedTextLength()` is not identical between browsers and OS, hence the text wrap varies. @ErikDahlström: `fontsize -= 0.01;` is a very small decrease, isn't 0.5 step more practical? – Alvin K. Aug 10 '14 at 06:20
  • @AlvinK. sure, it depends on how fine-grained you need it to be. Do note that the font-size will usually be scaled in more ways (e.g it depends on the user-space coordinate system, in other words the viewBox + additional transforms). – Erik Dahlström Aug 10 '14 at 19:32
  • 1
    I had to clone the text element without the path and use it to get the real actual width of all the characters. The width of the text on the textpath didn't include the non-visible characters. – Chad Feb 23 '18 at 21:39
1

align

pathLength on other elements works in FireFox, not on Chromium (march 2021)

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 40">
  <rect width="100%" height="100%" fill="beige"></rect>
  <path id="P" pathLength="100" d="M20 20h160" stroke="blue"></path>
  <text>
    <textPath href="#P" 
              startoffset="0" 
              text-anchor="start">start</textPath>
  </text>
  <text>
    <textPath href="#P"
              startoffset="50" 
              dominant-baseline="hanging"
              text-anchor="middle">middle</textPath>
  </text>
  <text>
    <textPath href="#P" 
              startoffset="100" 
              text-anchor="end">end</textPath>
  </text>
</svg>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
0

I had to tweak it like so to work as expected:

        var textpath = document.getElementById("tp");
        var path = document.getElementById("s3");
        var fontsize = 20;
        while ( (textpath.getComputedTextLength()*1.50) > path.getTotalLength())
        {
            fontsize -= 0.01;
            textpath.setAttribute("font-size", fontsize);
        }
dev4life
  • 10,785
  • 6
  • 60
  • 73
0

There is a trick to detect text overflow that works on Chrome: call getStartPositionOfChar on your textPath element for the first and last character indices (if there are such), and in case of an overflow the function simply will return an object which coordinates are the origin {x: 0, y: 0}.

Then you can linear/binary search your way to the solution as suggested by the other answers. It is more accurate than getComputedTextLength, however it does not work on Firefox; for some reason the browser will try to extrapolate the positions and return junk values.

6infinity8
  • 298
  • 4
  • 10