7

I want to display a text with JavaFX. The message is Farsi or Arabic. However, as described here, the representation shape of a Farsi or Arabic letter depends on its adjacent letters.

If I build a TextFlow with a single Text containing the whole message, it is displayed correctly. But when I split it across multiple Texts, the message become broken.

enter image description here

For instance, the following snippet yields above figure:

public class HelloFX extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Font font = new Font("Arial", 48);

        String message = "\u0627\u0644\u0633\u0644\u0627\u0645  \u0639\u0644\u064a\u0643\u0645";
        TextFlow textFlow1 = new TextFlow();
        Text text1 = new Text(message);
        text1.setFont(font);
        textFlow1.getChildren().addAll(text1);

        TextFlow textFlow2 = new TextFlow();
        for (int i = 0; i < message.length(); i++) {
            char ch = message.charAt(i);
            Text text2 = new Text(ch + "");
            if (i % 2 == 0)
                text2.setFill(Color.RED);
            text2.setFont(font);
            textFlow2.getChildren().add(text2);
        }

        VBox box = new VBox(textFlow1, textFlow2);
        box.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
        Scene scene = new Scene(box, 400, 150);
        stage.setScene(scene);
        stage.show();
    }
}

I'm using javafx version 18.0.1 and java 17 on Mac OS. But the result is same for Linux, as well.

vahidreza
  • 843
  • 1
  • 8
  • 19
  • https://stackoverflow.com/a/19762787/14855128 – Mehran Feb 14 '23 at 20:38
  • Thank you @Mehran. I'd seen that post. But I don't get the point that what is the relationship between html tags and javafx `TextFlow`. Do you mean that I should use javafx `WebView`? It is also has its issues and I don't want to use it. – vahidreza Feb 15 '23 at 10:19
  • 1
    Here is a comment by Tim Hollowway [here](https://coderanch.com/t/707390/java/arabic-letters-characters-Connected) that may be worth reading. I can't post the comment here because it is too long. – SedJ601 Feb 15 '23 at 20:10
  • Many thanks, @SedJ601. I didn't get you. Do you mean that the issue causes because I am using inappropriate font? I conducted the same scenario with the identical font in MS Word and also in html on web browser. Both of them are rendered the text correctly. It seems that the problem is with JavaFX. Isn't it? – vahidreza Feb 16 '23 at 05:35
  • @vahidreza As far as I know JavaFX can use CSS which presents some attributes about language, font, directions , text flow and alignment. – Mehran Feb 16 '23 at 09:24
  • I'm familiar with CSS in JavaFX. I just checked [the official documentation](https://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html) again but could not find any thing. Could you please check it as well? – vahidreza Feb 16 '23 at 12:44
  • 1
    The problem appears to be with `JavaFX`, but Tim offers his possible solutions given the problem. The part I am referring to starts like `There are really 2 factors here. Kerning is the term that refers to adjusting the spacing between letters to make them look better as a unit. `. – SedJ601 Feb 16 '23 at 13:51
  • @SedJ601 Could you please explain more about your idea? – vahidreza Feb 17 '23 at 15:58
  • It seems that it is a known bug in javafx and reported [here](https://bugs.openjdk.org/browse/JDK-8092022), in openjdk bug system. – vahidreza Feb 18 '23 at 06:18

1 Answers1

2

As I mentioned in the comments, it is a known bug and reported in openjdk issue tracking system about 10 years ago (see here). Unfortunately it is marked as low priority.

Therefore, I decided to tackle this issue manually, at this moment (thanks to @SedJ601 for his inspirational comments). I have to do some magics in the boundaries of Texts, if they becomes broken in flow. I think there are two possible solutions:

  1. Use Kashida
  2. Use arabic presentation forms B which includes all variants of each letter

However, both of these solutions have some issues, specially in case of lam with alef. But the second one looks better.

For example, in the following, I've added two new TextFlow with respect to the above solutions into my previous sample code.

public class HelloFX extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Font font = new Font("Arial", 48);

        String message = "\u0627\u0644\u0633\u0644\u0627\u0645  \u0639\u0644\u064a\u0643\u0645";
        TextFlow textFlow1 = new TextFlow();
        Text text1 = new Text(message);
        text1.setFont(font);
        textFlow1.getChildren().addAll(text1);

        TextFlow textFlow2 = new TextFlow();
        for (int i = 0; i < message.length(); i++) {
            char ch = message.charAt(i);
            Text text2 = new Text(ch + "");
            if (i % 2 == 0)
                text2.setFill(Color.RED);
            text2.setFont(font);
            textFlow2.getChildren().add(text2);
        }


        List prefixes = Arrays.asList(2,3,4,9,10,11,12);
        List suffixes = Arrays.asList(1, 2,3,8,9,10,11);
        message = "\u0627\u0644\u0633\u0644\u0627\u0645  \u0639\u0644\u064a\u0643\u0645";
        TextFlow textFlow3 = new TextFlow();
        for (int i = 0; i < message.length(); i++) {
            char ch = message.charAt(i);
            String suffix = "", prefix = "";
            if (suffixes.contains(i)) suffix = "\u0640";
            if (prefixes.contains(i)) prefix = "\u0640";
            Text text3 = new Text(prefix + ch + suffix);
            if (i % 2 == 0)
                text3.setFill(Color.RED);
            text3.setFont(font);
            textFlow3.getChildren().add(text3);
        }


        TextFlow textFlow4 = new TextFlow();
        message = "\u0627\uFEE0\uFEB4\uFEDf\uFE8e\u0645  \uFECb\uFEDf\uFEF4\uFEDc\uFEE2";
        for (int i = 0; i < message.length(); i++) {
            char ch = message.charAt(i);
            Text text4 = new Text(ch + "");
            if (i % 2 == 0)
                text4.setFill(Color.RED);
            text4.setFont(font);
            textFlow4.getChildren().add(text4);
        }


        VBox box = new VBox(textFlow1, textFlow2, textFlow3, textFlow4);
        box.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
        Scene scene = new Scene(box, 500, 250);
        stage.setScene(scene);
        stage.show();
    }
}

The result looks this: enter image description here

In this sample I've generated the final Texts by myself. However in the real applications, it needs some computations to find the positions.

Note that the first solutions looks very ugly. It is because of my sample which needs too many Kashidas. It gets better as the number of breaks and corresponding Kashidas decreases.

vahidreza
  • 843
  • 1
  • 8
  • 19