如何在 JavaFX 中渲染具有连接字形的波斯语或阿拉伯语文本

vah*_*eza 7 javafx arabic right-to-left farsi arabic-support

我想用 JavaFX 显示文本。该消息是波斯语或阿拉伯语。然而,如此处所述波斯语或阿拉伯语字母的表示形状取决于其相邻字母。

如果我构建一个包含整个消息的TextFlow单个消息,它会正确显示。Text但是当我将它分割成多个Texts 时,消息就被破坏了。

在此输入图像描述

例如,以下代码片段产生上图:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我在 Mac OS 上使用 javafx 版本 18.0.1 和 java 17。但对于 Linux 来说结果也是一样的。

vah*_*eza 2

正如我在评论中提到的,这是一个已知的错误,大约 10 年前在 openjdk 问题跟踪系统中报告过(请参见此处)。不幸的是它被标记为低优先级。

因此,我决定此时手动解决这个问题(感谢@SedJ601的鼓舞人心的评论)。Text如果 s 的边界在流动中被破坏,我必须在它们的边界上施展一些魔法。我认为有两种可能的解决方案:

  1. 使用樫田
  2. 使用阿拉伯语表达形式 B,其中包括每个字母的所有变体

然而,这两种解决方案都存在一些问题,特别是在 lam 和 alef 的情况下。但第二个看起来更好。

例如,在下面,我TextFlow在之前的示例代码中添加了关于上述解决方案的两个新内容。

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

结果看起来是这样的: 在此输入图像描述

在此示例中,我Text自己生成了最终的 s 。然而在实际应用中,需要一些计算来找到位置。

请注意,第一个解决方案看起来非常难看。因为我的样品需要太多的Kashidas。随着休息次数和相应的 Kashidas 的减少,情况会变得更好。