Certain FontAwesome glyphs don't render in Java Swing JToolBar buttons
Asked Answered
B

1

9

I'm having a problem displaying certain glyphs from the FontAwesome collection in buttons in a Swing JToolBar. Here is a screenshot to illustrate (notice that the top button in the toolbar on the right hand side is not a nice icon but instead shows three empty rectangles):

Screenshot to illustrate the problem

The code to reproduce this (at least on my Mac) is:

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;![enter image description here][2]
import java.awt.FontFormatException;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JToolBar;

public class TestFontAwesome {

    public static void main(String[] args) {
        new TestFontAwesome();
    }

    public TestFontAwesome() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try (InputStream is = TestFontAwesome.class.getResourceAsStream("/fontawesome-webfont_old.ttf")) {
                    Font font = Font.createFont(Font.TRUETYPE_FONT, is);
                    font = font.deriveFont(Font.PLAIN, 24f);

                    JToolBar toolBar = new JToolBar(JToolBar.VERTICAL);
                    JButton button1 = new JButton("\uf00e");
                    button1.setFont(font);
                    toolBar.add(button1);
                    JButton button2 = new JButton("\uf01e");
                    button2.setFont(font);
                    toolBar.add(button2);
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new JButton("Irrelevant content..."));
                    frame.add(toolBar, BorderLayout.EAST);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException | FontFormatException exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

}

I tried a few things: (1) Using different versions of the FontAwesome.ttf file, no change; (2) Trying different JDK versions, no change; (3) Displaying the same character in a regular JButton, this works as you can see in the following screenshot (so this is clearly not some issue with the font file):

Screenshot to show it works in a regular JButton

I tested on a non-Retina Mac and everything works, so I wonder if this is something specific to the Retina display. If anyone has any suggestions I'd appreciate hearing from you, thanks.

The code for the JButton only example (that works fine) is:

import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontFormatException;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.JButton;
import javax.swing.JFrame;

public class TestFontAwesome2 {

    public static void main(String[] args) {
        new TestFontAwesome2();
    }

    public TestFontAwesome2() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try (InputStream is = TestFontAwesome.class.getResourceAsStream("/fontawesome-webfont_old.ttf")) {
                    Font font = Font.createFont(Font.TRUETYPE_FONT, is);
                    font = font.deriveFont(Font.PLAIN, 24f);

                    JButton button1 = new JButton("\uf00e");
                    button1.setFont(font);
                    JButton button2 = new JButton("\uf01e");
                    button2.setFont(font);
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new FlowLayout());
                    frame.add(new JButton("Irrelevant content..."));
                    frame.add(button1);
                    frame.add(button2);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (IOException | FontFormatException exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

}
Bonn answered 28/8, 2014 at 11:53 Comment(8)
JTollBar uses BoxLayout (min, max and preferred size, preferred size is used for LayoutManager), then there is everything possible in the case that Font can't returns proper widht, the same issue can be by using FlowLayout for JFrame (only PreferredSize), added osx tag for trashgodAppreciative
see difference in the case that Font is instaled in Native OS and loaded at runtime, by default there isn't an issue to test (in pixels:-) for String widht by using SwingUtilities#computeStringWidth(FontMetrics fm, String str), e.i.Appreciative
btw and Retina isn't about 2times more pixels as is really used by GPU for rendering context, setting for effective resolution in pixelsAppreciative
In the first program, i changed the creation of button1 to JButton button1 = new JButton("\uf00e \uf00e \uf00e "); and the symbol prints correctly twice but is corrupted for the third. I submitted a bug report to Oracle for this.Bonn
Sorry, no retina display. Any help here?Swee
Definitely worth a try, but changing the toolbar layout manager to FlowLayout didn't change the result unfortunately (apart from making the icons run horizontally, of course).Bonn
@David Gilbert (before anthing on BugParade - is still Sun:-) you need to know the numbers of pixels used for PreferredSize, unfortunatelly bugs with TextLayout or measuring by using 3rd. party Font(s) aren't accepted there, just the very nice catchAppreciative
As a workaround, you can render the glyph(s) directly, as seen here or in an implementation of Icon, as seen here.Swee
A
4

i think the problem is the ComponentUi
Means in special: ToolbarUi or ButtonUi (-Implementation).

ToolbarUi (and ButtonUi) are abstract classes, which are implemented in your selected LookAndFeel.
The Implementation can be totally different for each LookAndFeel.
Some Implementations do ignore some "user" settings like e.g Font or Color.

JButtons can use a different Ui-Implementation than Buttons which are added to JToolBars!
And this Implementation may ignore your Font settings.

See for example ButtonUi Implementation (only part of) in MetalLookAndFeel

public void update(Graphics g, JComponent c) {
   AbstractButton button = (AbstractButton)c;
   if ((c.getBackground() instanceof UIResource) &&
             button.isContentAreaFilled() && c.isEnabled()) {
       ButtonModel model = button.getModel();
       if (!MetalUtils.isToolBarButton(c)) {
           if (!model.isArmed() && !model.isPressed() &&
                   MetalUtils.drawGradient(
                   c, g, "Button.gradient", 0, 0, c.getWidth(),
                   c.getHeight(), true)) {
               paint(g, c);
               return;
           }
       }
...

Here you can see the different behaviour when MetalUtils.isToolbarButton

You have to check your LookAndFeel Implementation behaviour.
(Maybe there is also a different Implementation, depending the screen resolution)

Arnoldarnoldo answered 31/8, 2014 at 0:32 Comment(8)
I think you're right that it's probably something about the screen density - the UI may be looking for a different glyph based on the Retina's doubled screen density. Is there a different or newer FontAwesome ttf file for high-density displays?Waverly
I doubt that the issue is with the TTF file, since all the glyphs display correctly in the JButton example.Bonn
Certainly the UI delegate is a factor. I tried with the Metal and Nimbus LAFs and the icon displays correctly, so it is only the (Mac) system look & feel that has the issue.Bonn
Maybe it is a similar poblem, like mine: https://mcmap.net/q/1319350/-unicode-char-not-rendering-in-swing-what-font-is-used-in-real/3887073 (it looks like a different Font than expected is used)Arnoldarnoldo
@DavidGilbert: do you feel that this answer adequately answers the question for the bounty?Superconductivity
It points in the right direction but I don't consider it a definitive answer. That said, I don't think we'll get any better info until someone at Oracle can look at the bug report I submitted. So I'll accept this as the answer.Bonn
@DavidGilbert: Fair enough, and so will I. Bounty delivered.Superconductivity
Here is the OpenJDK bug report I filed: bugs.openjdk.java.net/browse/JDK-8056982Bonn

© 2022 - 2024 — McMap. All rights reserved.