Search code examples
javaswingrenderingcustom-controlsjtextfield

JTextField render bug if Text-Not-Fitting (JDK 7 till 18)


JTextField has wrong text render on RIGHT_TO_LEFT component orientation

We need a workaround, since whenever JTextField is filled with text that contains:

  • Right to left text like Arabic,
  • Latin numbers,
  • and Latin text.

Then JTextField renders text parts at unexpected locations.

(It only renders right if the complete text Fits inside the JTextField.)

The text we use to reproduce is:

  • صندوق ۴۰×۳۰ پایه دار وایرنگ میتر تک فاز

Other info:

  • Above text is just name of a product, added inside an accounting software we created, but our users have match more render failures than we could reproduce.
  • We reported at: JDK-8194733
  • We have no experience with a custom-render in Java to make workaround, but render of entire text not considering the limit, and clipping that, should do the trick.

Source code for an executable test case:

//
// Like you may notice, below code shows simple JTextField, 
// but once you resize the Window smaller than the text Fits,
// then you experience numbers dancing (moving around randomly).
//
// And trying to select parts of text is even more fatal (random parts are rendered).
//
package test;

import java.awt.ComponentOrientation;

public class JavaBug extends javax.swing.JFrame {

    public static void main(String[] args) {
        JavaBug frame = new JavaBug();
        frame.show();
    }
    
    public JavaBug() {
        javax.swing.JTextField textField = new javax.swing.JTextField();
        
        textField.setFont(new java.awt.Font("Tahoma", 0, 24)); // NOI18N
        
        // Below is just name of a product, added inside an accounting software.
        textField.setText("\u0635\u0646\u062F\u0648\u0642 \u06F4\u06F0×\u06F3\u06F0 \u067E\u0627\u06CC\u0647 \u062F\u0627\u0631 \u0648\u0627\u06CC\u0631\u0646\u06AF \u0645\u06CC\u062A\u0631 \u062A\u06A9 \u0641\u0627\u0632");
        textField.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        
        getContentPane().add(textField);
        pack();
        this.setLocationRelativeTo(null); //enusre get showed at screen center
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    }
}

Screen shots

Just running above code results to:
just run

After resize, to something less:
after resize


Development Kit or Runtime version:

  • java version "1.8.0_25"
  • Java(TM) SE Runtime Environment (build 1.8.0_25-b18)
  • Java HotSpot(TM) Client VM (build 25.25-b02, mixed mode)

Solution

  • You can try this

    import javax.swing.*;
    import javax.swing.text.html.HTMLEditorKit;
    import java.awt.*;
    
    public class JavaBug extends javax.swing.JFrame {
    
        public static void main(String[] args) {
            JavaBug frame = new JavaBug();
            frame.show();
        }
    
        public JavaBug() {
            JTextPane textPane = new JTextPane();
            textPane.setEditorKit(new HTMLEditorKit());
            textPane.setText("<html><font size='+2'>\u0635\u0646\u062F\u0648\u0642 \u06F4\u06F0×\u06F3\u06F0 \u067E\u0627\u06CC\u0647 \u062F\u0627\u0631 \u0648\u0627\u06CC\u0631\u0646\u06AF \u0645\u06CC\u062A\u0631 \u062A\u06A9 \u0641\u0627\u0632</font></html>");
            textPane.getDocument().putProperty("i18n", Boolean.TRUE);
            JPanel noWrapPanel = new JPanel( new BorderLayout() );
            noWrapPanel.add( textPane );
            JScrollPane scrollPane = new JScrollPane( noWrapPanel );
            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
    
            /* without below right to left force there is an other bug when we press home and
             * try to navigate to end using left key it will never get at end and fall back at start. kind of crazy bug */
            java.util.Locale arabic = new java.util.Locale("ar", "KW");
            ComponentOrientation arabicOrientation = ComponentOrientation.getOrientation(arabic);
            textPane.applyComponentOrientation(arabicOrientation);
    
            getContentPane().add(scrollPane);
            pack();
            this.setLocationRelativeTo(null); //enusre get showed at screen center
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        }
    }