motifdeveloper.com
Sponsored by IST Limited
Formerly MW3: Motif on the World Wide Web [MW3 Logo]
Last Updated
November 18, 2002
 


X-Designer - The Leading X/Motif GUI Builder - Click to download a FREE evaluation
 

motifdeveloper.com
Home
About the site
Bulletin Board
News Archive
OpenGroup News
Events
Search
Frequently Asked Questions
The Motif FAQ
X/Motif FAQs
General FAQs
Ask Antony
Latest Q & A
All Q & As
Submit Question
Tips & Pointers
Code Examples
Software
OpenMotif
Widget Sets
GUI Toolkits & Libraries
Motif suppliers
Non-commercial S/W
Commercial Software
Multimedia
Miscellaneous
Organizations
Docs & Pubs
X/Motif Newsgroups
Security
Internationalization
Feedback
Feedback Form
Contributors
 

How are missing fonts handled when rendering Compound Strings?

1-Feb-01 16:00 GMT

Introduction

This paper describes some subtle differences between the Motif 1.2 and 2.1 toolkits with respect to the way in which compound strings are rendered. In particular, it looks at the logical way in which compound strings are drawn using the current font information (associated with the widget where the compound string is applied), and how the toolkit handles the situation where a compound string requires a particular font, but that font is not forthcoming.

This can arise for two reasons:

  • the required font is simply not available in the current operating environment
  • the fonts associated with the widget where the string is to be drawn have not been correctly specified

In the first case, either the font is simply absent from the machine, or the user's environment has not been correctly set up to find the required information. This might be a simple case of poorly specified font paths for the font server, which can be patched through an appropriate call to the xset command.

In the second case, the font may well be fully resident, locatable, and loadable on the host, but either the font resources associated with a widget are incorrect, or the compound string has been poorly constructed. That is, the font exists on the system, a compound string refers to the font (rightly or wrongly), but the current XmFontList or XmRenderTable does not list the font amongst its entries (rightly or wrongly).

The question here is not about how and why this situation comes about, but what the toolkit does about it, and what we may or are required to do to defend ourselves against the possible malfunction of our programs.

The Motif 1.2 Model

In Motif 1.2, compound strings are drawn using font information supplied in an XmFontList. The XmFontList we will define simply as an ordered list of X Fonts (or X Font Sets), where each item in the list can be assigned a logical name, or tag. Compound strings in turn consist of an ordered grouping of segments. There are various kinds of segment - textual data, direction indicators, logical separators, and so forth, which specify not only the contents of the string, but also the way in which it is laid out or rendered. Most importantly for this discussion, compound string segments can also be tagged - segments of the string can be associated with particular named elements in the current XmFontList.

The way in which compound strings are drawn in Motif 1.2 is as follows: unless specified otherwise, compound string segments are implicitly drawn using the default font in the current XmFontList. The default font does in fact have a tag - XmFONTLIST_DEFAULT_TAG - so that we could also explicitly associate a compound string segment with the default font by associating the segment with the tag XmFONTLIST_DEFAULT_TAG. The following standard code does precisely this:

    XmString xms = XmStringCreate ("hello world", XmFONTLIST_DEFAULT_TAG)

Suppose, however, we associate another font tag with our compound string segment. What happens now? The algorithm in this case is straightforward: the XmFontList is searched from the start until a matching tag entry is found. The first match is used. But what happens if:

  • a compound string segment is associated with XmFONTLIST_DEFAULT_TAG, and all of the fonts in the current XmFontList are tagged otherwise, or more generally
  • a compound string segment is associated with font tag F, but there is no such tagged font in this XmFontList

The Motif 1.2 solution to each of these issues is the same. In the absence of an XmFontList entry which is tagged XmFONTLIST_DEFAULT_TAG, the compound string segment is simply rendered using the first font entry in the current XmFontList, irrespective of its tag. This means that in Motif 1.2, compound string segments are always drawn, even if they are drawn in a completely inappropriate font for the task to hand. There is no point of interception available in the toolkit if it does decide to draw using the first font regardlessly: the program is not notified of the mismatch, and it is the user who sees the problem first.

You know that a particular XmFontList entry refers to a loadable font, because you would not be able to create the entry in the first place if the font was otherwise: you have to supply XFontStruct and XFontSet parameters to the routines which create the XmFontList, so you have already succeeded in loading the font.

The Motif 2.1 Model

In Motif 2.1, the XmFontList is obsolete, and has been replaced with the XmRenderTable. An XmRenderTable we define here as an ordered set of XmRendition objects. A more detailed description of render table and rendition objects, together with the routines which create and manipulate them, can be found in the companion article, What are Render Tables and how are they used? The XmRendition object contains as a resource an X Font or an X Font Set, amongst other things; Motif 2.1 is still using the same low-level X font mechanisms underneath - it is simply wrapped up in another way. Importantly, XmRendition objects also have a tag attribute. More importantly, the X font attribute of an XmRendition object can be marked as load-deferred. That is, unlike adding an entry to an XmFontList, an XmRendition object can be added to a render table even though it may refer to an unloadable font. Further, rendition objects can inherit attributes from objects earlier in the render table; adding rendition objects in the wrong order can also result in mis-applying the wrong font when rendering a given compound string segment. This we will treat as a pure programming error, and not attempt to devise any scheme to intercept the problem. It will only overlap the schemes presented below if we fail to inherit any font at all.

In Motif 2.1, the way in which compound strings are rendered using entries in a render table is very similar to the Motif 1.2 scheme, except that this time, the search algorithm is not matching against tagged entries in an XmFontList, but against tag attributes of XmRendition objects found within the current XmRenderTable.

There is, however, one minor difference. Whereas in Motif 1.2, compound strings are by default rendered using the first font entry in an XmFontList when the tag matching algorithm fails, in Motif 2.1 the toolkit caches a default rendition object, using the tag XmSTRING_DEFAULT_CHARSET. This rendition object itself may or may not refer to an X font which is loadable. This implies that you would not necessarily know of a problem until the rendition is actually required at some point down the line, precisely because the font to which it refers might be load-deferred.

We can create a rendition object with the special tag _MOTIF_DEFAULT_LOCALE, and this will match against any compound string segment which refers to a font tagged XmFONTLIST_DEFAULT_TAG. However, this special rendition is not used as an absolute fallback: that is provided by the cached rendition that has the tag XmSTRING_DEFAULT_CHARSET. A side effect is the fact that the emergency fallback font in any given situation can in principle be entirely different in the two versions of the toolkit even if the operating system has precisely the same fonts available.

The point is this (and admittedly the environment would have to be particularly hostile): in Motif 2.1, because of the load-deferred attribute, it is technically possible that a compound string segment may simply not be rendered at all.

  • In Motif 1.2, we know that we can inherit font information from the widget hierarchy; if an XmFontList is NULL on some widget, we inherit from defaults which can be set for a BulletinBoard or VendorShell. If we do inherit, we know the XmFontList refers to loadable fonts.
  • In Motif 2.1, we can also inherit an XmRenderTable, but there is nothing preventing that render table from referring to unloadable fonts; furthermore, if a widget has a specific render table which refers to unloadable fonts, this would prevent us inheriting from an ancestor which may have perfectly good rendition information.

That is, in Motif 1.2 we can only inherit good font data; in Motif 2.1 we can inherit rendition data which is unusable.

Before we decide that the Motif 2.1 model is broken, you should consider why the change has come about. In Motif 1.2 you have to pre-load all fonts in a font list, irrespective of whether any font entry is actually referenced by any compound string segment. This is potentially extremely expensive in terms of resource usage. In Motif 2.1, the load deferral guarrantees that you can arrange to only use a font if and when it is required. For internationalized applications which require many fonts to satisfy the current locale, this can potentially result in a significant reduction in overhead if as it turns out some font in the set is not actually required.

This brings us to the second change to the system in Motif 2.1. Whereas in Motif 1.2 problems with fonts are generally discovered by the user first, in Motif 2.1 the programmer is now notified by new callbacks, and she is responsible for rectifying the problem by supplying new rendition data if and as required.

If the programmer fails to provide any such callbacks, whether the compound string is rendered will depend on whether the cached default XmSTRING_DEFAULT_CHARSET rendition refers to a loadable font. This is where the programmer has to modify the implementation of his program between Motif 1.2 and Motif 2.1, because although the obsolete XmFontList remains forwards compatible, being internally re-implemented in terms of a skeleton render table, the render table is in this aspect not quite fully backwards compatible in behavior; the mechanisms whereby the toolkit handles the default font for rendering a compound string, the timing whereby it actually loads the fonts required for the rendition process, and whereby it notifies the programmer of a problem with the rendition data to hand, are entirely different.

The Motif 2.1 callbacks which provide this programmatic feedback are the XmNnoFontCallback, and the XmNnoRenditionCallback, both of which are implemented into the XmDisplay object.

The XmNnoFontCallback

The XmNnoFontCallback is invoked by the toolkit when an attempt is made to render a compound string using some rendition, and that rendition has as an attribute a font which is discovered to be not loadable (either at at the actual point of rendition for a load-deferred rendition, or on rendition create for a load-immediate object). For example, if we were to construct a rendition object as follows, and attempt to draw a compound string using the rendition, we can expect any XmNnoFontCallback to be invoked:

    extern Widget widget;       /* An arbitrary widget      */

    XmRendition   rendition;    /* The new rendition object */
    Arg           args[8];      /* New rendition resources  */
    Cardinal      n = 0;        /* Count of new resources   */

    ...

    /* Specify a name for a font which does not exist         */
    /* We delay problem detection to some point in the future */
    XtSetArg (args[n], XmNfontName, "CompletelyBogusFontName"); n++;
    XtSetArg (args[n], XmNfontType,  XmFONT_IS_FONT); n++;
    XtSetArg (args[n], XmNloadModel, XmLOAD_DEFERRED); n++;

    /* Create the rendition */
    rendition = XmRenditionCreate (widget, "some-tag", args, n) ;

This is the difference: creating a Motif 2.1 rendition object can succeed irrespective of whether the referenced font is good or otherwise; creating a Motif 1.2 fontlist entry can only be achieved with a good font.

Note that the XmNnoFontCallback indicates a flaw within a rendition object attributes. For the XmNnoFontCallback to be invoked here, the rendition object itself has passed the matching test: it has a tag corresponding to that referenced within some compound string segment.

The XmNnoRenditionCallback

Conversely, if we attempt to render a compound string, and that string refers to some font (rendition) tag, and the current render table has no such rendition with a matching tag, then the XmNnoRenditionCallback is invoked by the toolkit. In other words, this is a referencing error rather than a font error per se. For example, if we construct a compound string and render table as follows, we would expect the XmNnoRenditionCallback to be invoked, because the compound string refers to a rendition tag which is entirely absent from the render table.

    extern Widget label;

    XmRendition   rendition;    /* The new rendition object */
    XmRenderTable render_table; /* A Render Table           */
    Arg           args[8];      /* New rendition resources  */
    Cardinal      n = 0;        /* Count of new resources   */
    XmString      xms ;         /* A Compound String        */

    ...

    /* Create a compound string which refers to some tag */
    xms = XmStringCreate ("hello world", "some-tag");

    /* Specify a font which does exist. Load model does not matter */
    XtSetArg (args[n], XmNfontName, "fixed"); n++;
    XtSetArg (args[n], XmNfontType, XmFONT_IS_FONT); n++;

    /* Create the rendition, but with a non-referenced tag */
    rendition = XmRenditionCreate (label, "not-referenced", args, n) ;

    /* Merge into a Render Table */
    render_table = XmRenderTableAddRenditions (NULL, &rendition, 1, XmMERGE_NEW);

    /* Throw the compound string and the render table at some widget */
    XtVaSetValues (label, XmNrenderTable, render_table, XmNlabelString, xms, NULL);

The Motif 2.1 XmDisplayCallbackStruct

The XmDisplayCallbackStruct is passed to XmNnoFontCallback and XmNnoRenditionCallback functions, and is defined as follows:

    typedef struct
    {
	    int             reason;
	    XEvent         *event;
	    XmRendition     rendition;
	    char           *font_name;
	    XmRenderTable   render_table;
	    XmStringTag     tag;
    } XmDisplayCallbackStruct;

Missing Fonts

For an XmNnoFontCallback, the reason element will have the value XmCR_NO_FONT. The callback is invoked with the font_name element filled in to the name of the font which could not be loaded by the system, and the rendition element will be the rendition object which refers to this font. All other callback elements are irrelevant. The programmers task is as follows:

  • Consider the problematic font, as specified by the font_name element
  • Deduce or otherwise guess a new font specification. This could be some complex algorithm which attempts to find a close match to the original, or a simple fixed font solution.
  • Update the rendition element using XmRenditionUpdate() - see Updating Rendition Resources in What are Render Tables and how are they used?

For example, a simple fixed-font solution is as follows:

    void no_font_callback (Widget w, XtPointer client_data, XtPointer call_data)
    {
	XmDisplayCallbackStruct *dp = (XmDisplayCallbackStruct *) call_data;

	if (dp->reason == XmCR_NO_FONT) {
	    Arg      args[3];
	    Cardinal n = 0;

	    XtSetArg (args[n], XmNfontName,  "fixed"); n++;
	    XtSetArg (args[n], XmNfontType,  XmFONT_IS_FONT); n++;
	    XtSetArg (args[n], XmNloadModel, XmLOAD_IMMEDIATE); n++;

	    XmRenditionUpdate(dp->rendition, args, n);
	}
    }

Missing Renditions

For an XmNnoRenditionCallback, the reason element will have the value XmCR_NO_RENDITION. The callback is invoked with the render_table element set to the render table with the missing rendition data, and the tag element contains the name of the tag for which rendition data is required. Again, other callback elements are irrelevant to this case. The programmers task is as follows:

  • Create a new rendition object with the requisite tag. What the rendition object has in the way of attributes might be deducible from the tag element, or it may not. Again, a simple solution would be to create a rendition object which simply has a fixed font attribute.
  • Merge the new rendition object into the render_table through XmRenderTableAddRenditions() - see Adding Renditions to a Render Table in What are Render Tables and how are they used?
  • Dereference the original render_table element using XmRenderTableFree() - see Freeing a Render Table in What are Render Tables and how are they used? The callback data supplies a copy of the render table taken from the source of the problem.

For example, the following is a specimen fixed-rendition solution:

    void no_rendition_callback (Widget w, XtPointer client_data, XtPointer call_data)
    {
	XmDisplayCallbackStruct *dp = (XmDisplayCallbackStruct *) call_data;

	if (dp->reason == XmCR_NO_RENDITION) {
	    Arg               args[3];
	    Cardinal      n = 0;
	    XmRendition   rendition ;
	    XmRenderTable new_table ;

	    /* Create a simple new rendition object        */
	    /* Using the tag supplied in the callback data */

	    XtSetArg (args[n], XmNfontName,  "fixed"); n++;
	    XtSetArg (args[n], XmNfontType,  XmFONT_IS_FONT); n++;
	    XtSetArg (args[n], XmNloadModel, XmLOAD_IMMEDIATE); n++;

	    rendition = XmRenditionCreate (w, dp->tag, args, n);

	    /* Add the rendition object into the render table */
	    /* Supplied in the callback data                  */
	    new_table = XmRenderTableAddRenditions (dp->render_table,
				       &rendition, 1, XmMERGE_NEW) ;

	    /* Dereference the original table, passed as a copy */
	    XmRenderTableFree (dp->render_table) ;

	    /* Reset the render table element to the new table */
	    dp->render_table = new_table ;
	}
    }

We can add these callbacks into our programs using the following kind of code: we use XmGetXmDisplay() to deduce the XmDisplay object, and thereafter use XtAddCallback() to apply the callback resources.

    extern Display *display ;

    Widget xmdisplay = XmGetXmDisplay (display) ;

    XtAddCallback (xmdisplay, XmNnoFontCallback,      no_font_callback, NULL) ;
    XtAddCallback (xmdisplay, XmNnoRenditionCallback, no_rendition_callback, NULL) ;

 


Sponsored by X-Designer - The Leading X/Motif GUI Builder - Click to download a FREE evaluation

 

Goto top of page

 

[IST Limited]
IST Limited is the
proud sponsor of motifdeveloper.com.
 

Thanks to all the contributors to these pages.

Privacy Policy

 
"Motif" is a registered trademark of The Open Group. All other trademarks are the property of their respective owners.

Some articles/papers here have their own copyright notice. All other information is copyright ©1999-2008 IST Limited

[CommerceOne] MW3 site originally by Ken Sall of Commerce One (formerly Century Computing).