Introduction to named fonts

If you've ever written a commercial app, or any app that you share with others, you've probably been asked "how can I change the fonts?" Maybe you have a finely crafted set of fonts custom tailored to your design or maybe you rely on the defaults provided by tk, but no matter what you provide some user will want to change it.

One of the more unique features of Tk is support for something called named fonts, part of the standard tk font command [1]. This article briefly describes how and why you should use named fonts. In addition, example code is given for adding the ability to dynamically change the fonts at runtime with a simple binding.

What is a named font?

What is a named font? A named font can be thought of as an alias for a complete font description. You create a named font with the font create command. Once created, you can use the named font anywhere you would normally use a standard font specification. That's all there is to it.

The following example illustrates how to create and use a named font:

font create customFont -family Helvetica -size 14 -slant italic
label .label -text "A label with a named font" -font customFont
pack .label

Changing the font at runtime

The advantage to using named fonts is that when the font is changed, all widgets that use the font automatically get updated. To see the effect, start an intereactive Wish shell and enter the code from the above examples.

The main window should look something like this:

image of two labels

Changing the font is as simple as calling the font configure command, giving it the name of the font and any new attributes:

font configure customFont -slant roman -weight bold

If you run the above command in your interactive shell the window should now look like this:

image of label after configuring the font

Growing and shrinking fonts

Once you start using named fonts it becomes easy to provide a mechanism to let the user grow or shrink the fonts. For example, you might want to bind Control-plus to increase the font and Control-minus to decrease it. Or, you may want a menubar View menu item for "Bigger Fonts" and "Smaller Fonts". The following example shows one way to accomplish that task:

proc bump_fonts {incr} {
  set size [font configure customFont -size]
  catch {
    incr size $incr
    # keep font sizes sane...
    if {$size >== 8 && $size <== 32} {
      font configure customFont -size $size
    }
  }
}
# Control-plus actually requires the user to press Control-Shift-equals;
# using Control-equal is a convenience to make it a bit easier on the user
bind all <Control-plus> [list bump_fonts +2]
bind all <Control-equal> [list bump_fonts +2]
bind all <Control-minus> [list bump_fonts -2]

Of course, you can have multiple named fonts within an application and bump_fonts could iterate over each font to change them all at the same time. For example, my apps often have several fonts such as normalFont, boldFont, italicFont, etc.

Using a named font as the default font

You can use named fonts by supplying the name of the font for every widget that has a font. This can be tedious. An arguably better solution is to use the built-in option database [2] features of Tk. The following example shows how to use a named font for all widgets:

option add *font customFont

Putting it all together

The following example creates an application with a single text widget and a menubar, and uses three different named fonts:

proc main {} {
  font create defaultFont -family Helvetica -size 12
  font create boldFont -family Helvetica -size 12 -weight bold
  font create italicFont -family Helvetica -size 12 -slant italic

  option add *font defaultFont

  menu .mb
  menu .mb.file
  menu .mb.view
  .mb.file add command -label "Exit" -command exit
  .mb.view add command -label "Bigger Fonts" \
      -accelerator "Control-plus" -command [list bump_fonts +2]
  .mb.view add command -label "Smaller Fonts" \
      -accelerator "Control-minus" -command [list bump_fonts -2]
  .mb add cascade -label File -menu .mb.file
  .mb add cascade -label View -menu .mb.view
  . configure -menu .mb

  text .t -wrap word -width 20 -height 5
  pack .t
  .t tag configure normal -font defaultFont
  .t tag configure bold -font boldFont
  .t tag configure italics -font italicFont

  bind all <Control-plus>  [list bump_fonts +2]
  bind all <Control-equal> [list bump_fonts +2]
  bind all <Control-minus> [list bump_fonts -2]

  .t insert end "Normal\n" "normal" "Bold\n" bold "Italics\n" italics
}

proc bump_fonts {incr} {
  foreach font {defaultFont boldFont italicFont} {
    catch {
      set size [font configure $font -size]
      incr size $incr
      if {$size >= 8 && $size <= 32} {
        font configure $font -size $size
      }
    }
  }
}

main

Limitations

One limitation of named fonts is that you can't directly copy a font. You can use the output of tk's font actual command to get the attributes of a font and assign them to another font. Doing so, however, doesn't yield perfect results. For example, most unix systems have a bitmapped font alias named "6x13". As of tk 8.5 there doesn't seem to be any way to create a named font that is exactly that font. For example, with the following code it seems like the two labels should be identical but they are not:

eval font create myFont [font actual "6x13"]
label .l1 -text "ABCabc123 this is 6x13" -font 6x13 -anchor w
label .l2 -text "ABCabc123 this is myFont" -font myFont -anchor w
pack .l1 .l2 -side top -fill x

If you try this on a non-X11 system the widgets may look the same; on most flavors of unix they will look slightly different:

Summary

As long as you don't need to use one of the X11 bitmapped fonts, named fonts provide a lot of flexibility. By creating a handful of named fonts at startup it becomes easy to build in the ability for the user to reconfigure fonts on the fly without having to manually re-apply font definitions to every widget. In addition, if you need to make font adjustments for specific platforms you can do so all in one place without having to track down dozens of font references in your code.

Get in the habit of always using named fonts and you'll have fewer font related problems as your code matures.


References

  1. Font man page, http://www.tcl.tk/man/tcl8.4/TkCmd/font.htm
  2. Option man page, http://www.tcl.tk/man/tcl8.4/TkCmd/option.htm

Further Reading