Enumerating fonts with ATS

Q: ATS provides several different methods for enumerating the fonts and font families available in Mac OS X. Which method should I use for my application?

A: First you'll want to validate that your situation requires you to enumerate the font list. If your goal is to gather information about all fonts installed in the system, you should enumerate once and then cache the results. Enumerating the entire system can be time-consuming and degrade system performance, especially on systems with many fonts installed. ATS includes notification mechanisms to inform you of font system changes thus eliminating the need to poll the system. See Setting Up Notifications in the 'Managing Fonts: ATS' guide.

If your goal is to provide a font selection menu to the user, you should implement the fonts window instead. This ensures that your application user interface remains consistent with the rest of Mac OS X. For more information on how to display and manage a fonts window see Fonts Window Services Reference.

The following code listings demonstrate using ATS to iterate the system font database within a local context with an unrestricted scope. While similar, Listing 1 creates an iterator to enumerate all the fonts and Listing 2 creates an iterator to enumerate all the font families.

It's important to note here that your application should gracefully handle the kATSIterationScopeModified error that could be returned by ATSFontIteratorNext. This is not a fatal error, it simply indicates that one or more changes occurred in the font database since you started the iteration. In most cases, you should reset the iterator and start again. However, you should take preventive measures to not get stuck in a reset loop should the user make sudden, drastic changes to the font database, for example, adding many fonts at once.

Note: When you iterate using a global context and a restricted scope, you enumerate only those fonts that are activated globally. Locally activated fonts, including those activated locally for your application, are not enumerated. A font whose context is local can be accessed by your application, a font with a global context can be accessed by all applications on a system.

Listing 1: ATS Font Iterator

CFArrayRef CreateCarbonSystemFontList(void)
{
  ATSFontIterator theFontIterator = NULL;
  CFMutableArrayRef outArray = NULL;
  ATSFontRef theATSFontRef = 0;
  OSStatus status = noErr;

  // Create array to store font names
  outArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
  if(!outArray)
    return NULL;

  // Create the iterator
  status = ATSFontIteratorCreate(kATSFontContextLocal, nil,nil,
                  kATSOptionFlagsUnRestrictedScope,
                  &theFontIterator );
  while (status == noErr)
  {
    // Get the next font in the iteration.
    status = ATSFontIteratorNext( theFontIterator, &theATSFontRef );
    if(status == noErr)
    {
      CFStringRef theName = NULL;

      // Add your code here to do something with font information.
      // This example gets font name and stores it in our array
      ATSFontGetName(theATSFontRef, kATSOptionFlagsDefault, &theName);
      CFArrayAppendValue(outArray, theName);
      CFRelease(theName);
    }
    else if (status == kATSIterationScopeModified) // Make sure the font database hasn’t changed.
    {
      // reset the iterator
      status = ATSFontIteratorReset (kATSFontContextLocal, nil, nil,
                      kATSOptionFlagsUnRestrictedScope,
                      &theFontIterator);
      CFArrayRemoveAllValues(outArray);
    }
  }

  ATSFontIteratorRelease(&theFontIterator);

  return outArray;
}

Listing 2: ATS Font Family Iterator

  ATSFontFamilyIterator theFontFamilyIterator = NULL;
  CFMutableArrayRef outArray = NULL;
  ATSFontFamilyRef theATSFontFamilyRef = 0;
  OSStatus status = noErr;

  // Create array to store font names
  outArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
  if(!outArray)
    return NULL;

  // Create the iterator
  status = ATSFontFamilyIteratorCreate(kATSFontContextLocal, nil,nil,
                      kATSOptionFlagsUnRestrictedScope,
                      &theFontFamilyIterator );
  while (status == noErr)
  {
    // Get the next font family in the iteration.
    status = ATSFontFamilyIteratorNext( theFontFamilyIterator, &theATSFontFamilyRef );
    if(status == noErr)
    {
      CFStringRef theName = NULL;

      // Add your code here to do something with font information.
      // This example gets font name and stores it in our array
      ATSFontFamilyGetName(theATSFontFamilyRef, kATSOptionFlagsDefault, &theName);
      CFArrayAppendValue(outArray, theName);
      CFRelease(theName);
    }
    else if (status == kATSIterationScopeModified) // Make sure the font database hasn’t changed.
    {
      // reset the iterator
      status = ATSFontFamilyIteratorReset (kATSFontContextLocal, nil, nil,
                          kATSOptionFlagsUnRestrictedScope,
                          &theFontFamilyIterator);
      CFArrayRemoveAllValues(outArray);
    }
  }

  ATSFontFamilyIteratorRelease(&theFontFamilyIterator);

  return outArray;
}

References

Back to Top 

Document Revision History

DateNotes
2006-04-14First Version

Posted: 2006-04-14


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.