HTML5 Canvas fillText with Right-to-Left string

8.3k views Asked by At

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?

4

There are 4 answers

2
Ansari On

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.

0
karianpour On

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.

4
Shervin On

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

0
mansim On

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

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