How can I render a Farsi or Arabic text with connected glyphs in JavaFX
Asked Answered
A

1

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.

Afterglow answered 14/2, 2023 at 18:14 Comment(9)
https://mcmap.net/q/1627474/-how-to-display-persian-or-arabic-characters-in-javafx-scene-builderSnot
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.Afterglow
Here is a comment by Tim Hollowway here that may be worth reading. I can't post the comment here because it is too long.Thermostatics
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?Afterglow
@Afterglow As far as I know JavaFX can use CSS which presents some attributes about language, font, directions , text flow and alignment.Snot
I'm familiar with CSS in JavaFX. I just checked the official documentation again but could not find any thing. Could you please check it as well?Afterglow
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. .Thermostatics
@Thermostatics Could you please explain more about your idea?Afterglow
It seems that it is a known bug in javafx and reported here, in openjdk bug system.Afterglow
A
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.

Afterglow answered 18/2, 2023 at 12:44 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.