站内搜索: 请输入搜索关键词
当前页面: 图书首页 > SWT: The Standard Widget Toolkit

18.2 Class Font - SWT: The Standard Widget Toolkit

Previous Section  < Day Day Up >  Next Section

18.2 Class Font

Fonts are device specific objects. Because they hold onto operating system memory, you must follow the standard life cycle rules for SWT when working with them: If you create the Font, you must call dispose() when you are done with it.

Font instances can be created using one of the following constructors.

Font(Device device, FontData fd) Constructs an instance of Font for use on the given device, based on the description given by the FontData parameter. The font that is created will be the best match for the description but can be significantly different if, for example, there is no font on the system whose name matches the name given in the FontData.

Font(Device display, String name, int height, int style) This is equivalent to Font(device, new FontData(name, height, style)).

Instances of class GC provide accessors for setting and getting the font. Note that you can pass in an instance of Font that was created only on the same device as the one that is associated with the GC.

setFont(Font font) Causes the given font to be used for all text operations on the GC. If the parameter is null, the default font for the GC is used.

getFont() Gets the font that is being used by the GC. If a font had not previously been explicitly set on the GC, the result will be the font associated with the Drawable on which the GC was created. For widgets, this will be Widget's current font; for devices, it will be the system font for the device.

The system font for a device can be queried using this method.

getSystemFont() Returns the font that the device will use to draw text if some other font has not been explicitly set.

Here is an example that shows the correlation between the default font in GC on a device and the system font for the device.






public class GetSystemFont {

public static void main(String[] args) {

    Display display = new Display();

    System.out.println("System Font is:");

    printFontData(display.getSystemFont().getFontData());

    System.out.println("Font in GC on Display is:");

    GC gc = new GC(display);

    printFontData(gc.getFont().getFontData());

    gc.dispose();

    display.dispose();

}

private static void printFontData(FontData[] fds) {

    String style;

    for (int i=0; i<fds.length; ++i) {

        FontData fd = fds[i];

        switch (fd.getStyle()) {

            case SWT.NORMAL: style = "NORMAL"; break;

            case SWT.BOLD: style = "BOLD"; break;

            case SWT.ITALIC: style = "ITALIC"; break;

            case (SWT.BOLD|SWT.ITALIC):

                style = "BOLD|ITALIC"; break;

            default:

                style = "STYLE("+fd.getStyle()+")";

        }

        System.out.println(

            "    FontData[" + i + "] " + fd.getName() +

            ", " + fd.getHeight() +

            ", " + style);

    }

}}


Running this example produced the following output on a Macintosh OS X machine.






System Font is:

    FontData[0] Geneva, 13, NORMAL

Font in GC on Display is:

    FontData[0] Geneva, 13, NORMAL


Notice that in this example, printFontData() is coded to take an array of FontData instances. This is required because of the way the getFontData() method on class Font is specified:

getFontData() Returns an array of FontData instances that provide an exact description of the font.

If you are wondering why an array of FontData instances could be required to represent the font, read on.

18.2.1 Using More Than One Instance of FontData

There is a third form of the Font constructor, which takes as a parameter an array of FontData objects.

Font(Device device, FontData[ ] fds) Constructs a Font for use on the given device, based on the given array of FontData instances.

This constructor was created because some platforms allow text to be drawn using a collection of fonts, rather than a single font. The only current example of this is X/Motif, where the equivalent operating system structure is called a FontSet.

The value of supporting a collection of fonts in graphics operations is that it allows the system to take several fonts that separately only partially cover the range of required glyphs (as was noted in Character Encoding and Locale) and effectively merge them together to create one union font[6] that contains all needed glyphs.

[6] This is not a term used in SWT but is useful for the purposes of this discussion. From the perspective of an SWT program, a union font is simply another font.

Here is an example of how that could be used: Assume that a given system had several fonts, for example, Times, Courier, etc., that provide only the glyphs specified by the ISO-8859-1 "Latin-1" character encoding, which is an 8-bit encoding commonly used by many North American and European countries. If this system also had another font that had glyphs for all the Unicode characters and the user asked for the Times font, the system could return a union font that contained first the Times font, followed by the standard Unicode font. Given this, any characters with glyphs in the Times font could be drawn in Times; any other characters would be drawn by the Unicode font. By doing this kind of font substitution, it is possible to draw text in multiple fonts without requiring each one to contain all possible Unicode characters.

Current X/Motif implementations use this mechanism extensively, sometimes creating union fonts made from five or more other fonts. To allow applications built with SWT the greatest degree of control over the font substitution process, the SWT API was extended to allow arrays of FontData objects to be used in several places where previously only single FontData instances were allowed. Although adding this support was important for us to be able to provide real national language support on Motif, it has two significant drawbacks for the developer.

  1. Picking the right fonts is difficult.

  2. Applications need to use different API to deal with arrays of FontData instances.

The first problem arises from the fact that only the platform has enough information to decide which glyphs are present in a particular font and how these fonts should be composed to create the union font. It is possible for a smart developer to duplicate this process, given a deep knowledge of the fonts on a particular system, but there are no guarantees that the mapping will be constant across platforms or even across other occurrences of the same platform. The situation is not as bad as it sounds, however. Because the default font used by widgets and drawables is chosen by the platform, if you do not change the font, it will be a union font if that is the platform default. In addition, because the FontDialog class uses the platform dialog for choosing fonts, it can return union fonts.

The way that FontDialog communicates the union font to the application leads us to the second problem. As we have already shown, SWT uses an array of FontData instances to represent a union font, but the FontDialog class is specified to return a single instance of FontData as its result. To get around this, after the dialog has exited, you can get the full list of FontData instances using the following FontDialog method.

getFontList() Answers the array of FontData instances that represents the last font chosen by the user or null if no font was chosen.

Similarly, to set the initial font to be displayed by FontDialog, you can use a setter method that takes an array.

setFontList(FontData[ ] fds) Sets the font that FontDialog will display based on an array of FontData instances.

The return value for the dialog is just the first instance of FontData that would have been returned by the getFontList() method. If you use this instance of FontData to create a new font, SWT will attempt to produce a reasonable union font for you, but it is not guaranteed to be the same as the one returned by the dialog.

Here is a new version of the chooseFont() method example from the Remembering Font Choices section that has been rewritten to use arrays of FontData instances.[7] Notice that an array of FontData instances is read from or written to the file by storing the descriptions one per line, and getFontList() and setFontList() are used to access the dialog.

[7] In the source code that is provided with this book, this chooseFont() function is part of the ShowFont.java example in package part2. ShowFont allows the user to choose a font and displays a line of text using it.






public static FontData[] chooseFont(Shell parent) {

    FontData fd[];

    FontDialog dialog = new FontDialog(parent);

    final String fileName = "the-font";



    // Get FontData strings from file if it exists

    ArrayList inputFDs = new ArrayList(10);

    try {

        BufferedReader reader =

            new BufferedReader(

                new FileReader(fileName));

        String line = reader.readLine();

        while (line != null) {

            inputFDs.add(new FontData(line));

            line = reader.readLine();

        }

        reader.close();

    } catch (IOException e) { }

    if (inputFDs.size() > 0) {

        fd = new FontData[inputFDs.size()];

        inputFDs.toArray(fd);

        dialog.setFontList(fd);

    }



    dialog.open();

    fd = dialog.getFontList();



    // If a font was chosen, save the FontData strings.

    if (fd != null) {

        try {

            BufferedWriter writer =

                new BufferedWriter(

                    new FileWriter(fileName));

            for (int i=0; i < fd.length; ++i) {

                writer.write(fd[i].toString());

                writer.newLine();

            }

            writer.close();

        } catch (IOException e1) { }

    }



    return fd;

}


Note that the original version of this function will work on all platforms. The only interesting difference between them is that on X/Motif, this version of the function preserves the exact font choice made by the user, whereas the original version will manufacture a font based on a single instance of FontData.

18.2.2 Rules for Writing Platform-Independent Font Code

What then have we learned from this rather lengthy discussion of Font and FontData? To be maximally portable across all platforms, your application should always assume that more than one instance of FontData is required to represent a font. It should also avoid setting fonts in widgets to "hand constructed" fonts if possible, either by simply not setting the font in the widget and allowing the platform to provide a default value or by using FontDialog (and the getFontList() method) to choose the font.

If you are writing your own custom widget and you want to pick a font that is appropriate for the platform, a good way to do this is to create an instance of a similar existing widget, then query its font. For example, if you were building a new kind of text widget, you could get the default font from an instance of a text widget.






Text text = new Text(shell, SWT.MULTI);

Font textFont = text.getFont();

text.dispose();


This way, your widget would use the same font as the native text widgets by default, regardless of the platform.

    Previous Section  < Day Day Up >  Next Section