Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

8    Text

Palm OS® Programmer's Companion

Volume I

     

This chapter describes how to work with text in the user interface—whether it's text the user has entered or text that your application has created to display on the screen. When you work with text, you must take special care to do so in a way that makes your application easily localizable. This chapter describes how to write code that manipulates characters and strings in such a way that it works properly for any language that is supported by Palm OS®. It covers:

When you work with text, you work mainly with the Text Manager and the String Manager. Text Manager support begins in Palm OS 3.1. If you want to support releases earlier than Palm OS 3.1, use the PalmOSGlue library described in "Backward Compatibility with PalmOSGlue." This chapter notes all functions that have a glue equivalent in parentheses and shows code examples using the PalmOSGlue equivalents.


IMPORTANT: Palm OS version 3.1 introduced some changes to the Latin character encoding and to some of the String Manager functions. If you are updating a legacy application written before the release of Palm OS 3.1, these changes may affect your application. See "3.1 New Feature Set" of the Palm OS Programmer's API Reference for details about these changes.

Text Manager and International Manager ^TOP^

The Palm OS provides two managers that help you work with localizable strings and characters. These managers are called the Text Manager and the International Manager.

Computers represent the characters in an alphabet with a numeric code. The set of numeric codes for a given alphabet is called a character encoding. Of course, a character encoding contains more than codes for the letters of an alphabet. It also encodes punctuation, numbers, control characters, and any other characters deemed necessary. The set of characters that a character encoding represents is called, appropriately enough, a character set.

Different languages use different alphabets. Most European languages use the Latin alphabet. The Latin alphabet is relatively small, so its characters can be represented using a single-byte encoding ranging from 32 to 255. On the other hand, Asian languages such as Chinese, Korean, and Japanese require their own alphabets, which are much larger. These larger character sets are represented by a combination of single-byte and double-byte numeric codes ranging from 32 to 65,535.

A given Palm Powered handheld supports one character encoding. Although Palm OS supports multiple character encodings, a given handheld uses only one of those encodings. For example, a French handheld uses the Palm Latin encoding, which is identical to the Microsoft® Windows® code page 1252 character encoding (an extension of ISO Latin 1) but includes Palm-specific characters in the control range. A Japanese handheld, on the other hand would use the Palm Shift JIS character encoding, which is identical to Microsoft Windows code page 932 (an extension of Shift JIS) but includes Palm-specific characters in the control range. Code page 932 is not supported on the French handheld, and code page 1252 is not supported on the Japanese handheld even though they both use the same version of Palm OS. No matter what the encoding is on a handheld, Palm guarantees that the low ASCII characters (0 to 0x7F) are the same. The exception to this rule is 0x5C, which is a yen symbol on Japanese handhelds and a backslash on all others.

The Text Manager allows you to work with text, strings, and characters independent of the character encoding. If you use Text Manager routines and don't work directly with string data, your code should work on any system, regardless of which language and character encoding the handheld supports (as long as it supports the Text Manager).

The International Manager's job is to detect which character encoding a handheld uses and initialize the corresponding version of the Text Manager. The International Manager also sets system features that identify which encoding and fonts are used. For the most part, you don't work with the International Manager directly.

Characters ^TOP^

Depending on the handheld's supported languages, Palm OS may encode characters using either a single-byte encoding or a multi-byte encoding. Because you do not know which character encoding is used until runtime, you should never make an assumption about the size of a character.

For the most part, your application does not need to know which character encoding is used, and in fact, it should make no assumptions about the encoding or about the size of characters. Instead, your code should use Text Manager functions to manipulate characters. This section describes how to work with characters correctly. It covers:

Declaring Character Variables ^TOP^

Declare all character variables to be of type WChar. WChar is a 16-bit unsigned type that can accommodate characters of any encoding. Don't use Char. Char is an 8-bit variable that cannot accommodate larger character encodings. The only time you should ever use Char is to pass a parameter to an older Palm OS function.


WChar ch; // Right. 16-bit character.  
Char ch; // Wrong. 8-bit character.  

When you receive input characters through the keyDownEvent, you'll receive a WChar value. (That is, the data.keyDown.chr field is a WChar.)

Even though character variables are now declared as WChar, string variables are still declared as Char *, even though they may contain multi-byte characters. See the section "Strings" for more information on strings.

Using Character Constants ^TOP^

Character constants are defined in several header files. The header file Chars.h contains characters that are guaranteed to be supported on all systems regardless of the encoding. Other header files exist for each supported character encoding and contain characters specific to that encoding. The character encoding-specific header files are not included in the PalmOS.h header by default because they define characters that are not available on every system.

To make it easier for the compiler to find character encoding problems with your project, make a practice of using the character constants defined in these header files rather than directly assigning a character variable to a value. For example, suppose your code contained this statement:


WChar ch = 'å'; // WRONG! Don't use.  

This statement may work on a Latin system, but it would cause problems on an Asian-language system because the å character does not exist. If you instead assign the value this way:


WChar ch = chrSmall_A_RingAbove;  

you'll find the problem at compile time because the chrSmall_A_RingAbove constant is defined in CharLatin.h, which is not included by default.

Missing and Invalid Characters ^TOP^

If during application testing, you see an open rectangle, a shaded rectangle, or a gray square displayed on the screen, you have a missing character.

A missing character is one that is valid within the character encoding but the current font is not able to display it. In this case, nothing is wrong with your code other than you have chosen the wrong font. The system displays an open rectangle in place of a missing single-byte rectangle (see Figure 8.1).

Figure 8.1  Missing characters

In multi-byte character encodings, a character may be missing as described above, or it may be invalid. In single-byte character encodings, there's a one-to-one correspondence between numeric values and characters to represent. This is not the case with multi-byte character encodings. In multi-byte character encodings, there are more possible values than there are characters to represent. Thus, a character variable could end up containing an invalid character—a value that doesn't actually represent a character.

If the system is asked to display an invalid character, it prints an open rectangle for the first invalid byte. Then it starts over at the next byte. Thus, the next character displayed and possibly even the remaining text displayed is probably not what you want. Check your code for the following:

  • Truncating strings. You might have truncated a string in the middle of a multi-byte character.
  • Appending characters from one encoding set to a string in a different encoding.
  • Arithmetic on character variables that could result in an invalid character value.
  • Arithmetic on a string pointer that could result in pointing to an intra-character boundary. See "Performing String Pointer Manipulation" for more information.
  • Assumptions that a character is always a single byte long.

Use the Text Manager function TxtCharIsValid (TxtGlueCharIsValid) to determine whether a character is valid or not.

Retrieving a Character's Attributes ^TOP^

The Text Manager defines certain functions that retrieve a character's attributes, such as whether the character is alphanumeric, and so on. You can use these functions on any character, regardless of its size and encoding.

A character also has attributes unique to its encoding. Functions to retrieve those attributes are defined in the header files specific to the encoding.


WARNING! In previous versions of the Palm OS, the header file CharAttr.h defined character attribute macros such as IsAscii. Using these macros on double-byte characters produces incorrect results. Use the Text Manager macros instead of the CharAttr.h macros.

Virtual Characters ^TOP^

Virtual characters are nondisplayable characters that trigger special events in the operating system, such as displaying low battery warnings or displaying the keyboard dialog. Virtual characters should never occur in any data and should never appear on the screen.

The Palm OS uses character codes 256 decimal and greater for virtual characters. The range for these characters may actually overlap the range for "real" characters (characters that should appear on the screen). The keyDownEvent distinguishes a virtual character from a displayable character by setting the command bit in the event record.

The best way to check for virtual characters, including virtual characters that represent the hard keys, is to use the TxtGlueCharIsVirtual function defined in the PalmOSGlue library. See Listing 8.1.

Listing 8.1  Checking for virtual characters


if (TxtGlueCharIsVirtual (eventP->data.keyDown.modifiers,  
   eventP->data.keyDown.chr)) { 
   if (TxtCharIsHardKey (event->data.keyDown.modifiers,  
      event->data.keyDown.chr)) { 
      // Handle hard key virtual character. 
   } else { 
      // Handle standard virtual character. 
   } 
} else { 
   // Handle regular character. 
} 

Retrieving the Character Encoding ^TOP^

Occasionally, you may need to determine which character encoding is being used. For example, your application may need to do some unique text manipulation if it is being run on a European handheld. You can retrieve the character encoding from the system feature set using the FtrGet function as shown in Listing 8.2.

Listing 8.2  Retrieving the character encoding


UInt32 encoding;  
Char* encodingName; 
if (FtrGet(sysFtrCreator, sysFtrNumEncoding, &encoding) != 0)
   encoding = charEncodingPalmLatin;  
   //default encoding 
if (encoding == charEncodingPalmSJIS) { 
   // encoding for Palm Shift-JIS 
} else if (encoding == charEncodingPalmLatin) { 
   // extension of ISO Latin 1 
} 
  
// The following Text Manager function returns the  
// official name of the encoding as required by  
// Internet applications.  
encodingName = TxtGlueEncodingName(encoding); 

Strings ^TOP^

When working with text as strings, you use the String Manager and the Text Manager.

The String Manager is supported in all releases of Palm OS. It is closely modeled after the standard C string-manipulation functions like strcpy, strcat, and so on. Note that the standard C functions are not built in to Palm OS. Use String Manager calls instead of standard C calls to make your application smaller.

The Text Manager was added in Palm OS 3.1 to provide support for multi-byte strings. On systems that support the Text Manager, strings are made up of characters that are either a single-byte long or multiple bytes long, up to four bytes. As stated previously, character variables are always two bytes long. However, when you add a character to a string, the operating system may shrink it down to a single byte if it's a low ASCII character. Thus, any string that you work with may contain a mix of single-byte and multi-byte characters.

Applications can use both the Text Manager and the String Manager to work with strings. The String Manager functions in Palm OS 3.1 and later can work with strings containing multi-byte characters. Use the Text Manager functions when:

  • A String Manager equivalent is not available.
  • The length of the matching strings are important. For example, to compare two strings, you can use either StrCompare or TxtCompare. The difference between the two is that StrCompare does not return the length of the characters that matched. TxtCompare does.

This section discusses the following topics:


TIP: Many of the pre-3.1 Palm OS functions have been modified to work with strings containing multi-byte characters. All Palm OS functions that return the length of a string, such as FldGetTextLength and StrLen, always return the size of the string in bytes, not the number of characters in the string. Similarly, functions that work with string offsets always use the offset in bytes, not characters.

Manipulating Strings ^TOP^

Any time that you want to work with character pointers, you need to be careful not to point to an intra-character boundary (a middle or end byte of a multi-byte character). For example, any time that you want to set the insertion point position in a text field or set the text field's selection, you must make sure that you use byte offsets that point to inter-character boundaries. (The inter-character boundary is both the start of one character and the end of the previous character, except when the offset points to the very beginning or very end of a string.)

Suppose you want to iterate through a string character by character. Traditionally, C code uses a character pointer or byte counter to iterate through a string a character at a time. Such code will not work properly on systems with multi-byte characters. Instead, if you want to iterate through a string a character at a time, use Text Manager functions:

  • TxtGetNextChar (TxtGlueGetNextChar) retrieves the next character in a string.
  • TxtGetPreviousChar (TxtGlueGetPreviousChar) retrieves the previous character in a string.
  • TxtSetNextChar (TxtGlueSetNextChar) changes the next character in a string and can be used to fill a string buffer.

Each of these three functions returns the size of the character in question, so you can use it to determine the offset to use for the next character. For example, Listing 8.3 shows how to iterate through a string character by character until a particular character is found.

Listing 8.3  Iterating through a string or text


Char* buffer; // assume this exists 
UInt16 bufLen = StrLen(buffer);  
// Length of the input text.  
WChar ch = 0; 
UInt16 i = 0; 
while ((i < bufLen) && (ch != chrAsterisk)) 
   i+= TxtGlueGetNextChar(buffer, i, &ch)); 

The Text Manager also contains functions that let you determine the size of a character in bytes without iterating through the string:

  • TxtCharSize (TxtGlueCharSize) returns how much space a given character will take up inside of a string.
  • TxtCharBounds (TxtGlueCharBounds) determines the boundaries of a given character within a given string.

Listing 8.4  Working with arbitrary limits


UInt32* charStart, charEnd; 
Char* fldTextP = FldGetTextPtr(fld); 
TxtGlueCharBounds(fldTextP,  
   min(kMaxBytesToProcess, FldGetTextLength(fld)),  
   &charStart, &charEnd); 
// process only the first charStart bytes of text. 
  

Performing String Pointer Manipulation ^TOP^

Never perform any pointer manipulation on strings you pass to the Text Manager unless you use Text Manager calls to do the manipulation. For Text Manager functions to work properly, the string pointer must point to the first byte of a character. If you use Text Manager functions when manipulating a string pointer, you can be certain that your pointer always points to the beginning of a character. Otherwise, you run the risk of pointing to an inter-character boundary.

Listing 8.5  String pointer manipulation


// WRONG! buffer + kMaxStrLength is not  
// guaranteed to point to start of character. 
buffer[kMaxStrLength] = '\0'; 
  
// Right. Truncate at a character boundary. 
UInt32 charStart, charEnd; 
TxtCharBounds(buffer, kMaxStrLength,  
  &charStart, &charEnd); 
TxtGlueSetNextChar(buffer, charStart, chrNull); 

Truncating Displayed Text ^TOP^

If you're performing drawing operations, you often have to determine where to truncate a string if it's too long to fit in the available space. Two functions help you perform this task on strings with multi-byte characters:

  • WinDrawTruncChars (WinGlueDrawTruncChars) — This function draws a string within a specified width, determining automatically where to truncate the string. If it can, it draws the entire string. If the string doesn't fit in the space, it draws one less than the number of characters that fit and then ends the string with an ellipsis (...).
  • FntWidthToOffset (FntGlueWidthToOffset) — This function returns the byte offset of the character displayed at a given pixel position. It can also return the width of the text up to that offset.

    Listing 8.6 shows how you can use FntWidthToOffset to determine how many lines are necessary to write a string to the screen. This example passes 160 as the pixel position so that upon return, widthToOffset contains the byte offset of the last character in the string that can be displayed on a single line. The characters up to and including the one at widthToOffset are drawn, then the msg pointer is advanced in the string by widthToOffset characters, and FntWidthToOffset is called again to find out how many characters fit on the next line of text. The process is repeated until all of the characters in the string have been drawn.

Listing 8.6  Drawing multiple lines of text


Coord y; 
Char *msg; 
Int16 msgWidth; 
Int16 widthToOffset = 0; 
Int16 pixelWidth = 160; 
Int16 msgLength = StrLen(msg); 
 
while (msg && *msg) { 
   widthToOffset = FntGlueWidthToOffset(msg, msgLength,  
      pixelWidth, NULL, &msgWidth); 
   WinDrawChars(msg, widthToOffset, 0, y); 
   y += FntLineHeight(); 
   msg += widthToOffset; 
   msgLength = StrLen(msg); 
} 

Comparing Strings ^TOP^

Use the Text Manager functions TxtCompare (TxtGlueCompare) and TxtCaselessCompare (TxtGlueCaselessCompare) to perform comparisons of strings.

In character encodings that use multi-byte characters, some characters are accurately represented as either single-byte characters or multi-byte characters. That is, a character might have both a single-byte representation and a double-byte representation. One string might use the single-byte representation and another might use the multi-byte representation. Users expect the characters to match regardless of how many bytes a string uses to store that character. TxtCompare and TxtCaselessCompare can accurately match single-byte characters with their multi-byte equivalents.

Because a single-byte character might be matched with a multi-byte character, two strings might be considered equal even though they have different lengths. For this reason, TxtCompare and TxtCaselessCompare take two parameters in which they pass back the length of matching text in each of the two strings. See the function descriptions in the Palm OS Programmer's API Reference for more information.

Note that the String Manager functions StrCompare and StrCaselessCompare are equivalent, but they do not pass back the length of the matching text.

Global Find ^TOP^

A special case of performing string comparison is implementing the global system find facility. To implement this facility, you should call TxtFindString (TxtGlueFindString). As with TxtCompare and TxtCaselessCompare, TxtFindString accurately matches single-byte characters with their corresponding multi-byte characters. Plus, it passes back the length of the matched text. You'll need this value to highlight the matching text when the system requests that you display the matching record.

Older versions of Palm OS use the function FindStrInStr. FindStrInStr is not able to return the length of the matching text. Instead, it assumes that characters within the string are always one byte long.

When the user taps the Find icon, the system sends the launch code sysAppLaunchCmdFind to each application. Listing 8.7 shows an example of a function that should be called in response to that launch code. This function implements a global find that works on all systems whether the Text Manager exists or not. When the user taps one of the results displayed in the Find Results dialog, the system sends a sysAppLaunchCmdGoTo launch code to the application containing the matching record. Listing 8.8 shows how to respond to the sysAppLaunchCmdGoto launch code.

These two listings are only code excerpts. For the complete implementation of these two functions, see the example code in the Palm OS SDK.

Note that if you want to use TxtFindString to implement a search within your application (as opposed to the global find facility), you need to call TxtGluePrepFindString before you call TxtFindString to ensure that the string is in the proper format. (In the global find facility, the system has already prepared the string before your code is executed.)

Listing 8.7  Implementing global find


static void Search (FindParamsPtr findParams) 
{ 
   UInt16 recordIndex = 0; 
   DmOpenRef dbP; 
   UInt16 cardNo = 0; 
   LocalID dbID; 
   MemoDBRecordPtr memoPadRecP; 
  
  
   // Open the database to be searched. 
   dbP = DmOpenDatabaseByTypeCreator(memoDBType,  
      sysFileCMemo, findParams->dbAccesMode); 
   DmOpenDatabaseInfo(dbP, &dbID, 0, 0, &cardNo,  
      0); 
  
   // Get first record to search.  
   memoRecP = GetRecordPtr(dbP, recordIndex); 
   while (memoRecP != NULL) { 
      Boolean done; 
      Boolean match; 
      UInt32 matchPos, matchLength; 
  
      // TxtGlueFindString calls TxtFindString if it 
      // exists, or else it implements the Latin 
      // equivalent of it.  
      match = TxtGlueFindString (&(memoRecP->note),  
         findParams->strToFind, &matchPos,  
         &matchLength); 
       
      if (match) { 
         done = FindSaveMatch (findParams,  
            recordIndex, matchPos, 0, matchLength, 
            cardNo, dbIDP); 
      } 
      MemPtrUnlock (memoRecP); 
  
      if (done) break; 
      recordIndex += 1; 
   } 
   DmCloseDatabase (dbP); 
} 

Listing 8.8  Displaying the matching record


static void GoToRecord (GoToParamsPtr goToParams, Boolean 
launchingApp) 
{ 
   UInt16 recordNum; 
   EventType event; 
  
   recordNum = goToParams->recordNum; 
   ... 
  
   // Send an event to goto a form and select the  
   // matching text. 
   MemSet (&event, sizeof(EventType), 0); 
  
   event.eType = frmLoadEvent; 
   event.data.frmLoad.formID = EditView; 
   EvtAddEventToQueue (&event); 
  
   MemSet (&event, sizeof(EventType), 0); 
   event.eType = frmGotoEvent; 
   event.data.frmGoto.recordNum = recordNum; 
   event.data.frmGoto.matchPos =  
      goToParams->matchPos; 
   event.data.formGoto.matchLen =  
      goToParams->matchCustom; 
   event.data.frmGoto.matchFieldNum =  
      goToParams->matchFieldNum; 
   event.data.frmGoto.formID = EditView; 
   EvtAddEventToQueue (&event); 
   ... 
} 

Dynamically Creating String Content ^TOP^

When working with strings in a localized application, you never hard code them. Instead, you store strings in a resource and use the resource to display the text. If you need to create the contents of the string at runtime, store a template for the string as a resource and then substitute values as needed.

For example, consider the Edit view of the Memo application. Its title bar contains a string such as "Memo 3 of 10." The number of the memo being displayed and the total number of memos cannot be determined until runtime.

To create such a string, use a template resource and the Text Manager function TxtParamString (TxtGlueParamString). TxtParamString allows you to search for the sequence ^0, ^1, up to ^3 and replace each of these with a different string. If you need more parameters, you can use TxtReplaceStr (TxtGlueReplaceStr), which allows you to replace up to ^9; however, TxtReplaceStr only allows you to replace one of these sequences at a time.

In the Memo title bar example, you'd create a string resource that looks like this:


Memo ^0 of ^1 

And your code might look like this:

Listing 8.9  Using string templates


static void EditViewSetTitle (void) 
{ 
   Char* titleTemplateP; 
   FormPtr frm; 
   Char posStr [maxStrIToALen]; 
   Char totalStr [maxStrIToALen]; 
   UInt16 pos; 
   UInt16 length; 
  
   // Format as strings, the memo's postion within  
   // its category, and the total number of memos  
   // in the category. 
   pos = DmPositionInCategory (MemoPadDB,  
      CurrentRecord, RecordCategory); 
   StrIToA (posStr, pos+1); 
  
   if (MemosInCategory == memosInCategoryUnknown) 
      MemosInCategory = DmNumRecordsInCategory  
         (MemoPadDB, RecordCategory); 
   StrIToA (totalStr, MemosInCategory); 
  
   // Get the title template string.  It contains  
   // '^0' and '^1' chars which we replace with the  
   // position of CurrentRecord within  
   // CurrentCategory and with the total count of  
   // records in CurrentCategory (). 
   titleTemplateP = MemHandleLock (DmGetResource  
      (strRsc, EditViewTitleTemplateStringString)); 
  
   EditViewTitlePtr =  
      TxtGlueParamString(titleTemplateP, posStr,  
      totalStr, NULL, NULL); 
  
   // Now set the title to use the new title  
   // string. 
   frm = FrmGetFormPtr (MemoPadEditForm); 
   FrmSetTitle (frm, EditViewTitlePtr); 
   MemPtrUnlock(titleTemplateP); 
} 

Using the StrVPrintF Function ^TOP^

Like the C vsprintf function, the StrVPrintF function is designed to be called by your own function that takes a variable number of arguments and passes them to StrVPrintF for formatting. This section gives a brief overview of how to use StrVPrintF. For more details, refer to vsprintf and the use of the stdarg.h macros in a standard C reference book.

When you call StrVPrintF, you must use the special macros from stdarg.h to access the optional arguments (those specified after the fixed arguments) passed to your function. This is necessary, because when you declare your function that takes an optional number of arguments, you declare it using an ellipsis at the end of the argument list:


MyPrintF(CharPtr s, CharPtr formatStr, ...); 

The ellipsis indicates that zero or more optional arguments may be passed to the function following the formatStr argument. Since these optional arguments don't have names, the stdarg.h macros must be used to access them before they can be passed to StrVPrintF.

To use these macros in your function, first declare an args variable as type va_list:


va_list args; 

Next, initialize the args variable to point to the optional argument list by using va_start:


va_start(args, formatStr); 

Note that the second argument to the va_start macro is the last required argument to your function (last before the optional arguments begin). Now you can pass the args variable as the last parameter to the StrVPrintF function:


StrVPrintF(text, formatStr, args); 

When you are finished, invoke the macro va_end before returning from your function:


va_end(args); 

Note that the StrPrintF and StrVPrintF functions implement only a subset of the conversion specifications allowed by the ANSI C function vsprintf. See the StrVPrintF function reference for details.

Fonts ^TOP^

All fonts in Palm OS are bitmapped fonts. A bitmapped font is one that provides a separate bitmap for each glyph in each size and style. Scalable fonts such as TrueType or PostScript fonts are not supported.

Each font is associated with a particular character encoding. The font contains glyphs that define how to draw each character in the encoding.

Palm OS provides built-in fonts, and in Palm OS 3.0 and later, allows you to create your own fonts. If the High-Density Display Feature Set is present, high-density fonts are supported and used on high-density displays. This section describes the font support in Palm OS 3.0 and later. It covers:

For more general information on high-density displays, see "High-Density Displays."

Built-in Fonts ^TOP^

There are several fonts built into Palm OS. The Font.h file defines constants that can be used to access the built-in fonts programmatically. These constants are defined on all versions of Palm OS no matter what language or character code; however, they may point to different fonts. For example, stdFont on a Japanese system may be quite different from stdFont on a Latin system.

Table 8.1 lists and describes the built-in fonts that may be used to display text.

Table 8.1  Built-in text fonts 

Constant

Description

stdFont

A small standard font used to display user input. This font is small to display as much text as possible.

largeFont

A larger font provided as an alternative for users who find the standard font too small to read.

boldFont

Same size as stdFont but bold for easier reading. Used for text labels in the user interface.

largeBoldFont

In Palm OS 3.0 and later only. Same size as largeFont but bold.

Figure 8.2 shows what each of the fonts in Table 8.1 looks like.

Figure 8.2  Built-in text fonts

Palm OS also defines the fonts listed in Table 8.2. These fonts do not contain most letters of the alphabet. They are used only for special purposes.

Table 8.2  Built-in symbol fonts

Constant

Description

symbolFont

Contains many special characters such as arrows, shift indicators, and so on.

symbol11Font

Contains the check boxes, the large left arrow, and the large right arrow.

symbol7Font

Contains the up and down arrows used for the repeating button scroll arrows and the dimmed version of the same arrows.

ledFont

Contains the numbers 0 through 9, –, ., and the comma (,). Used by the Calculator application for its numeric display.

Selecting Which Font to Use ^TOP^

The default fonts used to display normal text and bold text vary based on the handheld's character encoding. Handhelds with the Palm Latin encoding typically use stdFont and boldFont, while Japanese handhelds use largeFont and largeBoldFont as the default. When your application starts up for the first time, it should respect the system defaults. Use the FntGlueGetDefaultFontID function to determine what the default fonts are (see Listing 8.10).

Listing 8.10  Determining the default system fonts


FontID textFont = FntGlueGetDefaultFontID(defaultSystemFont); 
FontID labelFont = FntGlueGetDefaultFontID(defaultBoldFont); 

In general, where users can enter text, you should allow them to select the font through the Select Font dialog (see Figure 8.3).

Figure 8.3  Select Font dialog

The FontSelect function displays the Select Font dialog. This function takes as an argument a FontID, which specifies the value that is initially selected in the dialog. It returns the FontID that the user selected.


newFontID = FontSelect(textFont); 

Because the default fonts vary based on the character encoding, the font size choices displayed in the Font Select dialog also vary based on character encoding. For this reason, you must call FntGlueGetDefaultFontID to obtain the default system font and pass the returned value to FontSelect when you call it for the first time. On subsequent calls to FontSelect, you can pass the user's current font choice.

Fonts for High-Density Displays ^TOP^

If the High-Density Display Feature Set is defined, Palm OS supports both low-density (160 X 160) and double-density (320 X 320 displays. The 1.5x Display Feature Set adds support for 320 X 240—"one-and-a-half density," also known as 1.5x—displays. High-density displays, whether double or one-and-a-half density, pack more pixels into the same space to create a finer resolution.

To support multiple display densities, Palm OS uses an extended font resource. An extended font resource contains a separate set of glyphs for each supported density. At runtime, Palm OS determines the current display density and then draws text using the glyphs that match the display density. On a double-density display, text is drawn using the double-density glyphs. On a low-density display, text is drawn using the low-density glyphs.

Palm OS includes extended font resources for each of the built-in fonts when the high-density display feature set is defined. If your application uses only these fonts, your text is drawn by default using high-density glyphs on high-density displays. You do not have to make any changes to your code for this to occur.

Figure 8.4  Low-density and high-density fonts

You only need to be concerned about the font density if you need to display more text than normal on a single high-density screen (see the description of the WinSetScalingMode function in Palm OS Programmer's API Reference) or if you use custom fonts:

  • When creating a custom font, you'll want to create an extended font resource. See "Creating Custom Fonts." If an extended font resource is not available or does not contain double-density glyphs, Palm OS pixel doubles the low-density glyphs when drawing to a double-density display.
  • When drawing text in a custom font to an off-screen window, you must take care. Off-screen windows also have a display density. If you create a low-density off-screen window and draw text to it, use a low-density font or an extended font with low-density glyphs. If the resource contains only high-density glyphs, the blitter shrinks the high-density font bitmaps. This results in poor quality text when the offscreen bitmap is subsequently transferred, and pixel-doubled, to a high-density display. If a low-density font is available, the blitter substitutes a low-density font when drawing text to the window in an attempt to produce the best possible aesthetic result.

    Because Palm OS includes low-density and high-density glyphs for each of the built-in fonts, this is only a potential problem if you are using a custom font.


WARNING! The blitter's behavior when attempting to draw double-density data (text or bitmap) to a 1.5x destination, or attempting to draw 1.5x data to a double-density destination, is undefined. Drawing low-density font or bitmap data to a high-density destination (either 1.5 or double density), however, works as expected.

Setting the Font Programmatically ^TOP^

To set the font that a user interface element uses for its label or for its textual contents, you use different functions depending on the element. Table 8.3 shows which functions set fonts for which user interface elements.

Table 8.3  Setting the font 

UI object

Function

Field

FldSetFont

Field within a table

TblSetItemFont

Command button, push button, pop-up trigger, selector trigger, or check box

CtlGlueSetFont

Label resource

FrmGlueSetLabelFont

List items

LstGlueSetFont

All other text (text drawn directly on the screen)

FntSetFont

The FntSetFont function changes the font that the system uses by default. It returns the previously used font. You should save the font returned by FntSetFont and restore it once you are done. Listing 8.11 shows an example of setting the font to draw items in the custom list drawing function.

Listing 8.11  Setting the font programmatically


void DrawOneRowInList(Int16 itemNum, RectangleType *bounds,  
   Char **itemsText) 
{ 
   Boolean didSetFont = false; 
   FontID oldFont; 
 
   if ((itemNum % 5) == 0) { 
      oldFont = FntSetFont(boldFont); 
      didSetFont = true; 
   } 
   WinDrawChars(itemsText[itemNum], 
     StrLen(itemsText[itemNum]), bounds.topLeft.x, 
        bounds.topLeft.y); 
   if (didSetFont) 
      FntSetFont(oldFont); 
} 

Obtaining Font Information ^TOP^

Use functions in the Font Manager to obtain information about a font and how it is drawn to the screen. Figure 8.5 shows graphically the characteristics of a font. Table 8.4 describes the types of information that can be retrieved with the Font Manager.

Figure 8.5  Font characteristics

Table 8.4  Obtaining font information

Characteristic

Function

Font height

FntCharHeight

Ascent

FntBaseLine

Descent

FntDescenderHeight

Leading + font height

FntLineHeight

Maximum width of a character in the font

FntAverageCharWidth

Width of a specific character

FntWCharWidth (FntGlueWCharWidth)

Character displayed at particular location

FntWidthToOffset (FntGlueWidthToOffset)

The functions listed in Table 8.4 all work on the current font, that is, the font listed in the draw state. To change the current font, use FntSetFont. For example, to determine the line height of the font used for text in a field, do the following:

Listing 8.12  Obtaining characteristics of a field font


FieldType *fieldP; 
FontID oldFont; 
Int16 lineHeight; 
 
oldFont = FntSetFont(FldGetFont(fieldP)); 
lineHeight = FntLineHeight(); 
FntSetFont(oldFont); 

Creating Custom Fonts ^TOP^

Palm OS 3.0 and later supports the use of custom fonts. You can create your own font resource ('NFNT') and use it within your application.

If the High-Density Display Feature Set is present, you can use an extended font resource ('nfnt') instead. The extended font resource contains a separate set of glyphs for each of the supported display densities.

Both the font and extended font resources are only large enough to support a font for the Palm Latin character encoding (256 characters). Defining a custom font for larger character sets is not supported.

Creating a Font Resource

The Palm OS SDK does not provide tools to create a custom font; however, several third party applications, such as xFont and PilRC, are available that support the creation of custom fonts.


TIP: Leave a vertical column of blank space on the right side of each glyph in a font. Palm OS draws characters side by side, in contrast to the Mac OS, which draws a character, clears the pixels to its right and then draws the next character. If your font requires leading, you should leave blank space at the bottom of each glyph as well because Palm OS does not support leading.

To create an extended font resource, you use Constructor for Palm OS:

  1. Create two font resources using a third party tool (such as xFont). Every aspect of the second font must be exactly double that of the first font. That is, the second font must be twice as many pixels high, twice as many pixels wide, and so on.
  2. Create a font family using Constructor for Palm OS as described in the book Constructor for Palm OS.
  3. Use the first 'NFNT' resource as the Normal density font. Use the second 'NFNT' resource as the Double density font.

The PalmRez post linker uses the font family resource to create the extended font resource that you use on the device.

Using a Custom Font in Your Application

Once you have defined a custom font in a resource file, you must assign it a font ID before you can use the font in your application. The font ID is different from the font's resource ID. It's a number between 0 and 255. The font ID you use must be greater than or equal to fntAppFontCustomBase. All IDs less than that are reserved for system use. The function FntDefineFont assigns a font ID to a font resource. The font resource must be locked for the entire time that the font is in use. It's a good idea to load the font resource, lock it, and assign a font ID in your application's AppStart function. Unlock and release the font resource in the AppStop function.

Listing 8.13 shows code that loads a font resource, assigns a font ID to that resource, and then draws characters to the screen using the new font.

Listing 8.13  Loading and using a custom font


#define customFontID ((FontID) fntAppFontCustomBase) 
 
MemHandle customFontH; 
FontType *customFontP; 
 
Err AppStart(void) 
{ 
   ... 
   // Load the font resource and assign it a font ID.  
   customFontH = DmGetResource(fontRscType, MyCoolFontRscID); 
   customFontP = (FontType *)MemHandleLock(customFontH); 
   FntDefineFont(customFontID, customFontP); 
   ... 
} 
 
void AppStop(void) 
{ 
   ... 
   //Release the font resource when app quits.  
   MemHandleUnlock(customFontH); 
   DmReleaseResource(customFontH); 
   ... 
} 
 
void DrawCharsInNewFont(void)  
{ 
   FontID oldFont = FntSetFont(customFontID); 
   Char *msg = "Look, Mom. It's a new font!"); 
   WinDrawChars(msg, StrLen(msg), 28, 0); 
   FntSetFont(oldFont); 
} 

To use a extended font, you use essentially the same code as above, except that you must change the resource type used in the DmGetResource call to fontExtRscType:


customFontH = DmGetResource(fontExtRscType,  
   MyCoolExtFontRscID); 
// rest as shown above.  

Note that you still use a pointer to a FontType structure to access the extended font.

The 'NFNT' resources you created to build the extended font are discarded after the extended font is created. For a backward compatible application, you need to define a separate 'NFNT' resource containing the low-density glyphs in your font.

It's possible to create an extended font resource that contains only double-density glyphs and not low-density glyphs. You could define an 'NFNT' resource for the low-density glyphs and an extended font resource with just the double-density glyphs. Then you could load and use the 'NFNT' resource on all handhelds with low-density screens (including those that don't support the high-density feature set) and load and use the extended font resource when the display is not low-density. If you do this, you must carefully check the display density before deciding which resource to load (see Listing 8.14).


WARNING! If you write text to a low-density off-screen window using double-density glyphs, the result is undefined and may cause Palm OS to crash. If you load your fonts as shown in Listing 8.14, load and use the old font resource when drawing text to a low-density off-screen window. See "Fonts for High-Density Displays" for more information.

Listing 8.14  Conditionally loading a font resource


#define customFontID ((FontID) fntAppFontCustomBase) 
MemHandle fontH = NULL; 
FontType *fontP; 
UInt16 winVersion; 
Err error; 
 
error = FtrGet(sysFtrCreator, sysFtrNumWinVersion,  
   &winVersion); 
// If winVersion is >= 4, the high-density feature set 
// is present. Check what type of display we are on 
// and load the appropriate font resource.  
if (!error && (winVersion >= 4)) { 
   UInt32 density; 
   error = WinScreenGetAttribute(winScreenDensity, &density); 
   if (!error && (density != kDensityLow)) { 
         // load and use the extended font  
         // resource. 
         fontH = DmGetResource(fontExtRscType, 
            MyNewFontRscID); 
         fontP = MemHandleLock(fontH); 
   } 
} 
 
if (!fontH) { 
  // Either the feature set is not present or we're on a  
  // low-density screen. Load and use the 'NFNT' resource. 
  fontH = DmGetResource(fontRscType, MyOldFontRscID); 
  fontP = (FontType *)MemHandleLock(fontH); 
} 
 
FntDefineFont(customFontID, fontP); 

Summary of Text API ^TOP^