.. _rfc40:

===============================================
MS RFC 40: Support Label Text Transformations
===============================================

:Date: 2007/12/03
:Author: Thomas Bonfort
:Contact: thomas.bonfort at gmail.com
:Last Edited: 2007/12/03
:Status: Implemented
:Version: MapServer 5.4

Overview
--------

MapServer 5.0 supports "hard" text wrapping, where the wrap character is 
unconditionally treated as a newline. This RFC proposes the addition of:

- conditional wrapping, i.e. newlines are only added when the current 
  line length has met a user adjustable criteria
- line alignment, i.e. text aligned per user request, namely right, center and left
 
These two additions are independent from one another, i.e. can be activated 
together or not.

Line Wrapping
------------------

A new keyword, MAXLENGTH is added to the label object, and interacts with the 
label WRAP parameter as follows:

- the wrap character specifies what character can be replaced by a newline 
  (no change w.r.t. the current meaning). Probably the most useful wrap 
  character will become ' ' (i.e. the space character)
- the MAXLENGTH value specifies the maximum number of characters that can 
  compose a line before a newline is inserted. A value of 0 specifies the 
  current "hard" wrapping. A positive value triggers wrapping at the WRAP 
  character for lines longer than MAXLENGTH. A negative value triggers 
  wrapping that is allowed to break words, i.e. not necessarily at 
  the WRAP character.

The WRAP / MAXLENGTH combinations are summarized here:

+----------------+-----------------+-----------------------------+-------------------+
|                |   maxlen =0     |     maxlen > 0              |      maxlen < 0   |
+================+=================+=============================+===================+
| wrap = 'char'  |  current way    | conditional wrap if > maxlen| hard wrap         |
+----------------+-----------------+-----------------------------+-------------------+  
| no wrap        |  no processing  | skip label if > maxlen      | hard wrap         |
+----------------+-----------------+-----------------------------+-------------------+

Line Centering
----------------
A new keyword ALIGN is added to the label object. It supports 3 values: left, 
center and right,  and controls how text lines should be aligned w.r.t. the 
label bounding box. Precise placement of text can only be done at the renderer 
level, by exactly defining the starting pixel of the current line. While this 
approach could be taken, it would considerably burden the renderer code.

I propose to use a more generic though less precise way of doing, by 
padding the text lines with space characters to approximate indentation. 
The initial implementation could only rely on the number of characters in 
each text line:

 - loop through text lines to find the line with the most characters l_max
 - pad all the other lines with (l_max - l_cur)/2 space characters
 
A more advanced implementation will be to use the exact line lengths as 
returned by the renderers with the msGetLabelSize function:

 - compute the size in pixels of the "  " string (two spaces, 
   accounts for kerning): l_2space
 - loop through text lines to longest line of length pix_l_max
 - pad all the other lines with 
   (pix_l_max - pix_l_cur)/2 * 1/(2*l_2space) space characters
 
'''Limitations''': aligning text to the right will produce ugly results using 
this method, unless using a monospace font.  

Modifications to the source code
--------------------------------

* MAXLENGTH and ALIGN will be added to the LABEL object in map.h, in the 
  mapfile parser/writer (mapfile.c) and in MapScript

* msTransformLabelText() in maplabel.c will be updated to support the 
  modifications proposed. No other modifications should be required as the 
  text itself is modified to fit the user's request (i.e. it is padded with 
  spaces, and/or newlines are added to it)

MapScript Implications
----------------------

The labelObj will have new maxlength property of type integer 
and align property (ms_align_*).

Files affected
--------------

::

 map.h
 mapfile.c
 maplabel.c
 maplexer.l
 maplexer.c

Backwards compatibility issues
------------------------------

None.

Bug ID
------

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

Voting history
--------------
* +1: SteveL, SteveW
* +0: FrankW, TomK

Questions/Comments from the review period
-----------------------------------------
* SteveW: allow preciser computation of line offsets when 
  padding with spaces: done. The precise size of each line is 
  calculated in pixels, and the closest number of space characters 
  required for alignment is computed accordingly
* DanielM: allow multiple characters to be used when wrapping lines: 
  not addressed. Space characters can be *replaced* by a newline, while the 
  hyphen character should generally be kept. Any idea how we can specify that 
  some wrapping characters are to be kept while others are just markers 
  for potential linebreaks?
