MS RFC 80: Font Fallback Support

Date

2011/12/07

Author

Thomas “fake” Jakobi

Author

Thomas Bonfort

Contact

thomas at derfake.com

Contact

tbonfort at terriscope.fr

Status

Passed

Version

MapServer 6.2

1. Overview

This RFC proposes allowing for the definition of fallback fonts for labels, to be used when a glyph is not available in the initial font.

Presently, if a glyph is not available in the font of a label, the font’s error glyph is drawn (usually an empty box). This is problematic when rendering international data, as it may contain a wide range of unicode characters. Up to now, this can only be fixed using a (near-)complete unicode font. The free “unifont” contains all glyphs of the basic unicode set (~60.000), but does not look very pleasing as a default font.

With fallback fonts, glyph lookup would try a set of fonts, in a given order, before accepting the error glyph. This way, a unicode font like unifont would only be used as “last resort” on a glyph-by-glyph basis.

In the mapfile, a list of fonts would be given as a comma-separated string like “font1,font2,font3”, where each font is listed in the fonts.lst as usual. If only one font is specified, this functionality has no effect. The same fontlist support is available for attribute binding.

2. Proposed Technical Change

2.1 Core Object Changes

The Label Style Object would need to support multiple fonts. The proposed implementation uses a hard limit to the number of fonts, to avoid overhead. The number of fonts can be changed using the MS_MAX_LABEL_FONTS define, the suggested default is 5.

2.2 Label Rendering Changes

The function pointer signature of GetTruetypeTextBBox needs to be changed to be able to receive multiple fonts and their count. This requires a change to every renderer, as does the change of the labelStyleObj.

Renderers supporting single-glyph operation like cairo and agg can use the font list passed in to implement the glyph fallback. Renderers operating on complete strings can use fonts[0] for no change in behavior.

2.3 MapScript

The mapscript getters and setters are unchanged, but the semantics of the string passed to labelObj->setFont() is modified as it now expects a comma-separated list of fontset keys.

3. Implementation Details

The proposed implementation adds font fallback support to the cairo and agg renderers.

The labelObj is unchanged, but its “char* font” member semantics is changed to contain a comma separated list of fontset keys instead of a single fontset key. This ensures minimal backwards incompatibility for mapscript applications and datasource bindings.

A new function “msFontsetLookupFonts()” is called before labelling renderer-specific functions are called, and maps the comma-separated labelObj->font into an array of full font filenames, by iterating through the labelObj->font keys and calling msFontsetLookup.

The renderer-specific labelling function signatures are modified to take a list of font filenames instead of a single filename as is the case now. Renderers that do not (yet?) support multiple font fallbacks are slightly modified to use the first entry of the font array, while the other renderers use some specific code to detect missing characters and use the fallback fonts when needed.

3.1. Files Affected

  • maplabel.c:

  • add msFontsetLookupFonts implementation that splits the input font on commas and looks up each font key in the fontset.

  • call msFontsetLookupFonts before calling the renderer vtable boundingbox calculation functions.

  • maprendering.c: call msFontsetLookupFonts to set list of fonts in labelStyleObj

  • mapagg.cpp: glyph fallback implementation, agg specific

  • mapcairo.c: glyph fallback implementation, cairo and freetype specific

  • mapgd.c, mapkml.c, mapogl.cpp:

  • use only the first font of the given font list

  • getTruetypeTextBBox function signature update

  • mapserver.h:

  • add msFontsetLookupFonts function and MS_MAX_LABEL_FONTS define

  • change labelStyleObj char* font to char* font[MS_MAX_LABEL_FONTS]

  • mapsymbol.c: symbol font fallback adoptions

  • maputil.c: only bind font from shape if it’s different

  • mapdummyrenderer.c: getTruetypeTextBBox signature change

3.2 Bug ID

4. Backwards compatibility issues

None expected, unless existing fontset keys contained commas. I’m not sure this was valid syntax anyways, and was definitely bad practice.

5. Error reporting

The hard limit on the maximum number of fonts allowed is enforced in msFontsetLookupFonts() which will halt processing and return an error message stating that the maximum number of fonts is set to MS_MAX_LABEL_FONTS. The renderers themselves will report the usual error message if one of the referenced fonts could not be loaded (due to an inaccessible or corrupt ttf file).

6. Example Usage

7. Voting history

Passed with +1 from ThomasB, SteveW, MichaelS, SteveL, PerryN, DanielM