1

I am working with a JS charting library that I did not write and I would like to add some functionality to insert HTML content in the element of an SVG graphic. The reason is that it contains a mix of RTL and LTR text:

<text class="tc-data-layout-2 tc-data-text tc-data-text-1 tc-price-chart-text-color tc-price-chart-font-size" transform="translate(13,0)" opacity="1" y="15.6875" x="5" text-anchor="start">صعود, 58 %</text>

Notice that the % symbol is at the wrong end of the string. To fix this, I'd like to add a tspan element with the LTR content:

<text class="tc-data-layout-2 tc-data-text tc-data-text-1 tc-price-chart-text-color tc-price-chart-font-size" transform="translate(13,0)" opacity="1" y="15.6875" x="5" text-anchor="start"><tspan direction="ltr" unicode-bidi="embed" xml:lang="en">% 58 </tspan>,صعود</text>

I have located the code that adds the text element:

var textElement = document.createElementNS("http://www.w3.org/2000/svg", "text");
svgElement.appendChild(textElement);
if (classStr) {
   textElement.setAttribute("class", classStr);
}
textElement.textContent = text;

EDIT: One of my goals is to keep the percentage figure as one unit so that it's expressed as "99 %" or "% 99" in the markup itself.

Thanks!

Rob

Flimm
  • 136,138
  • 45
  • 251
  • 267
Rob Gravelle
  • 325
  • 5
  • 23
  • 2
    The text itself is in a wrong format for both of the directions: https://jsbin.com/fojeqib/edit?html,css,output (There is an example there with the right format) – Mosh Feu Nov 10 '20 at 16:58
  • Mosh Feu - thanks so much for mentioning the formatting. You're absolutely correct, that what I have there is wrong. In fact, one of my goals is to keep the percentage figure as one unit so that it's expressed as "99 %" or "% 99" in the markup itself. – Rob Gravelle Nov 13 '20 at 20:36
  • Mosh Feu - while the formatting looks good in the markup of the "Right Format" because the % is beside the number, I need the output to look like the RTL example. – Rob Gravelle Nov 13 '20 at 20:44

2 Answers2

3

The following was once explained to me by a translator, so it is not really language knowledge. Please, before publishing this, if you don't speak Persian yourself, check with a translator whether the rendering is correct.

The error occurs because in the course of the text run, the automatic identification of text direction fails. In byte order, the arabic letters have the order they are spoken in. Every program that knows RTL scripts and their Unicode encoding will render this correctly, showing the first letter in byte order to the right.

The bytes representing the comma and latin numbers are considered "direction-neutral", so if they show up next to arabic script, the RTL direction is used further on. In case you are wondering, in arabic script the lower-value digit is written to the right of the higher-level value, so for people only knowing latin script it looks as if it was written left-to-right, but that is just our ignorance. At the end of the numbers the program still thinks it is processing arabic script.

But it seems the percentage sign is interpreted as definitely LTR, and so the rendering falsely goes on on the right side.

SVG supports a inheritable direction property to force the right text run direction in cases where the mix of latin and arabic letters makes it ambiguous. The following example sets the property on the <text> element. I copied your string into the tag the way you provided it, but separated the numeric part into a separate <tspan> element. Now the percentage sign renders on the left. The result remains the same whether you divide the content or not.

var svg = document.querySelector('svg');

var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", 90);
text.setAttribute("y", 20);
text.style.fontSize = 15;
text.style.direction = "rtl";
text.textContent = "صعود, ";

var numeral = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
numeral.textContent = "58 %";
text.appendChild(numeral);

svg.appendChild(text);
<svg viewBox="0 0 100 25"></svg>
ccprog
  • 20,308
  • 4
  • 27
  • 44
  • Thanks for your great answer! One of my goals is to keep the percentage figure as one unit so that it's expressed as "99 %" or "% 99" in the markup itself. Do you know a way to do that? – Rob Gravelle Nov 16 '20 at 14:33
  • 1
    `direction` is inheritable, so it would make no difference if you separated the "99 %" part out in a `` element, positioned after the static text content. – ccprog Nov 17 '20 at 12:35
2

I've just noticed my answer is basically the same as ccprog. I'll leave this here for now, since the code examples might be useful to you.


Try adding direction="rtl" to your <text> element.

<svg viewBox="0 0 150 50">

  <text class="tc-data-layout-2 tc-data-text tc-data-text-1 tc-price-chart-text-color tc-price-chart-font-size"
        transform="translate(13,0)" opacity="1" y="15.6875" x="5"
        text-anchor="end" direction="rtl">صعود, 58 %</text>

</svg>

Note that direction="rtl" also flips the behaviour of the text-anchor attribute, so you might also want to change text-anchor="start" to text-anchor="end". As I have done in my example above.

Is this what you wanted?

If you need to change the code, then it would look something like this:

var textElement = document.createElementNS("http://www.w3.org/2000/svg", "text");
svgElement.appendChild(textElement);
if (classStr) {
   textElement.setAttribute("class", classStr);
}
textElement.setAttribute("direction", "rtl");
textElement.textContent = text;

Somewhere in the code, it'll also be setting text-anchor. You'll need to change that also.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181