.. _rfc80:

=========================================================================
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
----------

* https://github.com/mapserver/mapserver/issues/4114

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
----------------

* see msautotest entry http://trac.osgeo.org/mapserver/changeset/12863

7. Voting history
-----------------
Passed with +1 from ThomasB, SteveW, MichaelS, SteveL, PerryN, DanielM
