Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JTextPane/JTextField strange behaviour with rare characters

I've discovered a strange bug in JTextPane/JTextField (or somewhere in the font rendering underneath them). I wonder if anyone else has encountered the same and might have a solution for this.

I'm trying to display some "special" or rare characters in a JTextPane, and as soon as I change the font of the JTextField (which is completely unrelated to JTextPane!), the JTextPane "breaks up", and no longer displays these characters.

This should do a better job explaining what I mean:

public class Scrap {

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(200, 200);
    frame.setLayout(new BorderLayout());

    JTextField field = new JTextField();

    // Uncomment this line... and the JTextPane nor the JTextField
    // no longer display the characters
    // field.setFont(new Font("Arial", Font.PLAIN, 14));

    frame.add(field, BorderLayout.SOUTH);

    JTextPane textPane = new JTextPane();
    textPane.setFont(new Font("Arial", Font.PLAIN, 14));

    JScrollPane scroll = new JScrollPane(textPane);
    frame.add(scroll, BorderLayout.CENTER);

    StyledDocument doc = (StyledDocument) textPane.getDocument();

    try {
        String str = "◕ ◡◡ ◕";

        doc.insertString(doc.getLength(), str, null);

    } catch (BadLocationException e) {
        e.printStackTrace();
    }

    frame.setVisible(true);
    frame.setLocationRelativeTo(null);
}
}

EDIT: Here's a better example of the problem. It appears to be related to the size of the Font. Move the Slider and you'll notice how the size 14 does not render the glyphs, and 14 happens to be the size of the JTextField's Font.

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.*;
import java.awt.*;

public class Scrap {

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(600, 200);
    frame.setLayout(new BorderLayout());

    final JTextField field = new JTextField(10);

    final JTextPane textPane = new JTextPane();

    StyledDocument doc = (StyledDocument) textPane.getDocument();

    JPanel panel = new JPanel();
    frame.add(panel, BorderLayout.SOUTH);

    // Set the Font of the JTextField, and the JTextPane
    // no longer displays the text of that size correctly...

    int changeMe = 14;

    field.setFont(new Font("Tahoma", Font.PLAIN, changeMe));

    // If we change the Font Family, the problem goes away...
    // field.setFont(new Font("Dialog", Font.PLAIN, 14));

    panel.add(field);

    final JLabel label = new JLabel();

    final JSlider slider = new JSlider(6, 32);
    slider.addChangeListener(new ChangeListener() {
        @Override
        public void stateChanged(ChangeEvent e) {
            textPane.setFont(new Font("Tahoma", Font.PLAIN, slider.getValue()));

            textPane.selectAll();

            SimpleAttributeSet attr = new SimpleAttributeSet();
            StyleConstants.setFontSize(attr, slider.getValue());
            textPane.setCharacterAttributes(attr, true);

            label.setText("" + slider.getValue());
        }
    });

    slider.setValue(14);

    panel.add(slider);

    panel.add(label);

    JScrollPane scroll = new JScrollPane(textPane);
    frame.add(scroll, BorderLayout.CENTER);

    Style s = doc.addStyle("test", null);

    try {
        String str = "◕ ◡◡ ◕";

        doc.insertString(doc.getLength(), str, doc.getStyle("test"));

    } catch (BadLocationException e) {
        e.printStackTrace();
    }

    frame.setVisible(true);
    frame.setLocationRelativeTo(null);
}
}
like image 656
n00bster Avatar asked Oct 06 '22 08:10

n00bster


1 Answers

I had a similar problem when I tried to make an application that would have to support multiple languages (including languages with "non-standard" characters, such as chinese). I used to set the font of my widgets to Arial, and had the problem. The following solution fixed my problem, but it might not fix yours.

Java has a fallback mechanism whenever it encounters characters from specific charsets it can't display. It can be configured using the fontconfig.properties file, which is provided with the JRE (the file is originally provided as "fontconfig.properties.src", you have to manually rename it).

When you force a font that isn't among Dialog, Serif, SansSerif, Monospaced or DialogInput, Java has no way to use a different charset if the current one (in your case Arial) cannot represent the character (or glyph) you are trying to draw on screen.

If you look at the fontconfig.properties.src file, you will see that is has many entries for many types of fonts (e.g. Dialog.plain, Serif.bold, etc). These are the actual fallback fonts to use whenever the fonts above cannot display a specific glyph. So, settings your widgets' font to let's say Font.DIALOG will allow Java to try a list of fonts to display your characters.

More information is available on Oracle's website (here for Java 7). Note that the use of fontconfig.properties isn't officially supported by Oracle.

like image 84
Laf Avatar answered Oct 10 '22 01:10

Laf