Attached is a patch for some text rendering improvements. The TextSymbolizer
gets the following new parameters:

* text_convert="none|toupper|tolower"
  Convert all text to upper/lower case before rendering. "none" doesn't do
  anything with the text and is the default. Works for labels along lines
  or at points.

* line_spacing="<number>"
  Add this many pixels space between two lines in text labels that have
  been broken into several lines. Default is 0. Doesn't do anything for
  labels along lines.

* character_spacing="<number>"
  Add this many pixels space between two characters in a text. Default is 0.
  Currently only works for text labels on point geometries. This should
  also be implemented for labels along lines, but I'll leave that for
  another day.

* wrap_character="<character>"
  Instead of breaking text into lines on spaces, use this character. This
  is useful, when you want to make sure that labels are broken at the right
  spot. Note that you'll probably want to make wrap_width small so that
  your lines are actually broken, otherwise you'll see the wrap_character
  in the output. Default is ' ' (space). Doesn't do anything for labels
  along lines.

I have added those parameters to the load_map and save_map functions and
implemented the functionality. I have *not* added this to the python
bindings, because I don't know how they work.

Jochen
-- 
Jochen Topf  [email protected]  http://www.remote.org/jochen/  +49-721-388298
Index: include/mapnik/placement_finder.hpp
===================================================================
--- include/mapnik/placement_finder.hpp	(revision 1253)
+++ include/mapnik/placement_finder.hpp	(working copy)
@@ -58,6 +58,7 @@
          boost::ptr_vector<placement_element> placements;
          
          int wrap_width;
+         unsigned char wrap_char;
          int text_ratio;
 
          int label_spacing; // distance between repeated labels on a single geometry
@@ -81,7 +82,7 @@
       placement_finder(DetectorT & detector);
          
       //Try place a single label at the given point
-      void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = MIDDLE);
+      void find_point_placement(placement & p, double pos_x, double pos_y, vertical_alignment_e = MIDDLE, unsigned line_spacing=0, unsigned character_spacing=0);
          
       //Iterate over the given path, placing point labels with respect to label_spacing
       template <typename T>
Index: include/mapnik/text_symbolizer.hpp
===================================================================
--- include/mapnik/text_symbolizer.hpp	(revision 1253)
+++ include/mapnik/text_symbolizer.hpp	(working copy)
@@ -55,6 +55,16 @@
 
    DEFINE_ENUM( vertical_alignment_e, vertical_alignment );
 
+   enum text_convert
+   {
+      NONE = 0,
+      TOUPPER,
+      TOLOWER,
+      text_convert_MAX
+   };
+
+   DEFINE_ENUM( text_convert_e, text_convert );
+
    typedef boost::tuple<double,double> position;
     
    struct MAPNIK_DECL text_symbolizer
@@ -70,6 +80,14 @@
          void set_text_ratio(unsigned ratio);
          unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio
          void set_wrap_width(unsigned ratio);
+         unsigned char get_wrap_char() const; // character used to wrap lines
+         void set_wrap_char(unsigned char character);
+         text_convert_e get_text_convert() const; // text conversion on strings before display
+         void set_text_convert(text_convert_e convert);
+         unsigned get_line_spacing() const; // spacing between lines of text
+         void set_line_spacing(unsigned spacing);
+         unsigned get_character_spacing() const; // spacing between characters in text
+         void set_character_spacing(unsigned spacing);
          unsigned get_label_spacing() const; // spacing between repeated labels on lines
          void set_label_spacing(unsigned spacing);
          unsigned get_label_position_tolerance() const; //distance the label can be moved on the line to fit, if 0 the default is used
@@ -111,6 +129,10 @@
          unsigned size_;
          unsigned text_ratio_;
          unsigned wrap_width_;
+         unsigned char wrap_char_;
+         text_convert_e text_convert_;
+         unsigned line_spacing_;
+         unsigned character_spacing_;
          unsigned label_spacing_;
          unsigned label_position_tolerance_;
          bool force_odd_labels_;
Index: src/placement_finder.cpp
===================================================================
--- src/placement_finder.cpp	(revision 1253)
+++ src/placement_finder.cpp	(working copy)
@@ -75,6 +75,7 @@
         displacement_(sym.get_displacement()),
         label_placement(sym.get_label_placement()), 
         wrap_width(sym.get_wrap_width()), 
+        wrap_char(sym.get_wrap_char()),
         text_ratio(sym.get_text_ratio()), 
         label_spacing(sym.get_label_spacing()), 
         label_position_tolerance(sym.get_label_position_tolerance()), 
@@ -214,7 +215,9 @@
    void placement_finder<DetectorT>::find_point_placement(placement & p, 
                                                           double label_x, 
                                                           double label_y,
-                                                          vertical_alignment_e valign)
+                                                          vertical_alignment_e valign,
+                                                          unsigned line_spacing,
+                                                          unsigned character_spacing)
    {
       double x, y;
       std::auto_ptr<placement_element> current_placement(new placement_element);
@@ -239,7 +242,7 @@
       std::vector<double> line_heights;
       if (wrap_at < string_width && p.info.num_characters() > 0)
       {
-         int last_space = 0;
+         int last_wrap_char = 0;
          string_width = 0;
          string_height = 0;
          double line_width = 0;
@@ -250,14 +253,16 @@
          {
             character_info ci;
             ci = p.info.at(ii);
+
+            unsigned cwidth = ci.width + character_spacing;
                 
             unsigned c = ci.character;
-            word_width += ci.width;
-            word_height = word_height > ci.height ? word_height : ci.height;
+            word_width += cwidth;
+            word_height = word_height > (ci.height + line_spacing) ? word_height : (ci.height + line_spacing);
         
-            if (c == ' ')
+            if (c == p.wrap_char)
             {
-               last_space = ii;
+               last_wrap_char = ii;
                line_width += word_width;
                line_height = line_height > word_height ? line_height : word_height;
                word_width = 0;
@@ -266,13 +271,13 @@
             if (line_width > 0 && line_width > wrap_at)
             {
                // Remove width of breaking space character since it is not rendered
-               line_width -= ci.width;
+               line_width -= cwidth;
                string_width = string_width > line_width ? string_width : line_width;
                string_height += line_height;
-               line_breaks.push_back(last_space);
+               line_breaks.push_back(last_wrap_char);
                line_widths.push_back(line_width);
                line_heights.push_back(line_height);
-               ii = last_space;
+               ii = last_wrap_char;
                line_width = 0;
                line_height = 0;
                word_width = 0;
@@ -326,6 +331,8 @@
          character_info ci;
          ci = p.info.at(i);
             
+         unsigned cwidth = ci.width + character_spacing;
+
          unsigned c = ci.character;
          if (i == index_to_wrap_at)
          {
@@ -367,7 +374,7 @@
             
             p.envelopes.push(e);
          }
-         x += ci.width;
+         x += cwidth;
       }
       p.placements.push_back(current_placement.release());
       //update_detector(p);
Index: src/load_map.cpp
===================================================================
--- src/load_map.cpp	(revision 1253)
+++ src/load_map.cpp	(working copy)
@@ -885,6 +885,33 @@
                 text_symbol.set_wrap_width(*wrap_width);
             }
 
+            // character used to break long strings
+            optional<std::string> wrap_char = 
+                get_opt_attr<std::string>(sym, "wrap_character");
+            if (wrap_char && (*wrap_char).size() > 0)
+            {
+                text_symbol.set_wrap_char((*wrap_char)[0]);
+            }
+
+            // text conversion before rendering
+            text_convert_e tconvert =
+                get_attr<text_convert_e>(sym, "text_convert", NONE);
+            text_symbol.set_text_convert(tconvert);
+
+            // spacing between text lines
+            optional<unsigned> line_spacing = get_opt_attr<unsigned>(sym, "line_spacing");
+            if (line_spacing)
+            {
+                text_symbol.set_line_spacing(*line_spacing);
+            }
+
+            // spacing between characters in text
+            optional<unsigned> character_spacing = get_opt_attr<unsigned>(sym, "character_spacing");
+            if (character_spacing)
+            {
+                text_symbol.set_character_spacing(*character_spacing);
+            }
+
             // spacing between repeated labels on lines
             optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
             if (spacing)
Index: src/cairo_renderer.cpp
===================================================================
--- src/cairo_renderer.cpp	(revision 1253)
+++ src/cairo_renderer.cpp	(working copy)
@@ -1006,6 +1006,14 @@
       typedef coord_transform2<CoordTransform,geometry2d> path_type;
 
       UnicodeString text = feature[sym.get_name()].to_unicode();
+      if ( sym.get_text_convert() == TOUPPER)
+      {
+         text = text.toUpper();
+      }
+      else if ( sym.get_text_convert() == TOLOWER)
+      {
+         text = text.toLower();
+      }
 
       if (text.length() > 0)
       {
Index: src/agg_renderer.cpp
===================================================================
--- src/agg_renderer.cpp	(revision 1253)
+++ src/agg_renderer.cpp	(working copy)
@@ -764,6 +764,15 @@
       typedef  coord_transform2<CoordTransform,geometry2d> path_type;
       
       UnicodeString text = feature[sym.get_name()].to_unicode();
+      if ( sym.get_text_convert() == TOUPPER)
+      {
+         text = text.toUpper();
+      }
+      else if ( sym.get_text_convert() == TOLOWER)
+      {
+         text = text.toLower();
+      }
+
       if ( text.length() > 0 )
       {
          color const& fill = sym.get_fill();
@@ -807,7 +816,7 @@
                      geom.label_position(&label_x, &label_y);
                      prj_trans.backward(label_x,label_y, z);
                      t_.forward(&label_x,&label_y);
-                     finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment());
+                     finder.find_point_placement(text_placement,label_x,label_y,sym.get_vertical_alignment(),sym.get_line_spacing(),sym.get_character_spacing());
                      finder.update_detector(text_placement);
                   }
                   else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT)
Index: src/save_map.cpp
===================================================================
--- src/save_map.cpp	(revision 1253)
+++ src/save_map.cpp	(working copy)
@@ -275,6 +275,22 @@
                 {
                     set_attr( node, "wrap_width", sym.get_wrap_width() );    
                 }
+                if (sym.get_wrap_char() != dfl.get_wrap_char() || explicit_defaults_ )
+                {
+                    set_attr( node, "wrap_character", std::string(1, sym.get_wrap_char()) );
+                }
+                if (sym.get_text_convert() != dfl.get_text_convert() || explicit_defaults_ )
+                {
+                    set_attr( node, "text_convert", sym.get_text_convert() );
+                }
+                if (sym.get_line_spacing() != dfl.get_line_spacing() || explicit_defaults_ )
+                {
+                    set_attr( node, "line_spacing", sym.get_line_spacing() );    
+                }
+                if (sym.get_character_spacing() != dfl.get_character_spacing() || explicit_defaults_ )
+                {
+                    set_attr( node, "character_spacing", sym.get_character_spacing() );    
+                }
                 if (sym.get_label_spacing() != dfl.get_label_spacing() || explicit_defaults_ )
                 {
                     set_attr( node, "spacing", sym.get_label_spacing() );    
Index: src/text_symbolizer.cpp
===================================================================
--- src/text_symbolizer.cpp	(revision 1253)
+++ src/text_symbolizer.cpp	(working copy)
@@ -48,6 +48,17 @@
 
 IMPLEMENT_ENUM( mapnik::vertical_alignment_e, vertical_alignment_strings );
 
+static const char * text_convert_strings[] = {
+    "none",
+    "toupper",
+    "tolower",
+    ""
+};
+
+
+IMPLEMENT_ENUM( mapnik::text_convert_e, text_convert_strings );
+
+
 namespace mapnik
 {
     text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size, color const& fill)
@@ -57,6 +68,10 @@
           size_(size),
           text_ratio_(0),
           wrap_width_(0),
+          wrap_char_(' '),
+          text_convert_(NONE),
+          line_spacing_(0),
+          character_spacing_(0),
           label_spacing_(0),
           label_position_tolerance_(0),
           force_odd_labels_(false),
@@ -78,6 +93,10 @@
           size_(size),
           text_ratio_(0),
           wrap_width_(0),
+          wrap_char_(' '),
+          text_convert_(NONE),
+          line_spacing_(0),
+          character_spacing_(0),
           label_spacing_(0),
           label_position_tolerance_(0),
           force_odd_labels_(false),
@@ -99,6 +118,10 @@
           size_(rhs.size_),
           text_ratio_(rhs.text_ratio_),
           wrap_width_(rhs.wrap_width_),
+          wrap_char_(rhs.wrap_char_),
+          text_convert_(rhs.text_convert_),
+          line_spacing_(rhs.line_spacing_),
+          character_spacing_(rhs.character_spacing_),
           label_spacing_(rhs.label_spacing_),
           label_position_tolerance_(rhs.label_position_tolerance_),
           force_odd_labels_(rhs.force_odd_labels_),
@@ -124,6 +147,10 @@
         size_ = other.size_;
         text_ratio_ = other.text_ratio_;
         wrap_width_ = other.wrap_width_;
+        wrap_char_ = other.wrap_char_;
+        text_convert_ = other.text_convert_;
+        line_spacing_ = other.line_spacing_;
+        character_spacing_ = other.character_spacing_;
         label_spacing_ = other.label_spacing_;
         label_position_tolerance_ = other.label_position_tolerance_;
         force_odd_labels_ = other.force_odd_labels_;
@@ -191,6 +218,46 @@
         wrap_width_ = width;
     }    
 
+    unsigned char text_symbolizer::get_wrap_char() const
+    {
+        return wrap_char_;
+    }
+
+    void  text_symbolizer::set_wrap_char(unsigned char character) 
+    {
+        wrap_char_ = character;
+    }    
+
+    text_convert_e  text_symbolizer::get_text_convert() const
+    {
+        return text_convert_;
+    }
+
+    void  text_symbolizer::set_text_convert(text_convert_e convert)
+    {
+        text_convert_ = convert;
+    }
+
+    unsigned  text_symbolizer::get_line_spacing() const
+    {
+        return line_spacing_;
+    }
+
+    void  text_symbolizer::set_line_spacing(unsigned spacing) 
+    {
+        line_spacing_ = spacing;
+    }
+
+    unsigned  text_symbolizer::get_character_spacing() const
+    {
+        return character_spacing_;
+    }
+
+    void  text_symbolizer::set_character_spacing(unsigned spacing) 
+    {
+        character_spacing_ = spacing;
+    }
+
     unsigned  text_symbolizer::get_label_spacing() const
     {
         return label_spacing_;
_______________________________________________
Mapnik-devel mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/mapnik-devel

Reply via email to