svgjquery-svgsvg.js

How to get Y-coordinate of elements in SVG image as they appear on the screen?


I would like to parse out text values from SVG images and sort them in the vertical order they appear on the screen. I managed to find and parse out text elements from SVG, but I cannot calculate y-coordinate for these text relative to root viewport.

Is their a simple way to do so?


Solution

  • The amount of effort you have to invest depends on the complexity of your SVGs and details of what you mean exactly by vertical position. Besides the various tranformations applied to the <text> element, <tspan> child elements or individual glyphs can be moved around with the dx, dy and rotate attributes.

    The following plain JS solution orders by the point where the first glyph starts, using getStartPositionOfChar(), and getScreenCTM() to transform that value to screen coordinates. The red arrows illustrate which point is evaluated.

    The solution will fail if text is referenced in <use> elements.

    var texts = Array.from(document.querySelectorAll('text'));
    var list = document.querySelector('ol');
    
    var ordered = texts.map(function (t) {
        var pos = t.getStartPositionOfChar(0), // start of first glyph at baseline {x, y}
            ctm = t.getScreenCTM(); // transformation matrix to screen {a, b, c, d, e, f}
        return {
            text: t.textContent,
            y: ctm.b*pos.x + ctm.d*pos.y + ctm.f
        };
    }).sort(function (a, b) {
        return a.y - b.y;
    }).forEach(function (o) {
        var li = document.createElement('li');
        li.textContent = o.text + ': ' + o.y.toFixed(2);
        list.append(li);
    });
    #arrow {
        fill: none;
        stroke: red;
    }
    text {
        font-family: sans-serif;
        font-size: 15px;
    }
    ol {
        position:absolute;
        top:20px;
        left:300px;
    }
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
         width="300" height="150" viewBox="0 50 200 100">
      <defs>
        <path id="arrow" d="M 0 0 -10 0 M -4 -4 0 0 -4 4" />
      </defs>
      <text x="10" y="100" dy="-2 10 -5">red</text>
      <text x="150" y="60" transform="rotate(30)"><tspan y="10">green</tspan></text>
      <g transform="matrix(0.6,-0.3,-0.2,-0.6,60,340)">
        <text x="100" y="300">blue</text>
      </g>
      <use xlink:href="#arrow" x="10" y="98" />
      <use xlink:href="#arrow" x="124.904" y="83.6603" />
      <use xlink:href="#arrow" x="60" y="130" />
    </svg>
    <ol></ol>