JavaFX中的Unicode补充平面

adi*_*tsu 5 java unicode javafx javafx-8

我在JavaFX中从补充("星体")平面处理Unicode字符时遇到问题.具体来说,我不能将这些字符粘贴在一个TextInputDialog(我得到一些奇怪的字符,比如ð),并且不能在WebView中使用它们(它们被渲染为??????).

如果我通过输入并将它们JOptionPane.showInputDialog打印到控制台,相同的字符工作得非常好.他们甚至在JavaFX中显示Alert,尽管它最后添加了一些垃圾.

有办法解决这些问题吗?

我在Linux中使用Oracle JDK版本1.8.0_51.
补充平面字符的示例:
如果您看不到它们,则可能需要安装其他字体,如Symbola或Noto.

这是一个示例程序(使用Label而不是a WebView):

import javax.swing.JOptionPane;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.control.TextInputDialog;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class UniTest extends Application {
    @Override
    public void start(final Stage stage) throws Exception {
        final String s = new String(new int[]{127137, 178050, 3232, 128512, 241}, 0, 5);
        System.out.println("The string: " + s);
        System.out.println("Characters: " + s.length());
        System.out.println("Code points: " + s.codePoints().count());

        JOptionPane.showMessageDialog(null, s, "JOptionPane", JOptionPane.INFORMATION_MESSAGE);

        final Alert al = new Alert(AlertType.INFORMATION);
        al.setTitle("Alert");
        al.setContentText(s);
        al.showAndWait();

        final TextInputDialog dlg = new TextInputDialog();
        dlg.setTitle("TextInputDialog");
        dlg.setContentText("Try to paste the string in here");
        dlg.showAndWait().ifPresent(x -> System.out.println("Your input: " + x));

        final StackPane root = new StackPane();
        root.getChildren().add(new Label(s));
        stage.setScene(new Scene(root, 400, 300));
        stage.setTitle("Stage");
        stage.show();
    }

    public static void main(final String... args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是我得到的结果:

截图

注意:并非示例中的所有字符都来自辅助平面,其中一个字符仅在控制台中正确呈现.

Ben*_*Ben 3

TL;DR:显然 JavaFX 是有缺陷的。

\n\n

这是您正在使用的文本。

\n\n
\xe0\xb2\xa0\xc3\xb1\n
Run Code Online (Sandbox Code Playgroud)\n\n

十进制码点表示:

\n\n
127137 178050 3232 128512 241\n
Run Code Online (Sandbox Code Playgroud)\n\n

十六进制表示:

\n\n
0x1F0A1 0x2B782 0xCA0 0x1F600 0xF1\n
Run Code Online (Sandbox Code Playgroud)\n\n

显示错误

\n\n

Java 内部使用 UTF-16。因此考虑 UTF-16 表示:

\n\n

UTF-16 表示:

\n\n
D83C DCA1 D86D DF82 0CA0 D83D DE00 00F1\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们可以看到显示屏显示了您期望的五个字符,但随后显示了三个垃圾字符。

\n\n

所以它显然试图显示 8 个字形,而实际上只有 5 个。这几乎可以肯定是因为显示代码计算 8 个字符,因为 UTF-16 中将三个字符编码为代理对,因此每个字符取两个 16 位字。换句话说,在存在代理项对的情况下,它使用了错误的字符串长度值。

\n\n

粘贴文本错误

\n\n

测试数据的UTF-8表示:

\n\n
F0 9F 82 A1 F0 AB 9E 82 E0 B2 A0 F0 9F 98 80 C3 B1\n
Run Code Online (Sandbox Code Playgroud)\n\n

看到的是

\n\n
00F0 \xc3\xb0 LATIN SMALL LETTER ETH \n009F \xee\x82\x9f <control> = APC = APPLICATION PROGRAM COMMAND \n0082 \xee\x82\x82 <control> = BPH = BREAK PERMITTED HERE\n00A1 \xc2\xa1 INVERTED EXCLAMATION MARK \n00F0 \xc3\xb0 LATIN SMALL LETTER ETH \n
Run Code Online (Sandbox Code Playgroud)\n\n

(这两个控制字符可以在某些字体中具有包含其缩写或十六进制代码的字形。这些在您的示例中可见。)

\n\n

Latin1 十六进制表示:

\n\n
F0 9F 82 A1 F0\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,这五个字节与目标文本的 UTF-8 表示形式的前五个字节相同。

\n\n

结论:粘贴的数据已粘贴为占用 17 个字节的 5 个 UTF-8 代码点,但被解释为占用 5 个字节的 5 个 Latin1 代码点。同样,长度使用了错误的属性。

\n