HTML5 Canvas fillText with Right-to-Left string
Asked Answered
L

4

10

I am trying to use the fillText() method on an HTML5 Canvas' 2d Context to draw a string written in Arabic. It works perfectly, until I put a punctuation mark at the end of the string. Then the punctuation mark appears on the wrong side of the string (at the beginning, rather than the end, as if it were a ltr not rtl string). I played with the Context.textAlign property, but that seems to concern only the way the string is drawn relative to the specified position, not the actual direction of the text. Does anyone know of a solution to this?

Thanks.

Update: The answer I found is to add a "dir" attribute to the canvas element on the page. For example,

<canvas dir="rtl">

However, I still don't know how to change the dir attribute for individual strings sent to fillText. Any ideas?

Lillia answered 22/1, 2012 at 14:8 Comment(3)
Sorry for jumping the gun. I found the solution in the spec: whatwg.org/specs/web-apps/current-work/multipage/… Apparently, one must set the "dir" element on the canvas to "rtl", ie, "<canvas dir="rtl">".Lillia
Please add your solution as an answer. Thanks!Michamichael
what font do you use? What is the name of the font?Slapbang
K
11

You don't need to set the direction for each individual string. See the following example which also shows the proper use of implicit bidi control marks for proper display order:

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>

  <body>
    <canvas id="myCanvas" width="700" dir="rtl" height="250" style="border:1px solid #d3d3d3;">
      Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script type="text/javascript" charset="utf-8">
      var c = document.getElementById("myCanvas");
      var cArabic = c.getContext("2d");
      cArabic.font="25px Arial";

      // Simple Sentence with punctuation.
      var str1 = "این یک آزمایش است.";
      // Few sentences with punctuation and numerals. 
      var str2 = "۱ آزمایش. 2 آزمایش، سه آزمایش & Foo آزمایش!";
      // Needs implicit bidi marks to display correctly.
      var str3 = "آزمایش برای Foo Ltd. و Bar Inc. باشد که آزموده شود.";
      // Implicit bidi marks added; "Foo Ltd.&lrm; و Bar Inc.&lrm;"
      var str4 = "آزمایش برای Foo Ltd.‎ و Bar Inc.‎ باشد که آزموده شود.";

      cArabic.fillText(str1, 600, 60);
      cArabic.fillText(str2, 600, 100);
      cArabic.fillText(str3, 600, 140);
      cArabic.fillText(str4, 600, 180);
    </script>

  </body>
</html>

And here is the output: as rendered by Chrome <code>26.0.1410.64 m</code> on MS Windows XP SP3

Kira answered 12/4, 2013 at 19:57 Comment(4)
Would you happen to know how to connect Arabic letters if you're drawing them one at a time in canvas?Conduct
This should be transparent to the developer. Text layout engine should take care of it. Do you have any specific scenario where it doesn't work?Kira
The approach I'm following is similar to this: #7665842 -- Conceptually, the first solution works well but when using Arabic letters, they do not connect in this case because you're invoking context.fillText once for each letter instead of the entire string.Conduct
I see. That workaround doesn't conceptually work for joining scripts as a whole (Arabic being one). If it's immensely crucial, you can imitate the joining behaviour and color every joining run of letter forms separately in one color (which is kind of re-inventing some part of a text rendering engine).Kira
K
5

The problem is that the '.' character is 'directional neutral character', it means that it get the direction from the next strong directional character, if there is no character afterwards, it get it from the container. In your case it is the latter one. One solution, as already found, is to make the container right-to-left. But there are still two other solutions :

1- Put another invisible strong right-to-left directional character after the '.'. Fortunately the unicode has one special character for that which is "right-to-left mark" (U+200F)

2- Put the "right-to-left override character" (U+202E) before the text, so after this mark any neutral character get the right-to-left direction. You can end the override by adding "pop directional formatting" character (U+202C).

I changed the @shervin's sample code to demonstrate it.

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>

  <body>
    <canvas id="myCanvas" width="700" height="250" style="border:1px solid #d3d3d3;">
      Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script type="text/javascript" charset="utf-8">
      var c = document.getElementById("myCanvas");
      var cArabic = c.getContext("2d");
      cArabic.font="20px Arial";

      //option 1 : adding right-to-left mark at the end to put the '.' between two strong right to left charachters, so it will treated as right to left.
      var str1 = "این یک آزمایش است." + "\u200F";

      //option 2 : adding right-to-left override at the begining of the text so every neutral charachter afterward becomes righ to left.
      var str2;
      str2 = "\u202E";
      str2+= "این یک آزمایش است.";
      //           you might want to finish the override by 'pop directional formatting' charachter
      str2 += "\u202C";

      cArabic.fillText(str1, 200, 30);
      cArabic.fillText(str2, 200, 70);
    </script>

  </body>
</html>

Result image

There are other solutions. You can find them in unicode specification in this page. They are, "direction isolation" (U+2067) which has rendering problem in Chrome. And "direction embedding" with U+202B, which is similar to direction override.

Koto answered 14/4, 2018 at 9:48 Comment(0)
C
3

Setting the dir option is for the whole canvas - for individual strings that behave badly, you might consider manually adding an RTL marker (U+200F) after the quote.

Collettecolletti answered 6/5, 2012 at 16:21 Comment(2)
could you please elaborate or give an example on how this is done?Shonna
example: "אבג" + "\u200F";Alterative
T
0

You can do it with that:
ctx.textAlign="end";
or
ctx.textAlign="right"

And to make it again left-to-right:
ctx.textAlign="start";

Teethe answered 28/1, 2018 at 18:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.