1

I guess this is similar in spirit to vertical alignment of text element in SVG - consider this test.svg, which I made in Inkscape (with some manual text edits afterwards), where I just placed a "text box", by choosing the Text tool, and dragging it while the mouse is pressed, so it draws a rectangle:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   width="110"
   height="80"
   viewBox="0 0 110 80"
   version="1.1"
   id="svg12"
   sodipodi:docname="test.svg"
   inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
  <sodipodi:namedview
     id="namedview14"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:showpageshadow="2"
     inkscape:pageopacity="0.0"
     inkscape:pagecheckerboard="0"
     inkscape:deskcolor="#d1d1d1"
     inkscape:document-units="px"
     showgrid="false"
     inkscape:zoom="2.9362515"
     inkscape:cx="67.432916"
     inkscape:cy="27.245623"
     inkscape:window-width="1280"
     inkscape:window-height="657"
     inkscape:window-x="-8"
     inkscape:window-y="-8"
     inkscape:window-maximized="1"
     inkscape:current-layer="layer1" />
  <defs
     id="defs9">
    <rect
       x="5"
       y="5"
       width="100"
       height="70"
       id="rect_shape_textbox" />
  </defs>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <text
       xml:space="preserve"
       id="text_texbox"
       style="font-size:12px;line-height:1;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;dominant-baseline:hanging;white-space:pre;shape-inside:url(#rect_shape_textbox);display:inline"><tspan
         id="tspan671">Hello Texbox</tspan></text>
    <use
       xlink:href="#rect_shape_textbox"
       id="use_rect_shape_textbox"
       style="fill:none;stroke:#00FF00;stroke-width:1;stop-color:#000000" />
  </g>
</svg>

Inkscape handles the "text box" by having a <rect id="rect_shape_textbox"> in <defs>, and then using shape-inside:url(#rect_shape_textbox); in the style attribute of the <text> element; I have made this rect visible with <use>. I have also deleted the x and y attributes from both the <text> and its child <tspan>, that Inkscape otherwise inserts.

Inkview (from Inkscape Inkscape 1.2.1 (9c6d41e410, 2022-07-14)) renders test.svg like this:

test.svg inkscape

... while Firefox 115 renders this (Edge 114 is pretty much the same):

test.svg firefox

I have discovered, that Inkscape totally ignores attributes x and y on <text> and its child <tspan> if shape-inside:url(#rect_shape_textbox); is used, only dominant-baseline style attribute seems to matter - while browsers seem to take <text> or its child <tspan> x and y attributes into account as well (if both are specified, then <tspan>'s x and y "win", and parent <text>'s x and y are ignored).

So, I'm thinking, in order to have an SVG with "text box" (one which uses shape-inside:) render consistently in both browsers and inkscape, I should allow either the <text> or its child <tspan> have x and y attributes, and use these x and y to manually finetune the text position solely for browsers (considering that Inkscape otherwise ignores them).

Is there a better approach than this - as in, one where one could possibly avoid having to manually fine tune attributes?

sdbbs
  • 4,270
  • 5
  • 32
  • 87
  • 1
    Keep your inkscape svg as an editor/master file and save another file as a browser version via **svg-optimised** format option. **In 2023 no browser supports this "text box" concept** based on `shape-inside`. It is not made up – but there is no guarantee this will ever be supported for SVG text elements in the near future. SVG has quite a **long history of discontinued multiline text or textbox concepts** that never found their way to final specs or browser implementations. So you'll find a lot of w3c pages describing promising text box concepts – ignore them! – herrstrietzel Jul 15 '23 at 23:06
  • Many thanks for that, @herrstrietzel - feel free to post your comment as an answer, I'll accept it - I think it definitely explains the situation. – sdbbs Jul 15 '23 at 23:49

1 Answers1

1

You can position SVG elements with <animateMotion> on a <path>

You can create this all in SVG without JavaScript (take the SVG output from the SO snippet below)

<text alignment-baseline="middle">Line #
  <animateMotion dur="1s" fill="freeze" keyPoints="0;.2" keyTimes="0;1" calcMode="linear">
    <mpath href="#verticalline" />
  </animateMotion>
</text>

All you have to do is recreate this for every line and set the keyPoints value

Not sure if InkScape does SMIL animation, it for sure won't run JavaScript

But with JavaScript (in a native Web Component) its easier to create:

<svg-text-align in="BOX" duration="2">
  <svg viewBox="0 0 110 80" style="background:pink;height:180px">
    <template id="BOX">
      Line #1
      Line #2
      Line #3
      Line #4
    </template>
    <rect id="BOXRECT" x="5" y="5" width="100" height="70" fill="lightgreen" stroke="green" />
  </svg>
</svg-text-align>
<script>
  customElements.define("svg-text-align", class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => { // let innerHTML SVG parse
        let id = "#" + this.getAttribute("in");
        let box = this.querySelector(id + "RECT");
        let x = ~~box.getAttribute("x") + 5;
        let y = ~~box.getAttribute("y");
        let h = ~~box.getAttribute("height");
        let svg = `<path id="${id}LINE" stroke="red" d="m${x} ${y}v${h}"/>`;
        let lines = this.querySelector(id).innerHTML.split("\n").filter(x => x.trim());
        let dur = this.getAttribute("duration"); // set to .001 for "instant" display
        let startpoint = .15; // todo calc based on line count
        let step = 1/lines.length;
        svg += `<g fill="blue" font-size="15px" font-family="arial">`;
        svg += lines.map((line, idx) => `<text alignment-baseline="middle">${line}
            <animateMotion dur="${dur}s" keyPoints="0;${startpoint+idx*step}" 
                           fill="freeze" keyTimes="0;1" calcMode="linear">
              <mpath href="#${id}LINE" /></animateMotion></text>`).join("");
        svg += "</g>";
        box.insertAdjacentHTML("afterend", svg);
      })
    }
  });
</script>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49