The Basics

Every ScriptUI element (such as Window, Button, Group, Checkbox, StaticText, Listbox…) exposes a graphics property that in turn exposes a font property. The graphics property is documented as a ScriptUIGraphics object which “can be used to customize the element's appearance, in response to the onDraw() event.” There is much to say on this statement—in particular the onDraw method neither is an event, nor an actual event handler—but for now I want to focus on the font side. Given a ScriptUIGraphics object, its font property is described as a ScriptUIFont object which represents the “default font to use for displaying text.”

The first thing to notice is that the ScriptUIFont object is in no way an interface to actually interact with fonts. Rather, it is a very basic object that simply stores a set of read-only properties:
family: (String) The font family name.
name: (String) The complete font name, “consisting of the family and style, if specified.” In fact, according to my tests, the two above properties contain the same string!
size: (Number) The font size, in points. Floating-point numbers are supported but the size may be rounded to the nearest integer on rendering.
style: (String) The font style, if specified (or the empty string). One of: "Regular", "Bold", "Italic", "BoldItalic".
substitute: (String) “The name of a substitution font, a fallback font to substitute for this font if the requested font family or style is not available.” Although, I never found that property set to any non-empty value.

About ScriptUI.newFont()

Interestingly, the ScriptUIFont class does not even offer a regular constructor. The official way to instantiate such object is to use the generic ScriptUI.newFont() method, which normally expects three arguments: the font name, the style, and/or the size. If you supply a single argument, it is assumed to be the font name, then the style and size properties are set to default values. If you provide two arguments, the second parameter is regarded as the size! Finally, you must use the (name, style, size) order to specify a full structure, where style is a String or a predefined alias (integer) found in ScriptUI.FontStyle. That static enumerator just gathers a set of constant key-int pairs—{REGULAR: 0, BOLD: 1, ITALIC: 2, BOLDITALIC: 3}—anyway there is no point in using that structure since ScriptUI.newFont(...) supports, case-insensitively, the related strings: "Regular", "Bold", "Italic", "BoldItalic".

Note that you can invoke ScriptUI.newFont() anywhere in your code, even with no ScriptUI element visible or created. The ScriptUIFont object provides a user-friendly toString() method that brings together the intrinsic properties of the object, formatted as follows: <NAME>[-<STYLE>]:<SIZE>.

Here are some examples:

// The 'official' newFont(...) syntax
// ==================
 
// ---
// Due to alerts ScriptUIFont.toString()
// is implicitly invoked below:
// ---
 
alert( ScriptUI.newFont("Arial") );
// => "Arial:10.0"     --so, 10 is the default size in my OS
 
alert( ScriptUI.newFont("Arial", 16) );
// => "Arial:16.0"
 
alert( ScriptUI.newFont("Arial", "BOLD", 14) );
// => Arial-Bold:14.0
 
alert( ScriptUI.newFont("Arial", 1, 14) );
// => Arial-Bold:14.0  --same result as above, 1 means Bold
 
alert( ScriptUI.newFont("Arial", 1) );
// => Arial:1.0        --hey! this time 1 is the size!
 

The nice thing is that you can also supply a full formatted string as a unique argument to ScriptUI.newFont():

// Cool shortcut:
var ft = ScriptUI.newFont( "Arial-BoldItalic:24" );
 
alert( ft.name );   // => "Arial"
alert( ft.style );  // => "BoldItalic"
alert( ft.size );   // => 24
 

Fonts Availability

As noted by Peter Kahrel in ScriptUI For Dummies: “For the font name you must use the font's PostScript name, which is not necessarily the same as the menu name used in InDesign's or PhotoShop's interface. If setting a font throws an error, chances are that that font's PostScript name is not the same as its menu name. […] To find a font's PostScript name, run this one-line script in the ESTK with the Console visible: app.fonts.item("Gill Sans").postscriptName;

In this regard, Mac OS and Windows deal very differently with missing fonts. The main issue is that Mac OS mutely allows an invalid font name to be loaded into the ScriptUIFont object (without throwing any error), whereas ScriptUI / Win instantly rejects the request:

// ==================
// Tested on Windows
// ==================
 
var ft = ScriptUI.newFont( "MyMissingFont-Regular:24" );
 
// Execution Error:
// Error Number: 510 -- Invalid font name 'MyMissingFont'
 

By contrast:

// ==================
// Tested on Mac OS
// ==================
 
var ft = ScriptUI.newFont( "MyMissingFont-Regular:24" ); // OK
 
alert( ft.name ); // => "MyMissingFont"  --all sounds OK!
 

So, when your code is running on a Win platform, you can easily detect ScriptUIFont errors through try...catch and provide gracefully degraded alternatives. On the contrary, checking whether the right typeface is actually applied to a Mac OS widget may be knotty, since the system noiselessly switches to a default font if a problem arises.

Note. — In fact, it is not correct that ScriptUI / Mac always ignores font errors. There are critical issues with corrupted or temporary unavailable fonts. In Mac OS Lion, for example, you can easily reach a OS Error: [-982] by playing with the graphics.font property of a ProgressBar control. Also, some Mac OS X platforms may throw a “Bad argument” error when one attempts to set a non existing or corrupted font wich the system is supposed to own, e.g. Monaco. This issue has been mainly reported on MacPro or iMac machines with OS X 10.5.x or 10.6.x installed. In such circumstances, a try...catch block is still needed to prevent obscure issues.

Regarding default typefaces, ScriptUI.applicationFonts collects a (tiny) set of predefined ScriptUIFont objects that fit the system preferences. The available keys usually are 'dialog', 'palette', and 'window'—which all appear to point out to the same structure, e.g. Tahoma:12.0 (Win) or Lucida Grande:14.0 (Mac). Each of those generic aliases can be supplied in every syntax that expects a ScriptUIFont name. In such case, the actual name is internally set:

alert( ScriptUI.newFont("dialog:12").name );
// => "Lucida Grande", or "Tahoma", etc.
//    depending on the OS
 

Using ScriptUI Fonts

Despite what we have just learned, it is not so common we explicitly instantiate a ScriptUIFont object in a script, since ScriptUI also allows to set the ScriptUIGraphics.font property using the corresponding formatted string:

var u,
    w = new Window('dialog'),
    myStatic = w.add('statictext', u, "Hello World!");
 
myStatic.graphics.font = "Arial-Bold:24";
// Rather than:        = ScriptUI.newFont("Arial-Bold:24");
w.show();
 

The above shortcut is extremely handful, although not documented! Apart from its brevity, it prevents the script engine from unnecessarily allocating space to a ScriptUIFont object that you will never use by itself. Indeed, $.summary() does not report any reference to a ScriptUIFont instance.

What if you need to change the font of a widget at execution time? Just affect a new formatted string to myWidget.graphics.font:

var u,
    w = new Window('dialog'),
    myStatic = w.add('statictext', u, "Hello World!"),
    b = w.add('button', u, "Change font");
 
myStatic.graphics.font = "Arial-Bold:24";
 
b.onClick = function(){ myStatic.graphics.font = "Verdana:16"; };
 
w.show();
 

Note. — Changing the font of a widget automatically causes a redraw (or calls myWidget.onDraw(), if defined) but does not invoke the layout() method of the window's layout object. If necessary, it's your responsibility to call w.layout.layout(1) in order to update respective widgets bounds.

The following snippet illustrates how we can easily implement a simple editor—based on a multiline EditText—that allows to dynamically increase/decrease the text size. Here again, all is done through string manipulation so that no explicit ScriptUIFont is involved:

var u,
    w = new Window('dialog'),
    myEdit = w.add('edittext', u, "Sample", {multiline:true});
 
w.add('button', u, "Increase size", {step:1});
w.add('button', u, "Decrease size", {step:-1});
 
myEdit.preferredSize = [300,200];
myEdit.graphics.font = "dialog:14";
 
w.addEventListener('click', function(/*MouseEvent*/ev)
{
    var ps = ev.target.properties,
        step = ps && ps.step,
        ft, sz;
 
    if( step )
        {
        ft = myEdit.graphics.font;
        sz = Math.max(1,ft.size + step);
 
        // Change font size
        // ---
        myEdit.graphics.font = ft.toString().
                replace(/:[\d.]+$/, ':'+sz);
        }
 
    ps = ft = null;
});
 
w.show();
 

Ultimately, there are only two situations where you have to handle explicit ScriptUIFont instances:

1) When you invoke myWidget.graphics.drawString(text, pen, x, y, font) with a font argument which is not the current font applied to myWidget.graphics.

2) When you invoke myWidget.graphics.measureString(text, font, boundingWidth) with a font argument which is not the current font applied to myWidget.graphics.

Indeed, those methods (exposed by any ScriptUIGraphics object) both require a true ScriptUIFont argument, if supplied. I can hardly imagine practical cases when you need to invoke drawString() with different fonts within a single widget. Anyway, just remember this is possible.

How to Select a Valid Monospaced Font

Since ScriptUI / Mac usually does not protest against missing fonts, a widget can mistakenly claim it uses e.g. a Menlo typeface while it is actually rendering in Lucida Grande (assumed that Menlo is unavailable). Substitute fonts can become very annoying if your UI requires a fixed-width text at some location. Luckily, monospaced fonts are subject to a condition you can address through ScriptUIGraphics.measureString(): all characters share the same width! Therefore, comparing the measured width of two very distinct characters, such as 'M' and the vertical line (U+007C), allows to control whether the loaded font is OK.

This trick allows to validate and automatically select, from a list of common candidates, the first fixed-width font available in any Mac OS platform:

var findMonoFont = function F()
// ------------------------------------
{
    F.candidates ||
        (F.candidates = ["Courier New","Monaco","Vera Mono","Andale Mono","Menlo"]);
 
    var a = F.candidates,
        i = a.length,
        r = '',
        w = new Window('dialog'),
        gx = w.graphics;
 
    while( i-- )
        {
        gx.font = (r=a[i])+':24';
        if( gx.measureString('|')[0]===gx.measureString('M')[0] )
            {
            break;
            }
        }
 
    a = w = gx = null;
    return i >=0 ? r : '';
};
 

Of course the above code will not work on Win platforms, but it would be useless since you can address font availability through try...catch:

// . . .
try{ gx.font = "MissingFont:12" }
catch(_){/*the font is missing*/}
// . . .
 

Have fun!