javajavafxemoji

Is it possible to display emoji inside JavaFX TextArea?


I need an emoji along with text editing support, but when I insert any emoji in the text area (no matter if via code or via clipboard) it doesn't display at all. I tried downloading NotoColorEmoji font and setting it for the text area, but again nothing is displayed. Moreover, this font only contains emoji, so regular characters are not displayed as well.

var url = getClass().getResource("NotoColorEmoji-Regular.ttf").toExternalForm();
var ta = new TextArea("๐Ÿ˜€๐Ÿ˜ƒ๐Ÿ˜„");
ta.setFont(Font.loadFont(url, 12));

I know about 3rd party libs, but they can't all be used for various reasons. So, please, don't recommend any of them.

Is there a way to get emoji support in standard text input control? Any workarounds?

UPDATE:

TL;DR: I think it's a JavaFX bug. Some emoji fonts fully/partially cannot be rendered. Those that do only rendered as greyscale.

Environment: Ubuntu 22.04 (KDE 5.24) or Fedora 39 (KDE 5.27), JDK/JFX 21 or JDK/JFX 22 (latest)

Out-of-the-box, both Ubuntu and Fedora use the 'Noto Color Emoji' font, which cannot be rendered by JavaFX. Most symbols are missing.

JavaFX has own font renderer engine which reads the Linux font config settings. It's easy to check by not closing the XML tag. You'll get a JavaFX warning on startup.

So I downloaded all the available emoji TTF fonts and tried them out. To change the font, you need to put the TTF files in ~/.fonts and the configuration in ~/.config/fontconfig.

 <match>
  <test name="family">
   <string>sans-serif</string>
  </test>
  <edit binding="strong" name="family" mode="prepend">
   <string>Segoe UI Emoji</string>
  </edit>
 </match>
 <match>
  <test name="family">
   <string>serif</string>
  </test>
  <edit binding="strong" name="family" mode="prepend">
   <string>Segoe UI Emoji</string>
  </edit>
 </match>
 <match>
  <test name="family">
   <string>monospace</string>
  </test>
  <edit binding="strong" name="family" mode="prepend">
   <string>Segoe UI Emoji</string>
  </edit>
 </match>
 <!-- optionally override system emoji font -->
 <match>
  <test name="family">
   <string>Noto Emoji</string>
  </test>
  <edit binding="strong" name="family" mode="prepend">
   <string>Segoe UI Emoji</string>
  </edit>
 </match>

Here is the test node:

var eta = new TextArea("""
                ๐Ÿ˜€ ๐Ÿ˜ƒ ๐Ÿ˜„ ๐Ÿ˜ ๐Ÿ˜† ๐Ÿ˜… ๐Ÿ˜‚ ๐Ÿคฃ ๐Ÿฅฒ ๐Ÿฅน ๐Ÿ˜Š ๐Ÿ˜‡ ๐Ÿ™‚ ๐Ÿ™ƒ ๐Ÿ˜‰ ๐Ÿ˜Œ ๐Ÿ˜ ๐Ÿฅฐ ๐Ÿ˜˜ ๐Ÿ˜— ๐Ÿ˜™ ๐Ÿ˜š ๐Ÿ˜‹ ๐Ÿ˜› ๐Ÿ˜ ๐Ÿ˜œ ๐Ÿคช 
                ๐Ÿคจ ๐Ÿง ๐Ÿค“ ๐Ÿ˜Ž ๐Ÿฅธ ๐Ÿคฉ ๐Ÿฅณ ๐Ÿ™‚โ€ ๐Ÿ˜ ๐Ÿ˜’ ๐Ÿ™‚โ€ ๐Ÿ˜ž ๐Ÿ˜” ๐Ÿ˜Ÿ ๐Ÿ˜• ๐Ÿ™ โ˜น๏ธ ๐Ÿ˜ฃ ๐Ÿ˜– ๐Ÿ˜ซ ๐Ÿ˜ฉ ๐Ÿฅบ ๐Ÿ˜ข ๐Ÿ˜ญ ๐Ÿ˜ฎ ๐Ÿ˜ค ๐Ÿ˜  
                ๐Ÿ˜ก ๐Ÿคฌ ๐Ÿคฏ ๐Ÿ˜ณ ๐Ÿฅต ๐Ÿฅถ ๐Ÿ˜ฑ ๐Ÿ˜จ ๐Ÿ˜ฐ ๐Ÿ˜ฅ ๐Ÿ˜“ ๐Ÿซฃ ๐Ÿค— ๐Ÿซก ๐Ÿค” ๐Ÿซข ๐Ÿคญ ๐Ÿคซ ๐Ÿคฅ ๐Ÿ˜ถ ๐Ÿ˜ถ ๐Ÿ˜ ๐Ÿ˜‘ ๐Ÿ˜ฌ ๐Ÿซจ ๐Ÿซ  ๐Ÿ™„ 
                ๐Ÿ˜ฏ ๐Ÿ˜ฆ ๐Ÿ˜ง ๐Ÿ˜ฎ ๐Ÿ˜ฒ ๐Ÿฅฑ ๐Ÿ˜ด ๐Ÿคค ๐Ÿ˜ช ๐Ÿ˜ต ๐Ÿ˜ต ๐Ÿซฅ ๐Ÿค ๐Ÿฅด ๐Ÿคข ๐Ÿคฎ ๐Ÿคง ๐Ÿ˜ท ๐Ÿค’ ๐Ÿค• ๐Ÿค‘ ๐Ÿค  ๐Ÿ˜ˆ ๐Ÿ‘ฟ ๐Ÿ‘น ๐Ÿ‘บ ๐Ÿคก 
                ๐Ÿ’ฉ ๐Ÿ‘ป ๐Ÿ’€ ๐Ÿ‘ฝ ๐Ÿ‘พ ๐Ÿค– ๐ŸŽƒ ๐Ÿ˜บ ๐Ÿ˜ธ ๐Ÿ˜น ๐Ÿ˜ป ๐Ÿ˜ผ ๐Ÿ˜ฝ ๐Ÿ™€ ๐Ÿ˜ฟ ๐Ÿ˜พ 
                """);
.root {
    -fx-font-family: "serif";
}

I also found a very similar bug for MacOS JDK-8290866, but unfortunately setting -Dprism.lcdtext=true as well as changing -fx-smoothing-type didn't help.

And the results:

enter image description here


Solution

  • An example to demonstrate current behavior and summarize comments, showing how various emoji fonts are handled (not a comprehensive cross-platform answer).

    I only ran the example app on OS X, other OS environments may exhibit different behavior.

    OS X output

    Environment

    emojis

    Sample app

    In the example app, the fonts required are downloaded dynamically at runtime, so they don't need to be pre-downloaded and installed in the OS for you to use them. However, the drawback of this approach is that it takes a few seconds for the app to start as it gathers and initializes the fonts off of the net.

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextArea;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    import java.util.List;
    
    public class EmoApp extends Application {
        private static final String CSS_DATA = "data:text/css,";
    
        private static final String CSS = CSS_DATA + """
            .root {
               -fx-font-size: 16px;
            }
            .default {}
            .noto-color-emoji {
              -fx-font-family: "Noto Color Emoji";
            }
            .open-sans-emoji {
              -fx-font-family: "OpenSansEmoji";
            }
            .segoe-emoji {
              -fx-font-family: "Segoe UI Emoji";
            }
            .apple-color-emoji {
              -fx-font-family: "Apple Color Emoji";
            }
            """;
    
        private static final String EMOJI_TEXT = """
            ๐Ÿ˜€ ๐Ÿ˜ƒ ๐Ÿ˜„ ๐Ÿ˜ ๐Ÿ˜† ๐Ÿ˜… ๐Ÿ˜‚ ๐Ÿคฃ ๐Ÿฅฒ ๐Ÿฅน ๐Ÿ˜Š ๐Ÿ˜‡ ๐Ÿ™‚ ๐Ÿ™ƒ ๐Ÿ˜‰ ๐Ÿ˜Œ ๐Ÿ˜ ๐Ÿฅฐ ๐Ÿ˜˜ ๐Ÿ˜— ๐Ÿ˜™ ๐Ÿ˜š ๐Ÿ˜‹ ๐Ÿ˜› ๐Ÿ˜ ๐Ÿ˜œ ๐Ÿคช 
            ๐Ÿคจ ๐Ÿง ๐Ÿค“ ๐Ÿ˜Ž ๐Ÿฅธ ๐Ÿคฉ ๐Ÿฅณ ๐Ÿ™‚โ€ ๐Ÿ˜ ๐Ÿ˜’ ๐Ÿ™‚โ€ ๐Ÿ˜ž ๐Ÿ˜” ๐Ÿ˜Ÿ ๐Ÿ˜• ๐Ÿ™ โ˜น๏ธ ๐Ÿ˜ฃ ๐Ÿ˜– ๐Ÿ˜ซ ๐Ÿ˜ฉ ๐Ÿฅบ ๐Ÿ˜ข ๐Ÿ˜ญ ๐Ÿ˜ฎ ๐Ÿ˜ค ๐Ÿ˜  
            ๐Ÿ˜ก ๐Ÿคฌ ๐Ÿคฏ ๐Ÿ˜ณ ๐Ÿฅต ๐Ÿฅถ ๐Ÿ˜ฑ ๐Ÿ˜จ ๐Ÿ˜ฐ ๐Ÿ˜ฅ ๐Ÿ˜“ ๐Ÿซฃ ๐Ÿค— ๐Ÿซก ๐Ÿค” ๐Ÿซข ๐Ÿคญ ๐Ÿคซ ๐Ÿคฅ ๐Ÿ˜ถ ๐Ÿ˜ถ ๐Ÿ˜ ๐Ÿ˜‘ ๐Ÿ˜ฌ ๐Ÿซจ ๐Ÿซ  ๐Ÿ™„ 
            ๐Ÿ˜ฏ ๐Ÿ˜ฆ ๐Ÿ˜ง ๐Ÿ˜ฎ ๐Ÿ˜ฒ ๐Ÿฅฑ ๐Ÿ˜ด ๐Ÿคค ๐Ÿ˜ช ๐Ÿ˜ต ๐Ÿ˜ต ๐Ÿซฅ ๐Ÿค ๐Ÿฅด ๐Ÿคข ๐Ÿคฎ ๐Ÿคง ๐Ÿ˜ท ๐Ÿค’ ๐Ÿค• ๐Ÿค‘ ๐Ÿค  ๐Ÿ˜ˆ ๐Ÿ‘ฟ ๐Ÿ‘น ๐Ÿ‘บ ๐Ÿคก 
            ๐Ÿ’ฉ ๐Ÿ‘ป ๐Ÿ’€ ๐Ÿ‘ฝ ๐Ÿ‘พ ๐Ÿค– ๐ŸŽƒ ๐Ÿ˜บ ๐Ÿ˜ธ ๐Ÿ˜น ๐Ÿ˜ป ๐Ÿ˜ผ ๐Ÿ˜ฝ ๐Ÿ™€ ๐Ÿ˜ฟ ๐Ÿ˜พ 
            """;
    
    
        private static final String NOTO_COLOR_EMOJI_CSS =
                "https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap";
    
        private static final List<String> emojiStyleClasses = List.of(
                "default",
                "noto-color-emoji",
                "open-sans-emoji",
                "segoe-emoji",
                "apple-color-emoji"
        );
    
        @Override
        public void start(Stage stage) throws IOException {
            Font.loadFont(
                    "https://github.com/MorbZ/OpenSansEmoji/raw/master/OpenSansEmoji.ttf",
                    10
            );
            Font.loadFont(
                    "https://github.com/mrbvrz/segoe-ui/raw/master/font/seguiemj.ttf",
                    10
            );
    
            VBox layout = new VBox(10);
            layout.getChildren().addAll(
                    emojiStyleClasses.stream()
                            .map(this::emojiSample)
                            .toList()
            );
            layout.setPadding(new Insets(10));
    
            Scene scene = new Scene(layout);
            scene.getStylesheets().addAll(
                    NOTO_COLOR_EMOJI_CSS,
                    CSS
            );
            stage.setScene(scene);
            stage.show();
        }
    
        public Pane emojiSample(String styleClass) {
            TextArea textArea = new TextArea(EMOJI_TEXT);
            textArea.setPrefSize(750, 160);
            textArea.getStyleClass().add(styleClass);
    
            return new VBox(5,
                    new Label(styleClass),
                    textArea
            );
        }
    }
    

    Notes

    FAQ

    How did you get the behavior of rendered monochrome or not at all if the Apple Color Emoji font is used as a fallback? Is there a way to prevent the fallback?

    I didn't do anything to have that fallback behavior, it just seems to happen. You can see in the result that some emoji fonts are rendering monochrome for glyphs, which is coming from the emoji font. But when the emoji font does not seem to have an appropriate glyph you see color glyphs coming from the Apple emoji font.

    The Noto Color Emoji font has all the glyphs, but they don't render with JavaFX, ending up with blank text.

    Unrelated to JavaFX, the default emoji font used by Apple (Apple Emoji Font) can be replaced in the OS, see:

    https://superuser.com/questions/968509/is-is-possible-to-change-the-emoji-set-on-os-x

    The link (which is quite old), noted that the "Noto Color Emoji" font does not work on OS X, so the failure of the font may be at the OS level, rather than directly applicable to JavaFX.