SWT - OS agnostic way to get monospaced font

12,022

Solution 1

According to the section on Font Configuration Files in the JDK documentation of Internationalization Support-related APIs, the concept of Logical Fonts is used to define certain platform-independent fonts which are mapped to physical fonts in the default font configuration files:

The Java Platform defines five logical font names that every implementation must support: Serif, SansSerif, Monospaced, Dialog, and DialogInput. These logical font names are mapped to physical fonts in implementation dependent ways.

So in your case, I'd try

Font mono = new Font(parent.getDisplay(), "Monospaced", 10, SWT.NONE);

to get a handle to the physical monospaced font of the current platform your code is running on.

Edit: It seems that SWT doesn't know anything about logical fonts (Bug 48055 on eclipse.org describes this in detail). In this bug report a hackish workaround was suggested, where the name of the physical font may be retrieved from an AWT font...

Solution 2

I spent a while bashing my head against this one until I realised that obviously eclipse must have access to a monospace font for use in its text fields, console etc. A little digging turned up:

Font terminalFont = JFaceResources.getFont(JFaceResources.TEXT_FONT);

Which works if all you're interested in is getting hold of some monospace font.

Edit: Or based on @ctron's comment:

Font font = JFaceResources.getTextFont();

Edit: Caveat (based on @Lii's comment): this will get you the configured Text Font, which can be overriden by the user and may not be a monospace font. However, it will be consistent with, e.g., the font used in the editor and console which is probably what you want.

Solution 3

To the best of my knowledge, the AWT API does not expose underlying Font information. If you can get to it, I would expect it to be implementation dependent. Certainly, comparing the font mapping files in a couple of JRE lib directories, I can see that they are not defined in a consistent way.

You could load your own fonts, but that seems a little wasteful given you know the platform comes with what you need.

This is a hack that loads a JRE font:

private static Font loadMonospacedFont(Display display) {
    String jreHome = System.getProperty("java.home");
    File file = new File(jreHome, "/lib/fonts/LucidaTypewriterRegular.ttf");
    if (!file.exists()) {
        throw new IllegalStateException(file.toString());
    }
    if (!display.loadFont(file.toString())) {
        throw new IllegalStateException(file.toString());
    }
    final Font font = new Font(display, "Lucida Sans Typewriter", 10,
            SWT.NORMAL);
    display.addListener(SWT.Dispose, new Listener() {
        public void handleEvent(Event event) {
            font.dispose();
        }
    });
    return font;
}

It works on IBM/Win32/JRE1.4, Sun/Win32/JRE1.6, Sun/Linux/JRE1.6, but is a pretty fragile approach. Depending on your needs for I18N, it could be trouble there too (I haven't checked).

Another hack would be to test the fonts available on the platform:

public class Monotest {

    private static boolean isMonospace(GC gc) {
        final String wide = "wgh8";
        final String narrow = "1l;.";
        assert wide.length() == narrow.length();
        return gc.textExtent(wide).x == gc.textExtent(narrow).x;
    }

    private static void testFont(Display display, Font font) {
        Image image = new Image(display, 100, 100);
        try {
            GC gc = new GC(image);
            try {
                gc.setFont(font);
                System.out.println(isMonospace(gc) + "\t"
                        + font.getFontData()[0].getName());
            } finally {
                gc.dispose();
            }
        } finally {
            image.dispose();
        }
    }

    private static void walkFonts(Display display) {
        final boolean scalable = true;
        for (FontData fontData : display.getFontList(null, scalable)) {
            Font font = new Font(display, fontData);
            try {
                testFont(display, font);
            } finally {
                font.dispose();
            }
        }
    }

    public static void main(String[] args) {
        Display display = new Display();
        try {
            walkFonts(display);
        } finally {
            display.dispose();
        }
    }

}

This probably isn't a good approach as it may leave you exposed to locale issues. Besides, you don't know if the first monospaced font you come across isn't some windings icon set.

The best approach may just be to take your best guess based on a font/locale mapping whitelist and make sure that the users can easily reconfigure the UI to suit themselves via FontDialog.

Solution 4

For people who have the same problem, you can download any font ttf file, put it in the resource folder (in my case /font/**.ttf) and add this methode to your application. It's work 100 %.

public Font loadDigitalFont(int policeSize) {
    URL fontFile = YouClassName.class
            .getResource("/fonts/DS-DIGI.TTF");
    boolean isLoaded = Display.getCurrent().loadFont(fontFile.getPath());
    if (isLoaded) {
        FontData[] fd = Display.getCurrent().getFontList(null, true);
        FontData fontdata = null;
        for (int i = 0; i < fd.length; i++) {
            if (fd[i].getName().equals("DS-Digital")) {
                fontdata = fd[i];
                break;
            }}
        if (fontdata != null) {
            fontdata.setHeight(policeSize);
            fontdata.setStyle(SWT.BOLD);return new Font(getDisplay(), fontdata));}
    }return null;   }
Share:
12,022
JeeBee
Author by

JeeBee

Updated on July 06, 2022

Comments

  • JeeBee
    JeeBee almost 2 years

    Is there a way in SWT to get a monospaced font simply, that works across various operating systems?

    For example. this works on Linux, but not Windows:

    
    Font mono = new Font(parent.getDisplay(), "Mono", 10, SWT.NONE);
    
    

    or do I need to have a method that tries loading varying fonts (Consolas, Terminal, Monaco, Mono) until one isn't null? Alternatively I could specify it in a properties file on startup.

    I tried getting the system font from Display, but that wasn't monospaced.