diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index aa96897d9..f646133cb 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -611,7 +611,6 @@ __all__ = [ 'FontEngine', 'FontSet', 'Geometry2d', - 'GlyphSymbolizer', 'Image', 'ImageView', 'Grid', diff --git a/bindings/python/mapnik_glyph_symbolizer.cpp b/bindings/python/mapnik_glyph_symbolizer.cpp deleted file mode 100644 index 70f01ac73..000000000 --- a/bindings/python/mapnik_glyph_symbolizer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include - -#include -#include "mapnik_enumeration.hpp" -#include - -using mapnik::glyph_symbolizer; -using mapnik::position; -using mapnik::enumeration_; -using mapnik::angle_mode_e; -using mapnik::AZIMUTH; -using mapnik::TRIGONOMETRIC; -using namespace boost::python; - -namespace { -using namespace boost::python; - -tuple get_displacement(const glyph_symbolizer& s) -{ - boost::tuple pos = s.get_displacement(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); -} - -void set_displacement(glyph_symbolizer & s, boost::python::tuple arg) -{ - s.set_displacement(extract(arg[0]),extract(arg[1])); -} - -} - -void export_glyph_symbolizer() -{ - enumeration_("angle_mode") - .value("AZIMUTH", AZIMUTH) - .value("TRIGONOMETRIC", TRIGONOMETRIC) - ; - - class_("GlyphSymbolizer", - init()) - .add_property("face_name", - make_function(&glyph_symbolizer::get_face_name, - return_value_policy()), - &glyph_symbolizer::set_face_name, - "Get/Set the name of the font face (eg:\"DejaVu Sans " - "Book\") which contains the glyph" - ) - .add_property("char", - &glyph_symbolizer::get_char, - &glyph_symbolizer::set_char, - "Get/Set the char expression. The char is the unicode " - "character indexing the glyph in the font referred by " - "face_name." - ) - .add_property("allow_overlap", - &glyph_symbolizer::get_allow_overlap, - &glyph_symbolizer::set_allow_overlap, - "Get/Set the flag which controls if glyphs should " - "overlap any symbols previously rendered" - ) - .add_property("avoid_edges", - &glyph_symbolizer::get_avoid_edges, - &glyph_symbolizer::set_avoid_edges, - "Get/Set the flag which controls if glyphs should be " - "partially drawn beside the edge of a tile." - ) - .add_property("displacement", - &get_displacement, - &set_displacement) - - .add_property("halo_fill", - make_function(&glyph_symbolizer::get_halo_fill, - return_value_policy()), - &glyph_symbolizer::set_halo_fill) - - .add_property("halo_radius", - &glyph_symbolizer::get_halo_radius, - &glyph_symbolizer::set_halo_radius) - - .add_property("size", - &glyph_symbolizer::get_size, - &glyph_symbolizer::set_size, - "Get/Set the size expression used to size the glyph." - ) - - .add_property("angle", - &glyph_symbolizer::get_angle, - &glyph_symbolizer::set_angle, - "Get/Set the angle expression used to rotate the glyph " - "along its center." - ) - .add_property("angle_mode", - &glyph_symbolizer::get_angle_mode, - &glyph_symbolizer::set_angle_mode, - "Get/Set the angle_mode property. This controls how the " - "angle is interpreted. Valid values are AZIMUTH and " - "TRIGONOMETRIC." - ) - .add_property("value", - &glyph_symbolizer::get_value, - &glyph_symbolizer::set_value, - "Get/set the value expression which will be used to " - "retrieve a a value for the colorizer to use to choose " - "a color." - ) - .add_property("color", - &glyph_symbolizer::get_color, - &glyph_symbolizer::set_color, - "Get/Set the color expression used to color the glyph. " - "(See also the 'colorizer' attribute)" - - ) - .add_property("colorizer", - &glyph_symbolizer::get_colorizer, - &glyph_symbolizer::set_colorizer, - "Get/Set the RasterColorizer used to color the glyph " - "depending on the 'value' expression (which must be " - "defined).\n" - "Only needed if no explicit color is provided" - ) - ; -} diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index af4d0c836..bb9198c2e 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -66,7 +66,6 @@ void export_projection(); void export_proj_transform(); void export_view_transform(); void export_raster_colorizer(); -void export_glyph_symbolizer(); void export_inmem_metawriter(); void export_label_collision_detector(); @@ -84,6 +83,7 @@ void export_label_collision_detector(); #include #include #include +#include #include "python_grid_utils.hpp" #include "mapnik_value_converter.hpp" #include "python_optional.hpp" @@ -434,7 +434,6 @@ BOOST_PYTHON_MODULE(_mapnik) export_coord(); export_map(); export_raster_colorizer(); - export_glyph_symbolizer(); export_inmem_metawriter(); export_label_collision_detector(); diff --git a/bindings/python/mapnik_rule.cpp b/bindings/python/mapnik_rule.cpp index b6f3e4bc8..62de4e5f7 100644 --- a/bindings/python/mapnik_rule.cpp +++ b/bindings/python/mapnik_rule.cpp @@ -44,7 +44,6 @@ using mapnik::shield_symbolizer; using mapnik::text_symbolizer; using mapnik::building_symbolizer; using mapnik::markers_symbolizer; -using mapnik::glyph_symbolizer; using mapnik::symbolizer; using mapnik::to_expression_string; @@ -162,7 +161,6 @@ void export_rule() implicitly_convertible(); implicitly_convertible(); implicitly_convertible(); - implicitly_convertible(); implicitly_convertible(); class_("Symbolizers",init<>("TODO")) diff --git a/bindings/python/mapnik_shield_symbolizer.cpp b/bindings/python/mapnik_shield_symbolizer.cpp index 4bfe0fe61..e3180845e 100644 --- a/bindings/python/mapnik_shield_symbolizer.cpp +++ b/bindings/python/mapnik_shield_symbolizer.cpp @@ -64,17 +64,6 @@ void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg) t.set_displacement(extract(arg[0]),extract(arg[1])); } -tuple get_anchor(const shield_symbolizer& t) -{ - boost::tuple pos = t.get_anchor(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); -} - -void set_anchor(shield_symbolizer & t, boost::python::tuple arg) -{ - t.set_anchor(extract(arg[0]),extract(arg[1])); -} - const std::string get_filename(shield_symbolizer const& t) { return path_processor_type::to_string(*t.get_filename()); @@ -137,9 +126,6 @@ void export_shield_symbolizer() path_expression_ptr>("TODO") ) //.def_pickle(shield_symbolizer_pickle_suite()) - .add_property("anchor", - &get_anchor, - &set_anchor) .add_property("allow_overlap", &shield_symbolizer::get_allow_overlap, &shield_symbolizer::set_allow_overlap, diff --git a/bindings/python/mapnik_symbolizer.cpp b/bindings/python/mapnik_symbolizer.cpp index bd9f1c185..a4c4c3512 100644 --- a/bindings/python/mapnik_symbolizer.cpp +++ b/bindings/python/mapnik_symbolizer.cpp @@ -39,7 +39,6 @@ using mapnik::shield_symbolizer; using mapnik::text_symbolizer; using mapnik::building_symbolizer; using mapnik::markers_symbolizer; -using mapnik::glyph_symbolizer; struct get_symbolizer_type : public boost::static_visitor { @@ -95,12 +94,6 @@ public: { return "markers"; } - - std::string operator () ( const glyph_symbolizer & /*sym*/ ) - { - return "glyph"; - } - }; std::string get_symbol_type(const symbolizer& symbol) @@ -160,11 +153,6 @@ const markers_symbolizer& markers_( const symbolizer& symbol ) return boost::get(symbol); } -const glyph_symbolizer& glyph_( const symbolizer& symbol ) -{ - return boost::get(symbol); -} - void export_symbolizer() { using namespace boost::python; @@ -202,10 +190,6 @@ void export_symbolizer() .def("markers",markers_, return_value_policy()) - - .def("glyph",glyph_, - return_value_policy()) - ; } diff --git a/bindings/python/mapnik_text_symbolizer.cpp b/bindings/python/mapnik_text_symbolizer.cpp index 69d793ba7..31515f11a 100644 --- a/bindings/python/mapnik_text_symbolizer.cpp +++ b/bindings/python/mapnik_text_symbolizer.cpp @@ -48,27 +48,6 @@ void set_text_displacement(text_symbolizer & t, boost::python::tuple arg) t.set_displacement(extract(arg[0]),extract(arg[1])); } -tuple get_anchor(const text_symbolizer& t) -{ - position pos = t.get_anchor(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); -} - -void set_anchor(text_symbolizer & t, boost::python::tuple arg) -{ - t.set_anchor(extract(arg[0]),extract(arg[1])); -} - -void set_placement_options(text_symbolizer & t, placement_type_e arg, std::string const& placements) -{ - t.set_placement_options(arg, placements); -} - -void set_placement_options_2(text_symbolizer & t, placement_type_e arg) -{ - t.set_placement_options(arg, ""); -} - } struct text_symbolizer_pickle_suite : boost::python::pickle_suite @@ -86,7 +65,6 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite getstate(const text_symbolizer& t) { boost::python::tuple disp = get_text_displacement(t); - boost::python::tuple anchor = get_anchor(t); // so we do not exceed max args accepted by make_tuple, // lets put the increasing list of parameters in a list @@ -105,7 +83,7 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite return boost::python::make_tuple(disp,t.get_label_placement(), t.get_vertical_alignment(),t.get_halo_radius(),t.get_halo_fill(),t.get_text_ratio(), t.get_wrap_width(),t.get_label_spacing(),t.get_minimum_distance(),t.get_allow_overlap(), - anchor,t.get_force_odd_labels(),t.get_max_char_angle_delta(),extras + t.get_force_odd_labels(),t.get_max_char_angle_delta(),extras ); } @@ -146,15 +124,10 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite t.set_allow_overlap(extract(state[9])); - tuple anch = extract(state[10]); - double x = extract(anch[0]); - double y = extract(anch[1]); - t.set_anchor(x,y); + t.set_force_odd_labels(extract(state[10])); - t.set_force_odd_labels(extract(state[11])); - - t.set_max_char_angle_delta(extract(state[12])); - list extras = extract(state[13]); + t.set_max_char_angle_delta(extract(state[11])); + list extras = extract(state[12]); t.set_wrap_char_from_string(extract(extras[0])); t.set_line_spacing(extract(extras[1])); t.set_character_spacing(extract(extras[2])); @@ -190,6 +163,7 @@ void export_text_symbolizer() .value("LEFT",H_LEFT) .value("MIDDLE",H_MIDDLE) .value("RIGHT",H_RIGHT) + .value("AUTO",H_AUTO) ; enumeration_("justify_alignment") @@ -205,11 +179,6 @@ void export_text_symbolizer() .value("CAPITALIZE",CAPITALIZE) ; - enumeration_("placement_type") - .value("SIMPLE",T_SIMPLE) - .value("DUMMY",T_DUMMY) - ; - class_("TextSymbolizer",init()) /* // todo - all python classes can have kwargs and default constructors @@ -226,9 +195,6 @@ void export_text_symbolizer() */ //.def_pickle(text_symbolizer_pickle_suite()) - .add_property("anchor", - &get_anchor, - &set_anchor) .add_property("allow_overlap", &text_symbolizer::get_allow_overlap, &text_symbolizer::set_allow_overlap, @@ -302,10 +268,6 @@ void export_text_symbolizer() &text_symbolizer::get_text_opacity, &text_symbolizer::set_text_opacity, "Set/get the text opacity") - .add_property("placement", - &text_symbolizer::get_label_placement, - &text_symbolizer::set_label_placement, - "Set/get the placement of the label") .add_property("text_transform", &text_symbolizer::get_text_transform, &text_symbolizer::set_text_transform, @@ -329,9 +291,5 @@ void export_text_symbolizer() .add_property("wrap_before", &text_symbolizer::get_wrap_before, &text_symbolizer::set_wrap_before) - .add_property("placement_type", &text_symbolizer::get_placement_type) - .add_property("placements", &text_symbolizer::get_placements) - .def("set_placement_options", set_placement_options) - .def("set_placement_options", set_placement_options_2) ; } diff --git a/docs/textrendering.gv b/docs/textrendering.gv new file mode 100644 index 000000000..90a897a6a --- /dev/null +++ b/docs/textrendering.gv @@ -0,0 +1,24 @@ +#process with: dot textrendering.gv -Tpng > textrendering.png +digraph textrendering { +# Classes without important virtual members: Round +# Classes with important virtual members: Rect +# Pointers [style=dashed] +# Red: function is called + text_placements[shape=box] + text_placement_info[shape=box] + Renderer + + TextSymbolizer -> text_placements [label="placement_options_", style=dashed] + text_placements -> text_symbolizer_properties [label="properties"] + text_placements -> text_placement_info [label="get_placement_info()", style=dashed] + text_placement_info -> text_symbolizer_properties [label="properties"] + text_placement_info -> text_path [label="placements", style=dashed] + text_placement_info -> text_placement_info [label="next()"] + text_symbolizer_properties -> text_processor [label="processor"] + text_processor -> processed_text [label="process()", style=dashed] + processed_text -> string_info [label="get_string_info()"] + text_path -> Renderer [color=red, label="used by"] + Renderer -> text_placement_info [color=red, label="init()"] + Renderer -> processed_text [color=red, label="initializes"] + +} diff --git a/include/mapnik/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 1a74dd461..9fbe0e80c 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -101,10 +101,7 @@ public: proj_transform const& prj_trans); void process(markers_symbolizer const& sym, Feature const& feature, - proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); + proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, Feature const& /*feature*/, proj_transform const& /*prj_trans*/) diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index e256a6c9e..8efd91911 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -24,16 +24,11 @@ #define MAPNIK_ATTRIBUTE_COLLECTOR_HPP // mapnik -#include #include -#include -#include - // boost #include #include #include - // stl #include #include @@ -93,18 +88,12 @@ struct symbolizer_attributes : public boost::static_visitor<> void operator () (text_symbolizer const& sym) { - expression_ptr const& name_expr = sym.get_name(); - if (name_expr) + std::set::const_iterator it; + std::set expressions = sym.get_placement_options()->get_all_expressions(); + expression_attributes f_attr(names_); + for (it=expressions.begin(); it != expressions.end(); it++) { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*name_expr); - } - - expression_ptr const& orientation_expr = sym.get_orientation(); - if (orientation_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*orientation_expr); + if (*it) boost::apply_visitor(f_attr, **it); } collect_metawriter(sym); } @@ -152,11 +141,12 @@ struct symbolizer_attributes : public boost::static_visitor<> void operator () (shield_symbolizer const& sym) { - expression_ptr const& name_expr = sym.get_name(); - if (name_expr) + std::set::const_iterator it; + std::set expressions = sym.get_placement_options()->get_all_expressions(); + expression_attributes f_attr(names_); + for (it=expressions.begin(); it != expressions.end(); it++) { - expression_attributes name_attr(names_); - boost::apply_visitor(name_attr,*name_expr); + if (*it) boost::apply_visitor(f_attr, **it); } path_expression_ptr const& filename_expr = sym.get_filename(); @@ -167,45 +157,6 @@ struct symbolizer_attributes : public boost::static_visitor<> collect_metawriter(sym); } - void operator () (glyph_symbolizer const& sym) - { - expression_ptr const& char_expr = sym.get_char(); - if (char_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*char_expr); - } - - expression_ptr const& angle_expr = sym.get_angle(); - if (angle_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*angle_expr); - } - - expression_ptr const& value_expr = sym.get_value(); - if (value_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*value_expr); - } - - expression_ptr const& size_expr = sym.get_size(); - if (size_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*size_expr); - } - - expression_ptr const& color_expr = sym.get_color(); - if (color_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*color_expr); - } - collect_metawriter(sym); - } - void operator () (markers_symbolizer const& sym) { collect_metawriter(sym); diff --git a/include/mapnik/cairo_renderer.hpp b/include/mapnik/cairo_renderer.hpp index 821ed1334..ad2e0da90 100644 --- a/include/mapnik/cairo_renderer.hpp +++ b/include/mapnik/cairo_renderer.hpp @@ -111,9 +111,6 @@ public: void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, Feature const& /*feature*/, proj_transform const& /*prj_trans*/) diff --git a/src/grid/process_glyph_symbolizer.cpp b/include/mapnik/char_info.hpp similarity index 62% rename from src/grid/process_glyph_symbolizer.cpp rename to include/mapnik/char_info.hpp index b21453a7a..a1417735f 100644 --- a/src/grid/process_glyph_symbolizer.cpp +++ b/include/mapnik/char_info.hpp @@ -19,23 +19,34 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//$Id$ -// mapnik -#include -#include +#ifndef CHAR_INFO_HPP +#define CHAR_INFO_HPP + +#include namespace mapnik { +struct char_properties; -template -void grid_renderer::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - std::clog << "grid_renderer does not yet support glyph_symbolizer\n"; -} +class char_info { +public: + char_info(unsigned c_, double width_, double ymax_, double ymin_, double line_height_) + : c(c_), width(width_), line_height(line_height_), ymin(ymin_), ymax(ymax_) + { + } + char_info() + : c(0), width(0), line_height(0), ymin(0), ymax(0) + { + } -template void grid_renderer::process(glyph_symbolizer const&, - Feature const&, - proj_transform const&); + unsigned c; + double width; + double line_height; + double ymin; + double ymax; + double avg_height; + char_properties *format; + double height() const { return ymax-ymin; } +}; } +#endif diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp index e72dc4538..5ded5076d 100644 --- a/include/mapnik/font_engine_freetype.hpp +++ b/include/mapnik/font_engine_freetype.hpp @@ -145,13 +145,6 @@ private: class MAPNIK_DECL font_face_set : private boost::noncopyable { public: - class dimension_t { - public: - dimension_t(unsigned width_, int ymax_, int ymin_) : width(width_), height(ymax_-ymin_), ymin(ymin_) {} - unsigned width, height; - int ymin; - }; - font_face_set(void) : faces_() {} @@ -179,9 +172,9 @@ public: return boost::make_shared(*faces_.begin(), 0); } - dimension_t character_dimensions(const unsigned c); + char_info character_dimensions(const unsigned c); - void get_string_info(string_info & info); + void get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format); void set_pixel_sizes(unsigned size) { @@ -200,7 +193,7 @@ public: } private: std::vector faces_; - std::map dimension_cache_; + std::map dimension_cache_; }; // FT_Stroker wrapper @@ -313,6 +306,18 @@ public: return face_set; } + face_set_ptr get_face_set(std::string const& name, font_set const& fset) + { + if (fset.size() > 0) + { + return get_face_set(fset); + } + else + { + return get_face_set(name); + } + } + stroker_ptr get_stroker() { return stroker_; @@ -330,247 +335,21 @@ struct text_renderer : private boost::noncopyable struct glyph_t : boost::noncopyable { FT_Glyph image; - glyph_t(FT_Glyph image_) : image(image_) {} + char_properties *properties; + glyph_t(FT_Glyph image_, char_properties *properties_) : image(image_), properties(properties_) {} ~glyph_t () { FT_Done_Glyph(image);} }; typedef boost::ptr_vector glyphs_t; typedef T pixmap_type; - text_renderer (pixmap_type & pixmap, face_set_ptr faces, stroker & s) - : pixmap_(pixmap), - faces_(faces), - stroker_(s), - fill_(0,0,0), - halo_fill_(255,255,255), - halo_radius_(0.0), - opacity_(1.0) {} + text_renderer (pixmap_type & pixmap, face_manager &font_manager_, stroker & s); + box2d prepare_glyphs(text_path *path); + void render(double x0, double y0); + void render_id(int feature_id,double x0, double y0, double min_radius=1.0); - - void set_pixel_size(unsigned size) - { - faces_->set_pixel_sizes(size); - } - - void set_character_size(float size) - { - faces_->set_character_sizes(size); - } - - void set_fill(mapnik::color const& fill) - { - fill_=fill; - } - - void set_halo_fill(mapnik::color const& halo) - { - halo_fill_=halo; - } - - void set_halo_radius( double radius=1.0) - { - halo_radius_=radius; - } - - void set_opacity( double opacity=1.0) - { - opacity_=opacity; - } - - box2d prepare_glyphs(text_path *path) - { - //clear glyphs - glyphs_.clear(); - - FT_Matrix matrix; - FT_Vector pen; - FT_Error error; - - FT_BBox bbox; - bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we - bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later - - for (int i = 0; i < path->num_nodes(); i++) - { - int c; - double x, y, angle; - - path->vertex(&c, &x, &y, &angle); - -#ifdef MAPNIK_DEBUG - // TODO Enable when we have support for setting verbosity - //std::clog << "prepare_glyphs: " << c << "," << x << - // "," << y << "," << angle << std::endl; -#endif - - FT_BBox glyph_bbox; - FT_Glyph image; - - pen.x = int(x * 64); - pen.y = int(y * 64); - - glyph_ptr glyph = faces_->get_glyph(unsigned(c)); - FT_Face face = glyph->get_face()->get_face(); - - matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); - matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); - matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); - matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); - - FT_Set_Transform(face, &matrix, &pen); - - error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING); - if ( error ) - continue; - - error = FT_Get_Glyph(face->glyph, &image); - if ( error ) - continue; - - FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); - if (glyph_bbox.xMin < bbox.xMin) - bbox.xMin = glyph_bbox.xMin; - if (glyph_bbox.yMin < bbox.yMin) - bbox.yMin = glyph_bbox.yMin; - if (glyph_bbox.xMax > bbox.xMax) - bbox.xMax = glyph_bbox.xMax; - if (glyph_bbox.yMax > bbox.yMax) - bbox.yMax = glyph_bbox.yMax; - - // Check if we properly grew the bbox - if ( bbox.xMin > bbox.xMax ) - { - bbox.xMin = 0; - bbox.yMin = 0; - bbox.xMax = 0; - bbox.yMax = 0; - } - - // take ownership of the glyph - glyphs_.push_back(new glyph_t(image)); - } - - return box2d(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); - } - - void render(double x0, double y0) - { - FT_Error error; - FT_Vector start; - unsigned height = pixmap_.height(); - - start.x = static_cast(x0 * (1 << 6)); - start.y = static_cast((height - y0) * (1 << 6)); - - // now render transformed glyphs - typename glyphs_t::iterator pos; - - //make sure we've got reasonable values. - if (halo_radius_ > 0.0 && halo_radius_ < 1024.0) - { - stroker_.init(halo_radius_); - for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) - { - FT_Glyph g; - error = FT_Glyph_Copy(pos->image, &g); - if (!error) - { - FT_Glyph_Transform(g,0,&start); - FT_Glyph_Stroke(&g,stroker_.get(),1); - error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); - if ( ! error ) - { - - FT_BitmapGlyph bit = (FT_BitmapGlyph)g; - render_bitmap(&bit->bitmap, halo_fill_.rgba(), - bit->left, - height - bit->top); - } - } - FT_Done_Glyph(g); - } - } - //render actual text - for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) - { - - FT_Glyph_Transform(pos->image,0,&start); - - error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1); - if ( ! error ) - { - - FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image; - render_bitmap(&bit->bitmap, fill_.rgba(), - bit->left, - height - bit->top); - } - } - } - - void render_id(int feature_id,double x0, double y0, double min_radius=1.0) - { - FT_Error error; - FT_Vector start; - unsigned height = pixmap_.height(); - - start.x = static_cast(x0 * (1 << 6)); - start.y = static_cast((height - y0) * (1 << 6)); - - // now render transformed glyphs - typename glyphs_t::iterator pos; - - stroker_.init(std::max(halo_radius_,min_radius)); - for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) - { - FT_Glyph g; - error = FT_Glyph_Copy(pos->image, &g); - if (!error) - { - FT_Glyph_Transform(g,0,&start); - FT_Glyph_Stroke(&g,stroker_.get(),1); - error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); - //error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_MONO,0,1); - if ( ! error ) - { - - FT_BitmapGlyph bit = (FT_BitmapGlyph)g; - render_bitmap_id(&bit->bitmap, feature_id, - bit->left, - height - bit->top); - } - } - FT_Done_Glyph(g); - } - } - private: - - // unused currently, stroker is the new method for drawing halos - /* - void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius) - { - int x_max=x+bitmap->width; - int y_max=y+bitmap->rows; - int i,p,j,q; - - for (i=x,p=0;ibuffer[q*bitmap->width+p]; - if (gray) - { - for (int n=-halo_radius_; n <=halo_radius_; ++n) - for (int m=-halo_radius_;m <= halo_radius_; ++m) - pixmap_.blendPixel2(i+m,j+n,rgba,gray,opacity_); - } - } - } - } - */ - - void render_bitmap(FT_Bitmap *bitmap,unsigned rgba,int x,int y) + void render_bitmap(FT_Bitmap *bitmap, unsigned rgba, int x, int y, double opacity) { int x_max=x+bitmap->width; int y_max=y+bitmap->rows; @@ -583,7 +362,7 @@ private: int gray=bitmap->buffer[q*bitmap->width+p]; if (gray) { - pixmap_.blendPixel2(i,j,rgba,gray,opacity_); + pixmap_.blendPixel2(i, j, rgba, gray, opacity); } } } @@ -610,14 +389,11 @@ private: } pixmap_type & pixmap_; - face_set_ptr faces_; + face_manager &font_manager_; stroker & stroker_; - color fill_; - color halo_fill_; - double halo_radius_; glyphs_t glyphs_; - double opacity_; }; +typedef face_manager face_manager_freetype; } #endif // MAPNIK_FONT_ENGINE_FREETYPE_HPP diff --git a/include/mapnik/glyph_symbolizer.hpp b/include/mapnik/glyph_symbolizer.hpp deleted file mode 100644 index 25daf62b8..000000000 --- a/include/mapnik/glyph_symbolizer.hpp +++ /dev/null @@ -1,215 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#ifndef MAPNIK_GLYPH_SYMBOLIZER_HPP -#define MAPNIK_GLYPH_SYMBOLIZER_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// boost -#include - -namespace mapnik -{ - -typedef boost::tuple position; - -enum angle_mode_enum { - AZIMUTH, - TRIGONOMETRIC, - angle_mode_enum_MAX -}; - -DEFINE_ENUM(angle_mode_e, angle_mode_enum); - -struct MAPNIK_DECL glyph_symbolizer : public symbolizer_base -{ - glyph_symbolizer(std::string face_name, expression_ptr c) - : symbolizer_base(), - face_name_(face_name), - char_(c), - angle_(), - value_(), - size_(), - color_(), - colorizer_(), - allow_overlap_(false), - avoid_edges_(false), - displacement_(0.0, 0.0), - halo_fill_(color(255,255,255)), - halo_radius_(0), - angle_mode_(TRIGONOMETRIC) {} - - std::string const& get_face_name() const - { - return face_name_; - } - void set_face_name(std::string face_name) - { - face_name_ = face_name; - } - - expression_ptr get_char() const - { - return char_; - } - void set_char(expression_ptr c) - { - char_ = c; - } - - bool get_allow_overlap() const - { - return allow_overlap_; - } - void set_allow_overlap(bool allow_overlap) - { - allow_overlap_ = allow_overlap; - } - - bool get_avoid_edges() const - { - return avoid_edges_; - } - void set_avoid_edges(bool avoid_edges) - { - avoid_edges_ = avoid_edges; - } - - void set_displacement(double x, double y) - { - displacement_ = boost::make_tuple(x,y); - } - position const& get_displacement() const - { - return displacement_; - } - - void set_halo_fill(color const& fill) - { - halo_fill_ = fill; - } - color const& get_halo_fill() const - { - return halo_fill_; - } - - void set_halo_radius(unsigned radius) - { - halo_radius_ = radius; - } - - unsigned get_halo_radius() const - { - return halo_radius_; - } - - expression_ptr get_angle() const - { - return angle_; - } - void set_angle(expression_ptr angle) - { - angle_ = angle; - } - - expression_ptr get_value() const - { - return value_; - } - void set_value(expression_ptr value) - { - value_ = value; - } - - expression_ptr get_size() const - { - return size_; - } - void set_size(expression_ptr size) - { - size_ = size; - } - - expression_ptr get_color() const - { - return color_; - } - void set_color(expression_ptr color) - { - color_ = color; - } - - raster_colorizer_ptr get_colorizer() const - { - return colorizer_; - } - void set_colorizer(raster_colorizer_ptr const& colorizer) - { - colorizer_ = colorizer; - } - void set_angle_mode(angle_mode_e angle_mode) - { - angle_mode_ = angle_mode; - } - angle_mode_e get_angle_mode() const - { - return angle_mode_; - } - - - text_path_ptr get_text_path(face_set_ptr const& faces, - Feature const& feature) const; - UnicodeString eval_char(Feature const& feature) const; - double eval_angle(Feature const& feature) const; - unsigned eval_size(Feature const& feature) const; - color eval_color(Feature const& feature) const; - - -private: - std::string face_name_; - expression_ptr char_; - expression_ptr angle_; - expression_ptr value_; - expression_ptr size_; - expression_ptr color_; - raster_colorizer_ptr colorizer_; - bool allow_overlap_; - bool avoid_edges_; - position displacement_; - color halo_fill_; - unsigned halo_radius_; - angle_mode_e angle_mode_; -}; - -} // end mapnik namespace - -#endif // MAPNIK_GLYPH_SYMBOLIZER_HPP diff --git a/include/mapnik/grid/grid.hpp b/include/mapnik/grid/grid.hpp index 03679eab2..398826139 100644 --- a/include/mapnik/grid/grid.hpp +++ b/include/mapnik/grid/grid.hpp @@ -24,7 +24,6 @@ #define MAPNIK_GRID_HPP // mapnik -#include #include #include #include diff --git a/include/mapnik/grid/grid_renderer.hpp b/include/mapnik/grid/grid_renderer.hpp index 54b0bae29..4517e4845 100644 --- a/include/mapnik/grid/grid_renderer.hpp +++ b/include/mapnik/grid/grid_renderer.hpp @@ -98,9 +98,6 @@ public: void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, Feature const& /*feature*/, proj_transform const& /*prj_trans*/) diff --git a/include/mapnik/jpeg_io.hpp b/include/mapnik/jpeg_io.hpp index f5d96bc71..0f681d017 100644 --- a/include/mapnik/jpeg_io.hpp +++ b/include/mapnik/jpeg_io.hpp @@ -27,8 +27,11 @@ #include +#include + extern "C" { +#include #include } diff --git a/include/mapnik/label_placement.hpp b/include/mapnik/label_placement.hpp deleted file mode 100644 index a667516a9..000000000 --- a/include/mapnik/label_placement.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#ifndef MAPNIK_LABEL_PLACEMENT_HPP -#define MAPNIK_LABEL_PLACEMENT_HPP - -namespace mapnik -{ -struct point_ -{ - double x; - double y; - point_() - : x(0),y(0) {} - point_(double x_,double y_) - : x(x_),y(y_) {} -}; - -class label_placement -{ -private: - point_ anchor_; - point_ displacement_; - double rotation_; -public: - label_placement() - : anchor_(), - displacement_(), - rotation_(0.0) {} - -}; -} - -#endif // MAPNIK_LABEL_PLACEMENT_HPP diff --git a/include/mapnik/metawriter.hpp b/include/mapnik/metawriter.hpp index a25be915b..68792f4e4 100644 --- a/include/mapnik/metawriter.hpp +++ b/include/mapnik/metawriter.hpp @@ -41,7 +41,7 @@ namespace mapnik { -struct placement; +class text_placement_info; /** Implementation of std::map that also returns const& for operator[]. */ class metawriter_property_map @@ -103,8 +103,8 @@ public: virtual void add_box(box2d const& box, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties)=0; - virtual void add_text(placement const& placement, - face_set_ptr face, + virtual void add_text(text_placement_info const& placement, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties)=0; diff --git a/include/mapnik/metawriter_inmem.hpp b/include/mapnik/metawriter_inmem.hpp index 5fb00d624..870bdcb2d 100644 --- a/include/mapnik/metawriter_inmem.hpp +++ b/include/mapnik/metawriter_inmem.hpp @@ -65,8 +65,8 @@ public: virtual void add_box(box2d const& box, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); - virtual void add_text(placement const& p, - face_set_ptr face, + virtual void add_text(text_placement_info const& p, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); diff --git a/include/mapnik/metawriter_json.hpp b/include/mapnik/metawriter_json.hpp index eb7ae8ca9..6c30c2355 100644 --- a/include/mapnik/metawriter_json.hpp +++ b/include/mapnik/metawriter_json.hpp @@ -45,8 +45,8 @@ public: virtual void add_box(box2d const& box, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); - virtual void add_text(placement const& p, - face_set_ptr face, + virtual void add_text(text_placement_info const& p, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); diff --git a/include/mapnik/octree.hpp b/include/mapnik/octree.hpp index 755fbb3cc..bdba82890 100644 --- a/include/mapnik/octree.hpp +++ b/include/mapnik/octree.hpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace mapnik { diff --git a/include/mapnik/placement_finder.hpp b/include/mapnik/placement_finder.hpp index bf8869ec4..37a0219f6 100644 --- a/include/mapnik/placement_finder.hpp +++ b/include/mapnik/placement_finder.hpp @@ -23,86 +23,34 @@ #ifndef MAPNIK_PLACEMENT_FINDER_HPP #define MAPNIK_PLACEMENT_FINDER_HPP -// mapnik -#include -#include -#include -#include #include -#include #include -// stl -#include - namespace mapnik { -typedef text_path placement_element; - -struct placement : boost::noncopyable -{ - placement(string_info & info_, shield_symbolizer const& sym, double scale_factor, unsigned w, unsigned h, bool has_dimensions_= false); - - placement(string_info & info_, text_symbolizer const& sym, double scale_factor); - - ~placement(); - - string_info & info; // should only be used for finding placement. doesn't necessarily match placements.vertex() values - - double scale_factor_; - label_placement_e label_placement; - - std::queue< box2d > envelopes; - - //output - boost::ptr_vector placements; - - int wrap_width; - bool wrap_before; // wraps text at wrap_char immediately before current word - unsigned char wrap_char; - int text_ratio; - - int label_spacing; // distance between repeated labels on a single geometry - unsigned label_position_tolerance; //distance the label can be moved on the line to fit, if 0 the default is used - bool force_odd_labels; //Always try render an odd amount of labels - - double max_char_angle_delta; - double minimum_distance; - double minimum_padding; - double minimum_path_length; - bool avoid_edges; - bool has_dimensions; - bool allow_overlap; - std::pair dimensions; - bool collect_extents; - box2d extents; - - // additional boxes attached to the text labels which must also be - // placed in order for the text placement to succeed. e.g: shields. - std::vector > additional_boxes; -}; - - template class placement_finder : boost::noncopyable { public: - placement_finder(DetectorT & detector); - placement_finder(DetectorT & detector, box2d const& extent); + placement_finder(text_placement_info &p, string_info &info, DetectorT & detector); + placement_finder(text_placement_info &p, string_info &info, DetectorT & detector, box2d const& extent); //Try place a single label at the given point - void find_point_placement(placement & p, text_placement_info_ptr po, double pos_x, double pos_y, double angle=0.0, unsigned line_spacing=0, unsigned character_spacing=0); + void find_point_placement(double pos_x, double pos_y, double angle=0.0); //Iterate over the given path, placing point labels with respect to label_spacing template - void find_point_placements(placement & p, text_placement_info_ptr po, T & path); + void find_point_placements(T & path); //Iterate over the given path, placing line-following labels with respect to label_spacing template - void find_line_placements(placement & p, text_placement_info_ptr po, T & path); + void find_line_placements(T & path); - void update_detector(placement & p); + //Find placement, automatically select point or line placement + void find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans); + + void update_detector(); void clear(); @@ -117,15 +65,14 @@ private: // otherwise it will autodetect the orientation. // If >= 50% of the characters end up upside down, it will be retried the other way. // RETURN: 1/-1 depending which way up the string ends up being. - std::auto_ptr get_placement_offset(placement & p, - const std::vector & path_positions, + std::auto_ptr get_placement_offset(const std::vector & path_positions, const std::vector & path_distances, int & orientation, unsigned index, double distance); ///Tests wether the given placement_element be placed without a collision // Returns true if it can // NOTE: This edits p.envelopes so it can be used afterwards (you must clear it otherwise) - bool test_placement(placement & p, const std::auto_ptr & current_placement, const int & orientation); + bool test_placement(const std::auto_ptr & current_placement, const int & orientation); ///Does a line-circle intersect calculation // NOTE: Follow the strict pre conditions @@ -137,14 +84,26 @@ private: const double &x1, const double &y1, const double &x2, const double &y2, double &ix, double &iy); + void find_line_breaks(); + void init_string_size(); + void init_alignment(); + void adjust_position(placement_element *current_placement, double label_x, double label_y); + ///General Internals - - - DetectorT & detector_; box2d const& dimensions_; + string_info &info_; + text_symbolizer_properties &p; + text_placement_info π + double string_width_; + double string_height_; + double first_line_space_; + vertical_alignment_e valign_; + horizontal_alignment_e halign_; + std::vector line_breaks_; + std::vector > line_sizes_; }; - } + #endif // MAPNIK_PLACEMENT_FINDER_HPP diff --git a/include/mapnik/ptree_helpers.hpp b/include/mapnik/ptree_helpers.hpp index c97ce4d55..c3790ebdf 100644 --- a/include/mapnik/ptree_helpers.hpp +++ b/include/mapnik/ptree_helpers.hpp @@ -258,7 +258,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i } else { - str = node.get_optional(name ); + str = node.get_optional(name+"."); } if ( str ) { @@ -289,7 +289,7 @@ inline color get(boost::property_tree::ptree const& node, std::string const& nam } else { - str = node.get_optional(name ); + str = node.get_optional(name+"."); } if ( str ) @@ -322,7 +322,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i } else { - str = node.get_optional(name); + str = node.get_optional(name+"."); } if ( ! str ) { @@ -348,7 +348,18 @@ T get_value(const boost::property_tree::ptree & node, const std::string & name) { try { - return node.get_value(); + /* NOTE: get_child works as long as there is only one child with that name. + If this function is used this used this condition must always be satisfied. + */ + return node.get_child("").get_value(); + } + catch (boost::property_tree::ptree_bad_path) + { + /* If the XML parser did not find any non-empty data element the is no + node. But we don't want to fail here but simply return a + default constructed value of the requested type. + */ + return T(); } catch (...) { @@ -369,7 +380,7 @@ boost::optional get_optional(const boost::property_tree::ptree & node, const } else { - str = node.get_optional(name); + str = node.get_optional(name+"."); } boost::optional result; @@ -403,7 +414,7 @@ inline boost::optional get_optional(const boost::property_tree::ptree & n } else { - str = node.get_optional(name); + str = node.get_optional(name+"."); } boost::optional result; diff --git a/include/mapnik/rule.hpp b/include/mapnik/rule.hpp index 1fa8def03..9895578c3 100644 --- a/include/mapnik/rule.hpp +++ b/include/mapnik/rule.hpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -109,11 +108,6 @@ inline bool operator==(markers_symbolizer const& lhs, return (&lhs == &rhs); } -inline bool operator==(glyph_symbolizer const& lhs, - glyph_symbolizer const& rhs) -{ - return (&lhs == &rhs); -} typedef boost::variant symbolizer; + markers_symbolizer> symbolizer; diff --git a/include/mapnik/shield_symbolizer.hpp b/include/mapnik/shield_symbolizer.hpp index 847bff663..b5aaf94e8 100644 --- a/include/mapnik/shield_symbolizer.hpp +++ b/include/mapnik/shield_symbolizer.hpp @@ -36,6 +36,8 @@ namespace mapnik struct MAPNIK_DECL shield_symbolizer : public text_symbolizer, public symbolizer_with_image { + shield_symbolizer(text_placements_ptr placements = text_placements_ptr( + boost::make_shared())); shield_symbolizer(expression_ptr name, std::string const& face_name, float size, diff --git a/include/mapnik/svg_renderer.hpp b/include/mapnik/svg_renderer.hpp index 3459d0eb3..d4e5e1773 100644 --- a/include/mapnik/svg_renderer.hpp +++ b/include/mapnik/svg_renderer.hpp @@ -83,9 +83,6 @@ namespace mapnik void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); /*! * @brief Overload that process the whole set of symbolizers of a rule. diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index 546c4c1fe..159664617 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -96,10 +96,10 @@ public: void set_opacity(float opacity); float get_opacity() const; protected: - symbolizer_with_image(path_expression_ptr filename); + symbolizer_with_image(path_expression_ptr filename = path_expression_ptr()); symbolizer_with_image(symbolizer_with_image const& rhs); path_expression_ptr image_filename_; - float opacity_; + float image_opacity_; transform_type matrix_; }; } diff --git a/include/mapnik/symbolizer_helpers.hpp b/include/mapnik/symbolizer_helpers.hpp new file mode 100644 index 000000000..e471bb026 --- /dev/null +++ b/include/mapnik/symbolizer_helpers.hpp @@ -0,0 +1,165 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#ifndef SYMBOLIZER_HELPERS_HPP +#define SYMBOLIZER_HELPERS_HPP + +#include +#include +#include +#include +#include + +#include + + +namespace mapnik { + +template +class text_symbolizer_helper +{ +public: + text_symbolizer_helper(unsigned width, + unsigned height, + double scale_factor, + CoordTransform const &t, + FaceManagerT &font_manager, + DetectorT &detector) : + width_(width), + height_(height), + scale_factor_(scale_factor), + t_(t), + font_manager_(font_manager), + detector_(detector), + text_() + { + + } + + text_placement_info_ptr get_placement(text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); +private: + bool initialize_geometries(text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans); + + unsigned width_; + unsigned height_; + double scale_factor_; + CoordTransform const &t_; + FaceManagerT &font_manager_; + DetectorT &detector_; + boost::shared_ptr text_; /*TODO: Use shared pointers for text placement so we don't need to keep a reference here! */ + // Use a boost::ptr_vector here instread of std::vector? + std::vector geometries_to_process_; +}; + + +template +text_placement_info_ptr text_symbolizer_helper::get_placement( + text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) +{ + if (!initialize_geometries(sym, feature, prj_trans)) return text_placement_info_ptr(); + + text_ = boost::shared_ptr(new processed_text(font_manager_, scale_factor_)); + metawriter_with_properties writer = sym.get_metawriter(); + + box2d dims(0, 0, width_, height_); + + text_placement_info_ptr placement = sym.get_placement_options()->get_placement_info(); + placement->init(scale_factor_, width_, height_); + if (writer.first) + placement->collect_extents = true; + + unsigned num_geom = feature.num_geometries(); + if (!num_geom) return text_placement_info_ptr(); //Nothing to do + + while (placement->next()) + { + text_processor &processor = placement->properties.processor; + text_symbolizer_properties const& p = placement->properties; + /* TODO: Simplify this. */ + text_->clear(); + processor.process(*text_, feature); + string_info &info = text_->get_string_info(); + /* END TODO */ + double angle = 0.0; + if (p.orientation) + { + angle = boost::apply_visitor(evaluate(feature),*(p.orientation)).to_double(); + } + placement_finder finder(*placement, info, detector_, dims); + + unsigned num_geom = feature.num_geometries(); + for (unsigned i=0; iplacements.size()) + continue; + if (writer.first) writer.first->add_text(*placement, font_manager_, feature, t_, writer.second); + return placement; + } + } + return text_placement_info_ptr(); +} + +template +bool text_symbolizer_helper::initialize_geometries( + text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans) +{ + unsigned num_geom = feature.num_geometries(); + for (unsigned i=0; i 0) + { + // TODO - find less costly method than fetching full envelope + box2d gbox = t_.forward(geom.envelope(),prj_trans); + if (gbox.width() < sym.get_minimum_path_length()) + { + continue; + } + } + // TODO - calculate length here as well + geometries_to_process_.push_back(const_cast(&geom)); + } + + if (!geometries_to_process_.size() > 0) + { + // early return to avoid significant overhead of rendering setup + return false; + } + return true; +} + +} +#endif // SYMBOLIZER_HELPERS_HPP diff --git a/include/mapnik/text_path.hpp b/include/mapnik/text_path.hpp index eeebd97c4..b7d7daaf1 100644 --- a/include/mapnik/text_path.hpp +++ b/include/mapnik/text_path.hpp @@ -23,6 +23,12 @@ #ifndef MAPNIK_TEXT_PATH_HPP #define MAPNIK_TEXT_PATH_HPP +// mapnik +#include + +//stl +#include + // boost #include #include @@ -32,41 +38,39 @@ namespace mapnik { -struct character_info -{ - int character; - double width, height; - - character_info() : character(0), width(0), height(0) {} - character_info(int c_, double width_, double height_) : character(c_), width(width_), height(height_) {} - ~character_info() {} - - character_info(const character_info &ci) - : character(ci.character), width(ci.width), height(ci.height) - { - } - -}; class string_info : private boost::noncopyable { protected: - typedef boost::ptr_vector characters_t; + typedef std::vector characters_t; characters_t characters_; - UnicodeString const& text_; - double width_; - double height_; + UnicodeString text_; bool is_rtl; public: string_info(UnicodeString const& text) - : text_(text), - width_(0), - height_(0), - is_rtl(false) {} - - void add_info(int c, double width, double height) + : characters_(), + text_(text), + is_rtl(false) { - characters_.push_back(new character_info(c, width, height)); + + } + + string_info() + : characters_(), + text_(), + is_rtl(false) + { + + } + + void add_info(char_info const& info) + { + characters_.push_back(info); + } + + void add_text(UnicodeString text) + { + text_ += text; } unsigned num_characters() const @@ -74,29 +78,25 @@ public: return characters_.size(); } - void set_rtl(bool value) {is_rtl = value;} - bool get_rtl() const {return is_rtl;} + void set_rtl(bool value) + { + is_rtl = value; + } + + bool get_rtl() const + { + return is_rtl; + } - character_info at(unsigned i) const + char_info const& at(unsigned i) const { return characters_[i]; } - character_info operator[](unsigned i) const + char_info const& operator[](unsigned i) const { return at(i); } - - void set_dimensions(double width, double height) - { - width_ = width; - height_ = height; - } - - std::pair get_dimensions() const - { - return std::pair(width_, height_); - } UnicodeString const& get_string() const { @@ -108,69 +108,84 @@ public: UChar break_char = '\n'; return (text_.indexOf(break_char) >= 0); } + + /** Resets object to initial state. */ + void clear(void) + { + text_ = ""; + characters_.clear(); + } }; -struct text_path : boost::noncopyable + +/** List of all characters and their positions and formats for a placement. */ +class text_path : boost::noncopyable { struct character_node { int c; double x, y, angle; + char_properties *format; - character_node(int c_, double x_, double y_, double angle_) - : c(c_), x(x_), y(y_), angle(angle_) {} + character_node(int c_, double x_, double y_, double angle_, char_properties *format_) + : c(c_), x(x_), y(y_), angle(angle_), format(format_) {} ~character_node() {} - void vertex(int *c_, double *x_, double *y_, double *angle_) + void vertex(int *c_, double *x_, double *y_, double *angle_, char_properties **format_) { *c_ = c; *x_ = x; *y_ = y; *angle_ = angle; + *format_ = format; } }; + int itr_; +public: typedef std::vector character_nodes_t; + character_nodes_t nodes_; double starting_x; double starting_y; - character_nodes_t nodes_; - int itr_; - std::pair string_dimensions; +// std::pair string_dimensions; text_path() - : starting_x(0), - starting_y(0), - itr_(0) {} - - //text_path(text_path const& other) : - // itr_(0), - // nodes_(other.nodes_), - // string_dimensions(other.string_dimensions) - //{} + : itr_(0), + starting_x(0), + starting_y(0) + + { + + } ~text_path() {} - void add_node(int c, double x, double y, double angle) + /** Adds a new char to the list. */ + void add_node(int c, double x, double y, double angle, char_properties *format) { - nodes_.push_back(character_node(c, x, y, angle)); + nodes_.push_back(character_node(c, x, y, angle, format)); } - void vertex(int *c, double *x, double *y, double *angle) + /** Return node. Always returns a new node. Has no way to report that there are no more nodes. */ + void vertex(int *c, double *x, double *y, double *angle, char_properties **format) { - nodes_[itr_++].vertex(c, x, y, angle); + nodes_[itr_++].vertex(c, x, y, angle, format); } + /** Start again at first node. */ void rewind() { itr_ = 0; } + /** Number of nodes. */ int num_nodes() const { return nodes_.size(); } + /** Delete all nodes. */ void clear() { nodes_.clear(); diff --git a/include/mapnik/text_placements.hpp b/include/mapnik/text_placements.hpp index 7921031c0..fdb6530dc 100644 --- a/include/mapnik/text_placements.hpp +++ b/include/mapnik/text_placements.hpp @@ -24,20 +24,30 @@ #define MAPNIK_TEXT_PLACEMENTS_HPP // mapnik -#include -#include +#include +#include +#include +#include +#include // stl #include #include +#include +#include // boost #include #include #include +#include namespace mapnik { +class text_placements; + +typedef text_path placement_element; + typedef boost::tuple position; enum label_placement_enum { @@ -82,89 +92,140 @@ enum justify_alignment DEFINE_ENUM( justify_alignment_e, justify_alignment ); -enum text_transform +/** Contains all text symbolizer properties which are not directly related to text formating. */ +struct text_symbolizer_properties { - NONE = 0, - UPPERCASE, - LOWERCASE, - CAPITALIZE, - text_transform_MAX -}; + text_symbolizer_properties(); + /** Load all values and also the ```processor``` object from XML ptree. */ + void set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets); + /** Save all values to XML ptree (but does not create a new parent node!). */ + void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_symbolizer_properties const &dfl=text_symbolizer_properties()) const; -DEFINE_ENUM( text_transform_e, text_transform ); - -enum placement_type -{ - T_SIMPLE = 0, - T_DUMMY, - placement_type_MAX -}; - -DEFINE_ENUM( placement_type_e, placement_type ); - -class text_placements; - -class text_placement_info : boost::noncopyable -{ -public: - text_placement_info(text_placements const* parent); - /** Get next placement. - * This function is also called before the first placement is tried. */ - virtual bool next()=0; - /** Get next placement position. - * This function is also called before the first position is used. - * Each class has to return at least one position! - * If this functions returns false the placement data should be considered invalid! - */ - virtual bool next_position_only()=0; - virtual ~text_placement_info() {} - - /* NOTE: Values are public and non-virtual to avoid any performance problems. */ + //Per symbolizer options + expression_ptr orientation; position displacement; - float text_size; + label_placement_e label_placement; horizontal_alignment_e halign; justify_alignment_e jalign; vertical_alignment_e valign; + /** distance between repeated labels on a single geometry */ + unsigned label_spacing; + /** distance the label can be moved on the line to fit, if 0 the default is used */ + unsigned label_position_tolerance; + bool avoid_edges; + double minimum_distance; + double minimum_padding; + double minimum_path_length; + double max_char_angle_delta; + /** Always try render an odd amount of labels */ + bool force_odd_labels; + bool allow_overlap; + unsigned text_ratio; + unsigned wrap_width; + /** Contains everything related to text formating */ + text_processor processor; +}; + + +/** Generate a possible placement and store results of placement_finder. + * This placement has first to be tested by placement_finder to verify it + * can actually be used. + */ +class text_placement_info : boost::noncopyable +{ +public: + /** Constructor. Takes the parent text_placements object as a parameter + * to read defaults from it. */ + text_placement_info(text_placements const* parent); + /** Get next placement. + * This function is also called before the first placement is tried. + * Each class has to return at least one position! + * If this functions returns false the placement data should be + * considered invalid! + */ + virtual bool next()=0; + virtual ~text_placement_info() {} + /** Initialize values used by placement finder. Only has to be done once + * per object. + */ + void init(double scale_factor_, + unsigned w = 0, unsigned h = 0, bool has_dimensions_ = false); + + /** Properties actually used by placement finder and renderer. Values in + * here are modified each time next() is called. */ + text_symbolizer_properties properties; + + /** Scale factor used by the renderer. */ + double scale_factor; + /* TODO: Don't know what this is used for. */ + bool has_dimensions; + /* TODO: Don't know what this is used for. */ + std::pair dimensions; + /** Set scale factor. */ + void set_scale_factor(double factor) { scale_factor = factor; } + /** Get scale factor. */ + double get_scale_factor() { return scale_factor; } + /** Get label spacing taking the scale factor into account. */ + double get_actual_label_spacing() { return scale_factor * properties.label_spacing; } + /** Get minimum distance taking the scale factor into account. */ + double get_actual_minimum_distance() { return scale_factor * properties.minimum_distance; } + /** Get minimum padding taking the scale factor into account. */ + double get_actual_minimum_padding() { return scale_factor * properties.minimum_padding; } + + /** Collect a bounding box of all texts placed. */ + bool collect_extents; + //Output by placement finder + /** Bounding box of all texts placed. */ + box2d extents; + /* TODO */ + std::queue< box2d > envelopes; + /* TODO */ + boost::ptr_vector placements; }; typedef boost::shared_ptr text_placement_info_ptr; +/** This object handles the management of all TextSymbolizer properties. It can + * be used as a base class for own objects which implement new processing + * semantics. Basically this class just makes sure a pointer of the right + * class is returned by the get_placement_info call. + */ class text_placements { public: - text_placements() : - text_size_(10), halign_(H_MIDDLE), jalign_(J_MIDDLE), valign_(V_MIDDLE) {} + text_placements(); + /** Get a text_placement_info object to use in rendering. + * The returned object creates a list of settings which is + * used to try to find a placement and stores all + * information that is generated by + * the placement finder. + * + * This function usually is implemented as + * text_placement_info_ptr text_placements_XXX::get_placement_info() const + * { + * return text_placement_info_ptr(new text_placement_info_XXX(this)); + * } + */ virtual text_placement_info_ptr get_placement_info() const =0; + /** Get a list of all expressions used in any placement. + * This function is used to collect attributes. + */ + virtual std::set get_all_expressions(); - virtual void set_default_text_size(float size) { text_size_ = size; } - float get_default_text_size() const { return text_size_; } - - virtual void set_default_displacement(position const& displacement) { displacement_ = displacement;} - position const& get_default_displacement() { return displacement_; } - - virtual void set_default_halign(horizontal_alignment_e const& align) { halign_ = align;} - horizontal_alignment_e const& get_default_halign() { return halign_; } - - virtual void set_default_jalign(justify_alignment_e const& align) { jalign_ = align;} - justify_alignment_e const& get_default_jalign() { return jalign_; } - - virtual void set_default_valign(vertical_alignment_e const& align) { valign_ = align;} - vertical_alignment_e const& get_default_valign() { return valign_; } - + /** Destructor. */ virtual ~text_placements() {} -protected: - float text_size_; - position displacement_; - horizontal_alignment_e halign_; - justify_alignment_e jalign_; - vertical_alignment_e valign_; - friend class text_placement_info; + + /** List of all properties used as the default for the subclasses. */ + text_symbolizer_properties properties; }; +/** Pointer to object of class text_placements */ typedef boost::shared_ptr text_placements_ptr; + class text_placements_info_dummy; +/** Dummy placement algorithm. Always takes the default value. */ class MAPNIK_DECL text_placements_dummy: public text_placements { public: @@ -172,20 +233,20 @@ public: friend class text_placement_info_dummy; }; +/** Placement info object for dummy placement algorithm. Always takes the default value. */ class MAPNIK_DECL text_placement_info_dummy : public text_placement_info { public: text_placement_info_dummy(text_placements_dummy const* parent) : text_placement_info(parent), - state(0), position_state(0), parent_(parent) {} + state(0), parent_(parent) {} bool next(); - bool next_position_only(); private: unsigned state; - unsigned position_state; text_placements_dummy const* parent_; }; + } //namespace #endif // MAPNIK_TEXT_PLACEMENTS_HPP diff --git a/include/mapnik/text_placements_list.hpp b/include/mapnik/text_placements_list.hpp new file mode 100644 index 000000000..e43fe724a --- /dev/null +++ b/include/mapnik/text_placements_list.hpp @@ -0,0 +1,61 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Hermann Kraus + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#ifndef TEXT_PLACEMENTS_LIST_HPP +#define TEXT_PLACEMENTS_LIST_HPP +#include + +namespace mapnik { + +class text_placement_info_list; + + +/** Tries a list of placements. */ +class text_placements_list: public text_placements +{ +public: + text_placements_list(); + text_placement_info_ptr get_placement_info() const; + virtual std::set get_all_expressions(); + text_symbolizer_properties & add(); + text_symbolizer_properties & get(unsigned i); + unsigned size() const; +private: + std::vector list_; + friend class text_placement_info_list; +}; + +/** List placement strategy. + * See parent class for documentation of each function. */ +class text_placement_info_list : public text_placement_info +{ +public: + text_placement_info_list(text_placements_list const* parent) : + text_placement_info(parent), state(0), parent_(parent) {} + bool next(); +private: + unsigned state; + text_placements_list const* parent_; +}; + + +} //namespace +#endif diff --git a/include/mapnik/text_placements_simple.hpp b/include/mapnik/text_placements_simple.hpp index 9a52825e8..36d46c49b 100644 --- a/include/mapnik/text_placements_simple.hpp +++ b/include/mapnik/text_placements_simple.hpp @@ -67,8 +67,8 @@ public: text_placement_info_simple(text_placements_simple const* parent) : text_placement_info(parent), state(0), position_state(0), parent_(parent) {} bool next(); +protected: bool next_position_only(); -private: unsigned state; unsigned position_state; text_placements_simple const* parent_; diff --git a/include/mapnik/text_processing.hpp b/include/mapnik/text_processing.hpp new file mode 100644 index 000000000..450acb0bb --- /dev/null +++ b/include/mapnik/text_processing.hpp @@ -0,0 +1,134 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#ifndef MAPNIK_TEXT_PROCESSING_HPP +#define MAPNIK_TEXT_PROCESSING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +namespace mapnik +{ + +enum text_transform +{ + NONE = 0, + UPPERCASE, + LOWERCASE, + CAPITALIZE, + text_transform_MAX +}; + +DEFINE_ENUM( text_transform_e, text_transform ); + +struct char_properties +{ + char_properties(); + /** Construct object from XML. */ + void set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets); + /** Write object to XML ptree. */ + void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl=char_properties()) const; + std::string face_name; + font_set fontset; + float text_size; + double character_spacing; + double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen + double text_opacity; + bool wrap_before; + unsigned wrap_char; + text_transform_e text_transform; //Per expression + color fill; + color halo_fill; + double halo_radius; +}; + +class processed_expression +{ +public: + processed_expression(char_properties const& properties, UnicodeString const& text) : + p(properties), str(text) {} + char_properties p; + UnicodeString str; +}; + +class processed_text +{ +public: + processed_text(face_manager & font_manager, double scale_factor); + void push_back(processed_expression const& exp); + unsigned size() const { return expr_list_.size(); } + unsigned empty() const { return expr_list_.empty(); } + void clear(); + typedef std::list expression_list; + expression_list::const_iterator begin(); + expression_list::const_iterator end(); + string_info &get_string_info(); +private: + expression_list expr_list_; + face_manager & font_manager_; + double scale_factor_; + string_info info_; +}; + +class abstract_token; + +/** Stores formating information and uses this to produce formated text for a given feature. */ +class text_processor +{ +public: + text_processor(); + /** Construct object from XML. */ + void from_xml(boost::property_tree::ptree const& pt, std::map const &fontsets); + /** Write object to XML ptree. */ + void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_processor const& dfl) const; + + /** Takes a feature and produces formated text as output. + * The output object has to be created by the caller and passed in for thread safety. + */ + void process(processed_text &output, Feature const& feature); + /** Automatically create processing instructions for a single expression. */ + void set_old_style_expression(expression_ptr expr); + /** Add a new formating token. */ + void push_back(abstract_token *token); + /** Get a list of all expressions used in any placement. This function is used to collect attributes. */ + std::set get_all_expressions() const; + /** Default values for char_properties. */ + char_properties defaults; +protected: + void from_xml_recursive(boost::property_tree::ptree const& pt, std::map const &fontsets); +private: + std::list list_; + bool clear_on_write; //Clear list once +}; + +} /* namespace */ + +#endif diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp index 03beb5b04..3519bc186 100644 --- a/include/mapnik/text_symbolizer.hpp +++ b/include/mapnik/text_symbolizer.hpp @@ -26,8 +26,6 @@ // mapnik #include #include -#include -#include #include #include @@ -38,6 +36,13 @@ // stl #include +// Warning disabled for the moment +#if (0 && __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define func_deprecated __attribute__ ((deprecated)) +#else +#define func_deprecated +#endif + namespace mapnik { @@ -45,6 +50,7 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base { // Note - we do not use boost::make_shared below as VC2008 and VC2010 are // not able to compile make_shared used within a constructor + text_symbolizer(text_placements_ptr placements = text_placements_ptr(new text_placements_dummy)); text_symbolizer(expression_ptr name, std::string const& face_name, float size, color const& fill, text_placements_ptr placements = text_placements_ptr(new text_placements_dummy) @@ -54,73 +60,71 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base ); text_symbolizer(text_symbolizer const& rhs); text_symbolizer& operator=(text_symbolizer const& rhs); - expression_ptr get_name() const; + expression_ptr get_name() const func_deprecated; void set_name(expression_ptr expr); - expression_ptr get_orientation() const; // orienation (rotation angle atm) + expression_ptr get_orientation() const func_deprecated; // orienation (rotation angle atm) void set_orientation(expression_ptr expr); - unsigned get_text_ratio() const; // target ratio for text bounding box in pixels + unsigned get_text_ratio() const func_deprecated; // target ratio for text bounding box in pixels void set_text_ratio(unsigned ratio); - unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio + unsigned get_wrap_width() const func_deprecated; // 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 - std::string get_wrap_char_string() const; // character used to wrap lines as std::string + unsigned char get_wrap_char() const func_deprecated; // character used to wrap lines + std::string get_wrap_char_string() const func_deprecated; // character used to wrap lines as std::string void set_wrap_char(unsigned char character); void set_wrap_char_from_string(std::string const& character); - text_transform_e get_text_transform() const; // text conversion on strings before display + text_transform_e get_text_transform() const func_deprecated; // text conversion on strings before display void set_text_transform(text_transform_e convert); - unsigned get_line_spacing() const; // spacing between lines of text + unsigned get_line_spacing() const func_deprecated; // spacing between lines of text void set_line_spacing(unsigned spacing); - unsigned get_character_spacing() const; // spacing between characters in text + unsigned get_character_spacing() const func_deprecated; // spacing between characters in text void set_character_spacing(unsigned spacing); - unsigned get_label_spacing() const; // spacing between repeated labels on lines + unsigned get_label_spacing() const func_deprecated; // 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 + unsigned get_label_position_tolerance() const func_deprecated; //distance the label can be moved on the line to fit, if 0 the default is used void set_label_position_tolerance(unsigned tolerance); - bool get_force_odd_labels() const; // try render an odd amount of labels + bool get_force_odd_labels() const func_deprecated; // try render an odd amount of labels void set_force_odd_labels(bool force); - double get_max_char_angle_delta() const; // maximum change in angle between adjacent characters + double get_max_char_angle_delta() const func_deprecated; // maximum change in angle between adjacent characters void set_max_char_angle_delta(double angle); - float get_text_size() const; + float get_text_size() const func_deprecated; void set_text_size(float size); - std::string const& get_face_name() const; + std::string const& get_face_name() const func_deprecated; void set_face_name(std::string face_name); - font_set const& get_fontset() const; + font_set const& get_fontset() const func_deprecated; void set_fontset(font_set const& fset); - color const& get_fill() const; + color const& get_fill() const func_deprecated; void set_fill(color const& fill); void set_halo_fill(color const& fill); - color const& get_halo_fill() const; + color const& get_halo_fill() const func_deprecated; void set_halo_radius(double radius); - double get_halo_radius() const; + double get_halo_radius() const func_deprecated; void set_label_placement(label_placement_e label_p); - label_placement_e get_label_placement() const; + label_placement_e get_label_placement() const func_deprecated; void set_vertical_alignment(vertical_alignment_e valign); - vertical_alignment_e get_vertical_alignment() const; - void set_anchor(double x, double y); - position const& get_anchor() const; + vertical_alignment_e get_vertical_alignment() const func_deprecated; void set_displacement(double x, double y); void set_displacement(position const& p); - position const& get_displacement() const; + position const& get_displacement() const func_deprecated; void set_avoid_edges(bool avoid); - bool get_avoid_edges() const; + bool get_avoid_edges() const func_deprecated; void set_minimum_distance(double distance); - double get_minimum_distance() const; + double get_minimum_distance() const func_deprecated; void set_minimum_padding(double distance); - double get_minimum_padding() const; + double get_minimum_padding() const func_deprecated; void set_minimum_path_length(double size); - double get_minimum_path_length() const; + double get_minimum_path_length() const func_deprecated; void set_allow_overlap(bool overlap); - bool get_allow_overlap() const; + bool get_allow_overlap() const func_deprecated; void set_text_opacity(double opacity); - double get_text_opacity() const; - bool get_wrap_before() const; // wrap text at wrap_char immediately before current work + double get_text_opacity() const func_deprecated; void set_wrap_before(bool wrap_before); + bool get_wrap_before() const func_deprecated; // wrap text at wrap_char immediately before current work void set_horizontal_alignment(horizontal_alignment_e valign); - horizontal_alignment_e get_horizontal_alignment() const; + horizontal_alignment_e get_horizontal_alignment() const func_deprecated; void set_justify_alignment(justify_alignment_e valign); - justify_alignment_e get_justify_alignment() const; + justify_alignment_e get_justify_alignment() const func_deprecated; text_placements_ptr get_placement_options() const; void set_placement_options(text_placements_ptr placement_options); void set_placement_options(placement_type_e arg, std::string const& placements); @@ -128,32 +132,6 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base std::string get_placements() const; private: - expression_ptr name_; - expression_ptr orientation_; - std::string face_name_; - font_set fontset_; - unsigned text_ratio_; - unsigned wrap_width_; - unsigned char wrap_char_; - text_transform_e text_transform_; - unsigned line_spacing_; - unsigned character_spacing_; - unsigned label_spacing_; - unsigned label_position_tolerance_; - bool force_odd_labels_; - double max_char_angle_delta_; - color fill_; - color halo_fill_; - double halo_radius_; - label_placement_e label_p_; - position anchor_; - bool avoid_edges_; - double minimum_distance_; - double minimum_padding_; - double minimum_path_length_; - bool overlap_; - double text_opacity_; - bool wrap_before_; text_placements_ptr placement_options_; }; } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index a2ef94c34..6309758b8 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/src/agg/process_glyph_symbolizer.cpp b/src/agg/process_glyph_symbolizer.cpp deleted file mode 100644 index 4a82cdb4b..000000000 --- a/src/agg/process_glyph_symbolizer.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ -//$Id$ - -// mapnik -#include - -// agg -#include "agg_basics.h" -#include "agg_rendering_buffer.h" -#include "agg_pixfmt_rgba.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_scanline_u.h" - -namespace mapnik { - -template -void agg_renderer::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name()); - stroker_ptr strk = font_manager_.get_stroker(); - if (faces->size() > 0 && strk) - { - // Get x and y from geometry and translate to pixmap coords. - double x, y, z=0.0; - feature.get_geometry(0).label_position(&x, &y); - prj_trans.backward(x,y,z); - t_.forward(&x, &y); - - text_renderer ren(pixmap_, faces, *strk); - - // set fill and halo colors - color fill = sym.eval_color(feature); - ren.set_fill(fill); - if (fill != color("transparent")) { - ren.set_halo_fill(sym.get_halo_fill()); - ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); - } - - // set font size - float size = sym.eval_size(feature); - ren.set_character_size(size * scale_factor_); - faces->set_character_sizes(size * scale_factor_); - - // Get and render text path - // - text_path_ptr path = sym.get_text_path(faces, feature); - // apply displacement - position pos = sym.get_displacement(); - double dx = boost::get<0>(pos); - double dy = boost::get<1>(pos); - path->starting_x = x = x+dx; - path->starting_y = y = y+dy; - - // Prepare glyphs to set internal state and calculate the marker's - // final box so we can check for a valid placement - box2d dim = ren.prepare_glyphs(path.get()); - box2d ext(x-dim.width()/2, y-dim.height()/2, x+dim.width()/2, y+dim.height()/2); - if ((sym.get_allow_overlap() || detector_->has_placement(ext)) && - (!sym.get_avoid_edges() || detector_->extent().contains(ext))) - { - // Placement is valid, render glyph and update detector. - ren.render(x, y); - detector_->insert(ext); - metawriter_with_properties writer = sym.get_metawriter(); - if (writer.first) writer.first->add_box(ext, feature, t_, writer.second); - } - } - else - { - throw config_error( - "Unable to find specified font face in GlyphSymbolizer" - ); - } -} -template void agg_renderer::process(glyph_symbolizer const&, - Feature const&, - proj_transform const&); -} diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 17f9cd191..11c633259 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_basics.h" diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index eaef2e7e8..df0c977b3 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -24,6 +24,7 @@ // mapnik #include #include +#include // agg #include "agg_basics.h" diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index abe8983b8..a4dc9de28 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "agg_basics.h" #include "agg_rendering_buffer.h" diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 2427cdc3f..991d1018a 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -24,6 +24,7 @@ // mapnik #include #include +#include // agg #include "agg_basics.h" diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index 7d2c02741..fc74ea2ad 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -45,6 +45,7 @@ void agg_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { +#if 0 typedef coord_transform2 path_type; @@ -139,7 +140,7 @@ void agg_renderer::process(shield_symbolizer const& sym, string_info info(text); - faces->get_string_info(info); + faces->get_string_info(info, text, 0); metawriter_with_properties writer = sym.get_metawriter(); @@ -264,6 +265,7 @@ void agg_renderer::process(shield_symbolizer const& sym, } } } +#endif } diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index 91e73b2e6..fd857a576 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -24,231 +24,32 @@ // mapnik #include #include -#include +#include namespace mapnik { -struct largest_bbox_comp -{ - bool operator() (geometry_type const* g0, geometry_type const* g1) const - { - box2d b0 = g0->envelope(); - box2d b1 = g1->envelope(); - return b0.width()*b0.height() < b1.width()*b1.height(); - } - -}; - -typedef boost::tuple point_type; -typedef std::vector label_points_type; - -void get_label_points(label_points_type & labels, - Feature const& feature, - text_symbolizer const& sym, - proj_transform const& prj_trans, - CoordTransform const& t - ) -{ - std::vector geometries_to_process; - - unsigned num_geom = feature.num_geometries(); - for (unsigned i=0; i 0) - { - // TODO - find less costly method than fetching full envelope - box2d gbox = t.forward(geom.envelope(),prj_trans); - if (gbox.width() < sym.get_minimum_path_length()) - { - continue; - } - } - geometries_to_process.push_back(const_cast(&geom)); - } - - vector::const_iterator largest = std::max_element(geometries_to_process.begin(), - geometries_to_process.end(), - largest_bbox_comp()); - if (largest != geometries_to_process.end()) - { - double z=0.0; - double label_x,label_y; - if (sym.get_label_placement() == POINT_PLACEMENT) - (*largest)->label_position(&label_x, &label_y); - else if (sym.get_label_placement() == INTERIOR_PLACEMENT) - (*largest)->label_interior_position(&label_x, &label_y); - else // default to bbox center - { - box2d box = (*largest)->envelope(); - coord2d c = box.center(); - label_x = c.x; - label_y = c.y; - } - prj_trans.backward(label_x,label_y, z); - t.forward(&label_x,&label_y); - - labels.push_back(boost::make_tuple(label_x,label_y)); - } - - /* - std::sort(geometries_to_process.begin(), geometries_to_process.end(), largest_bbox_first()); - BOOST_FOREACH( geometry_type * g, geometries_to_process) - { - double z=0.0; - double label_x,label_y; - if (sym.get_label_placement() == POINT_PLACEMENT) - g->label_position(&label_x, &label_y); - else - g->label_interior_position(&label_x, &label_y); - prj_trans.backward(label_x,label_y, z); - t.forward(&label_x,&label_y); - - labels.push_back(boost::make_tuple(label_x,label_y)); - } - */ -} - template void agg_renderer::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - label_points_type labels; - get_label_points(labels, feature, sym, prj_trans, t_); - - if (labels.size() == 0) return; - - typedef coord_transform2 path_type; + /* This could also be a member of the renderer class, but I would have + to check if any of the variables changes and notify the helper. + It could be done at a later point, but for now keep the code simple. + */ + text_symbolizer_helper, label_collision_detector4> helper(width_, height_, scale_factor_, t_, font_manager_, *detector_); -////////////////////////////////////////////////////////////////////// + text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans); - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - UnicodeString text = result.to_unicode(); - - if ( sym.get_text_transform() == UPPERCASE) + if (!placement) return; + + text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(0); - } - - if ( text.length() <= 0 ) return; - color const& fill = sym.get_fill(); - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) - { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - stroker_ptr strk = font_manager_.get_stroker(); - if (!(faces->size() > 0 && strk)) - { - throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'"); - } - -///////////////////////// -////////////// renderer - text_renderer ren(pixmap_, faces, *strk); - ren.set_fill(fill); - ren.set_halo_fill(sym.get_halo_fill()); - ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); - ren.set_opacity(sym.get_text_opacity()); - -///////////////////////// - -////// - box2d dims(0,0,width_,height_); - placement_finder finder(*detector_,dims); - - -/////////// - - bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - while (!placement_found && placement_options->next()) - { - - ren.set_character_size(placement_options->text_size * scale_factor_); - - string_info info(text); - faces->get_string_info(info); - metawriter_with_properties writer = sym.get_metawriter(); - - //BOOST_FOREACH( geometry_type * geom, geometries_to_process ) - BOOST_FOREACH (point_type const& pt, labels) - { - //while (!placement_found && placement_options->next_position_only()) - { - placement text_placement(info, sym, scale_factor_); - text_placement.avoid_edges = sym.get_avoid_edges(); - if (writer.first) - text_placement.collect_extents =true; // needed for inmem metawriter - - //if (sym.get_label_placement() == POINT_PLACEMENT || - // sym.get_label_placement() == INTERIOR_PLACEMENT) - { - //double label_x=0.0; - //double label_y=0.0; - //double z=0.0; - //if (sym.get_label_placement() == POINT_PLACEMENT) - // geom->label_position(&label_x, &label_y); - //else - // geom->label_interior_position(&label_x, &label_y); - //prj_trans.backward(label_x,label_y, z); - //t_.forward(&label_x,&label_y); - - - double angle = 0.0; - expression_ptr angle_expr = sym.get_orientation(); - if (angle_expr) - { - // apply rotation - value_type result = boost::apply_visitor(evaluate(feature),*angle_expr); - angle = result.to_double(); - } - - finder.find_point_placement(text_placement, placement_options, boost::get<0>(pt),boost::get<1>(pt), - angle, 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) - // { - // path_type path(t_,*geom,prj_trans); - // finder.find_line_placements(text_placement, placement_options, path); - // } - - if (!text_placement.placements.size()) continue; - placement_found = true; - - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii) - { - double x = text_placement.placements[ii].starting_x; - double y = text_placement.placements[ii].starting_y; - ren.prepare_glyphs(&text_placement.placements[ii]); - ren.render(x,y); - } - - if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second); - } - } + double x = placement->placements[ii].starting_x; + double y = placement->placements[ii].starting_y; + ren.prepare_glyphs(&(placement->placements[ii])); + ren.render(x, y); } } diff --git a/src/build.py b/src/build.py index 511d2aa50..b5a4330d0 100644 --- a/src/build.py +++ b/src/build.py @@ -147,11 +147,11 @@ source = Split( symbolizer.cpp arrow.cpp unicode.cpp - glyph_symbolizer.cpp markers_symbolizer.cpp metawriter.cpp raster_colorizer.cpp text_placements.cpp + text_processing.cpp wkt/wkt_factory.cpp metawriter_inmem.cpp metawriter_factory.cpp @@ -223,7 +223,6 @@ source += Split( """ agg/agg_renderer.cpp agg/process_building_symbolizer.cpp - agg/process_glyph_symbolizer.cpp agg/process_line_symbolizer.cpp agg/process_line_pattern_symbolizer.cpp agg/process_text_symbolizer.cpp @@ -244,7 +243,6 @@ source += Split( """ grid/grid_renderer.cpp grid/process_building_symbolizer.cpp - grid/process_glyph_symbolizer.cpp grid/process_line_pattern_symbolizer.cpp grid/process_line_symbolizer.cpp grid/process_markers_symbolizer.cpp @@ -264,7 +262,6 @@ if env['SVG_RENDERER']: # svg backend svg/svg_output_attributes.cpp svg/process_symbolizers.cpp svg/process_building_symbolizer.cpp - svg/process_glyph_symbolizer.cpp svg/process_line_pattern_symbolizer.cpp svg/process_line_symbolizer.cpp svg/process_markers_symbolizer.cpp diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index a31b0de9a..13db7f47e 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -572,11 +573,7 @@ public: void add_text(text_path & path, cairo_face_manager & manager, - face_set_ptr const& faces, - unsigned text_size, - color const& fill, - unsigned halo_radius, - color const& halo_fill) + face_manager &font_manager) { double sx = path.starting_x; double sy = path.starting_y; @@ -587,8 +584,13 @@ public: { int c; double x, y, angle; + char_properties *format; - path.vertex(&c, &x, &y, &angle); + path.vertex(&c, &x, &y, &angle, &format); + + face_set_ptr faces = font_manager.get_face_set(format->face_name, format->fontset); + float text_size = format->text_size; + faces->set_character_sizes(text_size); glyph_ptr glyph = faces->get_glyph(c); @@ -608,42 +610,11 @@ public: set_font_face(manager, glyph->get_face()); glyph_path(glyph->get_index(), sx + x, sy - y); - } - } - - set_line_width(halo_radius); - set_line_join(ROUND_JOIN); - set_color(halo_fill); - stroke(); - - set_color(fill); - - path.rewind(); - - for (int iii = 0; iii < path.num_nodes(); iii++) - { - int c; - double x, y, angle; - - path.vertex(&c, &x, &y, &angle); - - glyph_ptr glyph = faces->get_glyph(c); - - if (glyph) - { - Cairo::Matrix matrix; - - matrix.xx = text_size * cos(angle); - matrix.xy = text_size * sin(angle); - matrix.yx = text_size * -sin(angle); - matrix.yy = text_size * cos(angle); - matrix.x0 = 0; - matrix.y0 = 0; - - set_font_matrix(matrix); - - set_font_face(manager, glyph->get_face()); - + set_line_width(format->halo_radius); + set_line_join(ROUND_JOIN); + set_color(format->halo_fill); + stroke(); + set_color(format->fill); show_glyph(glyph->get_index(), sx + x, sy - y); } } @@ -1080,11 +1051,11 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { +#if 0 typedef coord_transform2 path_type; text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); placement_options->next(); - placement_options->next_position_only(); UnicodeString text; if( sym.get_no_text() ) @@ -1146,7 +1117,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, placement_finder finder(detector_); faces->set_character_sizes(placement_options->text_size); - faces->get_string_info(info); + faces->get_string_info(info, text, 0); int w = (*marker)->width(); int h = (*marker)->height(); @@ -1280,6 +1251,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, } } } +#endif } void cairo_renderer_base::process(line_pattern_symbolizer const& sym, @@ -1459,187 +1431,21 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, } } -void cairo_renderer_base::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name()); - if (faces->size() > 0) - { - // Get x and y from geometry and translate to pixmap coords. - double x, y, z=0.0; - feature.get_geometry(0).label_position(&x, &y); - prj_trans.backward(x,y,z); - t_.forward(&x, &y); - - // set font size - unsigned size = sym.eval_size(feature); - faces->set_character_sizes(size); - - // Get and render text path - // - text_path_ptr path = sym.get_text_path(faces, feature); - // apply displacement - position pos = sym.get_displacement(); - double dx = boost::get<0>(pos); - double dy = boost::get<1>(pos); - path->starting_x = x = x+dx; - path->starting_y = y = y+dy; - - // get fill and halo params - color fill = sym.eval_color(feature); - color halo_fill = sym.get_halo_fill(); - double halo_radius = sym.get_halo_radius(); - if (fill==color("transparent")) - halo_radius = 0; - - double bsize = size/2 + 1; - box2d glyph_ext( - floor(x-bsize), floor(y-bsize), ceil(x+bsize), ceil(y+bsize) - ); - if ((sym.get_allow_overlap() || detector_.has_placement(glyph_ext)) && - (!sym.get_avoid_edges() || detector_.extent().contains(glyph_ext))) - { - // Placement is valid, render glyph and update detector. - cairo_context context(context_); - context.add_text(*path, - face_manager_, - faces, - size, - fill, - halo_radius, - halo_fill - ); - detector_.insert(glyph_ext); - metawriter_with_properties writer = sym.get_metawriter(); - if (writer.first) writer.first->add_box(glyph_ext, feature, t_, writer.second); - } - } - else - { - throw config_error( - "Unable to find specified font face in GlyphSymbolizer" - ); - } -} - void cairo_renderer_base::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - typedef coord_transform2 path_type; + text_symbolizer_helper, label_collision_detector4> helper(detector_.extent().width(), detector_.extent().height(), 1.0 /*scale_factor*/, t_, font_manager_, detector_); - bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - while (!placement_found && placement_options->next()) + text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans); + + if (!placement) return; + + cairo_context context(context_); + + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - UnicodeString text = result.to_unicode(); - - if ( sym.get_text_transform() == UPPERCASE) - { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - if (text.length() <= 0) continue; - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) - { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - if (faces->size() == 0) - { - throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'"); - } - cairo_context context(context_); - string_info info(text); - - faces->set_character_sizes(placement_options->text_size); - faces->get_string_info(info); - - placement_finder finder(detector_); - - metawriter_with_properties writer = sym.get_metawriter(); - - unsigned num_geom = feature.num_geometries(); - for (unsigned i=0; inext_position_only()) - { - placement text_placement(info, sym, 1.0); - text_placement.avoid_edges = sym.get_avoid_edges(); - if (writer.first) - text_placement.collect_extents = true; // needed for inmem metawriter - - if (sym.get_label_placement() == POINT_PLACEMENT || - sym.get_label_placement() == INTERIOR_PLACEMENT) - { - double label_x, label_y, z=0.0; - if (sym.get_label_placement() == POINT_PLACEMENT) - geom.label_position(&label_x, &label_y); - else - geom.label_interior_position(&label_x, &label_y); - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - double angle = 0.0; - expression_ptr angle_expr = sym.get_orientation(); - if (angle_expr) - { - // apply rotation - value_type result = boost::apply_visitor(evaluate(feature),*angle_expr); - angle = result.to_double(); - } - - finder.find_point_placement(text_placement, placement_options, - label_x, label_y, - angle, 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) - { - path_type path(t_, geom, prj_trans); - finder.find_line_placements(text_placement, placement_options, path); - } - - if (!text_placement.placements.size()) continue; - placement_found = true; - - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii) - { - context.add_text(text_placement.placements[ii], - face_manager_, - faces, - placement_options->text_size, - sym.get_fill(), - sym.get_halo_radius(), - sym.get_halo_fill() - ); - } - - if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second); - } - } + context.add_text(placement->placements[ii], face_manager_, font_manager_); } } diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp index 188aaa59d..5e58fa89c 100644 --- a/src/font_engine_freetype.cpp +++ b/src/font_engine_freetype.cpp @@ -19,10 +19,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//$Id$ // mapnik #include +#include +#include +#include // boost #include @@ -192,9 +194,10 @@ stroker_ptr freetype_engine::create_stroker() return stroker_ptr(); } -font_face_set::dimension_t font_face_set::character_dimensions(const unsigned c) +char_info font_face_set::character_dimensions(const unsigned c) { - std::map::const_iterator itr; + //Check if char is already in cache + std::map::const_iterator itr; itr = dimension_cache_.find(c); if (itr != dimension_cache_.end()) { return itr->second; @@ -222,33 +225,30 @@ font_face_set::dimension_t font_face_set::character_dimensions(const unsigned c) error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING); if ( error ) - return dimension_t(0, 0, 0); + return char_info(); error = FT_Get_Glyph(face->glyph, &image); if ( error ) - return dimension_t(0, 0, 0); + return char_info(); FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox); FT_Done_Glyph(image); unsigned tempx = face->glyph->advance.x >> 6; - //std::clog << "glyph: " << glyph_index << " x: " << tempx << " y: " << tempy << std::endl; - dimension_t dim(tempx, glyph_bbox.yMax, glyph_bbox.yMin); - //dimension_cache_[c] = dim; would need an default constructor for dimension_t - dimension_cache_.insert(std::pair(c, dim)); + char_info dim(c, tempx, glyph_bbox.yMax, glyph_bbox.yMin, face->size->metrics.height/64.0 /* >> 6 */); + dimension_cache_.insert(std::pair(c, dim)); return dim; } -void font_face_set::get_string_info(string_info & info) + +void font_face_set::get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format) { - unsigned width = 0; - unsigned height = 0; + double avg_height = character_dimensions('X').height(); UErrorCode err = U_ZERO_ERROR; UnicodeString reordered; UnicodeString shaped; - UnicodeString const& ustr = info.get_string(); int32_t length = ustr.length(); UBiDi *bidi = ubidi_openSized(length, 0, &err); @@ -270,10 +270,10 @@ void font_face_set::get_string_info(string_info & info) StringCharacterIterator iter(shaped); for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); - dimension_t char_dim = character_dimensions(ch); - info.add_info(ch, char_dim.width, char_dim.height); - width += char_dim.width; - height = (char_dim.height > height) ? char_dim.height : height; + char_info char_dim = character_dimensions(ch); + char_dim.format = format; + char_dim.avg_height = avg_height; + info.add_info(char_dim); } } @@ -286,11 +286,198 @@ void font_face_set::get_string_info(string_info & info) #endif ubidi_close(bidi); - info.set_dimensions(width, height); +} + +template +text_renderer::text_renderer (pixmap_type & pixmap, face_manager &font_manager_, stroker & s) + : pixmap_(pixmap), + font_manager_(font_manager_), + stroker_(s) +{ + +} + +template +box2d text_renderer::prepare_glyphs(text_path *path) +{ + //clear glyphs + glyphs_.clear(); + + FT_Matrix matrix; + FT_Vector pen; + FT_Error error; + + FT_BBox bbox; + bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we + bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later + + for (int i = 0; i < path->num_nodes(); i++) + { + int c; + double x, y, angle; + char_properties *properties; + + path->vertex(&c, &x, &y, &angle, &properties); + +#ifdef MAPNIK_DEBUG + // TODO Enable when we have support for setting verbosity + //std::clog << "prepare_glyphs: " << c << "," << x << + // "," << y << "," << angle << std::endl; +#endif + + FT_BBox glyph_bbox; + FT_Glyph image; + + pen.x = int(x * 64); + pen.y = int(y * 64); + + face_set_ptr faces = font_manager_.get_face_set(properties->face_name, properties->fontset); + faces->set_character_sizes(properties->text_size); + + glyph_ptr glyph = faces->get_glyph(unsigned(c)); + FT_Face face = glyph->get_face()->get_face(); + + matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); + matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); + matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); + matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); + + FT_Set_Transform(face, &matrix, &pen); + + error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING); + if ( error ) + continue; + + error = FT_Get_Glyph(face->glyph, &image); + if ( error ) + continue; + + FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); + if (glyph_bbox.xMin < bbox.xMin) + bbox.xMin = glyph_bbox.xMin; + if (glyph_bbox.yMin < bbox.yMin) + bbox.yMin = glyph_bbox.yMin; + if (glyph_bbox.xMax > bbox.xMax) + bbox.xMax = glyph_bbox.xMax; + if (glyph_bbox.yMax > bbox.yMax) + bbox.yMax = glyph_bbox.yMax; + + // Check if we properly grew the bbox + if ( bbox.xMin > bbox.xMax ) + { + bbox.xMin = 0; + bbox.yMin = 0; + bbox.xMax = 0; + bbox.yMax = 0; + } + + // take ownership of the glyph + glyphs_.push_back(new glyph_t(image, properties)); + } + + return box2d(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); +} + +template +void text_renderer::render(double x0, double y0) +{ + FT_Error error; + FT_Vector start; + unsigned height = pixmap_.height(); + + start.x = static_cast(x0 * (1 << 6)); + start.y = static_cast((height - y0) * (1 << 6)); + + // now render transformed glyphs + typename glyphs_t::iterator pos; + for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) + { + double halo_radius = pos->properties->halo_radius; + //make sure we've got reasonable values. + if (halo_radius <= 0.0 || halo_radius > 1024.0) continue; + stroker_.init(halo_radius); + FT_Glyph g; + error = FT_Glyph_Copy(pos->image, &g); + if (!error) + { + FT_Glyph_Transform(g,0,&start); + FT_Glyph_Stroke(&g,stroker_.get(),1); + error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); + if ( ! error ) + { + + FT_BitmapGlyph bit = (FT_BitmapGlyph)g; + render_bitmap(&bit->bitmap, pos->properties->halo_fill.rgba(), + bit->left, + height - bit->top, pos->properties->text_opacity); + } + } + FT_Done_Glyph(g); + } + //render actual text + for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) + { + + FT_Glyph_Transform(pos->image,0,&start); + + error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1); + if ( ! error ) + { + + FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image; + render_bitmap(&bit->bitmap, pos->properties->fill.rgba(), + bit->left, + height - bit->top, pos->properties->text_opacity); + } + } +} + + +template +void text_renderer::render_id(int feature_id,double x0, double y0, double min_radius) +{ + FT_Error error; + FT_Vector start; + unsigned height = pixmap_.height(); + + start.x = static_cast(x0 * (1 << 6)); + start.y = static_cast((height - y0) * (1 << 6)); + + // now render transformed glyphs + typename glyphs_t::iterator pos; + for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) + { + stroker_.init(std::max(pos->properties->halo_radius, min_radius)); + FT_Glyph g; + error = FT_Glyph_Copy(pos->image, &g); + if (!error) + { + FT_Glyph_Transform(g,0,&start); + FT_Glyph_Stroke(&g,stroker_.get(),1); + error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); + //error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_MONO,0,1); + if ( ! error ) + { + + FT_BitmapGlyph bit = (FT_BitmapGlyph)g; + render_bitmap_id(&bit->bitmap, feature_id, + bit->left, + height - bit->top); + } + } + FT_Done_Glyph(g); + } } #ifdef MAPNIK_THREADSAFE boost::mutex freetype_engine::mutex_; #endif std::map > freetype_engine::name2file_; +template void text_renderer::render(double, double); +template text_renderer::text_renderer(image_32&, face_manager&, stroker&); +template box2dtext_renderer::prepare_glyphs(text_path*); + +template void text_renderer::render_id(int, double, double, double); +template text_renderer::text_renderer(grid&, face_manager&, stroker&); +template box2dtext_renderer::prepare_glyphs(text_path*); } diff --git a/src/glyph_symbolizer.cpp b/src/glyph_symbolizer.cpp deleted file mode 100644 index 8ef41c638..000000000 --- a/src/glyph_symbolizer.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include -#include - -namespace mapnik -{ - -static const char * angle_mode_strings[] = { - "azimuth", - "trigonometric", - "" -}; - -IMPLEMENT_ENUM( angle_mode_e, angle_mode_strings ) - -text_path_ptr glyph_symbolizer::get_text_path(face_set_ptr const& faces, - Feature const& feature) const -{ - // Try to evaulate expressions against feature - UnicodeString char_ = eval_char(feature); - double angle = eval_angle(feature); - - // calculate displacement so glyph is rotated along center (default pivot is - // lowerbottom corner) - string_info info(char_); - faces->get_string_info(info); - - // XXX: Perhaps this limitation can be overcomed? - if (info.num_characters() != 1) - { - throw config_error("'char' length must be exactly 1"); - } - - character_info ci = info.at(0); - font_face_set::dimension_t cdim = faces->character_dimensions(ci.character); - double cwidth = static_cast(cdim.width)/2.0; - double cheight = static_cast(cdim.height)/2.0; - double xoff = cwidth*cos(angle) - cheight*sin(angle); - double yoff = cwidth*sin(angle) + cheight*cos(angle); - - // Create text path and add character with displacement and angle - text_path_ptr path_ptr = text_path_ptr(new text_path()); - path_ptr->add_node(ci.character, -xoff, -yoff, angle); - return path_ptr; -} - - - -UnicodeString glyph_symbolizer::eval_char(Feature const& feature) const -{ - expression_ptr expr = get_char(); - if (!expr) - throw config_error("No 'char' expression"); - value_type result = boost::apply_visitor( - evaluate(feature), - *expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "char_result=" << result.to_string() << "\n"; -#endif - return result.to_unicode(); -} - -double glyph_symbolizer::eval_angle(Feature const& feature) const -{ - double angle = 0.0; - expression_ptr expr = get_angle(); - if (expr) { - value_type result = boost::apply_visitor( - evaluate(feature), - *expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "angle_result=" << result.to_string() << "\n"; -#endif - angle = result.to_double(); - // normalize to first rotation in case an expression has made it go past - angle = std::fmod(angle, 360); - angle *= (M_PI/180); // convert to radians - if (get_angle_mode()==AZIMUTH) { - // angle is an azimuth, convert into trigonometric angle - angle = std::atan2(std::cos(angle), std::sin(angle)); - } - if (angle<0) - angle += 2*M_PI; - } - return angle; -} - -unsigned glyph_symbolizer::eval_size(Feature const& feature) const -{ - expression_ptr expr = get_size(); - if (!expr) throw config_error("No 'size' expression"); - value_type result = boost::apply_visitor( - evaluate(feature), - *expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "size_result=" << result.to_string() << "\n"; -#endif - float size = static_cast(result.to_double()); -#ifdef MAPNIK_DEBUG - std::clog << "size=" << size << "\n"; -#endif - return size; -} - -color glyph_symbolizer::eval_color(Feature const& feature) const -{ - raster_colorizer_ptr colorizer = get_colorizer(); - if (colorizer) - { - expression_ptr value_expr = get_value(); - if (!value_expr) - { - throw config_error( - "Must define a 'value' expression to use a colorizer" - ); - } - value_type value_result = boost::apply_visitor( - evaluate(feature), - *value_expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "value_result=" << value_result.to_string() << "\n"; -#endif - return colorizer->get_color((float)value_result.to_double()); - } - else - { - expression_ptr color_expr = get_color(); - if (color_expr) - { - value_type color_result = boost::apply_visitor( - evaluate(feature), - *color_expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "color_result=" << color_result.to_string() << "\n"; -#endif - return color(color_result.to_string()); - } - else - { - return color(0,0,0); // black - } - } -} - -} // end mapnik namespace diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index cf6c645d9..c4bada5c7 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -28,6 +28,7 @@ #include #include + #include #include #include @@ -35,10 +36,12 @@ #include #include #include +#include #include #include #include + // boost #include diff --git a/src/grid/process_line_pattern_symbolizer.cpp b/src/grid/process_line_pattern_symbolizer.cpp index f7510e35b..1d8a9c577 100644 --- a/src/grid/process_line_pattern_symbolizer.cpp +++ b/src/grid/process_line_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_line_symbolizer.cpp b/src/grid/process_line_symbolizer.cpp index b04999bff..212da4a1e 100644 --- a/src/grid/process_line_symbolizer.cpp +++ b/src/grid/process_line_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index 347324326..ba4bed42d 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp index 88f94a7d8..e3fd66e1c 100644 --- a/src/grid/process_point_symbolizer.cpp +++ b/src/grid/process_point_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/src/grid/process_polygon_pattern_symbolizer.cpp b/src/grid/process_polygon_pattern_symbolizer.cpp index 0f3b63f8b..7cce368ca 100644 --- a/src/grid/process_polygon_pattern_symbolizer.cpp +++ b/src/grid/process_polygon_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_polygon_symbolizer.cpp b/src/grid/process_polygon_symbolizer.cpp index 6b410a4eb..0beaab3f7 100644 --- a/src/grid/process_polygon_symbolizer.cpp +++ b/src/grid/process_polygon_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index d0f61cd67..c286f7409 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -44,6 +44,7 @@ void grid_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { +#if 0 typedef coord_transform2 path_type; bool placement_found = false; @@ -120,7 +121,7 @@ void grid_renderer::process(shield_symbolizer const& sym, string_info info(text); - faces->get_string_info(info); + faces->get_string_info(info, text, 0); // TODO- clamp to at least 4 px otherwise interactivity is too small int w = (*marker)->width()/pixmap_.get_resolution(); @@ -237,7 +238,7 @@ void grid_renderer::process(shield_symbolizer const& sym, } if (placement_found) pixmap_.add_feature(feature); - +#endif } template void grid_renderer::process(shield_symbolizer const&, diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index e88aed6c5..527196d36 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace mapnik { @@ -33,120 +34,26 @@ void grid_renderer::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - typedef coord_transform2 path_type; + text_symbolizer_helper, + label_collision_detector4> helper( + width_, height_, + scale_factor_ * (1.0/pixmap_.get_resolution()), + t_, font_manager_, detector_); - bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - while (!placement_found && placement_options->next()) + text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans); + + if (!placement) return; + + text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - UnicodeString text = result.to_unicode(); - - if ( sym.get_text_transform() == UPPERCASE) - { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - if ( text.length() <= 0 ) continue; - color const& fill = sym.get_fill(); - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) - { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - stroker_ptr strk = font_manager_.get_stroker(); - if (!(faces->size() > 0 && strk)) - { - throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'"); - } - text_renderer ren(pixmap_, faces, *strk); - ren.set_character_size(placement_options->text_size * (scale_factor_ * (1.0/pixmap_.get_resolution()))); - ren.set_fill(fill); - ren.set_halo_fill(sym.get_halo_fill()); - ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); - ren.set_opacity(sym.get_text_opacity()); - - // /pixmap_.get_resolution() ? - box2d dims(0,0,width_,height_); - placement_finder finder(detector_,dims); - - string_info info(text); - - faces->get_string_info(info); - unsigned num_geom = feature.num_geometries(); - for (unsigned i=0; inext_position_only()) - { - placement text_placement(info, sym, scale_factor_); - text_placement.avoid_edges = sym.get_avoid_edges(); - if (sym.get_label_placement() == POINT_PLACEMENT || - sym.get_label_placement() == INTERIOR_PLACEMENT) - { - double label_x, label_y, z=0.0; - if (sym.get_label_placement() == POINT_PLACEMENT) - geom.label_position(&label_x, &label_y); - else - geom.label_interior_position(&label_x, &label_y); - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - double angle = 0.0; - expression_ptr angle_expr = sym.get_orientation(); - if (angle_expr) - { - // apply rotation - value_type result = boost::apply_visitor(evaluate(feature),*angle_expr); - angle = result.to_double(); - } - - finder.find_point_placement(text_placement, placement_options, - label_x, label_y, - angle, 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) - { - path_type path(t_,geom,prj_trans); - finder.find_line_placements(text_placement, placement_options, path); - } - - if (!text_placement.placements.size()) continue; - placement_found = true; - - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii) - { - double x = text_placement.placements[ii].starting_x; - double y = text_placement.placements[ii].starting_y; - ren.prepare_glyphs(&text_placement.placements[ii]); - ren.render_id(feature.id(),x,y,2); - } - } - } + double x = placement->placements[ii].starting_x; + double y = placement->placements[ii].starting_y; + ren.prepare_glyphs(&(placement->placements[ii])); + ren.render_id(feature.id(),x,y,2); } - if (placement_found) - pixmap_.add_feature(feature); + pixmap_.add_feature(feature); + } template void grid_renderer::process(text_symbolizer const&, diff --git a/src/libxml2_loader.cpp b/src/libxml2_loader.cpp index f935243ce..fedfbfd44 100644 --- a/src/libxml2_loader.cpp +++ b/src/libxml2_loader.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -200,8 +201,13 @@ private: } break; case XML_TEXT_NODE: - pt.put_value( (char*) cur_node->content ); - break; + { + std::string trimmed = boost::algorithm::trim_copy(std::string((char*)cur_node->content)); + if (trimmed.empty()) break; + ptree::iterator it = pt.push_back(ptree::value_type("", ptree())); + it->second.put_value(trimmed); + } + break; case XML_COMMENT_NODE: { ptree::iterator it = pt.push_back( diff --git a/src/load_map.cpp b/src/load_map.cpp index 41c07c1d4..c6e3f47e8 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -48,6 +48,10 @@ #include #include +#include +#include +#include +#include // boost #include @@ -108,7 +112,6 @@ private: void parse_building_symbolizer(rule & rule, ptree const & sym ); void parse_raster_symbolizer(rule & rule, ptree const & sym ); void parse_markers_symbolizer(rule & rule, ptree const & sym ); - void parse_glyph_symbolizer(rule & rule, ptree const & sym ); void parse_raster_colorizer(raster_colorizer_ptr const& rc, ptree const& node ); void parse_stroke(stroke & strk, ptree const & sym); @@ -653,7 +656,7 @@ void map_parser::parse_layer( Map & map, ptree const & lay ) if (child.first == "StyleName") { ensure_attrs(child.second, "StyleName", "none"); - std::string style_name = child.second.data(); + std::string style_name = get_value(child.second, "style name"); if (style_name.empty()) { std::ostringstream ss; @@ -846,10 +849,6 @@ void map_parser::parse_rule( feature_type_style & style, ptree const & r ) { parse_markers_symbolizer(rule, sym.second); } - else if ( sym.first == "GlyphSymbolizer") - { - parse_glyph_symbolizer( rule, sym.second ); - } else if ( sym.first != "MinScaleDenominator" && sym.first != "MaxScaleDenominator" && @@ -1246,28 +1245,34 @@ void map_parser::parse_polygon_pattern_symbolizer( rule & rule, void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) { - std::stringstream s; - s << "name,face-name,fontset-name,size,fill,orientation," + std::stringstream s_common; + s_common << "name,face-name,fontset-name,size,fill,orientation," << "dx,dy,placement,vertical-alignment,halo-fill," << "halo-radius,text-ratio,wrap-width,wrap-before," << "wrap-character,text-transform,line-spacing," << "label-position-tolerance,character-spacing," << "spacing,minimum-distance,minimum-padding,minimum-path-length," << "avoid-edges,allow-overlap,opacity,max-char-angle-delta," - << "horizontal-alignment,justify-alignment," - << "placements,placement-type," + << "horizontal-alignment,justify-alignment"; + + std::stringstream s_symbolizer; + s_symbolizer << s_common.str() << ",placements,placement-type," << "meta-writer,meta-output"; - ensure_attrs(sym, "TextSymbolizer", s.str()); + ensure_attrs(sym, "TextSymbolizer", s_symbolizer.str()); try { text_placements_ptr placement_finder; + text_placements_list *list = 0; optional placement_type = get_opt_attr(sym, "placement-type"); if (placement_type) { if (*placement_type == "simple") { placement_finder = text_placements_ptr( new text_placements_simple( get_attr(sym, "placements", "X"))); + } else if (*placement_type == "list") { + list = new text_placements_list(); + placement_finder = text_placements_ptr(list); } else if (*placement_type != "dummy" && *placement_type != "") { throw config_error(std::string("Unknown placement type '"+*placement_type+"'")); } @@ -1276,214 +1281,25 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) placement_finder = text_placements_ptr(new text_placements_dummy()); } - std::string name; - optional old_name = get_opt_attr(sym, "name"); - if (old_name) { - std::clog << ": ### WARNING: Using 'name' in TextSymbolizer is deprecated (http://trac.mapnik.org/wiki/TextSymbolizer)\n"; - name = *old_name; - } else { - name = get_value(sym, "TextSymbolizer"); - if (name.empty()) throw config_error(std::string("TextSymbolizer needs a non-empty text")); - } - - optional face_name = - get_opt_attr(sym, "face-name"); - - optional fontset_name = - get_opt_attr(sym, "fontset-name"); - - float size = get_attr(sym, "size", 10.0f); - - color c = get_attr(sym, "fill", color(0,0,0)); - - text_symbolizer text_symbol = text_symbolizer(parse_expression(name, "utf8"), size, c, placement_finder); - - optional orientation = get_opt_attr(sym, "orientation"); - if (orientation) - { - text_symbol.set_orientation(parse_expression(*orientation, "utf8")); - } - - if (fontset_name && face_name) - { - throw config_error(std::string("Can't have both face-name and fontset-name")); - } - else if (fontset_name) - { - std::map::const_iterator itr = fontsets_.find(*fontset_name); - if (itr != fontsets_.end()) - { - text_symbol.set_fontset(itr->second); - } - else - { - throw config_error("Unable to find any fontset named '" + *fontset_name + "'"); + text_symbolizer text_symbol = text_symbolizer(placement_finder); + placement_finder->properties.set_values_from_xml(sym, fontsets_); + if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.face_name); + if (list) { + ptree::const_iterator symIter = sym.begin(); + ptree::const_iterator endSym = sym.end(); + for( ;symIter != endSym; ++symIter) { + if (symIter->first.find('<') != std::string::npos) continue; + if (symIter->first != "Placement") + { +// throw config_error("Unknown element '" + symIter->first + "'"); TODO + continue; + } + ensure_attrs(symIter->second, "TextSymbolizer/Placement", s_common.str()); + text_symbolizer_properties & p = list->add(); + p.set_values_from_xml(symIter->second, fontsets_); + if (strict_) ensure_font_face(p.processor.defaults.face_name); } } - else if (face_name) - { - if ( strict_ ) - { - ensure_font_face(*face_name); - } - text_symbol.set_face_name(*face_name); - } - else - { - throw config_error(std::string("Must have face-name or fontset-name")); - } - - double dx = get_attr(sym, "dx", 0.0); - double dy = get_attr(sym, "dy", 0.0); - text_symbol.set_displacement(dx,dy); - - label_placement_e placement = - get_attr(sym, "placement", POINT_PLACEMENT); - text_symbol.set_label_placement( placement ); - - // vertical alignment - vertical_alignment_e default_vertical_alignment = V_AUTO; - - vertical_alignment_e valign = get_attr(sym, "vertical-alignment", default_vertical_alignment); - text_symbol.set_vertical_alignment(valign); - - // halo fill and radius - optional halo_fill = get_opt_attr(sym, "halo-fill"); - if (halo_fill) - { - text_symbol.set_halo_fill( * halo_fill ); - } - optional halo_radius = - get_opt_attr(sym, "halo-radius"); - if (halo_radius) - { - text_symbol.set_halo_radius(*halo_radius); - } - - // text ratio and wrap width - optional text_ratio = - get_opt_attr(sym, "text-ratio"); - if (text_ratio) - { - text_symbol.set_text_ratio(*text_ratio); - } - - optional wrap_width = - get_opt_attr(sym, "wrap-width"); - if (wrap_width) - { - text_symbol.set_wrap_width(*wrap_width); - } - - optional wrap_before = - get_opt_attr(sym, "wrap-before"); - if (wrap_before) - { - text_symbol.set_wrap_before(*wrap_before); - } - - // character used to break long strings - optional wrap_char = - get_opt_attr(sym, "wrap-character"); - if (wrap_char && (*wrap_char).size() > 0) - { - text_symbol.set_wrap_char((*wrap_char)[0]); - } - - // text conversion before rendering - text_transform_e tconvert = - get_attr(sym, "text-transform", NONE); - text_symbol.set_text_transform(tconvert); - - // spacing between text lines - optional line_spacing = get_opt_attr(sym, "line-spacing"); - if (line_spacing) - { - text_symbol.set_line_spacing(*line_spacing); - } - - // tolerance between label spacing along line - optional label_position_tolerance = get_opt_attr(sym, "label-position-tolerance"); - if (label_position_tolerance) - { - text_symbol.set_label_position_tolerance(*label_position_tolerance); - } - - // spacing between characters in text - optional character_spacing = get_opt_attr(sym, "character-spacing"); - if (character_spacing) - { - text_symbol.set_character_spacing(*character_spacing); - } - - // spacing between repeated labels on lines - optional spacing = get_opt_attr(sym, "spacing"); - if (spacing) - { - text_symbol.set_label_spacing(*spacing); - } - - // minimum distance between labels - optional min_distance = get_opt_attr(sym, "minimum-distance"); - if (min_distance) - { - text_symbol.set_minimum_distance(*min_distance); - } - - // minimum distance from edge of the map - optional min_padding = get_opt_attr(sym, "minimum-padding"); - if (min_padding) - { - text_symbol.set_minimum_padding(*min_padding); - } - - // minimum path length - optional min_path_length = get_opt_attr(sym, "minimum-path-length"); - if (min_path_length) - { - text_symbol.set_minimum_path_length(*min_path_length); - } - - // do not render labels around edges - optional avoid_edges = - get_opt_attr(sym, "avoid-edges"); - if (avoid_edges) - { - text_symbol.set_avoid_edges( * avoid_edges); - } - - // allow_overlap - optional allow_overlap = - get_opt_attr(sym, "allow-overlap"); - if (allow_overlap) - { - text_symbol.set_allow_overlap( * allow_overlap ); - } - - // opacity - optional opacity = - get_opt_attr(sym, "opacity"); - if (opacity) - { - text_symbol.set_text_opacity( * opacity ); - } - - // max_char_angle_delta - optional max_char_angle_delta = - get_opt_attr(sym, "max-char-angle-delta"); - if (max_char_angle_delta) - { - text_symbol.set_max_char_angle_delta( (*max_char_angle_delta)*(M_PI/180)); - } - - // horizontal alignment - horizontal_alignment_e halign = get_attr(sym, "horizontal-alignment", H_AUTO); - text_symbol.set_horizontal_alignment(halign); - - // justify alignment - justify_alignment_e jalign = get_attr(sym, "justify-alignment", J_MIDDLE); - text_symbol.set_justify_alignment(jalign); - parse_metawriter_in_symbolizer(text_symbol, sym); rule.append(text_symbol); } @@ -1498,7 +1314,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) { std::stringstream s; - //std::string a[] = {"a","b"}; s << "name,face-name,fontset-name,size,fill," << "dx,dy,placement,vertical-alignment,halo-fill," << "halo-radius,text-ratio,wrap-width,wrap-before," @@ -1508,39 +1323,74 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) << "avoid-edges,allow-overlap,opacity,max-char-angle-delta," << "horizontal-alignment,justify-alignment," // additional for shield - /* transform instead of orientation */ + /* transform instead of orientation */ << "file,base,transform,shield-dx,shield-dy," << "text-opacity,unlock-image,no-text," - << "meta-writer,meta-output"; - + << "meta-writer,meta-output"; + ensure_attrs(sym, "ShieldSymbolizer", s.str()); try { - optional no_text = - get_opt_attr(sym, "no-text"); - std::string name; - optional old_name = get_opt_attr(sym, "name"); - if (old_name) { - std::clog << ": ### WARNING: Using 'name' in ShieldSymbolizer is deprecated (http://trac.mapnik.org/wiki/TextSymbolizer)\n"; - name = *old_name; - } else { - name = get_value(sym, "ShieldSymbolizer"); - if (name.empty() && (!no_text || !*no_text) ) throw config_error(std::string("ShieldSymbolizer needs a non-empty text")); + shield_symbolizer shield_symbol = shield_symbolizer(); + optional transform_wkt = get_opt_attr(sym, "transform"); + if (transform_wkt) + { + agg::trans_affine tr; + if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr)) + { + std::stringstream ss; + ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'"; + if (strict_) + throw config_error(ss.str()); // value_error here? + else + std::clog << "### WARNING: " << ss << endl; + } + boost::array matrix; + tr.store_to(&matrix[0]); + shield_symbol.set_transform(matrix); + } + // shield displacement + double shield_dx = get_attr(sym, "shield-dx", 0.0); + double shield_dy = get_attr(sym, "shield-dy", 0.0); + shield_symbol.set_shield_displacement(shield_dx,shield_dy); + + // opacity + optional opacity = get_opt_attr(sym, "opacity"); + if (opacity) + { + shield_symbol.set_opacity(*opacity); } - optional face_name = - get_opt_attr(sym, "face-name"); + // text-opacity + // TODO: Could be problematic because it is named opacity in TextSymbolizer but opacity has a diffrent meaning here. + optional text_opacity = + get_opt_attr(sym, "text-opacity"); + if (text_opacity) + { + shield_symbol.set_text_opacity( * text_opacity ); + } - optional fontset_name = - get_opt_attr(sym, "fontset-name"); + // unlock_image + optional unlock_image = + get_opt_attr(sym, "unlock-image"); + if (unlock_image) + { + shield_symbol.set_unlock_image( * unlock_image ); + } - float size = get_attr(sym, "size", 10.0f); - color fill = get_attr(sym, "fill", color(0,0,0)); + // no text + optional no_text = + get_opt_attr(sym, "no-text"); + if (no_text) + { + shield_symbol.set_no_text( * no_text ); + } + + parse_metawriter_in_symbolizer(shield_symbol, sym); std::string image_file = get_attr(sym, "file"); optional base = get_opt_attr(sym, "base"); - - optional transform_wkt = get_opt_attr(sym, "transform"); + try { if( base ) @@ -1553,202 +1403,7 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) } image_file = ensure_relative_to_xml(image_file); - - shield_symbolizer shield_symbol(parse_expression(name, "utf8"),size,fill,parse_path(image_file)); - - if (fontset_name && face_name) - { - throw config_error(std::string("Can't have both face-name and fontset-name")); - } - else if (fontset_name) - { - std::map::const_iterator itr = fontsets_.find(*fontset_name); - if (itr != fontsets_.end()) - { - shield_symbol.set_fontset(itr->second); - } - else - { - throw config_error("Unable to find any fontset named '" + *fontset_name + "'"); - } - } - else if (face_name) - { - if ( strict_ ) - { - ensure_font_face(*face_name); - } - shield_symbol.set_face_name(*face_name); - } - else - { - throw config_error(std::string("Must have face-name or fontset-name")); - } - // text displacement (relative to shield_displacement) - double dx = get_attr(sym, "dx", 0.0); - double dy = get_attr(sym, "dy", 0.0); - shield_symbol.set_displacement(dx,dy); - // shield displacement - double shield_dx = get_attr(sym, "shield-dx", 0.0); - double shield_dy = get_attr(sym, "shield-dy", 0.0); - shield_symbol.set_shield_displacement(shield_dx,shield_dy); - - label_placement_e placement = - get_attr(sym, "placement", POINT_PLACEMENT); - shield_symbol.set_label_placement( placement ); - - // don't render shields around edges - optional avoid_edges = - get_opt_attr(sym, "avoid-edges"); - if (avoid_edges) - { - shield_symbol.set_avoid_edges( *avoid_edges); - } - - // halo fill and radius - optional halo_fill = get_opt_attr(sym, "halo-fill"); - if (halo_fill) - { - shield_symbol.set_halo_fill( * halo_fill ); - } - optional halo_radius = - get_opt_attr(sym, "halo-radius"); - if (halo_radius) - { - shield_symbol.set_halo_radius(*halo_radius); - } - - // minimum distance between labels - optional min_distance = get_opt_attr(sym, "minimum-distance"); - if (min_distance) - { - shield_symbol.set_minimum_distance(*min_distance); - } - - // minimum distance from edge of the map - optional min_padding = get_opt_attr(sym, "minimum-padding"); - if (min_padding) - { - shield_symbol.set_minimum_padding(*min_padding); - } - - // spacing between repeated labels on lines - optional spacing = get_opt_attr(sym, "spacing"); - if (spacing) - { - shield_symbol.set_label_spacing(*spacing); - } - - // allow_overlap - optional allow_overlap = - get_opt_attr(sym, "allow-overlap"); - if (allow_overlap) - { - shield_symbol.set_allow_overlap( * allow_overlap ); - } - - // vertical alignment - vertical_alignment_e valign = get_attr(sym, "vertical-alignment", V_MIDDLE); - shield_symbol.set_vertical_alignment(valign); - - // horizontal alignment - horizontal_alignment_e halign = get_attr(sym, "horizontal-alignment", H_MIDDLE); - shield_symbol.set_horizontal_alignment(halign); - - // justify alignment - justify_alignment_e jalign = get_attr(sym, "justify-alignment", J_MIDDLE); - shield_symbol.set_justify_alignment(jalign); - - optional wrap_width = - get_opt_attr(sym, "wrap-width"); - if (wrap_width) - { - shield_symbol.set_wrap_width(*wrap_width); - } - - optional wrap_before = - get_opt_attr(sym, "wrap-before"); - if (wrap_before) - { - shield_symbol.set_wrap_before(*wrap_before); - } - - // character used to break long strings - optional wrap_char = - get_opt_attr(sym, "wrap-character"); - if (wrap_char && (*wrap_char).size() > 0) - { - shield_symbol.set_wrap_char((*wrap_char)[0]); - } - - // text conversion before rendering - text_transform_e tconvert = - get_attr(sym, "text-transform", NONE); - shield_symbol.set_text_transform(tconvert); - - // spacing between text lines - optional line_spacing = get_opt_attr(sym, "line-spacing"); - if (line_spacing) - { - shield_symbol.set_line_spacing(*line_spacing); - } - - // spacing between characters in text - optional character_spacing = get_opt_attr(sym, "character-spacing"); - if (character_spacing) - { - shield_symbol.set_character_spacing(*character_spacing); - } - - // opacity - optional opacity = - get_opt_attr(sym, "opacity"); - if (opacity) - { - shield_symbol.set_opacity( * opacity ); - } - - // text-opacity - optional text_opacity = - get_opt_attr(sym, "text-opacity"); - if (text_opacity) - { - shield_symbol.set_text_opacity( * text_opacity ); - } - - if (transform_wkt) - { - agg::trans_affine tr; - if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr)) - { - std::stringstream ss; - ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'"; - if (strict_) - throw config_error(ss.str()); // value_error here? - else - std::clog << "### WARNING: " << ss << endl; - } - boost::array matrix; - tr.store_to(&matrix[0]); - shield_symbol.set_transform(matrix); - } - - // unlock_image - optional unlock_image = - get_opt_attr(sym, "unlock-image"); - if (unlock_image) - { - shield_symbol.set_unlock_image( * unlock_image ); - } - - // no text - if (no_text) - { - shield_symbol.set_no_text( * no_text ); - } - - parse_metawriter_in_symbolizer(shield_symbol, sym); - rule.append(shield_symbol); + shield_symbol.set_filename(parse_path(image_file)); } catch (image_reader_exception const & ex ) { @@ -1763,7 +1418,9 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) std::clog << "### WARNING: " << msg << endl; } } - + text_placements_ptr placement_finder = shield_symbol.get_placement_options(); + placement_finder->properties.set_values_from_xml(sym, fontsets_); + rule.append(shield_symbol); } catch (const config_error & ex) { @@ -1994,117 +1651,6 @@ void map_parser::parse_raster_symbolizer( rule & rule, ptree const & sym ) } } -void map_parser::parse_glyph_symbolizer(rule & rule, ptree const & sym) -{ - ensure_attrs(sym, "GlyphSymbolizer", "face-name,char,angle,angle-mode,value,size,color,halo-fill,halo-radius,allow-overlap,avoid-edges,dx,dy,meta-writer,meta-output"); - try - { - // Parse required constructor args - std::string face_name = get_attr(sym, "face-name"); - std::string _char = get_attr(sym, "char"); - - glyph_symbolizer glyph_sym = glyph_symbolizer( - face_name, - parse_expression(_char, "utf8") - ); - - // - // parse and set optional attrs. - // - - // angle - optional angle = - get_opt_attr(sym, "angle"); - if (angle) - glyph_sym.set_angle(parse_expression(*angle, "utf8")); - - angle_mode_e angle_mode = - get_attr(sym, "angle-mode", TRIGONOMETRIC); - glyph_sym.set_angle_mode(angle_mode); - - // value - optional value = - get_opt_attr(sym, "value"); - if (value) - glyph_sym.set_value(parse_expression(*value, "utf8")); - - // size - std::string size = - get_attr(sym, "size"); - glyph_sym.set_size(parse_expression(size, "utf8")); - - // color - optional _color = - get_opt_attr(sym, "color"); - if (_color) - glyph_sym.set_color(parse_expression(*_color, "utf8")); - - // halo_fill - optional halo_fill = get_opt_attr(sym, "halo-fill"); - if (halo_fill) - glyph_sym.set_halo_fill(*halo_fill); - - // halo_radius - optional halo_radius = get_opt_attr( - sym, - "halo-radius"); - if (halo_radius) - glyph_sym.set_halo_radius(*halo_radius); - - // allow_overlap - optional allow_overlap = get_opt_attr( - sym, - "allow-overlap" - ); - if (allow_overlap) - glyph_sym.set_allow_overlap(*allow_overlap); - - // avoid_edges - optional avoid_edges = get_opt_attr( - sym, - "avoid-edges" - ); - if (avoid_edges) - glyph_sym.set_avoid_edges(*avoid_edges); - - // displacement - optional dx = get_opt_attr(sym, "dx"); - optional dy = get_opt_attr(sym, "dy"); - if (dx && dy) - glyph_sym.set_displacement(*dx, *dy); - - // colorizer - ptree::const_iterator childIter = sym.begin(); - ptree::const_iterator endChild = sym.end(); - - for (; childIter != endChild; ++childIter) - { - ptree::value_type const& tag = *childIter; - - if (tag.first == "RasterColorizer") - { - raster_colorizer_ptr colorizer(new raster_colorizer()); - glyph_sym.set_colorizer(colorizer); - parse_raster_colorizer(colorizer, tag.second); - } - else if (tag.first!="" && tag.first!="" ) - { - throw config_error(std::string("Unknown child node. ") + - "Expected 'RasterColorizer' but got '" + - tag.first + "'"); - } - } - - parse_metawriter_in_symbolizer(glyph_sym, sym); - rule.append(glyph_sym); - } - catch (const config_error & ex) - { - ex.append_context("in GlyphSymbolizer"); - throw; - } -} - void map_parser::parse_raster_colorizer(raster_colorizer_ptr const& rc, ptree const& node ) { diff --git a/src/metawriter.cpp b/src/metawriter.cpp index f3d72a9a0..e54dcac2a 100644 --- a/src/metawriter.cpp +++ b/src/metawriter.cpp @@ -23,7 +23,7 @@ // Mapnik #include #include -#include +#include // Boost #include @@ -174,8 +174,8 @@ void metawriter_json_stream::add_box(box2d const &box, Feature const& fe } -void metawriter_json_stream::add_text(placement const& p, - face_set_ptr face, +void metawriter_json_stream::add_text(text_placement_info const& p, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties) @@ -192,18 +192,19 @@ void metawriter_json_stream::add_text(placement const& p, Hightest y = baseline of top line */ -// if (p.placements.size()) std::cout << p.info.get_string() << "\n"; for (unsigned n = 0; n < p.placements.size(); n++) { placement_element & current_placement = const_cast(p.placements[n]); - bool inside = false; + bool inside = false; /* Part of text is inside rendering region */ bool straight = true; - int c; double x, y, angle; + int c; + double x, y, angle; + char_properties *format; current_placement.rewind(); for (int i = 0; i < current_placement.num_nodes(); ++i) { int cx = current_placement.starting_x; int cy = current_placement.starting_y; - current_placement.vertex(&c, &x, &y, &angle); + current_placement.vertex(&c, &x, &y, &angle, &format); if (cx+x >= 0 && cx+x < width_ && cy-y >= 0 && cy-y < height_) inside = true; if (angle > 0.001 || angle < -0.001) straight = false; if (inside && !straight) break; @@ -216,14 +217,13 @@ void metawriter_json_stream::add_text(placement const& p, //Reduce number of polygons double minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN; for (int i = 0; i < current_placement.num_nodes(); ++i) { - current_placement.vertex(&c, &x, &y, &angle); - font_face_set::dimension_t ci = face->character_dimensions(c); - if (x < minx) minx = x; - if (x+ci.width > maxx) maxx = x+ci.width; - if (y+ci.height+ci.ymin > maxy) maxy = y+ci.height+ci.ymin; - if (y+ci.ymin < miny) miny = y+ci.ymin; - // std::cout << (char) c << " height:" << ci.height << " ymin:" << ci.ymin << " y:" << y << " miny:"<< miny << " maxy:"<< maxy <<"\n"; - + current_placement.vertex(&c, &x, &y, &angle, &format); + face_set_ptr face = font_manager.get_face_set(format->face_name, format->fontset); + char_info ci = face->character_dimensions(c); + minx = std::min(minx, x); + maxx = std::max(maxx, x+ci.width); + maxy = std::max(maxy, y+ci.ymax); + miny = std::min(miny, y+ci.ymin); } add_box(box2d(current_placement.starting_x+minx, current_placement.starting_y-miny, @@ -239,9 +239,10 @@ void metawriter_json_stream::add_text(placement const& p, if (c != ' ') { *f_ << ","; } - current_placement.vertex(&c, &x, &y, &angle); + current_placement.vertex(&c, &x, &y, &angle, &format); if (c == ' ') continue; - font_face_set::dimension_t ci = face->character_dimensions(c); + face_set_ptr face = font_manager.get_face_set(format->face_name, format->fontset); + char_info ci = face->character_dimensions(c); double x0, y0, x1, y1, x2, y2, x3, y3; double sina = sin(angle); @@ -250,10 +251,10 @@ void metawriter_json_stream::add_text(placement const& p, y0 = current_placement.starting_y - y - cosa*ci.ymin; x1 = x0 + ci.width * cosa; y1 = y0 - ci.width * sina; - x2 = x0 + (ci.width * cosa - ci.height * sina); - y2 = y0 - (ci.width * sina + ci.height * cosa); - x3 = x0 - ci.height * sina; - y3 = y0 - ci.height * cosa; + x2 = x0 + (ci.width * cosa - ci.height() * sina); + y2 = y0 - (ci.width * sina + ci.height() * cosa); + x3 = x0 - ci.height() * sina; + y3 = y0 - ci.height() * cosa; *f_ << "\n [["; write_point(t, x0, y0); diff --git a/src/metawriter_inmem.cpp b/src/metawriter_inmem.cpp index d32b6a571..12cec9573 100644 --- a/src/metawriter_inmem.cpp +++ b/src/metawriter_inmem.cpp @@ -69,9 +69,9 @@ metawriter_inmem::add_box(box2d const& box, Feature const& feature, instances_.push_back(inst); } -void -metawriter_inmem::add_text(placement const& p, - face_set_ptr /*face*/, +void +metawriter_inmem::add_text(text_placement_info const& p, + face_manager_freetype & /*face*/, Feature const& feature, CoordTransform const& /*t*/, metawriter_properties const& properties) { diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index c965d03cf..2d2888cf4 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include // agg @@ -49,60 +50,6 @@ namespace mapnik { -placement::placement(string_info & info_, - shield_symbolizer const& sym, - double scale_factor, - unsigned w, unsigned h, - bool has_dimensions_) - : info(info_), - scale_factor_(scale_factor), - label_placement(sym.get_label_placement()), - wrap_width(sym.get_wrap_width()), - wrap_before(sym.get_wrap_before()), - wrap_char(sym.get_wrap_char()), - text_ratio(sym.get_text_ratio()), - label_spacing(scale_factor_ * sym.get_label_spacing()), - label_position_tolerance(sym.get_label_position_tolerance()), - force_odd_labels(sym.get_force_odd_labels()), - max_char_angle_delta(sym.get_max_char_angle_delta()), - minimum_distance(scale_factor_ * sym.get_minimum_distance()), - minimum_padding(scale_factor_ * sym.get_minimum_padding()), - minimum_path_length(0), - avoid_edges(sym.get_avoid_edges()), - has_dimensions(has_dimensions_), - allow_overlap(false), - dimensions(std::make_pair(w,h)), - collect_extents(false), - extents() -{} - -placement::placement(string_info & info_, - text_symbolizer const& sym, - double scale_factor) - : info(info_), - scale_factor_(scale_factor), - label_placement(sym.get_label_placement()), - wrap_width(sym.get_wrap_width()), - wrap_before(sym.get_wrap_before()), - wrap_char(sym.get_wrap_char()), - text_ratio(sym.get_text_ratio()), - label_spacing(scale_factor_ * sym.get_label_spacing()), - label_position_tolerance(sym.get_label_position_tolerance()), - force_odd_labels(sym.get_force_odd_labels()), - max_char_angle_delta(sym.get_max_char_angle_delta()), - minimum_distance(scale_factor_ * sym.get_minimum_distance()), - minimum_padding(scale_factor_ * sym.get_minimum_padding()), - minimum_path_length(scale_factor_ * sym.get_minimum_path_length()), - avoid_edges(sym.get_avoid_edges()), - has_dimensions(false), - allow_overlap(sym.get_allow_overlap()), - dimensions(), - collect_extents(false), - extents() -{} - - -placement::~placement() {} template std::pair get_position_at_distance(double target_distance, T & shape_path) @@ -151,22 +98,24 @@ double get_total_distance(T & shape_path) } template -placement_finder::placement_finder(DetectorT & detector) +placement_finder::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector) : detector_(detector), - dimensions_(detector_.extent()) + dimensions_(detector_.extent()), + info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_() { } template -placement_finder::placement_finder(DetectorT & detector, box2d const& extent) +placement_finder::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector, box2d const& extent) : detector_(detector), - dimensions_(extent) + dimensions_(extent), + info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_() { } template template -void placement_finder::find_point_placements(placement & p, text_placement_info_ptr po, T & shape_path) +void placement_finder::find_point_placements(T & shape_path) { unsigned cmd; double new_x = 0.0; @@ -182,15 +131,15 @@ void placement_finder::find_point_placements(placement & p, text_plac { double x, y; shape_path.vertex(&x,&y); - find_point_placement(p, po, x, y); + find_point_placement(x, y); return; } int num_labels = 1; if (p.label_spacing > 0) - num_labels = static_cast (floor(total_distance / p.label_spacing)); + num_labels = static_cast (floor(total_distance / pi.get_actual_label_spacing())); - if (p.force_odd_labels && num_labels%2 == 0) + if (p.force_odd_labels && num_labels % 2 == 0) num_labels--; if (num_labels <= 0) num_labels = 1; @@ -217,7 +166,7 @@ void placement_finder::find_point_placements(placement & p, text_plac { //Try place at the specified place double new_weight = (segment_length - (distance - target_distance))/segment_length; - find_point_placement(p, po, old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight); + find_point_placement(old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight); distance -= target_distance; //Consume the spacing gap we have used up target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label. @@ -231,245 +180,270 @@ void placement_finder::find_point_placements(placement & p, text_plac } template -void placement_finder::find_point_placement(placement & p, - text_placement_info_ptr po, - double label_x, - double label_y, - double angle, - unsigned line_spacing, - unsigned character_spacing) +void placement_finder::init_string_size() { - double x, y; - std::auto_ptr current_placement(new placement_element); - - std::pair string_dimensions = p.info.get_dimensions(); - double string_width = string_dimensions.first + (character_spacing *(p.info.num_characters()-1)); - double string_height = string_dimensions.second; + // Get total string size + string_width_ = 0; + string_height_ = 0; + first_line_space_ = 0; + if (!info_.num_characters()) return; //At least one character is required + for (unsigned i = 0; i < info_.num_characters(); i++) + { + char_info const& ci = info_.at(i); + if (!ci.width || !ci.line_height) continue; //Skip empty chars (add no character_spacing for them) + string_width_ += ci.width + ci.format->character_spacing; + string_height_ = std::max(string_height_, ci.line_height+ci.format->line_spacing); + first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height); + } + string_width_ -= info_.at(info_.num_characters()-1).format->character_spacing; //Remove last space +} - // use height of tallest character in the string for the 'line' spacing to obtain consistent line spacing - double max_character_height = string_height; // height of the tallest character in the string + + +template +void placement_finder::find_line_breaks() +{ + bool first_line = true; + line_breaks_.clear(); + line_sizes_.clear(); // check if we need to wrap the string - double wrap_at = string_width + 1.0; - if (p.wrap_width && string_width > p.wrap_width) + double wrap_at = string_width_ + 1.0; + if (p.wrap_width && string_width_ > p.wrap_width) { if (p.text_ratio) - for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; i += 1.0) ; + for (double i = 1.0; ((wrap_at = string_width_/i)/(string_height_*i)) > p.text_ratio && (string_width_/i) > p.wrap_width; i += 1.0) ; else wrap_at = p.wrap_width; } // work out where our line breaks need to be and the resultant width to the 'wrapped' string - std::vector line_breaks; - std::vector line_widths; - - if ((p.info.num_characters() > 0) && ((wrap_at < string_width) || p.info.has_line_breaks())) + if ((wrap_at < string_width_) || info_.has_line_breaks()) { - int last_wrap_char = 0; - int last_wrap_char_width = 0; - string_width = 0.0; - string_height = 0.0; + first_line_space_ = 0.0; + int last_wrap_char_pos = 0; //Position of last char where wrapping is possible + double last_char_spacing = 0.0; + double last_wrap_char_width = 0.0; //Include char_spacing before and after + string_width_ = 0.0; + string_height_ = 0.0; double line_width = 0.0; - double word_width = 0.0; + double line_height = 0.0; //Height of tallest char in line + double word_width = 0.0; //Current unfinished word width + double word_height = 0.0; + //line_width, word_width does include char width + spacing, but not the spacing after the last char - for (unsigned int ii = 0; ii < p.info.num_characters(); ii++) + for (unsigned int ii = 0; ii < info_.num_characters(); ii++) { - character_info ci; - ci = p.info.at(ii); + char_info const& ci = info_.at(ii); + unsigned c = ci.c; - double cwidth = ci.width + character_spacing; - - unsigned c = ci.character; - word_width += cwidth; - - if ((c == p.wrap_char) || (c == '\n')) + if ((c == ci.format->wrap_char) || (c == '\n')) { - last_wrap_char = ii; - last_wrap_char_width = cwidth; - line_width += word_width; + last_wrap_char_pos = ii; + //No wrap at previous position + line_width += word_width + last_wrap_char_width; + line_height = std::max(line_height, word_height); + last_wrap_char_width = last_char_spacing + ci.width + ci.format->character_spacing; + last_char_spacing = 0.0; word_width = 0.0; + word_height = 0.0; + } else { + //No wrap char + word_width += last_char_spacing + ci.width; + last_char_spacing = ci.format->character_spacing; + word_height = std::max(word_height, ci.line_height + ci.format->line_spacing); + if (first_line) first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height); } // wrap text at first wrap_char after (default) the wrap width or immediately before the current word if ((c == '\n') || - (line_width > 0 && (((line_width - character_spacing) > wrap_at && !p.wrap_before) || - ((line_width + word_width - character_spacing) > wrap_at && p.wrap_before)) )) + (line_width > 0 && ((line_width > wrap_at && !ci.format->wrap_before) || + ((line_width + last_wrap_char_width + word_width) > wrap_at && ci.format->wrap_before)) )) { - // Remove width of breaking space character since it is not rendered and the character_spacing for the last character on the line - line_width -= (last_wrap_char_width + character_spacing); - string_width = string_width > line_width ? string_width : line_width; - string_height += max_character_height; - line_breaks.push_back(last_wrap_char); - line_widths.push_back(line_width); - ii = last_wrap_char; + string_width_ = std::max(string_width_, line_width); //Total width is the longest line + string_height_ += line_height; + line_breaks_.push_back(last_wrap_char_pos); + line_sizes_.push_back(std::make_pair(line_width, line_height)); line_width = 0.0; - word_width = 0.0; + line_height = 0.0; + last_wrap_char_width = 0; //Wrap char supressed + first_line = false; } } - line_width += (word_width - character_spacing); // remove character_spacing from last character on the line - string_width = string_width > line_width ? string_width : line_width; - string_height += max_character_height; - line_breaks.push_back(p.info.num_characters()); - line_widths.push_back(line_width); + line_width += last_wrap_char_width + word_width; + line_height = std::max(line_height, word_height); + string_width_ = std::max(string_width_, line_width); + string_height_ += line_height; + line_sizes_.push_back(std::make_pair(line_width, line_height)); + } else { + //No linebreaks + line_sizes_.push_back(std::make_pair(string_width_, string_height_)); } - if (line_breaks.size() == 0) - { - line_breaks.push_back(p.info.num_characters()); - line_widths.push_back(string_width); + line_breaks_.push_back(info_.num_characters()); +} + + + +template +void placement_finder::init_alignment() +{ + valign_ = p.valign; + if (valign_ == V_AUTO) { + if (p.displacement.get<1>() > 0.0) + valign_ = V_BOTTOM; + else if (p.displacement.get<1>() < 0.0) + valign_ = V_TOP; + else + valign_ = V_MIDDLE; } - int total_lines = line_breaks.size(); - p.info.set_dimensions( string_width, (string_height + (line_spacing * (total_lines-1))) ); + halign_ = p.halign; + if (halign_ == H_AUTO) { + if (p.displacement.get<0>() > 0.0) + halign_ = H_RIGHT; + else if (p.displacement.get<0>() < 0.0) + halign_ = H_LEFT; + else + halign_ = H_MIDDLE; + } +} + +template +void placement_finder::adjust_position(placement_element *current_placement, double label_x, double label_y) +{ // if needed, adjust for desired vertical alignment current_placement->starting_y = label_y; // no adjustment, default is MIDDLE - - vertical_alignment_e real_valign = po->valign; - if (real_valign == V_AUTO) { - if (po->displacement.get<1>() > 0.0) - real_valign = V_BOTTOM; - else if (po->displacement.get<1>() < 0.0) - real_valign = V_TOP; - else - real_valign = V_MIDDLE; + if (valign_ == V_TOP) + current_placement->starting_y -= 0.5 * string_height_; // move center up by 1/2 the total height + else if (valign_ == V_BOTTOM) { + current_placement->starting_y += 0.5 * string_height_; // move center down by the 1/2 the total height + current_placement->starting_y -= first_line_space_; + } else if (valign_ == V_MIDDLE) { + current_placement->starting_y -= first_line_space_/2.0; } - horizontal_alignment_e real_halign = po->halign; - if (real_halign == H_AUTO) { - if (po->displacement.get<0>() > 0.0) - real_halign = H_RIGHT; - else if (po->displacement.get<0>() < 0.0) - real_halign = H_LEFT; - else - real_halign = H_MIDDLE; - } - - if (real_valign == V_TOP) - current_placement->starting_y -= 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center up by 1/2 the total height - - else if (real_valign == V_BOTTOM) - current_placement->starting_y += 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center down by the 1/2 the total height - - // correct placement for error, but BOTTOM does not need to be adjusted - // (text rendering is at text_size, but line placement is by line_height (max_character_height), - // and the rendering adds the extra space below the characters) - if (real_valign == V_TOP ) - current_placement->starting_y -= (po->text_size - max_character_height); // move up by the error - - else if (real_valign == V_MIDDLE) - current_placement->starting_y -= ((po->text_size - max_character_height) / 2.0); // move up by 1/2 the error - // set horizontal position to middle of text current_placement->starting_x = label_x; // no adjustment, default is MIDDLE - - if (real_halign == H_LEFT) - current_placement->starting_x -= 0.5 * string_width; // move center left by 1/2 the string width - - else if (real_halign == H_RIGHT) - current_placement->starting_x += 0.5 * string_width; // move center right by 1/2 the string width + if (halign_ == H_LEFT) + current_placement->starting_x -= 0.5 * string_width_; // move center left by 1/2 the string width + else if (halign_ == H_RIGHT) + current_placement->starting_x += 0.5 * string_width_; // move center right by 1/2 the string width // adjust text envelope position by user's x-y displacement (dx, dy) - current_placement->starting_x += p.scale_factor_ * boost::tuples::get<0>(po->displacement); - current_placement->starting_y += p.scale_factor_ * boost::tuples::get<1>(po->displacement); + current_placement->starting_x += pi.get_scale_factor() * boost::tuples::get<0>(p.displacement); + current_placement->starting_y += pi.get_scale_factor() * boost::tuples::get<1>(p.displacement); + +} + +template +void placement_finder::find_point_placement(double label_x, double label_y, double angle) +{ + init_string_size(); + find_line_breaks(); + init_alignment(); + + double rad = M_PI * angle/180.0; + double cosa = std::cos(rad); + double sina = std::sin(rad); + + double x, y; + std::auto_ptr current_placement(new placement_element); + + adjust_position(current_placement.get(), label_x, label_y); // presets for first line unsigned int line_number = 0; - unsigned int index_to_wrap_at = line_breaks[0]; - double line_width = line_widths[0]; + unsigned int index_to_wrap_at = line_breaks_[0]; + double line_width = line_sizes_[0].first; + double line_height = line_sizes_[0].second; + //TODO: Understand and document this // set for upper left corner of text envelope for the first line, bottom left of first character - x = -(line_width / 2.0); - if (p.info.get_rtl()==false) - { - y = (0.5 * (string_height + (line_spacing * (total_lines-1)))) - max_character_height; - } + y = (string_height_ / 2.0) - line_height; + + // adjust for desired justification + //TODO: Understand and document this + if (p.jalign == J_LEFT) + x = -(string_width_ / 2.0); + else if (p.jalign == J_RIGHT) + x = (string_width_ / 2.0) - line_width; else - { - y = -(0.5 * (string_height + (line_spacing * (total_lines-1)))) + max_character_height; - } - - // if needed, adjust for desired justification (J_MIDDLE is the default) - if( po->jalign == J_LEFT ) - x = -(string_width / 2.0); - - else if (po->jalign == J_RIGHT) - x = (string_width / 2.0) - line_width; + x = -(line_width / 2.0); // save each character rendering position and build envelope as go thru loop std::queue< box2d > c_envelopes; - for (unsigned i = 0; i < p.info.num_characters(); i++) + for (unsigned i = 0; i < info_.num_characters(); i++) { - character_info ci; - ci = p.info.at(i); + char_info const& ci = info_.at(i); - double cwidth = ci.width + character_spacing; + double cwidth = ci.width + ci.format->character_spacing; - unsigned c = ci.character; + unsigned c = ci.c; if (i == index_to_wrap_at) { - index_to_wrap_at = line_breaks[++line_number]; - line_width = line_widths[line_number]; + index_to_wrap_at = line_breaks_[++line_number]; + line_width = line_sizes_[line_number].first; + line_height= line_sizes_[line_number].second; - if (p.info.get_rtl()==false) - { - y -= (max_character_height + line_spacing); // move position down to line start - } - else - { - y += (max_character_height + line_spacing); // move position up to line start - } + y -= line_height; // move position down to line start // reset to begining of line position - x = ((po->jalign == J_LEFT)? -(string_width / 2.0): ((po->jalign == J_RIGHT)? ((string_width /2.0) - line_width): -(line_width / 2.0))); + if (p.jalign == J_LEFT) + x = -(string_width_ / 2.0); + else if (p.jalign == J_RIGHT) + x = (string_width_ / 2.0) - line_width; + else + x = -(line_width / 2.0); continue; } else { // place the character relative to the center of the string envelope - double rad = M_PI * angle/180.0; - double cosa = fast_cos(rad); - double sina = fast_sin(rad); - double dx = x * cosa - y*sina; double dy = x * sina + y*cosa; - current_placement->add_node(c, dx, dy, rad); + current_placement->add_node(c, dx, dy, rad, ci.format); // compute the Bounding Box for each character and test for: // overlap, minimum distance or edge avoidance - exit if condition occurs box2d e; - if (p.has_dimensions) + /*x axis: left to right, y axis: top to bottom (negative values higher)*/ + if (pi.has_dimensions) { - e.init(current_placement->starting_x - (p.dimensions.first/2.0), // Top Left - current_placement->starting_y - (p.dimensions.second/2.0), + e.init(current_placement->starting_x - (pi.dimensions.first/2.0), // Top Left + current_placement->starting_y - (pi.dimensions.second/2.0), - current_placement->starting_x + (p.dimensions.first/2.0), // Bottom Right - current_placement->starting_y + (p.dimensions.second/2.0)); + current_placement->starting_x + (pi.dimensions.first/2.0), // Bottom Right + current_placement->starting_y + (pi.dimensions.second/2.0)); } else { e.init(current_placement->starting_x + dx, // Bottom Left - current_placement->starting_y - dy, + current_placement->starting_y - dy - ci.ymin, /*ymin usually <0 */ current_placement->starting_x + dx + ci.width, // Top Right - current_placement->starting_y - dy - max_character_height); + current_placement->starting_y - dy - ci.ymax); } // if there is an overlap with existing envelopes, then exit - no placement - if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e,p.minimum_distance))) + if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e, pi.get_actual_minimum_distance()))) { return; + } // if avoid_edges test dimensions contains e - if (p.avoid_edges && !dimensions_.contains(e)) + if (p.avoid_edges && !dimensions_.contains(e)) { return; + } - if (p.minimum_padding > 0) + if (p.minimum_padding > 0) { - box2d epad(e.minx()-p.minimum_padding, - e.miny()-p.minimum_padding, - e.maxx()+p.minimum_padding, - e.maxy()+p.minimum_padding); + double min_pad = pi.get_actual_minimum_padding(); + box2d epad(e.minx()-min_pad, + e.miny()-min_pad, + e.maxx()+min_pad, + e.maxy()+min_pad); if (!dimensions_.contains(epad)) { return; @@ -482,6 +456,8 @@ void placement_finder::find_point_placement(placement & p, x += cwidth; // move position to next character } +#if 0 + //TODO // check the placement of any additional envelopes if (!p.allow_overlap && !p.additional_boxes.empty()) { @@ -498,22 +474,24 @@ void placement_finder::find_point_placement(placement & p, c_envelopes.push(pt); } } +#endif // since there was no early exit, add the character envelopes to the placements' envelopes while( !c_envelopes.empty() ) { - p.envelopes.push( c_envelopes.front() ); + pi.envelopes.push( c_envelopes.front() ); c_envelopes.pop(); } - p.placements.push_back(current_placement.release()); + pi.placements.push_back(current_placement.release()); } template template -void placement_finder::find_line_placements(placement & p, text_placement_info_ptr po, PathT & shape_path) +void placement_finder::find_line_placements(PathT & shape_path) { + init_string_size(); unsigned cmd; double new_x = 0.0; double new_y = 0.0; @@ -556,29 +534,26 @@ void placement_finder::find_line_placements(placement & p, text_place return; double distance = 0.0; - std::pair string_dimensions = p.info.get_dimensions(); - double string_width = string_dimensions.first; - - double displacement = boost::tuples::get<1>(po->displacement); // displace by dy + double displacement = boost::tuples::get<1>(p.displacement); // displace by dy //Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring - if (total_distance < string_width) //Can't place any strings + if (total_distance < string_width_) //Can't place any strings return; //If there is no spacing then just do one label, otherwise calculate how many there should be int num_labels = 1; if (p.label_spacing > 0) - num_labels = static_cast (floor(total_distance / (p.label_spacing + string_width))); + num_labels = static_cast (floor(total_distance / (pi.get_actual_label_spacing() + string_width_))); - if (p.force_odd_labels && num_labels%2 == 0) + if (p.force_odd_labels && (num_labels % 2 == 0)) num_labels--; if (num_labels <= 0) num_labels = 1; //Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly double spacing = total_distance / num_labels; - double target_distance = (spacing - string_width) / 2; // first label should be placed at half the spacing + double target_distance = (spacing - string_width_) / 2; // first label should be placed at half the spacing //Calculate or read out the tolerance double tolerance_delta, tolerance; @@ -620,7 +595,7 @@ void placement_finder::find_line_placements(placement & p, text_place { //Record details for the start of the string placement int orientation = 0; - std::auto_ptr current_placement = get_placement_offset(p, path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir)); + std::auto_ptr current_placement = get_placement_offset(path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir)); //We were unable to place here if (current_placement.get() == NULL) @@ -639,22 +614,20 @@ void placement_finder::find_line_placements(placement & p, text_place } anglesum /= current_placement->nodes_.size(); //Now it is angle average - double disp_x = p.scale_factor_ * displacement*fast_cos(anglesum+M_PI/2); - double disp_y = p.scale_factor_ * displacement*fast_sin(anglesum+M_PI/2); //Offset all the characters by this angle for (unsigned i = 0; i < current_placement->nodes_.size(); i++) { - current_placement->nodes_[i].x += disp_x; - current_placement->nodes_[i].y += disp_y; + current_placement->nodes_[i].x += pi.get_scale_factor() * displacement*cos(anglesum+M_PI/2); + current_placement->nodes_[i].y += pi.get_scale_factor() * displacement*sin(anglesum+M_PI/2); } } - bool status = test_placement(p, current_placement, orientation); + bool status = test_placement(current_placement, orientation); if (status) //We have successfully placed one { - p.placements.push_back(current_placement.release()); - update_detector(p); + pi.placements.push_back(current_placement.release()); + update_detector(); //Totally break out of the loops diff = tolerance; @@ -663,8 +636,8 @@ void placement_finder::find_line_placements(placement & p, text_place else { //If we've failed to place, remove all the envelopes we've added up - while (!p.envelopes.empty()) - p.envelopes.pop(); + while (!pi.envelopes.empty()) + pi.envelopes.pop(); } //Don't need to loop twice when diff = 0 @@ -684,7 +657,7 @@ void placement_finder::find_line_placements(placement & p, text_place } template -std::auto_ptr placement_finder::get_placement_offset(placement & p, const std::vector &path_positions, const std::vector &path_distances, int &orientation, unsigned index, double distance) +std::auto_ptr placement_finder::get_placement_offset(const std::vector &path_positions, const std::vector &path_distances, int &orientation, unsigned index, double distance) { //Check that the given distance is on the given index and find the correct index and distance if not while (distance < 0 && index > 1) @@ -710,7 +683,6 @@ std::auto_ptr placement_finder::get_placement_offs std::auto_ptr current_placement(new placement_element); - double string_height = p.info.get_dimensions().second; double old_x = path_positions[index-1].x; double old_y = path_positions[index-1].y; @@ -728,7 +700,7 @@ std::auto_ptr placement_finder::get_placement_offs current_placement->starting_x = old_x + dx*distance/segment_length; current_placement->starting_y = old_y + dy*distance/segment_length; - double angle = fast_atan2(-dy, dx); + double angle = atan2(-dy, dx); bool orientation_forced = (orientation != 0); //Wether the orientation was set by the caller if (!orientation_forced) @@ -736,17 +708,14 @@ std::auto_ptr placement_finder::get_placement_offs unsigned upside_down_char_count = 0; //Count of characters that are placed upside down. - for (unsigned i = 0; i < p.info.num_characters(); ++i) + for (unsigned i = 0; i < info_.num_characters(); ++i) { - character_info ci; - unsigned c; + // grab the next character according to the orientation + char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1); + unsigned c = ci.c; double last_character_angle = angle; - // grab the next character according to the orientation - ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1); - c = ci.character; - //Coordinates this character will start at if (segment_length == 0) { // Not allowed to place across on 0 length segments or discontinuities @@ -826,19 +795,20 @@ std::auto_ptr placement_finder::get_placement_offs double render_y = start_y; //Center the text on the line - render_x += (((double)string_height/2.0) - 1.0)*sina; - render_y += (((double)string_height/2.0) - 1.0)*cosa; + double char_height = ci.avg_height; + render_x -= char_height/2.0*cos(render_angle+M_PI/2); + render_y += char_height/2.0*sin(render_angle+M_PI/2); if (orientation < 0) { // rotate in place - render_x += ci.width*cosa - (string_height-2)*sina; - render_y -= ci.width*sina + (string_height-2)*cosa; + render_x += ci.width*cos(render_angle) - (char_height-2)*sin(render_angle); + render_y -= ci.width*sin(render_angle) + (char_height-2)*cos(render_angle); render_angle += M_PI; } current_placement->add_node(c,render_x - current_placement->starting_x, -render_y + current_placement->starting_y, - render_angle); + render_angle, ci.format); //Normalise to 0 <= angle < 2PI while (render_angle >= 2*M_PI) @@ -851,13 +821,13 @@ std::auto_ptr placement_finder::get_placement_offs } //If we placed too many characters upside down - if (upside_down_char_count >= p.info.num_characters()/2.0) + if (upside_down_char_count >= info_.num_characters()/2.0) { //if we auto-detected the orientation then retry with the opposite orientation if (!orientation_forced) { orientation = -orientation; - current_placement = get_placement_offset(p, path_positions, path_distances, orientation, initial_index, initial_distance); + current_placement = get_placement_offset(path_positions, path_distances, orientation, initial_index, initial_distance); } else { @@ -871,57 +841,50 @@ std::auto_ptr placement_finder::get_placement_offs } template -bool placement_finder::test_placement(placement & p, const std::auto_ptr & current_placement, const int & orientation) +bool placement_finder::test_placement(const std::auto_ptr & current_placement, const int & orientation) { - std::pair string_dimensions = p.info.get_dimensions(); - - double string_height = string_dimensions.second; - - //Create and test envelopes bool status = true; - for (unsigned i = 0; i < p.info.num_characters(); ++i) + for (unsigned i = 0; i < info_.num_characters(); ++i) { // grab the next character according to the orientation - character_info ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1); + char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1); int c; double x, y, angle; - current_placement->vertex(&c, &x, &y, &angle); + char_properties *properties; + current_placement->vertex(&c, &x, &y, &angle, &properties); x = current_placement->starting_x + x; y = current_placement->starting_y - y; if (orientation < 0) { - double sina = fast_sin(angle); - double cosa = fast_cos(angle); // rotate in place - x += ci.width*cosa - (string_height-2)*sina; - y -= ci.width*sina + (string_height-2)*cosa; + /* TODO: What's the meaning of -2? */ + x += ci.width*cos(angle) - (string_height_-2)*sin(angle); + y -= ci.width*sin(angle) + (string_height_-2)*cos(angle); angle += M_PI; } box2d e; - if (p.has_dimensions) + if (pi.has_dimensions) { - e.init(x, y, x + p.dimensions.first, y + p.dimensions.second); + e.init(x, y, x + pi.dimensions.first, y + pi.dimensions.second); } else { - double sina = fast_sin(angle); - double cosa = fast_cos(angle); // put four corners of the letter into envelope - e.init(x, y, x + ci.width*cosa, - y - ci.width*sina); - e.expand_to_include(x - ci.height*sina, - y - ci.height*cosa); - e.expand_to_include(x + (ci.width*cosa - ci.height*sina), - y - (ci.width*sina + ci.height*cosa)); + e.init(x, y, x + ci.width*cos(angle), + y - ci.width*sin(angle)); + e.expand_to_include(x - ci.height()*sin(angle), + y - ci.height()*cos(angle)); + e.expand_to_include(x + (ci.width*cos(angle) - ci.height()*sin(angle)), + y - (ci.width*sin(angle) + ci.height()*cos(angle))); } if (!detector_.extent().intersects(e) || - !detector_.has_placement(e, p.info.get_string(), p.minimum_distance)) + !detector_.has_placement(e, info_.get_string(), pi.get_actual_minimum_distance())) { //std::clog << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_ << std::endl; - //std::clog << "No Placements:" << !detector_.has_placement(e, p.info.get_string(), p.minimum_distance) << std::endl; + //std::clog << "No Placements:" << !detector_.has_placement(e, info.get_string(), p.minimum_distance) << std::endl; status = false; break; } @@ -932,19 +895,20 @@ bool placement_finder::test_placement(placement & p, const std::auto_ status = false; break; } - if (p.minimum_padding > 0) + if (p.minimum_padding > 0) { - box2d epad(e.minx()-p.minimum_padding, - e.miny()-p.minimum_padding, - e.maxx()+p.minimum_padding, - e.maxy()+p.minimum_padding); + double min_pad = pi.get_actual_minimum_padding(); + box2d epad(e.minx()-min_pad, + e.miny()-min_pad, + e.maxx()+min_pad, + e.maxy()+min_pad); if (!dimensions_.contains(epad)) { status = false; break; } } - p.envelopes.push(e); + pi.envelopes.push(e); } current_placement->rewind(); @@ -1000,27 +964,27 @@ void placement_finder::find_line_circle_intersection( } template -void placement_finder::update_detector(placement & p) +void placement_finder::update_detector() { bool first = true; // add the bboxes to the detector and remove from the placement - while (!p.envelopes.empty()) + while (!pi.envelopes.empty()) { - box2d e = p.envelopes.front(); - detector_.insert(e, p.info.get_string()); - p.envelopes.pop(); + box2d e = pi.envelopes.front(); + detector_.insert(e, info_.get_string()); + pi.envelopes.pop(); - if (p.collect_extents) + if (pi.collect_extents) { if(first) { first = false; - p.extents = e; + pi.extents = e; } else { - p.extents.expand_to_include(e); + pi.extents.expand_to_include(e); } } } @@ -1032,11 +996,58 @@ void placement_finder::clear() detector_.clear(); } +template +void placement_finder::find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans) +{ + double label_x=0.0; + double label_y=0.0; + double z=0.0; + if (p.label_placement == POINT_PLACEMENT || + p.label_placement == VERTEX_PLACEMENT || + p.label_placement == INTERIOR_PLACEMENT) + { + unsigned iterations = 1; + if (p.label_placement == VERTEX_PLACEMENT) + { + iterations = geom.num_points(); + geom.rewind(0); + } + for(unsigned jj = 0; jj < iterations; jj++) { + switch (p.label_placement) + { + case POINT_PLACEMENT: + geom.label_position(&label_x, &label_y); + break; + case INTERIOR_PLACEMENT: + geom.label_interior_position(&label_x, &label_y); + break; + case VERTEX_PLACEMENT: + geom.vertex(&label_x, &label_y); + break; + case LINE_PLACEMENT: + case label_placement_enum_MAX: + /*not handled here*/ + break; + } + prj_trans.backward(label_x, label_y, z); + t.forward(&label_x, &label_y); + + find_point_placement(label_x, label_y, angle); + } + update_detector(); + } else if (p.label_placement == LINE_PLACEMENT && geom.num_points() > 1) + { + typedef coord_transform2 path_type; + path_type path(t, geom, prj_trans); + find_line_placements(path); + } +} + typedef coord_transform2 PathType; typedef label_collision_detector4 DetectorType; template class placement_finder; -template void placement_finder::find_point_placements (placement&, text_placement_info_ptr po, PathType & ); -template void placement_finder::find_line_placements (placement&, text_placement_info_ptr po, PathType & ); +template void placement_finder::find_point_placements(PathType &); +template void placement_finder::find_line_placements(PathType &); } // namespace diff --git a/src/save_map.cpp b/src/save_map.cpp index 3cf56b014..87bc14c4f 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include // boost #include @@ -310,100 +312,6 @@ public: add_metawriter_attributes(sym_node, sym); } - void operator () ( glyph_symbolizer const& sym) - { - ptree &node = rule_.push_back( - ptree::value_type("GlyphSymbolizer", ptree()) - )->second; - - glyph_symbolizer dfl("", expression_ptr()); - - // face_name - set_attr( node, "face-name", sym.get_face_name() ); - - // char - if (sym.get_char()) { - const std::string &str = - to_expression_string(*sym.get_char()); - set_attr( node, "char", str ); - } - - // angle - if (sym.get_angle()) { - const std::string &str = - to_expression_string(*sym.get_angle()); - set_attr( node, "angle", str ); - } - - // value - if (sym.get_value()) { - const std::string &str = - to_expression_string(*sym.get_value()); - set_attr( node, "value", str ); - } - - // size - if (sym.get_size()) { - const std::string &str = - to_expression_string(*sym.get_size()); - set_attr( node, "size", str ); - } - - // color - if (sym.get_color()) { - const std::string &str = - to_expression_string(*sym.get_color()); - set_attr( node, "color", str ); - } - - // colorizer - if (sym.get_colorizer()) { - serialize_raster_colorizer(node, sym.get_colorizer(), - explicit_defaults_); - } - - // allow_overlap - if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ ) - { - set_attr( node, "allow-overlap", sym.get_allow_overlap() ); - } - // avoid_edges - if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ ) - { - set_attr( node, "avoid-edges", sym.get_avoid_edges() ); - } - - // displacement - position displacement = sym.get_displacement(); - if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ ) - { - set_attr( node, "dx", displacement.get<0>() ); - } - if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ ) - { - set_attr( node, "dy", displacement.get<1>() ); - } - - // halo fill & radius - const color & c = sym.get_halo_fill(); - if ( c != dfl.get_halo_fill() || explicit_defaults_ ) - { - set_attr( node, "halo-fill", c ); - } - - if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ ) - { - set_attr( node, "halo-radius", sym.get_halo_radius() ); - } - - // angle_mode - if (sym.get_angle_mode() != dfl.get_angle_mode() || explicit_defaults_ ) - { - set_attr( node, "angle-mode", sym.get_angle_mode() ); - } - add_metawriter_attributes(node, sym); - } - private: serialize_symbolizer(); @@ -451,134 +359,28 @@ private: } void add_font_attributes(ptree & node, const text_symbolizer & sym) { - expression_ptr const& expr = sym.get_name(); - const std::string & name = to_expression_string(*expr); - - if (!name.empty()) { - ptree& text_node = node.push_back(ptree::value_type("", ptree()))->second; - text_node.put_value(name); + text_placements_ptr p = sym.get_placement_options(); + p->properties.to_xml(node, explicit_defaults_); + /* Known types: + - text_placements_dummy: no handling required + - text_placements_simple: positions string + - text_placements_list: list string + */ + text_placements_simple *simple = dynamic_cast(p.get()); + text_placements_list *list = dynamic_cast(p.get()); + if (simple) { + set_attr(node, "placment-type", "simple"); + set_attr(node, "placements", simple->get_positions()); } - const std::string & face_name = sym.get_face_name(); - if ( ! face_name.empty() ) { - set_attr( node, "face-name", face_name ); - } - const std::string & fontset_name = sym.get_fontset().get_name(); - if ( ! fontset_name.empty() ) { - set_attr( node, "fontset-name", fontset_name ); - } - - set_attr( node, "size", sym.get_text_size() ); - set_attr( node, "fill", sym.get_fill() ); - - // pseudo-default-construct a text_symbolizer. It is used - // to avoid printing ofattributes with default values without - // repeating the default values here. - // maybe add a real, explicit default-ctor? - // FIXME - text_symbolizer dfl(expression_ptr(), "", - 0, color(0,0,0) ); - - position displacement = sym.get_displacement(); - if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ ) - { - set_attr( node, "dx", displacement.get<0>() ); - } - if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ ) - { - set_attr( node, "dy", displacement.get<1>() ); - } - - if (sym.get_label_placement() != dfl.get_label_placement() || explicit_defaults_ ) - { - set_attr( node, "placement", sym.get_label_placement() ); - } - - if (sym.get_vertical_alignment() != dfl.get_vertical_alignment() || explicit_defaults_ ) - { - set_attr( node, "vertical-alignment", sym.get_vertical_alignment() ); - } - - if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ ) - { - set_attr( node, "halo-radius", sym.get_halo_radius() ); - } - const color & c = sym.get_halo_fill(); - if ( c != dfl.get_halo_fill() || explicit_defaults_ ) - { - set_attr( node, "halo-fill", c ); - } - if (sym.get_text_ratio() != dfl.get_text_ratio() || explicit_defaults_ ) - { - set_attr( node, "text-ratio", sym.get_text_ratio() ); - } - if (sym.get_wrap_width() != dfl.get_wrap_width() || explicit_defaults_ ) - { - set_attr( node, "wrap-width", sym.get_wrap_width() ); - } - if (sym.get_wrap_before() != dfl.get_wrap_before() || explicit_defaults_ ) - { - set_attr( node, "wrap-before", sym.get_wrap_before() ); - } - 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_transform() != dfl.get_text_transform() || explicit_defaults_ ) - { - set_attr( node, "text-transform", sym.get_text_transform() ); - } - 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_position_tolerance() != dfl.get_label_position_tolerance() || explicit_defaults_ ) - { - set_attr( node, "label-position-tolerance", sym.get_label_position_tolerance() ); - } - if (sym.get_label_spacing() != dfl.get_label_spacing() || explicit_defaults_ ) - { - set_attr( node, "spacing", sym.get_label_spacing() ); - } - if (sym.get_minimum_distance() != dfl.get_minimum_distance() || explicit_defaults_ ) - { - set_attr( node, "minimum-distance", sym.get_minimum_distance() ); - } - if (sym.get_minimum_padding() != dfl.get_minimum_padding() || explicit_defaults_ ) - { - set_attr( node, "minimum-padding", sym.get_minimum_padding() ); - } - if (sym.get_minimum_path_length() != dfl.get_minimum_path_length() || explicit_defaults_ ) - { - set_attr( node, "minimum-path-length", sym.get_minimum_path_length() ); - } - if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ ) - { - set_attr( node, "allow-overlap", sym.get_allow_overlap() ); - } - if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ ) - { - set_attr( node, "avoid-edges", sym.get_avoid_edges() ); - } - // for shield_symbolizer this is later overridden - if (sym.get_text_opacity() != dfl.get_text_opacity() || explicit_defaults_ ) - { - set_attr( node, "opacity", sym.get_text_opacity() ); - } - if (sym.get_max_char_angle_delta() != dfl.get_max_char_angle_delta() || explicit_defaults_ ) - { - set_attr( node, "max-char-angle-delta", sym.get_max_char_angle_delta() ); - } - if (sym.get_horizontal_alignment() != dfl.get_horizontal_alignment() || explicit_defaults_ ) - { - set_attr( node, "horizontal-alignment", sym.get_horizontal_alignment() ); - } - if (sym.get_justify_alignment() != dfl.get_justify_alignment() || explicit_defaults_ ) - { - set_attr( node, "justify-alignment", sym.get_justify_alignment() ); + if (list) { + set_attr(node, "placment-type", "list"); + unsigned i; + text_symbolizer_properties *dfl = &(list->properties); + for (i=0; i < list->size(); i++) { + ptree &placement_node = node.push_back(ptree::value_type("Placement", ptree()))->second; + list->get(i).to_xml(placement_node, explicit_defaults_, *dfl); + dfl = &(list->get(i)); + } } } diff --git a/src/shield_symbolizer.cpp b/src/shield_symbolizer.cpp index fcf54a73e..5de905935 100644 --- a/src/shield_symbolizer.cpp +++ b/src/shield_symbolizer.cpp @@ -34,6 +34,15 @@ namespace mapnik { +shield_symbolizer::shield_symbolizer(text_placements_ptr placements) + : text_symbolizer(placements), + symbolizer_with_image(), + unlock_image_(false), + no_text_(false), + shield_displacement_(boost::make_tuple(0,0)) +{ +} + shield_symbolizer::shield_symbolizer( expression_ptr name, std::string const& face_name, diff --git a/src/svg/process_glyph_symbolizer.cpp b/src/svg/process_glyph_symbolizer.cpp deleted file mode 100644 index 7ac35bb4d..000000000 --- a/src/svg/process_glyph_symbolizer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ -//$Id$ - -// mapnik -#include - -namespace mapnik -{ - template - void svg_renderer::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) - { - // nothing yet. - } - - template void svg_renderer >::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); -} diff --git a/src/symbolizer.cpp b/src/symbolizer.cpp index 4eba20036..e954aa5a1 100644 --- a/src/symbolizer.cpp +++ b/src/symbolizer.cpp @@ -72,7 +72,7 @@ metawriter_with_properties symbolizer_base::get_metawriter() const symbolizer_with_image::symbolizer_with_image(path_expression_ptr file) : image_filename_( file ), - opacity_(1.0f) + image_opacity_(1.0f) { matrix_[0] = 1.0; @@ -85,7 +85,7 @@ symbolizer_with_image::symbolizer_with_image(path_expression_ptr file) symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs) : image_filename_(rhs.image_filename_), - opacity_(rhs.opacity_), + image_opacity_(rhs.image_opacity_), matrix_(rhs.matrix_) {} path_expression_ptr symbolizer_with_image::get_filename() const @@ -120,12 +120,12 @@ std::string const symbolizer_with_image::get_transform_string() const void symbolizer_with_image::set_opacity(float opacity) { - opacity_ = opacity; + image_opacity_ = opacity; } float symbolizer_with_image::get_opacity() const { - return opacity_; + return image_opacity_; } } // end of namespace mapnik diff --git a/src/text_placements.cpp b/src/text_placements.cpp index 9ed8b20b2..a9b9d77c4 100644 --- a/src/text_placements.cpp +++ b/src/text_placements.cpp @@ -22,11 +22,16 @@ #include #include +#include +#include +#include +#include #include #include #include #include +#include namespace mapnik { @@ -36,14 +41,298 @@ using boost::spirit::ascii::space; using phoenix::push_back; using phoenix::ref; using qi::_1; +using boost::optional; + +text_symbolizer_properties::text_symbolizer_properties() : + label_placement(POINT_PLACEMENT), + halign(H_AUTO), + jalign(J_MIDDLE), + valign(V_AUTO), + label_spacing(0), + label_position_tolerance(0), + avoid_edges(false), + minimum_distance(0.0), + minimum_padding(0.0), + max_char_angle_delta(22.5 * M_PI/180.0), + force_odd_labels(false), + allow_overlap(false), + text_ratio(0), + wrap_width(0), + processor() +{ + +} + +void text_symbolizer_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets) +{ + optional placement_ = get_opt_attr(sym, "placement"); + if (placement_) label_placement = *placement_; + optional valign_ = get_opt_attr(sym, "vertical-alignment"); + if (valign_) valign = *valign_; + optional text_ratio_ = get_opt_attr(sym, "text-ratio"); + if (text_ratio_) text_ratio = *text_ratio_; + optional wrap_width_ = get_opt_attr(sym, "wrap-width"); + if (wrap_width_) wrap_width = *wrap_width_; + optional label_position_tolerance_ = get_opt_attr(sym, "label-position-tolerance"); + if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_; + optional spacing_ = get_opt_attr(sym, "spacing"); + if (spacing_) label_spacing = *spacing_; + optional minimum_distance_ = get_opt_attr(sym, "minimum-distance"); + if (minimum_distance_) minimum_distance = *minimum_distance_; + optional min_padding_ = get_opt_attr(sym, "minimum-padding"); + if (min_padding_) minimum_padding = *min_padding_; + optional min_path_length_ = get_opt_attr(sym, "minimum-path-length"); + if (min_path_length_) minimum_path_length = *min_path_length_; + optional avoid_edges_ = get_opt_attr(sym, "avoid-edges"); + if (avoid_edges_) avoid_edges = *avoid_edges_; + optional allow_overlap_ = get_opt_attr(sym, "allow-overlap"); + if (allow_overlap_) allow_overlap = *allow_overlap_; + optional halign_ = get_opt_attr(sym, "horizontal-alignment"); + if (halign_) halign = *halign_; + optional jalign_ = get_opt_attr(sym, "justify-alignment"); + if (jalign_) jalign = *jalign_; + /* Attributes needing special care */ + optional orientation_ = get_opt_attr(sym, "orientation"); + if (orientation_) orientation = parse_expression(*orientation_, "utf8"); + optional dx = get_opt_attr(sym, "dx"); + if (dx) displacement.get<0>() = *dx; + optional dy = get_opt_attr(sym, "dy"); + if (dy) displacement.get<1>() = *dy; + optional max_char_angle_delta_ = get_opt_attr(sym, "max-char-angle-delta"); + if (max_char_angle_delta_) max_char_angle_delta=(*max_char_angle_delta_)*(M_PI/180); + processor.from_xml(sym, fontsets); + optional name_ = get_opt_attr(sym, "name"); + if (name_) { + std::clog << "### WARNING: Using 'name' in TextSymbolizer/ShieldSymbolizer is deprecated!\n"; + processor.set_old_style_expression(parse_expression(*name_, "utf8")); + } +} + +void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_symbolizer_properties const &dfl) const +{ + if (orientation) + { + const std::string & orientationstr = to_expression_string(*orientation); + if (!dfl.orientation || orientationstr != to_expression_string(*(dfl.orientation)) || explicit_defaults) { + set_attr(node, "orientation", orientationstr); + } + } + + if (displacement.get<0>() != dfl.displacement.get<0>() || explicit_defaults) + { + set_attr(node, "dx", displacement.get<0>()); + } + if (displacement.get<1>() != dfl.displacement.get<1>() || explicit_defaults) + { + set_attr(node, "dy", displacement.get<1>()); + } + if (label_placement != dfl.label_placement || explicit_defaults) + { + set_attr(node, "placement", label_placement); + } + if (valign != dfl.valign || explicit_defaults) + { + set_attr(node, "vertical-alignment", valign); + } + if (text_ratio != dfl.text_ratio || explicit_defaults) + { + set_attr(node, "text-ratio", text_ratio); + } + if (wrap_width != dfl.wrap_width || explicit_defaults) + { + set_attr(node, "wrap-width", wrap_width); + } + if (label_position_tolerance != dfl.label_position_tolerance || explicit_defaults) + { + set_attr(node, "label-position-tolerance", label_position_tolerance); + } + if (label_spacing != dfl.label_spacing || explicit_defaults) + { + set_attr(node, "spacing", label_spacing); + } + if (minimum_distance != dfl.minimum_distance || explicit_defaults) + { + set_attr(node, "minimum-distance", minimum_distance); + } + if (minimum_padding != dfl.minimum_padding || explicit_defaults) + { + set_attr(node, "minimum-padding", minimum_padding); + } + if (minimum_path_length != dfl.minimum_path_length || explicit_defaults) + { + set_attr(node, "minimum-path-length", minimum_path_length); + } + if (allow_overlap != dfl.allow_overlap || explicit_defaults) + { + set_attr(node, "allow-overlap", allow_overlap); + } + if (avoid_edges != dfl.avoid_edges || explicit_defaults) + { + set_attr(node, "avoid-edges", avoid_edges); + } + if (max_char_angle_delta != dfl.max_char_angle_delta || explicit_defaults) + { + set_attr(node, "max-char-angle-delta", max_char_angle_delta); + } + if (halign != dfl.halign || explicit_defaults) + { + set_attr(node, "horizontal-alignment", halign); + } + if (jalign != dfl.jalign || explicit_defaults) + { + set_attr(node, "justify-alignment", jalign); + } + if (valign != dfl.valign || explicit_defaults) + { + set_attr(node, "vertical-alignment", valign); + } + processor.to_xml(node, explicit_defaults, dfl.processor); +} + +char_properties::char_properties() : + text_size(10.0), + character_spacing(0), + line_spacing(0), + text_opacity(1.0), + wrap_before(false), + wrap_char(' '), + text_transform(NONE), + fill(color(0,0,0)), + halo_fill(color(255,255,255)), + halo_radius(0) +{ + +} + +void char_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets) +{ + optional text_size_ = get_opt_attr(sym, "size"); + if (text_size_) text_size = *text_size_; + optional character_spacing_ = get_opt_attr(sym, "character-spacing"); + if (character_spacing_) character_spacing = *character_spacing_; + optional fill_ = get_opt_attr(sym, "fill"); + if (fill_) fill = *fill_; + optional halo_fill_ = get_opt_attr(sym, "halo-fill"); + if (halo_fill_) halo_fill = *halo_fill_; + optional halo_radius_ = get_opt_attr(sym, "halo-radius"); + if (halo_radius_) halo_radius = *halo_radius_; + optional wrap_before_ = get_opt_attr(sym, "wrap-before"); + if (wrap_before_) wrap_before = *wrap_before_; + optional tconvert_ = get_opt_attr(sym, "text-transform"); + if (tconvert_) text_transform = *tconvert_; + optional line_spacing_ = get_opt_attr(sym, "line-spacing"); + if (line_spacing_) line_spacing = *line_spacing_; + optional opacity_ = get_opt_attr(sym, "opacity"); + if (opacity_) text_opacity = *opacity_; + optional wrap_char_ = get_opt_attr(sym, "wrap-character"); + if (wrap_char_ && (*wrap_char_).size() > 0) wrap_char = ((*wrap_char_)[0]); + optional face_name_ = get_opt_attr(sym, "face-name"); + if (face_name_) + { + face_name = *face_name_; + } + optional fontset_name_ = get_opt_attr(sym, "fontset-name"); + if (fontset_name_) { + std::map::const_iterator itr = fontsets.find(*fontset_name_); + if (itr != fontsets.end()) + { + fontset = itr->second; + } else + { + throw config_error("Unable to find any fontset named '" + *fontset_name_ + "'"); + } + } + if (!face_name.empty() && !fontset.get_name().empty()) + { + throw config_error(std::string("Can't have both face-name and fontset-name")); + } + if (face_name.empty() && fontset.get_name().empty()) + { + throw config_error(std::string("Must have face-name or fontset-name")); + } +} + +void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl) const +{ + const std::string & fontset_name = fontset.get_name(); + const std::string & dfl_fontset_name = dfl.fontset.get_name(); + if (fontset_name != dfl_fontset_name || explicit_defaults) + { + set_attr(node, "fontset-name", fontset_name); + } + + if (face_name != dfl.face_name || explicit_defaults) + { + set_attr(node, "face-name", face_name); + } + + if (text_size != dfl.text_size || explicit_defaults) + { + set_attr(node, "size", text_size); + } + + if (fill != dfl.fill || explicit_defaults) + { + set_attr(node, "fill", fill); + } + if (halo_radius != dfl.halo_radius || explicit_defaults) + { + set_attr(node, "halo-radius", halo_radius); + } + if (halo_fill != dfl.halo_fill || explicit_defaults) + { + set_attr(node, "halo-fill", halo_fill); + } + if (wrap_before != dfl.wrap_before || explicit_defaults) + { + set_attr(node, "wrap-before", wrap_before); + } + if (wrap_char != dfl.wrap_char || explicit_defaults) + { + set_attr(node, "wrap-character", std::string(1, wrap_char)); + } + if (text_transform != dfl.text_transform || explicit_defaults) + { + set_attr(node, "text-transform", text_transform); + } + if (line_spacing != dfl.line_spacing || explicit_defaults) + { + set_attr(node, "line-spacing", line_spacing); + } + if (character_spacing != dfl.character_spacing || explicit_defaults) + { + set_attr(node, "character-spacing", character_spacing); + } + // for shield_symbolizer this is later overridden + if (text_opacity != dfl.text_opacity || explicit_defaults) + { + set_attr(node, "opacity", text_opacity); + } +} + +/************************************************************************/ + +text_placements::text_placements() : properties() +{ +} + +std::set text_placements::get_all_expressions() +{ + std::set result, tmp; + tmp = properties.processor.get_all_expressions(); + result.insert(tmp.begin(), tmp.end()); + result.insert(properties.orientation); + return result; +} /************************************************************************/ text_placement_info::text_placement_info(text_placements const* parent): - displacement(parent->displacement_), - text_size(parent->text_size_), halign(parent->halign_), jalign(parent->jalign_), - valign(parent->valign_) + properties(parent->properties), + scale_factor(1), + has_dimensions(false), + collect_extents(false) { } @@ -55,73 +344,81 @@ bool text_placement_info_dummy::next() return true; } -bool text_placement_info_dummy::next_position_only() -{ - if (position_state) return false; - position_state++; - return true; -} - text_placement_info_ptr text_placements_dummy::get_placement_info() const { return text_placement_info_ptr(new text_placement_info_dummy(this)); } +void text_placement_info::init(double scale_factor_, + unsigned w, unsigned h, bool has_dimensions_) +{ + scale_factor = scale_factor_; + dimensions = std::make_pair(w, h); + has_dimensions = has_dimensions_; +} /************************************************************************/ bool text_placement_info_simple::next() { - position_state = 0; - if (state == 0) { - text_size = parent_->text_size_; - } else { - if (state > parent_->text_sizes_.size()) return false; - text_size = parent_->text_sizes_[state-1]; + while (1) { + if (state == 0) { + properties.processor.defaults.text_size = parent_->properties.processor.defaults.text_size; + } else { + if (state > parent_->text_sizes_.size()) return false; + properties.processor.defaults.text_size = parent_->text_sizes_[state-1]; + } + if (!next_position_only()) { + state++; + position_state = 0; + } else { + break; + } } - state++; return true; } bool text_placement_info_simple::next_position_only() { + const position &pdisp = parent_->properties.displacement; + position &displacement = properties.displacement; if (position_state >= parent_->direction_.size()) return false; directions_t dir = parent_->direction_[position_state]; switch (dir) { case EXACT_POSITION: - displacement = parent_->displacement_; + displacement = pdisp; break; case NORTH: - displacement = boost::make_tuple(0, -abs(parent_->displacement_.get<1>())); + displacement = boost::make_tuple(0, -abs(pdisp.get<1>())); break; case EAST: - displacement = boost::make_tuple(abs(parent_->displacement_.get<0>()), 0); + displacement = boost::make_tuple(abs(pdisp.get<0>()), 0); break; case SOUTH: - displacement = boost::make_tuple(0, abs(parent_->displacement_.get<1>())); + displacement = boost::make_tuple(0, abs(pdisp.get<1>())); break; case WEST: - displacement = boost::make_tuple(-abs(parent_->displacement_.get<0>()), 0); + displacement = boost::make_tuple(-abs(pdisp.get<0>()), 0); break; case NORTHEAST: displacement = boost::make_tuple( - abs(parent_->displacement_.get<0>()), - -abs(parent_->displacement_.get<1>())); + abs(pdisp.get<0>()), + -abs(pdisp.get<1>())); break; case SOUTHEAST: displacement = boost::make_tuple( - abs(parent_->displacement_.get<0>()), - abs(parent_->displacement_.get<1>())); + abs(pdisp.get<0>()), + abs(pdisp.get<1>())); break; case NORTHWEST: displacement = boost::make_tuple( - -abs(parent_->displacement_.get<0>()), - -abs(parent_->displacement_.get<1>())); + -abs(pdisp.get<0>()), + -abs(pdisp.get<1>())); break; case SOUTHWEST: displacement = boost::make_tuple( - -abs(parent_->displacement_.get<0>()), - abs(parent_->displacement_.get<1>())); + -abs(pdisp.get<0>()), + abs(pdisp.get<1>())); break; default: std::cerr << "WARNING: Unknown placement\n"; @@ -130,13 +427,12 @@ bool text_placement_info_simple::next_position_only() return true; } - text_placement_info_ptr text_placements_simple::get_placement_info() const { return text_placement_info_ptr(new text_placement_info_simple(this)); } -/** Positiion string: [POS][SIZE] +/** Position string: [POS][SIZE] * [POS] is any combination of * N, E, S, W, NE, SE, NW, SW, X (exact position) (separated by commas) * [SIZE] is a list of font sizes, separated by commas. The first font size @@ -196,4 +492,75 @@ text_placements_simple::text_placements_simple(std::string positions) { set_positions(positions); } + +std::string text_placements_simple::get_positions() +{ + return positions_; //TODO: Build string from data in direction_ and text_sizes_ +} + +/***************************************************************************/ + +bool text_placement_info_list::next() +{ + if (state == 0) { + properties = parent_->properties; + } else { + if (state-1 >= parent_->list_.size()) return false; + properties = parent_->list_[state-1]; + } + state++; + return true; +} + +text_symbolizer_properties & text_placements_list::add() +{ + if (list_.size()) { + text_symbolizer_properties &last = list_.back(); + list_.push_back(last); //Preinitialize with old values + } else { + list_.push_back(properties); + } + return list_.back(); +} + +text_symbolizer_properties & text_placements_list::get(unsigned i) +{ + return list_[i]; +} + +/***************************************************************************/ + +text_placement_info_ptr text_placements_list::get_placement_info() const +{ + return text_placement_info_ptr(new text_placement_info_list(this)); +} + +text_placements_list::text_placements_list() : text_placements(), list_(0) +{ + +} + +std::set text_placements_list::get_all_expressions() +{ + std::set result, tmp; + tmp = properties.processor.get_all_expressions(); + result.insert(tmp.begin(), tmp.end()); + result.insert(properties.orientation); + + std::vector::const_iterator it; + for (it=list_.begin(); it != list_.end(); it++) + { + tmp = it->processor.get_all_expressions(); + result.insert(tmp.begin(), tmp.end()); + result.insert(it->orientation); + } + return result; +} + +unsigned text_placements_list::size() const +{ + return list_.size(); +} + + } //namespace diff --git a/src/text_processing.cpp b/src/text_processing.cpp new file mode 100644 index 000000000..7d616ff18 --- /dev/null +++ b/src/text_processing.cpp @@ -0,0 +1,447 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace mapnik { +using boost::property_tree::ptree; +using boost::optional; + +class abstract_token +{ +public: + virtual ~abstract_token() {} + virtual ptree *to_xml(ptree *node) = 0; +}; + +class abstract_formating_token : public abstract_token +{ +public: + virtual void apply(char_properties &p, Feature const& feature) = 0; +}; + +class abstract_text_token : public abstract_token +{ +public: + virtual UnicodeString to_string(Feature const& feature) = 0; +}; + +class end_format_token : public abstract_token +{ +public: + end_format_token() {} + ptree *to_xml(ptree *node); +}; + +class expression_token: public abstract_text_token +{ +public: + expression_token(expression_ptr text); + UnicodeString to_string(Feature const& feature); + ptree *to_xml(ptree *node); + void set_expression(expression_ptr text); + expression_ptr get_expression(); +private: + expression_ptr text_; +}; + +class fixed_formating_token : public abstract_formating_token +{ +public: + fixed_formating_token(); + virtual void apply(char_properties &p, Feature const& feature); + ptree* to_xml(ptree *node); + void from_xml(ptree const& node); + void set_face_name(optional face_name); + void set_text_size(optional text_size); + void set_character_spacing(optional character_spacing); + void set_line_spacing(optional line_spacing); + void set_text_opacity(optional opacity); + void set_wrap_before(optional wrap_before); + void set_wrap_char(optional wrap_char); + void set_text_transform(optional text_trans); + void set_fill(optional fill); + void set_halo_fill(optional halo_fill); + void set_halo_radius(optional radius); +private: + boost::optional face_name_; +// font_set fontset; + boost::optional text_size_; + boost::optional character_spacing_; + boost::optional line_spacing_; + boost::optional text_opacity_; + boost::optional wrap_before_; + boost::optional wrap_char_; + boost::optional text_transform_; + boost::optional fill_; + boost::optional halo_fill_; + boost::optional halo_radius_; +}; + +/************************************************************/ + +expression_token::expression_token(expression_ptr text): + text_(text) +{ +} + +void expression_token::set_expression(expression_ptr text) +{ + text_ = text; +} + +expression_ptr expression_token::get_expression() +{ + return text_; +} + +UnicodeString expression_token::to_string(const Feature &feature) +{ + value_type result = boost::apply_visitor(evaluate(feature), *text_); + return result.to_unicode(); +} + +ptree *expression_token::to_xml(ptree *node) +{ + ptree &new_node = node->push_back(ptree::value_type( + "", ptree()))->second; + new_node.put_value(to_expression_string(*text_)); + return &new_node; +} + +/************************************************************/ + +fixed_formating_token::fixed_formating_token(): + fill_() +{ +} + +void fixed_formating_token::apply(char_properties &p, const Feature &feature) +{ + if (face_name_) p.face_name = *face_name_; + if (text_size_) p.text_size = *text_size_; + if (character_spacing_) p.character_spacing = *character_spacing_; + if (line_spacing_) p.line_spacing = *line_spacing_; + if (text_opacity_) p.text_opacity = *text_opacity_; + if (wrap_before_) p.wrap_before = *wrap_before_; + if (wrap_char_) p.wrap_char = *wrap_char_; + if (text_transform_) p.text_transform = *text_transform_; + if (fill_) p.fill = *fill_; + if (halo_fill_) p.halo_fill = *halo_fill_; + if (halo_radius_) p.halo_radius = *halo_radius_; +} + +ptree *fixed_formating_token::to_xml(ptree *node) +{ + + ptree &new_node = node->push_back(ptree::value_type("Format", ptree()))->second; + if (face_name_) set_attr(new_node, "face-name", face_name_); + if (text_size_) set_attr(new_node, "size", text_size_); + if (character_spacing_) set_attr(new_node, "character-spacing", character_spacing_); + if (line_spacing_) set_attr(new_node, "line-spacing", line_spacing_); + if (text_opacity_) set_attr(new_node, "opacity", text_opacity_); + if (wrap_before_) set_attr(new_node, "wrap-before", wrap_before_); + if (wrap_char_) set_attr(new_node, "wrap-character", wrap_char_); + if (text_transform_) set_attr(new_node, "text-transform", text_transform_); + if (fill_) set_attr(new_node, "fill", fill_); + if (halo_fill_) set_attr(new_node, "halo-fill", halo_fill_); + if (halo_radius_) set_attr(new_node, "halo-radius", halo_radius_); + return &new_node; +} + +void fixed_formating_token::from_xml(ptree const& node) +{ + set_face_name(get_opt_attr(node, "face-name")); + /*TODO: Fontset is problematic. We don't have the fontsets pointer here... */ + set_text_size(get_opt_attr(node, "size")); + set_character_spacing(get_opt_attr(node, "character-spacing")); + set_line_spacing(get_opt_attr(node, "line-spacing")); + set_text_opacity(get_opt_attr(node, "opactity")); + set_wrap_before(get_opt_attr(node, "wrap-before")); + set_wrap_char(get_opt_attr(node, "wrap-character")); + set_text_transform(get_opt_attr(node, "text-transform")); + set_fill(get_opt_attr(node, "fill")); + set_halo_fill(get_opt_attr(node, "halo-fill")); + set_halo_radius(get_opt_attr(node, "halo-radius")); +} + +void fixed_formating_token::set_face_name(optional face_name) +{ + face_name_ = face_name; +} + +void fixed_formating_token::set_text_size(optional text_size) +{ + text_size_ = text_size; +} + +void fixed_formating_token::set_character_spacing(optional character_spacing) +{ + character_spacing_ = character_spacing; +} + +void fixed_formating_token::set_line_spacing(optional line_spacing) +{ + line_spacing_ = line_spacing; +} + +void fixed_formating_token::set_text_opacity(optional text_opacity) +{ + text_opacity_ = text_opacity; +} + +void fixed_formating_token::set_wrap_before(optional wrap_before) +{ + wrap_before_ = wrap_before; +} + +void fixed_formating_token::set_wrap_char(optional wrap_char) +{ + wrap_char_ = wrap_char; +} + +void fixed_formating_token::set_text_transform(optional text_transform) +{ + text_transform_ = text_transform; +} + +void fixed_formating_token::set_fill(optional c) +{ + fill_ = c; +} + +void fixed_formating_token::set_halo_fill(optional c) +{ + halo_fill_ = c; +} + +void fixed_formating_token::set_halo_radius(optional radius) +{ + halo_radius_ = radius; +} + +/************************************************************/ + +ptree *end_format_token::to_xml(ptree *node) +{ + return 0; +} + +/************************************************************/ + +text_processor::text_processor(): + list_(), clear_on_write(false) +{ +} + +void text_processor::push_back(abstract_token *token) +{ + if (clear_on_write) list_.clear(); + clear_on_write = false; + list_.push_back(token); +} + +void text_processor::from_xml(const boost::property_tree::ptree &pt, std::map const &fontsets) +{ + clear_on_write = true; + defaults.set_values_from_xml(pt, fontsets); + from_xml_recursive(pt, fontsets); +} + +void text_processor::from_xml_recursive(const boost::property_tree::ptree &pt, std::map const &fontsets) +{ + ptree::const_iterator itr = pt.begin(); + ptree::const_iterator end = pt.end(); + for (; itr != end; ++itr) { + if (itr->first == "") { + std::string data = itr->second.data(); + boost::trim(data); + if (data.empty()) continue; + expression_token *token = new expression_token(parse_expression(data, "utf8")); + push_back(token); + } else if (itr->first == "Format") { + fixed_formating_token *token = new fixed_formating_token(); + token->from_xml(itr->second); + push_back(token); + from_xml_recursive(itr->second, fontsets); /* Parse children, making a list out of a tree. */ + push_back(new end_format_token()); + } else if (itr->first != "" && itr->first != "" && itr->first != "Placement") { + std::cerr << "Unknown item" << itr->first; + } + } +} + +void text_processor::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_processor const& dfl) const +{ + defaults.to_xml(node, explicit_defaults, dfl.defaults); + std::list::const_iterator itr = list_.begin(); + std::list::const_iterator end = list_.end(); + std::stack nodes; + ptree *current_node = &node; + for (; itr != end; ++itr) { + abstract_token *token = *itr; + ptree *new_node = token->to_xml(current_node); + if (dynamic_cast(token)) { + nodes.push(current_node); + current_node = new_node; + } else if (dynamic_cast(token)) { + current_node = nodes.top(); + nodes.pop(); + } + } +} + +void text_processor::process(processed_text &output, Feature const& feature) +{ + std::list::const_iterator itr = list_.begin(); + std::list::const_iterator end = list_.end(); + std::stack formats; + formats.push(defaults); + + for (; itr != end; ++itr) { + abstract_text_token *text = dynamic_cast(*itr); + abstract_formating_token *format = dynamic_cast(*itr);; + end_format_token *end = dynamic_cast(*itr);; + if (text) { + UnicodeString text_str = text->to_string(feature); + char_properties const& p = formats.top(); + /* TODO: Make a class out of text_transform which does the work! */ + if (p.text_transform == UPPERCASE) + { + text_str = text_str.toUpper(); + } + else if (p.text_transform == LOWERCASE) + { + text_str = text_str.toLower(); + } + else if (p.text_transform == CAPITALIZE) + { + text_str = text_str.toTitle(NULL); + } + if (text_str.length() > 0) { + output.push_back(processed_expression(p, text_str)); + } else { +#ifdef MAPNIK_DEBUG + std::cerr << "Warning: Empty expression.\n"; +#endif + } + } else if (format) { + char_properties next_properties = formats.top(); + format->apply(next_properties, feature); + formats.push(next_properties); + } else if (end) { + /* Always keep at least the defaults_ on stack. */ + if (formats.size() > 1) { + formats.pop(); + } else { + std::cerr << "Warning: Internal mapnik error. More elements popped than pushed in text_processor::process()\n"; + output.clear(); + return; + } + } + } + if (formats.size() != 1) { + std::cerr << "Warning: Internal mapnik error. Less elements popped than pushed in text_processor::process()\n"; + } +} + +std::set text_processor::get_all_expressions() const +{ + std::set result; + std::list::const_iterator itr = list_.begin(); + std::list::const_iterator end = list_.end(); + for (; itr != end; ++itr) { + expression_token *text = dynamic_cast(*itr); + if (text) result.insert(text->get_expression()); + } + return result; +} + +void text_processor::set_old_style_expression(expression_ptr expr) +{ + list_.push_back(new expression_token(expr)); +} + +/************************************************************/ + +void processed_text::push_back(processed_expression const& exp) +{ + expr_list_.push_back(exp); +} + +processed_text::expression_list::const_iterator processed_text::begin() +{ + return expr_list_.begin(); +} + +processed_text::expression_list::const_iterator processed_text::end() +{ + return expr_list_.end(); +} + +processed_text::processed_text(face_manager & font_manager, double scale_factor) + : font_manager_(font_manager), scale_factor_(scale_factor) +{ + +} + +void processed_text::clear() +{ + info_.clear(); + expr_list_.clear(); +} + + +string_info &processed_text::get_string_info() +{ + //info_.clear(); TODO: if this function is called twice invalid results are returned, so clear string_info first + expression_list::iterator itr = expr_list_.begin(); + expression_list::iterator end = expr_list_.end(); + for (; itr != end; ++itr) + { + char_properties const &p = itr->p; + face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset); + if (faces->size() <= 0) + { + throw config_error("Unable to find specified font face '" + p.face_name + "'"); + } + faces->set_character_sizes(p.text_size * scale_factor_); + faces->get_string_info(info_, itr->str, &(itr->p)); + info_.add_text(itr->str); + } + return info_; +} + + + +} /* namespace */ diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 2b43527e2..2966e73d1 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -22,15 +22,11 @@ //$Id$ -#include - //mapnik #include -#include -#include - // boost #include +#include namespace mapnik { @@ -90,471 +86,363 @@ static const char * text_transform_strings[] = { IMPLEMENT_ENUM( text_transform_e, text_transform_strings ) -static const char * placement_type_strings[] = { - "dummy", - "simple", - "" -}; - - -IMPLEMENT_ENUM( placement_type_e, placement_type_strings ) +text_symbolizer::text_symbolizer(text_placements_ptr placements) + : symbolizer_base(), + placement_options_(placements) +{ +} text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_name, float size, color const& fill, text_placements_ptr placements) : symbolizer_base(), - name_(name), - face_name_(face_name), - //fontset_(default_fontset), - text_ratio_(0), - wrap_width_(0), - wrap_char_(' '), - text_transform_(NONE), - line_spacing_(0), - character_spacing_(0), - label_spacing_(0), - label_position_tolerance_(0), - force_odd_labels_(false), - max_char_angle_delta_(22.5 * M_PI/180.0), - fill_(fill), - halo_fill_(color(255,255,255)), - halo_radius_(0.0), - label_p_(POINT_PLACEMENT), - anchor_(0.0,0.5), - avoid_edges_(false), - minimum_distance_(0.0), - minimum_padding_(0.0), - minimum_path_length_(0.0), - overlap_(false), - text_opacity_(1.0), - wrap_before_(false), placement_options_(placements) { + set_name(name); + set_face_name(face_name); set_text_size(size); + set_fill(fill); } text_symbolizer::text_symbolizer(expression_ptr name, float size, color const& fill, text_placements_ptr placements) : symbolizer_base(), - name_(name), - //face_name_(""), - //fontset_(default_fontset), - text_ratio_(0), - wrap_width_(0), - wrap_char_(' '), - text_transform_(NONE), - line_spacing_(0), - character_spacing_(0), - label_spacing_(0), - label_position_tolerance_(0), - force_odd_labels_(false), - max_char_angle_delta_(22.5 * M_PI/180.0), - fill_(fill), - halo_fill_(color(255,255,255)), - halo_radius_(0.0), - label_p_(POINT_PLACEMENT), - anchor_(0.0,0.5), - avoid_edges_(false), - minimum_distance_(0.0), - minimum_padding_(0.0), - minimum_path_length_(0.0), - overlap_(false), - text_opacity_(1.0), - wrap_before_(false), placement_options_(placements) { + set_name(name); set_text_size(size); + set_fill(fill); } text_symbolizer::text_symbolizer(text_symbolizer const& rhs) : symbolizer_base(rhs), - name_(rhs.name_), - orientation_(rhs.orientation_), - face_name_(rhs.face_name_), - fontset_(rhs.fontset_), - text_ratio_(rhs.text_ratio_), - wrap_width_(rhs.wrap_width_), - wrap_char_(rhs.wrap_char_), - text_transform_(rhs.text_transform_), - 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_), - max_char_angle_delta_(rhs.max_char_angle_delta_), - fill_(rhs.fill_), - halo_fill_(rhs.halo_fill_), - halo_radius_(rhs.halo_radius_), - label_p_(rhs.label_p_), - anchor_(rhs.anchor_), - avoid_edges_(rhs.avoid_edges_), - minimum_distance_(rhs.minimum_distance_), - minimum_padding_(rhs.minimum_padding_), - minimum_path_length_(rhs.minimum_path_length_), - overlap_(rhs.overlap_), - text_opacity_(rhs.text_opacity_), - wrap_before_(rhs.wrap_before_), - placement_options_(rhs.placement_options_) /*TODO: Copy options! */ {} + placement_options_(rhs.placement_options_) /*TODO: Copy options! */ +{ +} text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other) { if (this == &other) return *this; - name_ = other.name_; - orientation_ = other.orientation_; - face_name_ = other.face_name_; - fontset_ = other.fontset_; - text_ratio_ = other.text_ratio_; - wrap_width_ = other.wrap_width_; - wrap_char_ = other.wrap_char_; - text_transform_ = other.text_transform_; - 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_; - max_char_angle_delta_ = other.max_char_angle_delta_; - fill_ = other.fill_; - halo_fill_ = other.halo_fill_; - halo_radius_ = other.halo_radius_; - label_p_ = other.label_p_; - anchor_ = other.anchor_; - avoid_edges_ = other.avoid_edges_; - minimum_distance_ = other.minimum_distance_; - minimum_padding_ = other.minimum_padding_; - minimum_path_length_ = other.minimum_path_length_; - overlap_ = other.overlap_; - text_opacity_ = other.text_opacity_; - wrap_before_ = other.wrap_before_; - placement_options_ = other.placement_options_; /*TODO?*/ - std::cout << "TODO: Metawriter (text_symbolizer::operator=)\n"; + placement_options_ = other.placement_options_; /*TODO: Copy options? */ + std::clog << "TODO: Metawriter (text_symbolizer::operator=)\n"; return *this; } expression_ptr text_symbolizer::get_name() const { - return name_; + return expression_ptr(); } void text_symbolizer::set_name(expression_ptr name) { - name_ = name; + placement_options_->properties.processor.set_old_style_expression(name); } expression_ptr text_symbolizer::get_orientation() const { - return orientation_; + return placement_options_->properties.orientation; } void text_symbolizer::set_orientation(expression_ptr orientation) { - orientation_ = orientation; + placement_options_->properties.orientation = orientation; } std::string const& text_symbolizer::get_face_name() const { - return face_name_; + return placement_options_->properties.processor.defaults.face_name; } void text_symbolizer::set_face_name(std::string face_name) { - face_name_ = face_name; + placement_options_->properties.processor.defaults.face_name = face_name; } void text_symbolizer::set_fontset(font_set const& fontset) { - fontset_ = fontset; + placement_options_->properties.processor.defaults.fontset = fontset; } font_set const& text_symbolizer::get_fontset() const { - return fontset_; + return placement_options_->properties.processor.defaults.fontset; } unsigned text_symbolizer::get_text_ratio() const { - return text_ratio_; + return placement_options_->properties.text_ratio; } void text_symbolizer::set_text_ratio(unsigned ratio) { - text_ratio_ = ratio; + placement_options_->properties.text_ratio = ratio; } unsigned text_symbolizer::get_wrap_width() const { - return wrap_width_; + return placement_options_->properties.wrap_width; } void text_symbolizer::set_wrap_width(unsigned width) { - wrap_width_ = width; + placement_options_->properties.wrap_width = width; } bool text_symbolizer::get_wrap_before() const { - return wrap_before_; + return placement_options_->properties.processor.defaults.wrap_before; } void text_symbolizer::set_wrap_before(bool wrap_before) { - wrap_before_ = wrap_before; + placement_options_->properties.processor.defaults.wrap_before = wrap_before; } unsigned char text_symbolizer::get_wrap_char() const { - return wrap_char_; + return placement_options_->properties.processor.defaults.wrap_char; } std::string text_symbolizer::get_wrap_char_string() const { - return std::string(1, wrap_char_); + return std::string(1, placement_options_->properties.processor.defaults.wrap_char); } void text_symbolizer::set_wrap_char(unsigned char character) { - wrap_char_ = character; + placement_options_->properties.processor.defaults.wrap_char = character; } void text_symbolizer::set_wrap_char_from_string(std::string const& character) { - wrap_char_ = (character)[0]; + placement_options_->properties.processor.defaults.wrap_char = (character)[0]; } text_transform_e text_symbolizer::get_text_transform() const { - return text_transform_; + return placement_options_->properties.processor.defaults.text_transform; } void text_symbolizer::set_text_transform(text_transform_e convert) { - text_transform_ = convert; + placement_options_->properties.processor.defaults.text_transform = convert; } unsigned text_symbolizer::get_line_spacing() const { - return line_spacing_; + return placement_options_->properties.processor.defaults.line_spacing; } void text_symbolizer::set_line_spacing(unsigned spacing) { - line_spacing_ = spacing; + placement_options_->properties.processor.defaults.line_spacing = spacing; } unsigned text_symbolizer::get_character_spacing() const { - return character_spacing_; + return placement_options_->properties.processor.defaults.character_spacing; } void text_symbolizer::set_character_spacing(unsigned spacing) { - character_spacing_ = spacing; + placement_options_->properties.processor.defaults.character_spacing = spacing; } unsigned text_symbolizer::get_label_spacing() const { - return label_spacing_; + return placement_options_->properties.label_spacing; } void text_symbolizer::set_label_spacing(unsigned spacing) { - label_spacing_ = spacing; + placement_options_->properties.label_spacing = spacing; } unsigned text_symbolizer::get_label_position_tolerance() const { - return label_position_tolerance_; + return placement_options_->properties.label_position_tolerance; } void text_symbolizer::set_label_position_tolerance(unsigned tolerance) { - label_position_tolerance_ = tolerance; + placement_options_->properties.label_position_tolerance = tolerance; } bool text_symbolizer::get_force_odd_labels() const { - return force_odd_labels_; + return placement_options_->properties.force_odd_labels; } void text_symbolizer::set_force_odd_labels(bool force) { - force_odd_labels_ = force; + placement_options_->properties.force_odd_labels = force; } double text_symbolizer::get_max_char_angle_delta() const { - return max_char_angle_delta_; + return placement_options_->properties.max_char_angle_delta; } void text_symbolizer::set_max_char_angle_delta(double angle) { - max_char_angle_delta_ = angle; + placement_options_->properties.max_char_angle_delta = angle; } void text_symbolizer::set_text_size(float size) { - placement_options_->set_default_text_size(size); + placement_options_->properties.processor.defaults.text_size = size; } float text_symbolizer::get_text_size() const { - return placement_options_->get_default_text_size(); + return placement_options_->properties.processor.defaults.text_size; } void text_symbolizer::set_fill(color const& fill) { - fill_ = fill; + placement_options_->properties.processor.defaults.fill = fill; } color const& text_symbolizer::get_fill() const { - return fill_; + return placement_options_->properties.processor.defaults.fill; } void text_symbolizer::set_halo_fill(color const& fill) { - halo_fill_ = fill; + placement_options_->properties.processor.defaults.halo_fill = fill; } color const& text_symbolizer::get_halo_fill() const { - return halo_fill_; + return placement_options_->properties.processor.defaults.halo_fill; } void text_symbolizer::set_halo_radius(double radius) { - halo_radius_ = radius; + placement_options_->properties.processor.defaults.halo_radius = radius; } double text_symbolizer::get_halo_radius() const { - return halo_radius_; + return placement_options_->properties.processor.defaults.halo_radius; } void text_symbolizer::set_label_placement(label_placement_e label_p) { - label_p_ = label_p; + placement_options_->properties.label_placement = label_p; } label_placement_e text_symbolizer::get_label_placement() const { - return label_p_; + return placement_options_->properties.label_placement; } -void text_symbolizer::set_anchor(double x, double y) +void text_symbolizer::set_displacement(double x, double y) { - anchor_ = boost::make_tuple(x,y); -} - -position const& text_symbolizer::get_anchor() const -{ - return anchor_; -} - -void text_symbolizer::set_displacement(double x, double y) -{ - placement_options_->set_default_displacement(boost::make_tuple(x,y)); + placement_options_->properties.displacement = boost::make_tuple(x,y); } void text_symbolizer::set_displacement(position const& p) { - placement_options_->set_default_displacement(p); + placement_options_->properties.displacement = p; } position const& text_symbolizer::get_displacement() const { - return placement_options_->get_default_displacement(); + return placement_options_->properties.displacement; } bool text_symbolizer::get_avoid_edges() const { - return avoid_edges_; + return placement_options_->properties.avoid_edges; } void text_symbolizer::set_avoid_edges(bool avoid) { - avoid_edges_ = avoid; + placement_options_->properties.avoid_edges = avoid; } double text_symbolizer::get_minimum_distance() const { - return minimum_distance_; + return placement_options_->properties.minimum_distance; } void text_symbolizer::set_minimum_distance(double distance) { - minimum_distance_ = distance; + placement_options_->properties.minimum_distance = distance; } double text_symbolizer::get_minimum_padding() const { - return minimum_padding_; + return placement_options_->properties.minimum_padding; } void text_symbolizer::set_minimum_padding(double distance) { - minimum_padding_ = distance; + placement_options_->properties.minimum_padding = distance; } double text_symbolizer::get_minimum_path_length() const { - return minimum_path_length_; + return placement_options_->properties.minimum_path_length; } void text_symbolizer::set_minimum_path_length(double size) { - minimum_path_length_ = size; + placement_options_->properties.minimum_path_length = size; } void text_symbolizer::set_allow_overlap(bool overlap) { - overlap_ = overlap; + placement_options_->properties.allow_overlap = overlap; } bool text_symbolizer::get_allow_overlap() const { - return overlap_; + return placement_options_->properties.allow_overlap; } void text_symbolizer::set_text_opacity(double text_opacity) { - text_opacity_ = text_opacity; + placement_options_->properties.processor.defaults.text_opacity = text_opacity; } double text_symbolizer::get_text_opacity() const { - return text_opacity_; + return placement_options_->properties.processor.defaults.text_opacity; } void text_symbolizer::set_vertical_alignment(vertical_alignment_e valign) { - placement_options_->set_default_valign(valign); + placement_options_->properties.valign = valign; } vertical_alignment_e text_symbolizer::get_vertical_alignment() const { - return placement_options_->get_default_valign(); + return placement_options_->properties.valign; } void text_symbolizer::set_horizontal_alignment(horizontal_alignment_e halign) { - placement_options_->set_default_halign(halign); + placement_options_->properties.halign = halign; } horizontal_alignment_e text_symbolizer::get_horizontal_alignment() const { - return placement_options_->get_default_halign(); + return placement_options_->properties.halign; } void text_symbolizer::set_justify_alignment(justify_alignment_e jalign) { - placement_options_->set_default_jalign(jalign); + placement_options_->properties.jalign = jalign; } justify_alignment_e text_symbolizer::get_justify_alignment() const { - return placement_options_->get_default_jalign(); + return placement_options_->properties.jalign; } text_placements_ptr text_symbolizer::get_placement_options() const @@ -567,47 +455,5 @@ void text_symbolizer::set_placement_options(text_placements_ptr placement_option placement_options_ = placement_options; } -void text_symbolizer::set_placement_options(placement_type_e arg, std::string const& placements) -{ - text_placements_ptr placement_finder; - switch (arg) - { - case T_SIMPLE: - placement_finder = text_placements_ptr( - new text_placements_simple(placements)); - break; - - case T_DUMMY: - placement_finder = text_placements_ptr(new text_placements_dummy()); - break; - - default: - throw config_error(std::string("Unknown placement type")); - break; - } - this->set_placement_options(placement_finder); -} - -placement_type_e text_symbolizer::get_placement_type() const -{ - text_placements_ptr placement_finder = this->get_placement_options(); - if (dynamic_cast(placement_finder.get()) != NULL) - { - return T_SIMPLE; - } - return T_DUMMY; -} - -std::string text_symbolizer::get_placements() const -{ - text_placements_ptr placement_finder = this->get_placement_options(); - text_placements_simple *placements_simple = dynamic_cast(placement_finder.get()); - - if (placements_simple != NULL) - { - return placements_simple->get_positions(); - } - return ""; -} } diff --git a/tests/data/good_maps/also_and_else_filter.xml b/tests/data/good_maps/also_and_else_filter.xml index c4acbe946..10d7c3ac5 100644 --- a/tests/data/good_maps/also_and_else_filter.xml +++ b/tests/data/good_maps/also_and_else_filter.xml @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/tests/data/good_maps/glyph_symbolizer.xml b/tests/data/good_maps/glyph_symbolizer.xml deleted file mode 100644 index 62f73d999..000000000 --- a/tests/data/good_maps/glyph_symbolizer.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - \ No newline at end of file diff --git a/tests/data/good_maps/sqlite_attachdb.xml b/tests/data/good_maps/sqlite_attachdb.xml index 2703bd165..476e4c853 100644 --- a/tests/data/good_maps/sqlite_attachdb.xml +++ b/tests/data/good_maps/sqlite_attachdb.xml @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/tests/data/good_maps/unique_filter_map.xml b/tests/data/good_maps/unique_filter_map.xml index acb3485e8..796d90c6d 100644 --- a/tests/data/good_maps/unique_filter_map.xml +++ b/tests/data/good_maps/unique_filter_map.xml @@ -22,4 +22,4 @@ shape - \ No newline at end of file + diff --git a/tests/python_tests/glyph_symbolizer_test.py b/tests/python_tests/glyph_symbolizer_test.py deleted file mode 100644 index bdf6d8a8c..000000000 --- a/tests/python_tests/glyph_symbolizer_test.py +++ /dev/null @@ -1,110 +0,0 @@ -#encoding: utf8 -#!/usr/bin/env python - -from nose.tools import * - -from utilities import execution_path, save_data, contains_word - -import os, mapnik - -def test_renders_with_agg(): - sym = mapnik.GlyphSymbolizer("DejaVu Sans Condensed", - mapnik.Expression("'í'")) - sym.allow_overlap = True - sym.angle = mapnik.Expression("[azimuth]+90") #+90 so the top of the glyph points upwards - sym.size = mapnik.Expression("[value]") - sym.color = mapnik.Expression("'#ff0000'") - - _map = create_map_and_append_symbolyzer(sym) - if _map: - im = mapnik.Image(_map.width,_map.height) - mapnik.render(_map, im) - save_data('agg_glyph_symbolizer.png', im.tostring('png')) - assert contains_word('\xff\x00\x00\xff', im.tostring()) - -def test_renders_with_cairo(): - if not mapnik.has_pycairo(): - return - sym = mapnik.GlyphSymbolizer("DejaVu Sans Condensed", - mapnik.Expression("'í'")) - sym.allow_overlap = True - sym.angle = mapnik.Expression("[azimuth]+90") #+90 so the top of the glyph points upwards - sym.size = mapnik.Expression("[value]") - sym.color = mapnik.Expression("'#ff0000'") - _map = create_map_and_append_symbolyzer(sym) - if _map: - from cStringIO import StringIO - import cairo - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 256, 256) - mapnik.render(_map, surface) - im = mapnik.Image.from_cairo(surface) - save_data('cairo_glyph_symbolizer.png', im.tostring('png')) - assert contains_word('\xff\x00\x00\xff', im.tostring()) - -def test_load_save_load_map(): - map = mapnik.Map(256,256) - in_map = "../data/good_maps/glyph_symbolizer.xml" - try: - mapnik.load_map(map, in_map) - style = map.find_style('arrows') - sym = style.rules[0].symbols[0] - assert isinstance(sym, mapnik.GlyphSymbolizer) - assert sym.angle_mode == mapnik.angle_mode.AZIMUTH - - out_map = mapnik.save_map_to_string(map).decode('utf8') - map = mapnik.Map(256,256) - mapnik.load_map_from_string(map, out_map.encode('utf8')) - assert 'GlyphSymbolizer' in out_map - # make sure non-ascii characters are well supported since most interesting - # glyphs for symbology are usually in that range - assert u'í' in out_map, out_map - except RuntimeError, e: - # only test datasources that we have installed - if not 'Could not create datasource' in str(e): - raise RuntimeError(e) - -# -# Utilities and setup code -# - -def setup(): - # All of the paths used are relative, if we run the tests - # from another directory we need to chdir() - os.chdir(execution_path('.')) - -def create_map_and_append_symbolyzer(sym): - srs = '+init=epsg:32630' - lyr = mapnik.Layer('arrows') - try: - lyr.datasource = mapnik.Shapefile( - file = '../data/shp/arrows.shp', - ) - lyr.srs = srs - _map = mapnik.Map(256,256, srs) - style = mapnik.Style() - rule = mapnik.Rule() - rule.symbols.append(sym) - - # put a test symbolizer to see what is the azimuth being read - ts = mapnik.TextSymbolizer(mapnik.Expression('[azimuth]'), - "DejaVu Sans Book", - 10, - mapnik.Color("black")) - ts.allow_overlap = True - rule.symbols.append(ts) - - style.rules.append(rule) - _map.append_style('foo', style) - lyr.styles.append('foo') - _map.layers.append(lyr) - _map.zoom_to_box(mapnik.Box2d(0,0,8,8)) - return _map - except RuntimeError, e: - # only test datasources that we have installed - if not 'Could not create datasource' in str(e): - raise RuntimeError(e) - -if __name__ == "__main__": - setup() - [eval(run)() for run in dir() if 'test_' in run] - diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 9313bc507..a4caf1c3a 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -29,7 +29,7 @@ def test_shieldsymbolizer_init(): eq_(s.allow_overlap, False) eq_(s.avoid_edges, False) eq_(s.character_spacing,0) - eq_(str(s.name), str(mapnik.Expression('[Field Name]'))) + #eq_(str(s.name), str(mapnik2.Expression('[Field Name]'))) name field is no longer supported eq_(s.face_name, 'DejaVu Sans Bold') eq_(s.allow_overlap, False) eq_(s.fill, mapnik.Color('#000000')) @@ -198,7 +198,7 @@ def test_linesymbolizer_init(): def test_textsymbolizer_init(): ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) - eq_(str(ts.name), str(mapnik.Expression('[Field_Name]'))) +# eq_(str(ts.name), str(mapnik2.Expression('[Field_Name]'))) name field is no longer supported eq_(ts.face_name, 'Font Name') eq_(ts.text_size, 8) eq_(ts.fill, mapnik.Color('black')) diff --git a/tests/data/placement/clean.sh b/tests/visual_tests/clean.sh similarity index 100% rename from tests/data/placement/clean.sh rename to tests/visual_tests/clean.sh diff --git a/tests/visual_tests/formating-500-reference.png b/tests/visual_tests/formating-500-reference.png new file mode 100644 index 000000000..0ab02b598 Binary files /dev/null and b/tests/visual_tests/formating-500-reference.png differ diff --git a/tests/visual_tests/formating.xml b/tests/visual_tests/formating.xml new file mode 100644 index 000000000..d59c7b8da --- /dev/null +++ b/tests/visual_tests/formating.xml @@ -0,0 +1,21 @@ + + + + + + My Style + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/list-100-reference.png b/tests/visual_tests/list-100-reference.png new file mode 100644 index 000000000..3b3cedbb2 Binary files /dev/null and b/tests/visual_tests/list-100-reference.png differ diff --git a/tests/visual_tests/list-150-reference.png b/tests/visual_tests/list-150-reference.png new file mode 100644 index 000000000..61dc7964d Binary files /dev/null and b/tests/visual_tests/list-150-reference.png differ diff --git a/tests/visual_tests/list-200-reference.png b/tests/visual_tests/list-200-reference.png new file mode 100644 index 000000000..14714cdf0 Binary files /dev/null and b/tests/visual_tests/list-200-reference.png differ diff --git a/tests/visual_tests/list-250-reference.png b/tests/visual_tests/list-250-reference.png new file mode 100644 index 000000000..a88f4707b Binary files /dev/null and b/tests/visual_tests/list-250-reference.png differ diff --git a/tests/visual_tests/list-300-reference.png b/tests/visual_tests/list-300-reference.png new file mode 100644 index 000000000..5d500aad9 Binary files /dev/null and b/tests/visual_tests/list-300-reference.png differ diff --git a/tests/visual_tests/list-400-reference.png b/tests/visual_tests/list-400-reference.png new file mode 100644 index 000000000..caee65ba5 Binary files /dev/null and b/tests/visual_tests/list-400-reference.png differ diff --git a/tests/visual_tests/list-600-reference.png b/tests/visual_tests/list-600-reference.png new file mode 100644 index 000000000..46a5c7198 Binary files /dev/null and b/tests/visual_tests/list-600-reference.png differ diff --git a/tests/visual_tests/list-800-reference.png b/tests/visual_tests/list-800-reference.png new file mode 100644 index 000000000..b362592ef Binary files /dev/null and b/tests/visual_tests/list-800-reference.png differ diff --git a/tests/visual_tests/list.xml b/tests/visual_tests/list.xml new file mode 100644 index 000000000..93ef3726d --- /dev/null +++ b/tests/visual_tests/list.xml @@ -0,0 +1,27 @@ + + + + + + My Style + + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/points.dbf b/tests/visual_tests/points.dbf new file mode 100644 index 000000000..ca544e92a Binary files /dev/null and b/tests/visual_tests/points.dbf differ diff --git a/tests/visual_tests/points.osm b/tests/visual_tests/points.osm new file mode 100644 index 000000000..086f0a479 --- /dev/null +++ b/tests/visual_tests/points.osm @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/visual_tests/points.shp b/tests/visual_tests/points.shp new file mode 100644 index 000000000..e2fb4a604 Binary files /dev/null and b/tests/visual_tests/points.shp differ diff --git a/tests/visual_tests/simple-100-reference.png b/tests/visual_tests/simple-100-reference.png new file mode 100644 index 000000000..e70c9c0f6 Binary files /dev/null and b/tests/visual_tests/simple-100-reference.png differ diff --git a/tests/visual_tests/simple-150-reference.png b/tests/visual_tests/simple-150-reference.png new file mode 100644 index 000000000..666ca6d66 Binary files /dev/null and b/tests/visual_tests/simple-150-reference.png differ diff --git a/tests/visual_tests/simple-200-reference.png b/tests/visual_tests/simple-200-reference.png new file mode 100644 index 000000000..aad33f405 Binary files /dev/null and b/tests/visual_tests/simple-200-reference.png differ diff --git a/tests/visual_tests/simple-250-reference.png b/tests/visual_tests/simple-250-reference.png new file mode 100644 index 000000000..a43a8016f Binary files /dev/null and b/tests/visual_tests/simple-250-reference.png differ diff --git a/tests/visual_tests/simple-300-reference.png b/tests/visual_tests/simple-300-reference.png new file mode 100644 index 000000000..f753151da Binary files /dev/null and b/tests/visual_tests/simple-300-reference.png differ diff --git a/tests/visual_tests/simple-400-reference.png b/tests/visual_tests/simple-400-reference.png new file mode 100644 index 000000000..ebc03be70 Binary files /dev/null and b/tests/visual_tests/simple-400-reference.png differ diff --git a/tests/visual_tests/simple-600-reference.png b/tests/visual_tests/simple-600-reference.png new file mode 100644 index 000000000..ffc72053a Binary files /dev/null and b/tests/visual_tests/simple-600-reference.png differ diff --git a/tests/visual_tests/simple-800-reference.png b/tests/visual_tests/simple-800-reference.png new file mode 100644 index 000000000..19c7acd32 Binary files /dev/null and b/tests/visual_tests/simple-800-reference.png differ diff --git a/tests/data/placement/simple-E-500-reference.png b/tests/visual_tests/simple-E-500-reference.png similarity index 100% rename from tests/data/placement/simple-E-500-reference.png rename to tests/visual_tests/simple-E-500-reference.png diff --git a/tests/data/placement/simple-E.xml b/tests/visual_tests/simple-E.xml similarity index 100% rename from tests/data/placement/simple-E.xml rename to tests/visual_tests/simple-E.xml diff --git a/tests/data/placement/simple-N-500-reference.png b/tests/visual_tests/simple-N-500-reference.png similarity index 100% rename from tests/data/placement/simple-N-500-reference.png rename to tests/visual_tests/simple-N-500-reference.png diff --git a/tests/data/placement/simple-N.xml b/tests/visual_tests/simple-N.xml similarity index 100% rename from tests/data/placement/simple-N.xml rename to tests/visual_tests/simple-N.xml diff --git a/tests/data/placement/simple-NE-500-reference.png b/tests/visual_tests/simple-NE-500-reference.png similarity index 100% rename from tests/data/placement/simple-NE-500-reference.png rename to tests/visual_tests/simple-NE-500-reference.png diff --git a/tests/data/placement/simple-NE.xml b/tests/visual_tests/simple-NE.xml similarity index 100% rename from tests/data/placement/simple-NE.xml rename to tests/visual_tests/simple-NE.xml diff --git a/tests/data/placement/simple-NW-500-reference.png b/tests/visual_tests/simple-NW-500-reference.png similarity index 100% rename from tests/data/placement/simple-NW-500-reference.png rename to tests/visual_tests/simple-NW-500-reference.png diff --git a/tests/data/placement/simple-NW.xml b/tests/visual_tests/simple-NW.xml similarity index 100% rename from tests/data/placement/simple-NW.xml rename to tests/visual_tests/simple-NW.xml diff --git a/tests/data/placement/simple-S-500-reference.png b/tests/visual_tests/simple-S-500-reference.png similarity index 100% rename from tests/data/placement/simple-S-500-reference.png rename to tests/visual_tests/simple-S-500-reference.png diff --git a/tests/data/placement/simple-S.xml b/tests/visual_tests/simple-S.xml similarity index 100% rename from tests/data/placement/simple-S.xml rename to tests/visual_tests/simple-S.xml diff --git a/tests/data/placement/simple-SE-500-reference.png b/tests/visual_tests/simple-SE-500-reference.png similarity index 100% rename from tests/data/placement/simple-SE-500-reference.png rename to tests/visual_tests/simple-SE-500-reference.png diff --git a/tests/data/placement/simple-SE.xml b/tests/visual_tests/simple-SE.xml similarity index 100% rename from tests/data/placement/simple-SE.xml rename to tests/visual_tests/simple-SE.xml diff --git a/tests/data/placement/simple-SW-500-reference.png b/tests/visual_tests/simple-SW-500-reference.png similarity index 100% rename from tests/data/placement/simple-SW-500-reference.png rename to tests/visual_tests/simple-SW-500-reference.png diff --git a/tests/data/placement/simple-SW.xml b/tests/visual_tests/simple-SW.xml similarity index 100% rename from tests/data/placement/simple-SW.xml rename to tests/visual_tests/simple-SW.xml diff --git a/tests/data/placement/simple-W-500-reference.png b/tests/visual_tests/simple-W-500-reference.png similarity index 100% rename from tests/data/placement/simple-W-500-reference.png rename to tests/visual_tests/simple-W-500-reference.png diff --git a/tests/data/placement/simple-W.xml b/tests/visual_tests/simple-W.xml similarity index 100% rename from tests/data/placement/simple-W.xml rename to tests/visual_tests/simple-W.xml diff --git a/tests/visual_tests/simple.xml b/tests/visual_tests/simple.xml new file mode 100644 index 000000000..cc008f598 --- /dev/null +++ b/tests/visual_tests/simple.xml @@ -0,0 +1,24 @@ + + + + + + My Style + + + shape + points.shp + + + + + + diff --git a/tests/data/placement/test.py b/tests/visual_tests/test.py similarity index 89% rename from tests/data/placement/test.py rename to tests/visual_tests/test.py index 1fabccbce..7fb48166a 100755 --- a/tests/data/placement/test.py +++ b/tests/visual_tests/test.py @@ -11,7 +11,7 @@ dirname = os.path.dirname(sys.argv[0]) widths = [ 800, 600, 400, 300, 250, 200, 150, 100] filenames = ["list", "simple"] filenames_one_width = ["simple-E", "simple-NE", "simple-NW", "simple-N", - "simple-SE", "simple-SW", "simple-S", "simple-W"] + "simple-SE", "simple-SW", "simple-S", "simple-W", "formating"] def render(filename, width): print "Rendering style \"%s\" with width %d" % (filename, width) @@ -29,4 +29,5 @@ for filename in filenames: mapnik.save_map(m, "%s-out.xml" % filename) for filename in filenames_one_width: - render(filename, 500) \ No newline at end of file + render(filename, 500) + diff --git a/utils/upgrade_map_xml/upgrade_map_xml.py b/utils/upgrade_map_xml/upgrade_map_xml.py index 1a69450d0..64fa17f87 100755 --- a/utils/upgrade_map_xml/upgrade_map_xml.py +++ b/utils/upgrade_map_xml/upgrade_map_xml.py @@ -204,9 +204,6 @@ def upgrade(input_xml,output_xml=None,indent_xml=True): for sym in rule.findall('BuildingSymbolizer') or []: fixup_sym_attributes(sym) underscore2dash(sym) - for sym in rule.findall('GlyphSymbolizer') or []: - fixup_sym_attributes(sym) - underscore2dash(sym) for sym in rule.findall('MarkersSymbolizer') or []: fixup_sym_attributes(sym) underscore2dash(sym) diff --git a/workspace/bindings.pri b/workspace/bindings.pri index acd4f6753..2798142e7 100644 --- a/workspace/bindings.pri +++ b/workspace/bindings.pri @@ -17,7 +17,6 @@ SOURCES += \ $$PWD/../bindings/python/mapnik_featureset.cpp \ $$PWD/../bindings/python/mapnik_font_engine.cpp \ $$PWD/../bindings/python/mapnik_geometry.cpp \ - $$PWD/../bindings/python/mapnik_glyph_symbolizer.cpp \ $$PWD/../bindings/python/mapnik_grid.cpp \ $$PWD/../bindings/python/mapnik_grid_view.cpp \ $$PWD/../bindings/python/mapnik_image.cpp \ diff --git a/workspace/mapnik.pro b/workspace/mapnik.pro index 666252555..5f9f9c615 100644 --- a/workspace/mapnik.pro +++ b/workspace/mapnik.pro @@ -80,7 +80,6 @@ HEADERS += \ ../include/mapnik/geometry.hpp \ ../include/mapnik/geom_util.hpp \ ../include/mapnik/global.hpp \ - ../include/mapnik/glyph_symbolizer.hpp \ ../include/mapnik/gradient.hpp \ ../include/mapnik/graphics.hpp \ ../include/mapnik/hextree.hpp \ @@ -92,7 +91,6 @@ HEADERS += \ ../include/mapnik/image_view.hpp \ ../include/mapnik/jpeg_io.hpp \ ../include/mapnik/label_collision_detector.hpp \ - ../include/mapnik/label_placement.hpp \ ../include/mapnik/layer.hpp \ ../include/mapnik/libxml2_loader.hpp \ ../include/mapnik/line_pattern_symbolizer.hpp \ @@ -162,7 +160,6 @@ HEADERS += \ SOURCES += \ ../src/agg/agg_renderer.cpp \ ../src/agg/process_building_symbolizer.cpp \ - ../src/agg/process_glyph_symbolizer.cpp \ ../src/agg/process_line_pattern_symbolizer.cpp \ ../src/agg/process_line_symbolizer.cpp \ ../src/agg/process_markers_symbolizer.cpp \ @@ -174,7 +171,6 @@ SOURCES += \ ../src/agg/process_text_symbolizer.cpp \ ../src/grid/grid_renderer.cpp \ ../src/grid/process_building_symbolizer.cpp \ - ../src/grid/process_glyph_symbolizer.cpp \ ../src/grid/process_line_pattern_symbolizer.cpp \ ../src/grid/process_line_symbolizer.cpp \ ../src/grid/process_markers_symbolizer.cpp \ @@ -188,7 +184,6 @@ SOURCES += \ ../src/svg/svg_output_attributes.cpp \ ../src/svg/process_symbolizers.cpp \ ../src/svg/process_building_symbolizer.cpp \ - ../src/svg/process_glyph_symbolizer.cpp \ ../src/svg/process_line_pattern_symbolizer.cpp \ ../src/svg/process_line_symbolizer.cpp \ ../src/svg/process_markers_symbolizer.cpp \ @@ -210,7 +205,6 @@ SOURCES += \ ../src/filter_factory.cpp \ ../src/font_engine_freetype.cpp \ ../src/font_set.cpp \ - ../src/glyph_symbolizer.cpp \ ../src/gradient.cpp \ ../src/graphics.cpp \ ../src/image_reader.cpp \