Part 3: Configuring Font Sources

Each text field in Flash has a font name associated with it that is either stored directly or encoded in its HTML tags. When text field is displayed, the font used for rendering is obtained up by searching the following font sources:

  1. Locally embedded fonts.
  2. Font symbols imported from separate SWF/GFX files.
  3. SWF/GFX files installed through GFx::FontLib, searched either by font name or through imported font substitution.
  4. System font provider, such as GFx::FontProviderWin32, if one is installed by the user.

The use of embedded and imported fonts has been described in the beginning of this document. For the most part, embedded fonts work identically to Flash and thus require no custom configuration. For the purposes of font lookup, imported font symbols act similar to the embedded fonts.

The three installable states that configure non-embedded font lookup are GFx::FontLib, GFx::FontMap and GFx::FontProvider. As described earlier in the document, GFx::FontLib and GFx::FontMap are used to look up SWF/GFX-loaded fonts for imported font substitution or device font emulation. The use of the system font provider will be covered in detail below.

The system font provider is installed with the GFx::Loader::SetFontProvider call; it allows font data to come from an alternative non-SWF file source. Font provider can only be used with a dynamic cache and is searched only if the font data is not embedded in the SWF or the font library. Currently, two font providers are included with Scaleform: - GFx::FontProviderWin32 – Relies on Win32 APIs to obtain font glyph data. - GFx::FontProviderFT2 – Uses the FreeType-2 library by David Turner to read and interpret stand-alone font files.

In this part we describe the font lookup order in detail and provide code examples of how different font sources can be configured.

Font Lookup Order

The flowchart on the following page illustrates the order of font lookup in Scaleform . As can be seen from the chart, the lookup behavior is modified by the Device Font flag, set if the “Use device fonts” rendering method is selected in text field properties.

In Adobe Flash, setting the device font flag causes the corresponding font to be obtained from the system in preference of identically named embedded fonts (in this case, the later are used as a fall-back). Scaleform replicates this behavior, although its installed font library is searched before the system font providers. Such setup does not cause conflicts since most console-portable games will rely on shared font libraries, while games which choose to use system providers can leave the font library in the un-initialized (null) state.

As can be seen from the diagram, embedded fonts are searched first if the Device Fonts are not used for a text field and last otherwise. Furthermore, embedded fonts are always looked up based on the original font name used in a text field, while a font map can be applied to substitute font names looked up from GFx::FontLib and GFx::FontProvider.

GFx::FontMap

GFx::FontMap is a state used to substitute font names, allowing alternative fonts to be used during internationalization if the required characters are not available in the development font. The font map is used with both imported font substitution and device font emulation. In the first case, it converts font symbol identifiers into font names. In the second case, it maps original font names into the translated alternative.

In Part 3, font maps were created with the following lines in the font configuration file:

[FontConfig "Korean"]
fontlib "fonts_kr.swf"
map "$TitleFont" = "Batang" Bold
map "$NormalFont" = "Batang" Normal
map "$SmallFont" = "Batang" Normal

In the example above, the mapping statements are used to map font import identifiers, such as “$TitleFont” to the actual fonts font names, which in this case are embedded in the font library file.

Equivalent font map setup is achieved with the following C++ statements.

#include "GFx/GFx_FontLib.h"
. . .
Ptr<GFx::FontMap> pfontMap = *new GFx::FontMap;
loader.SetFontMap(pfontMap);

pfontMap->MapFont("$TitleFont", "Batang", FontMap::MFF_Bold,scaleFactor = 1.0f);
pfontMap->MapFont("$NormalFont", "Batang", FontMap::MFF_Normal,scaleFactor = 1.0f);
pfontMap->MapFont("$SmallFont", "Batang", FontMap::MFF_Normal,scaleFactor = 1.0f);

In this case, all three fonts are mapped to the same font name; in particular “\(NormalFont” and “\)SmallFont” also share the same font style, saving memory used by the font library file. Different styles can be specified with a third argument to MapFont, forcing the mapping to use a particular embedding. If the argument is not specified, MFF_Original value is used, indicating that font lookup should retain the original style specified for the text field. The size of the font can be changed by a factor set by the parameter scaleFactor. By default, the scaleFactor is set to 1.0f. This parameter is useful if the font is not visible well enough and needs to be slightly increased.

Developers should be aware that GFx::FontMap is a binding state, meaning that movie instances created with it will use the state even if it is later changed in the loader. If a different font map is set, a different GFx::MovieDef will be returned for it by GFx::Loader::CreateMovie for the same file name. This is also true for all other font configuration states.

GFx::FontLib

As discussed in Part 3, GFx::FontLib state represents an installable font library that (1) provides alternatives for fonts imported from the default “fonts_en.swf” and (2) provides fonts for device font emulation. The font library is searched before system font providers. Its use should be clear from the following example.

#include "GFx/GFx_FontLib.h"
. . .
Ptr<GFx::FontLib> fontLib = *new GFx::FontLib;
loader.SetFontLib(fontLib);

Ptr<GFx::MovieDef>  m1 = *Loader.CreateMovie("<swf_or_gfx_file1>");
Ptr<GFx::MovieDef>  m2 = *Loader.CreateMovie("<swf_or_gfx_file2>");
. . .
fontLib->AddFontsFrom(m1, true);
fontLib->AddFontsFrom(m2, true);

The idea is to create and load the movies as usual, but instead of playing them, the movies are used as font storages. It is possible to load as many movies as needed. If the same font is defined in different movies, only the first one will be used.

The first parameter to AddFontsFrom is a movie definition that will serve as a source for fonts. The second parameter to AddFontsFrom is a pin flag, which should be set if the loader should AddRef to the movie in memory. This flag is only necessary when users do not keep smart pointers to the loaded movies (m1 and m2 in the example). If a pin flag is false, font binding data such as exported or packed textures may be released early and have to be reloaded/regenerated when the font is used.

Similar to the font map, GFx::FontLib is a binding state, which is referenced by the created movies until they die.

GFx::FontProviderWin32

The GFx::FontProviderWin32 font provider is available with Win32 API, where it can rely on the GetGlyphOutline() function to retrieve vector data. It can be used as outlined below:

#include "GFx/GFx_FontProviderWin32.h"
. . .
Ptr<GFx::FontProviderWin32> fontProvider = *new GFx::FontProviderWin32(::GetDC(0));
loader.SetFontProvider(fontProvider);

The constructor of GFx::FontProviderWin32 takes the handler of Windows Display Context as an argument. In most cases the use of the screen DC is appropriate (::GetDC(0)).

The fonts will be created as necessary, when requested.

Using Natively Hinted Text

In general, font hinting is a very non-trivial problem. Scaleform provides an auto-hinting mechanism, but it does not work well enough for Chinese, Japanese, and Korean (CJK) characters. Besides, most of well-designed CJK fonts contain raster images for glyphs of certain sizes, because at small sizes proper hinting of CJK becomes extremely complex. Duplicating vector glyphs with raster images is a good and practical solution. System font providers based on font APIs (GFx::FontProviderWin32 and GFx::FontProviderFT2) can produce glyphs in both, vector and raster representations. The font providers have an interface to control the native hinting. The interfaces are slightly different, but have the same principle. There are 4 parameters:

Font::NativeHintingRange vectorRange;
Font::NativeHintingRange rasterRange;
unsigned maxVectorHintedSize;
unsigned maxRasterHintedSize;

Parameters vectorRange and rasterRange control the character range in which the hinting is used. The values are:

Font::DontHint – do not use native hinting, Font::HintCJK – use native hinting for Chinese, Japanese, and Korean characters, Font::HintAll – use native hinting for all characters.

The rasterRange has a priority over the vectorRange. Parameters maxVectorHintedSize and maxRasterHintedSize define the maximal font size in pixels at which the native hinting is used. The picture below contains an example of the font SimSun, with different options.

As you can see, native vector hinting does not help much for this font, while using the raster images change the text dramatically. However, for certain fonts, such as “Arial Unicode MS” vector hints make sense.

Note that unlike Adobe Flash, Scaleform supports arbitrary affine transformations for device fonts. Rotation and skewing certainly blur the glyphs, but it still remains quite appropriate for animated text.

By default, both, Win32 and FreeType providers use the following values:

vectorRange = Font::DontHint;
rasterRange = Font::HintCJK;
maxVectorHintedSize = 24;
maxRasterHintedSize = 24;

Particular interfaces for configuring the hinting are described below.

Configuring Native Hinting

By default GFx::FontProviderWin32 uses native raster hinting for CJK characters and does use vector native hinting (). It is possible to change this behavior by calling:

fontProvider->SetHintingAllFonts(. . .);

or

fontProvider->SetHinting(fontName, . . .);

Note that if function SetHintingAllFonts() is called after SetHinting() it will not change the hinting behavior for this particular font. The exact functions prototypes are:

void SetHintingAllFonts(Font::NativeHintingRange vectorRange,
                        Font::NativeHintingRange rasterRange,
                        unsigned maxVectorHintedSize=24,
                        unsigned maxRasterHintedSize=24);

void SetHinting(const char* name,
                Font::NativeHintingRange vectorRange,
                Font::NativeHintingRange rasterRange,
                unsigned maxVectorHintedSize=24,
                unsigned maxRasterHintedSize=24);

Parameter name can use UTF-8 encoding.

GFx::FontProviderFT2

This font provider uses the FreeType-2 library by David Turner. Its use is similar to GFx::FontProviderWin32, except for the necessity of mapping the font names and attributes to actual font files.

First, we recommend developers read the FreeType manual to properly configure and build the library. It is the developers’ responsibility to decide to use static or dynamic linking, as well as proper runtime configuration settings such as font drivers, memory allocators, external file streams, and so on.

For Windows MSVC compilers with static linking use the following libraries:

freetype<ver>.lib – for Release Multi-threaded DLL code generation,
freetype<ver>_D.lib – for Debug Multi-threaded DLL,
freetype<ver>MT.lib – for Release Multi-threaded (static CRT),
freetype<ver>MT_D.lib – for Debug Multi-threaded (static CRT).

Where “” is the version of FreeType, for example, for version 2.1.9 it’s 219.

Also, make sure the directories freetype2/include and freetype2/objs are available in the paths for additional includes and libraries respectively.

In order to use FT2 font provider in addition to steps mentioned above, the following files need to be added to the build:

Src\Render\FontProvider\Render_FontProviderFT2.cpp, Src\Render\FontProvider\Render_FT2Helper.cpp ,
Src\GFx\GFx_FontProviderFT2.cpp

Creating of the GFx::FontProviderFT2 object looks as follows:

#include "GFx/GFx_FontProviderFT2.h"
. . .
Ptr<GFx::FontProviderFT2> fontProvider = *new GFx::FontProviderFT2;
<Map Font to Files or Memory>
loader.SetFontProvider(fontProvider);

The constructor has one argument:

FontProviderWin32(FT_Library lib=0);

It is the FreeType library handle. If zero (default value), the provider will initialize FreeType internally. The ability to specify an existing initialized external handler is provided in case the application already uses FreeType and wants to share the handle between Scaleform and other systems. Note that when using the external handler it’s the developer’s responsibility to properly release the library (call FT_Done_FreeType). Also, the application must guarantee that the life time of the handle is longer than the life time of GFx::Loader. Using the external handler is also supported in cases where it is desirable to reconfigure the run-time call-backs of FreeType (memory allocators and such). Note, that this configuration can be done with the internal initialization too. The fontProvider->GetFT_Library() function returns the used FT_Library handler and it is guaranteed that no calls to FreeType will be invoked by the provider before actual use of the fonts. As a result, there is a chance to reconfigure the FreeType at run-time after creating GFx::FontProviderFT2.

Mapping the FreeType Fonts to Files

Unlike Win32 API, FreeType does not provide mapping typeface names (and attributes) to actual font files. Thus, this mapping must be provided externally. GFx::FontProviderFT2 has a simple mechanism of mapping the fonts to files and memory.

void MapFontToFile(const char* fontName, unsigned fontFlags,
                   const char* fileName, unsigned faceIndex=0,
                   Font::NativeHintingRange vectorHintingRange = Font::DontHint,
                   Font::NativeHintingRange rasterHintingRange = Font::HintCJK,
                   unsigned maxVectorHintedSize=24,
                   unsigned maxRasterHintedSize=24);

Argument “fontName” specifies font typeface name, for example, “Times New Roman”. Argument “fileName” specifies the path to the font file, for example, “C:\WINDOWS\Fonts\times.ttf”. The “fontFlags” can have values “Font::FF_Bold”, “Font::FF_Italic”, or “Font::FF_BoldItalic”. Value “Font::FF_BoldItalic” actually equals to “Font::FF_Bold | Font::FF_Italic”. The “faceIndex” is what is passed to FT_New_Face. In most cases this parameter is 0, but not always, depending on the type and the content of the font file. Note that this function does not actually open the font files; it only forms the mapping table. The font files will be open when they are actually requested. Unlike the Win32 font provider, the FreeType uses just function’s arguments to configure the native hinting.

Mapping Fonts to Memory

FreeType allows fonts to be mapped to memory. It may make sense, for example, if a single font file contains many typefaces, or if the font files are already loaded by the application.

void MapFontToMemory(const char* fontName, unsigned fontFlags,
                     const char* fontData, unsigned dataSize,
                     unsigned faceIndex=0,
                     Font::NativeHintingRange vectorHintingRange = Font::DontHint,
                     Font::NativeHintingRange rasterHintingRange = Font::HintCJK,
                     unsigned maxVectorHintedSize=24,
                     unsigned maxRasterHintedSize=24);

A simple (and somewhat dirty) example of mapping fonts to memory is below.

FILE* fd = fopen("C:\\WINDOWS\\Fonts\\times.ttf", "rb");
if (fd) {
    fseek(fd, 0, SEEK_END);
    unsigned size = ftell(fd);
    fseek(fd, 0, SEEK_SET);
    char* font = (char*)malloc(size);
    fread(font, size, 1, fd);
    fclose(fd);
    fontProvider->MapFontToMemory("Times New Roman", 0, font, size);
}

Memory is not released in this example (and, yes, eventually it will result in a memory leak). It’s because the mapping table keeps only a naked constant pointer to the font data. It is the developer’s responsibility to properly release it. The application must guarantee that the life time of this block of memory is longer than the life time of GFx::Loader. It is designed to provide as much freedom as possible in the font mapping mechanism. For example, the application may already use FreeType with pre-loaded memory fonts whose allocation and destruction is handled externally from Scaleform.

An example of using the FreeType font mapping in Windows may look as follows.

Ptr<FontProviderFT2> fontProvider = *new FontProviderFT2;
fontProvider->MapFontToFile("Times New Roman", 0, "C:\\WINDOWS\\Fonts\\times.ttf");
fontProvider->MapFontToFile("Times New Roman", Font::FF_Bold, "C:\\WINDOWS\\Fonts\\timesbd.ttf");
fontProvider->MapFontToFile("Times New Roman", Font::FF_Italic, "C:\\WINDOWS\\Fonts\\timesi.ttf");
fontProvider->MapFontToFile("Times New Roman", Font::FF_BoldItalic, "C:\\WINDOWS\\Fonts\\timesbi.ttf");

fontProvider->MapFontToFile("Arial", 0, "C:\\WINDOWS\\Fonts\\arial.ttf");
fontProvider->MapFontToFile("Arial", Font::FF_Bold, "C:\\WINDOWS\\Fonts\\arialbd.ttf");
fontProvider->MapFontToFile("Arial", Font::FF_Italic, "C:\\WINDOWS\\Fonts\\ariali.ttf");
fontProvider->MapFontToFile("Arial", Font::FF_BoldItalic, "C:\\WINDOWS\\Fonts\\arialbi.ttf");

fontProvider->MapFontToFile("Verdana", 0, "C:\\WINDOWS\\Fonts\\verdana.ttf");
fontProvider->MapFontToFile("Verdana", Font::FF_Bold, "C:\\WINDOWS\\Fonts\\verdanab.ttf");
fontProvider->MapFontToFile("Verdana",Font::FF_Italic, "C:\\WINDOWS\\Fonts\\verdanai.ttf");
fontProvider->MapFontToFile("Verdana", Font::FF_BoldItalic, "C:\\WINDOWS\\Fonts\\verdanaz.ttf");
. . .
Loader.SetFontProvider(fontProvider);

Note that it is only an example; in a real application it is a bad idea to specify the absolute hard-coded file paths. Normally, it should come from a configuration file, or by automatically scanning the font typefaces. Specifying the explicit typeface names may seem like overkill, because the fonts typically contain these names, but it avoids file parsing which can be an expensive operation. The function MapFontToFile() only stores this information and does not open the file unless it’s requested. As a result, a large number of mapped fonts can be specified, while only a few of them are actually used. In this case no extra file operations will be performed.