Created
November 9, 2013 03:22
-
-
Save springmeyer/7381163 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/SConstruct b/SConstruct | |
index 4473928..1601f50 100644 | |
--- a/SConstruct | |
+++ b/SConstruct | |
@@ -83,6 +83,7 @@ pretty_dep_names = { | |
'png':'PNG C library | configure with PNG_LIBS & PNG_INCLUDES', | |
'webp':'WEBP C library | configure with WEBP_LIBS & WEBP_INCLUDES', | |
'icuuc':'ICU C++ library | configure with ICU_LIBS & ICU_INCLUDES or use ICU_LIB_NAME to specify custom lib name | more info: http://site.icu-project.org/', | |
+ 'harfbuzz':'HarfBuzz text shaping library | configure with HB_LIBS & HB_INCLUDES', | |
'z':'Z compression library | more info: http://www.zlib.net/', | |
'm':'Basic math library, part of C++ stlib', | |
'pkg-config':'pkg-config tool | more info: http://pkg-config.freedesktop.org', | |
@@ -332,7 +333,8 @@ opts.AddVariables( | |
PathVariable('ICU_INCLUDES', 'Search path for ICU include files', ICU_INCLUDES_DEFAULT, PathVariable.PathAccept), | |
PathVariable('ICU_LIBS','Search path for ICU include files',ICU_LIBS_DEFAULT + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), | |
('ICU_LIB_NAME', 'The library name for icu (such as icuuc, sicuuc, or icucore)', 'icuuc', PathVariable.PathAccept), | |
- | |
+ PathVariable('HB_INCLUDES', 'Search path for HarfBuzz include files', '/usr/include', PathVariable.PathAccept), | |
+ PathVariable('HB_LIBS','Search path for HarfBuzz include files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), | |
BoolVariable('PNG', 'Build Mapnik with PNG read and write support', 'True'), | |
PathVariable('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include', PathVariable.PathAccept), | |
PathVariable('PNG_LIBS','Search path for libpng library files','/usr/' + LIBDIR_SCHEMA_DEFAULT, PathVariable.PathAccept), | |
@@ -1192,7 +1194,7 @@ if not preconfigured: | |
# Adding the required prerequisite library directories to the include path for | |
# compiling and the library path for linking, respectively. | |
- for required in ('ICU', 'SQLITE'): | |
+ for required in ('ICU', 'SQLITE', 'HB'): | |
inc_path = env['%s_INCLUDES' % required] | |
lib_path = env['%s_LIBS' % required] | |
env.AppendUnique(CPPPATH = os.path.realpath(inc_path)) | |
@@ -1220,6 +1222,7 @@ if not preconfigured: | |
REQUIRED_LIBSHEADERS = [ | |
['z', 'zlib.h', True,'C'], | |
[env['ICU_LIB_NAME'],'unicode/unistr.h',True,'C++'], | |
+ ['harfbuzz', 'harfbuzz/hb.h',True,'C++'] | |
] | |
OPTIONAL_LIBSHEADERS = [] | |
diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py | |
index b0afbcf..2108b08 100644 | |
--- a/bindings/python/mapnik/__init__.py | |
+++ b/bindings/python/mapnik/__init__.py | |
@@ -285,11 +285,6 @@ class _Color(Color,_injector): | |
def __repr__(self): | |
return "Color(R=%d,G=%d,B=%d,A=%d)" % (self.r,self.g,self.b,self.a) | |
-class _ProcessedText(ProcessedText, _injector): | |
- def append(self, properties, text): | |
- #More pythonic name | |
- self.push_back(properties, text) | |
- | |
class _Symbolizers(Symbolizers,_injector): | |
def __getitem__(self, idx): | |
diff --git a/bindings/python/mapnik_symbolizer.cpp b/bindings/python/mapnik_symbolizer.cpp | |
index e343466..c1fbf78 100644 | |
--- a/bindings/python/mapnik_symbolizer.cpp | |
+++ b/bindings/python/mapnik_symbolizer.cpp | |
@@ -46,7 +46,7 @@ | |
#include <mapnik/expression_node.hpp> | |
#include <mapnik/value_error.hpp> | |
#include <mapnik/marker_cache.hpp> // for known_svg_prefix_ | |
- | |
+#include <mapnik/pixel_position.hpp> | |
using mapnik::symbolizer; | |
using mapnik::rule; | |
@@ -66,28 +66,26 @@ using mapnik::path_expression_ptr; | |
using mapnik::guess_type; | |
using mapnik::expression_ptr; | |
using mapnik::parse_path; | |
-using mapnik::position; | |
- | |
namespace { | |
using namespace boost::python; | |
tuple get_shield_displacement(const shield_symbolizer& s) | |
{ | |
- position const& pos = s.get_shield_displacement(); | |
- return boost::python::make_tuple(pos.first, pos.second); | |
+ mapnik::pixel_position const& pos = s.get_shield_displacement(); | |
+ return boost::python::make_tuple(pos.x, pos.y); | |
} | |
void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg) | |
{ | |
- s.get_placement_options()->defaults.displacement.first = extract<double>(arg[0]); | |
- s.get_placement_options()->defaults.displacement.second = extract<double>(arg[1]); | |
+ s.get_placement_options()->defaults.displacement.x = extract<double>(arg[0]); | |
+ s.get_placement_options()->defaults.displacement.y = extract<double>(arg[1]); | |
} | |
tuple get_text_displacement(const shield_symbolizer& t) | |
{ | |
- position const& pos = t.get_placement_options()->defaults.displacement; | |
- return boost::python::make_tuple(pos.first, pos.second); | |
+ mapnik::pixel_position const& pos = t.get_placement_options()->defaults.displacement; | |
+ return boost::python::make_tuple(pos.x, pos.y); | |
} | |
void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg) | |
diff --git a/bindings/python/mapnik_text_placement.cpp b/bindings/python/mapnik_text_placement.cpp | |
index 11f0a05..11f5521 100644 | |
--- a/bindings/python/mapnik_text_placement.cpp | |
+++ b/bindings/python/mapnik_text_placement.cpp | |
@@ -27,11 +27,13 @@ | |
#include <boost/noncopyable.hpp> | |
#include <mapnik/text/text_properties.hpp> | |
+#include <mapnik/text/placements/simple.hpp> | |
+#include <mapnik/text/placements/list.hpp> | |
#include <mapnik/text/formatting/text.hpp> | |
#include <mapnik/text/formatting/list.hpp> | |
#include <mapnik/text/formatting/format.hpp> | |
#include <mapnik/text/formatting/expression_format.hpp> | |
-#include <mapnik/text/processed_text.hpp> | |
+#include <mapnik/text/layout.hpp> | |
#include <mapnik/text_symbolizer.hpp> | |
#include "mapnik_enumeration.hpp" | |
@@ -96,7 +98,7 @@ public: | |
boost::python::tuple get_displacement(text_symbolizer_properties const& t) | |
{ | |
- return boost::python::make_tuple(t.displacement.first, t.displacement.second); | |
+ return boost::python::make_tuple(t.displacement.x, t.displacement.y); | |
} | |
void set_displacement(text_symbolizer_properties &t, boost::python::tuple arg) | |
@@ -112,7 +114,7 @@ void set_displacement(text_symbolizer_properties &t, boost::python::tuple arg) | |
double x = extract<double>(arg[0]); | |
double y = extract<double>(arg[1]); | |
- t.displacement = std::make_pair(x, y); | |
+ t.displacement.set(x, y); | |
} | |
@@ -123,7 +125,7 @@ struct NodeWrap: formatting::node, wrapper<formatting::node> | |
} | |
- void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
python_block_auto_unblock b; | |
this->get_override("apply")(ptr(&p), ptr(&feature), ptr(&output)); | |
@@ -161,7 +163,7 @@ struct TextNodeWrap: formatting::text_node, wrapper<formatting::text_node> | |
} | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
if(override o = this->get_override("apply")) | |
{ | |
@@ -174,7 +176,7 @@ struct TextNodeWrap: formatting::text_node, wrapper<formatting::text_node> | |
} | |
} | |
- void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ void default_apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
formatting::text_node::apply(p, feature, output); | |
} | |
@@ -182,7 +184,7 @@ struct TextNodeWrap: formatting::text_node, wrapper<formatting::text_node> | |
struct FormatNodeWrap: formatting::format_node, wrapper<formatting::format_node> | |
{ | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
if(override o = this->get_override("apply")) | |
{ | |
@@ -195,7 +197,7 @@ struct FormatNodeWrap: formatting::format_node, wrapper<formatting::format_node> | |
} | |
} | |
- void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ void default_apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
formatting::format_node::apply(p, feature, output); | |
} | |
@@ -203,7 +205,7 @@ struct FormatNodeWrap: formatting::format_node, wrapper<formatting::format_node> | |
struct ExprFormatWrap: formatting::expression_format, wrapper<formatting::expression_format> | |
{ | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
if(override o = this->get_override("apply")) | |
{ | |
@@ -216,7 +218,7 @@ struct ExprFormatWrap: formatting::expression_format, wrapper<formatting::expres | |
} | |
} | |
- void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ void default_apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
formatting::expression_format::apply(p, feature, output); | |
} | |
@@ -243,7 +245,7 @@ struct ListNodeWrap: formatting::list_node, wrapper<formatting::list_node> | |
/* TODO: Add constructor taking variable number of arguments. | |
http://wiki.python.org/moin/boost.python/HowTo#A.22Raw.22_function */ | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
if(override o = this->get_override("apply")) | |
{ | |
@@ -256,7 +258,7 @@ struct ListNodeWrap: formatting::list_node, wrapper<formatting::list_node> | |
} | |
} | |
- void default_apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+ void default_apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
formatting::list_node::apply(p, feature, output); | |
} | |
@@ -322,12 +324,12 @@ void insert_expression(expression_set *set, expression_ptr p) | |
set->insert(p); | |
} | |
-char_properties & get_format(text_symbolizer const& sym) | |
+char_properties_ptr get_format(text_symbolizer const& sym) | |
{ | |
return sym.get_placement_options()->defaults.format; | |
} | |
-void set_format(text_symbolizer const& sym, char_properties & format) | |
+void set_format(text_symbolizer const& sym, char_properties_ptr format) | |
{ | |
sym.get_placement_options()->defaults.format = format; | |
} | |
@@ -395,7 +397,7 @@ void export_text_placement() | |
&text_symbolizer::set_placement_options) | |
//TODO: Check return policy, is there a better way to do this? | |
.add_property("format", | |
- make_function(&get_format, return_value_policy<reference_existing_object>()), | |
+ make_function(&get_format), | |
&set_format, | |
"Shortcut for placements.defaults.default_format") | |
.add_property("properties", | |
@@ -439,6 +441,7 @@ void export_text_placement() | |
.def_readwrite("largest_bbox_only", &text_symbolizer_properties::largest_bbox_only) | |
.def_readwrite("text_ratio", &text_symbolizer_properties::text_ratio) | |
.def_readwrite("wrap_width", &text_symbolizer_properties::wrap_width) | |
+ .def_readwrite("wrap_before", &text_symbolizer_properties::wrap_before) | |
.def_readwrite("format", &text_symbolizer_properties::format) | |
.add_property ("format_tree", | |
&text_symbolizer_properties::format_tree, | |
@@ -451,7 +454,8 @@ void export_text_placement() | |
; | |
- class_with_converter<char_properties> | |
+ class_with_converter<char_properties, | |
+ std::shared_ptr<char_properties> > | |
("CharProperties") | |
.def_readwrite_convert("text_transform", &char_properties::text_transform) | |
.def_readwrite_convert("fontset", &char_properties::fontset) | |
@@ -463,7 +467,6 @@ void export_text_placement() | |
.def_readwrite("text_opacity", &char_properties::text_opacity) | |
.def_readwrite("wrap_char", &char_properties::wrap_char) | |
.def_readwrite("wrap_character", &char_properties::wrap_char) | |
- .def_readwrite("wrap_before", &char_properties::wrap_before) | |
.def_readwrite("fill", &char_properties::fill) | |
.def_readwrite("halo_fill", &char_properties::halo_fill) | |
.def_readwrite("halo_radius", &char_properties::halo_radius) | |
@@ -486,24 +489,12 @@ void export_text_placement() | |
("TextPlacementInfo", | |
init<text_placements const*, double>()) | |
.def("next", pure_virtual(&text_placement_info::next)) | |
- .def("get_actual_label_spacing", &text_placement_info::get_actual_label_spacing) | |
- .def("get_actual_minimum_distance", &text_placement_info::get_actual_minimum_distance) | |
- .def("get_actual_minimum_padding", &text_placement_info::get_actual_minimum_padding) | |
.def_readwrite("properties", &text_placement_info::properties) | |
.def_readwrite("scale_factor", &text_placement_info::scale_factor) | |
; | |
register_ptr_to_python<std::shared_ptr<text_placement_info> >(); | |
- class_<processed_text, | |
- std::shared_ptr<processed_text>, | |
- boost::noncopyable> | |
- ("ProcessedText", no_init) | |
- .def("push_back", &processed_text::push_back) | |
- .def("clear", &processed_text::clear) | |
- ; | |
- | |
- | |
class_<expression_set, | |
std::shared_ptr<expression_set>, | |
boost::noncopyable> | |
@@ -551,7 +542,6 @@ void export_text_placement() | |
.def_readwrite_convert("text_opacity", &formatting::format_node::text_opacity) | |
.def_readwrite_convert("wrap_char", &formatting::format_node::wrap_char) | |
.def_readwrite_convert("wrap_character", &formatting::format_node::wrap_char) | |
- .def_readwrite_convert("wrap_before", &formatting::format_node::wrap_before) | |
.def_readwrite_convert("text_transform", &formatting::format_node::text_transform) | |
.def_readwrite_convert("fill", &formatting::format_node::fill) | |
.def_readwrite_convert("halo_fill", &formatting::format_node::halo_fill) | |
@@ -591,7 +581,6 @@ void export_text_placement() | |
.def_readwrite("text_opacity", &formatting::expression_format::text_opacity) | |
.def_readwrite("wrap_char", &formatting::expression_format::wrap_char) | |
.def_readwrite("wrap_character", &formatting::expression_format::wrap_char) | |
- .def_readwrite("wrap_before", &formatting::expression_format::wrap_before) | |
.def_readwrite("fill", &formatting::expression_format::fill) | |
.def_readwrite("halo_fill", &formatting::expression_format::halo_fill) | |
.def_readwrite("halo_radius", &formatting::expression_format::halo_radius) | |
diff --git a/include/mapnik/box2d.hpp b/include/mapnik/box2d.hpp | |
index 6133ffb..a4f680f 100644 | |
--- a/include/mapnik/box2d.hpp | |
+++ b/include/mapnik/box2d.hpp | |
@@ -97,12 +97,15 @@ public: | |
void pad(T padding); | |
bool from_string(std::string const& str); | |
bool valid() const; | |
+ void move(T x, T y); | |
// define some operators | |
box2d_type& operator+=(box2d_type const& other); | |
box2d_type& operator*=(T); | |
box2d_type& operator/=(T); | |
T operator[](int index) const; | |
+ box2d_type operator +(T other) const; //enlarge box by given amount | |
+ box2d_type& operator +=(T other); //enlarge box by given amount | |
// compute the bounding box of this one transformed | |
box2d_type operator* (agg::trans_affine const& tr) const; | |
diff --git a/include/mapnik/cairo_context.hpp b/include/mapnik/cairo_context.hpp | |
index 6c95962..2471e6d 100644 | |
--- a/include/mapnik/cairo_context.hpp | |
+++ b/include/mapnik/cairo_context.hpp | |
@@ -33,6 +33,8 @@ | |
#include <mapnik/image_compositing.hpp> | |
#include <mapnik/font_engine_freetype.hpp> | |
#include <mapnik/gradient.hpp> | |
+#include <mapnik/text/text_properties.hpp> | |
+#include <mapnik/text/placements_list.hpp> | |
#include <mapnik/vertex.hpp> | |
#include <mapnik/noncopyable.hpp> | |
@@ -317,9 +319,9 @@ public: | |
void translate(double x, double y); | |
void save(); | |
void restore(); | |
- void show_glyph(unsigned long index, double x, double y); | |
- void glyph_path(unsigned long index, double x, double y); | |
- void add_text(text_path const& path, | |
+ void show_glyph(unsigned long index, pixel_position const& pos); | |
+ void glyph_path(unsigned long index, pixel_position const& pos); | |
+ void add_text(glyph_positions_ptr pos, | |
cairo_face_manager & manager, | |
face_manager<freetype_engine> & font_manager, | |
double scale_factor = 1.0); | |
diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp | |
index e7a8399..d484368 100644 | |
--- a/include/mapnik/font_engine_freetype.hpp | |
+++ b/include/mapnik/font_engine_freetype.hpp | |
@@ -24,15 +24,9 @@ | |
#define MAPNIK_FONT_ENGINE_FREETYPE_HPP | |
// mapnik | |
-#include <mapnik/debug.hpp> | |
-#include <mapnik/color.hpp> | |
-#include <mapnik/utils.hpp> | |
+#include <mapnik/config.hpp> | |
#include <mapnik/box2d.hpp> | |
-#include <mapnik/ctrans.hpp> | |
-#include <mapnik/geometry.hpp> | |
#include <mapnik/font_set.hpp> | |
-#include <mapnik/text/char_info.hpp> | |
-#include <mapnik/image_compositing.hpp> | |
#include <mapnik/text_symbolizer.hpp> | |
#include <mapnik/noncopyable.hpp> | |
#include <mapnik/value_types.hpp> | |
@@ -40,90 +34,38 @@ | |
// boost | |
#include <memory> | |
-#include <boost/ptr_container/ptr_vector.hpp> | |
+#include <boost/ptr_container/ptr_vector.hpp> | |
+#include <boost/optional.hpp> | |
#ifdef MAPNIK_THREADSAFE | |
#include <thread> | |
#endif | |
-// stl | |
-#include <string> | |
+//// stl | |
#include <vector> | |
-#include <map> | |
struct FT_LibraryRec_; | |
namespace mapnik | |
{ | |
-class font_face; | |
-class text_path; | |
-class string_info; | |
-struct char_properties; | |
-class stroker; | |
-struct glyph_t; | |
+class stroker; | |
+typedef std::shared_ptr<stroker> stroker_ptr; | |
+class font_face_set; | |
+typedef std::shared_ptr<font_face_set> face_set_ptr; | |
+class font_face; | |
typedef std::shared_ptr<font_face> face_ptr; | |
-class MAPNIK_DECL font_glyph : private mapnik::noncopyable | |
-{ | |
-public: | |
- font_glyph(face_ptr face, unsigned index) | |
- : face_(face), index_(index) {} | |
- | |
- face_ptr get_face() const | |
- { | |
- return face_; | |
- } | |
- | |
- unsigned get_index() const | |
- { | |
- return index_; | |
- } | |
-private: | |
- face_ptr face_; | |
- unsigned index_; | |
-}; | |
- | |
-typedef std::shared_ptr<font_glyph> glyph_ptr; | |
- | |
- | |
- | |
-class MAPNIK_DECL font_face_set : private mapnik::noncopyable | |
-{ | |
-public: | |
- typedef std::vector<face_ptr> container_type; | |
- typedef container_type::size_type size_type; | |
- | |
- font_face_set(void) | |
- : faces_(), | |
- dimension_cache_() {} | |
- | |
- void add(face_ptr face); | |
- size_type size() const; | |
- glyph_ptr get_glyph(unsigned c) const; | |
- char_info character_dimensions(unsigned c); | |
- void get_string_info(string_info & info, mapnik::value_unicode_string const& ustr, char_properties *format); | |
- void set_pixel_sizes(unsigned size); | |
- void set_character_sizes(double size); | |
-private: | |
- container_type faces_; | |
- std::map<unsigned, char_info> dimension_cache_; | |
-}; | |
- | |
-typedef std::shared_ptr<font_face_set> face_set_ptr; | |
-typedef std::shared_ptr<stroker> stroker_ptr; | |
class MAPNIK_DECL freetype_engine | |
{ | |
public: | |
static bool is_font_file(std::string const& file_name); | |
- | |
/*! \brief register a font file | |
* @param file_name path to a font file. | |
* @return bool - true if at least one face was successfully registered in the file. | |
*/ | |
static bool register_font(std::string const& file_name); | |
- | |
/*! \brief register a font file | |
* @param dir - path to a directory containing fonts or subdirectories. | |
* @param recurse - default false, whether to search for fonts in sub directories. | |
@@ -137,11 +79,11 @@ public: | |
virtual ~freetype_engine(); | |
freetype_engine(); | |
private: | |
- FT_LibraryRec_ * library_; | |
+ FT_LibraryRec_ *library_; | |
#ifdef MAPNIK_THREADSAFE | |
static std::mutex mutex_; | |
#endif | |
- static std::map<std::string,std::pair<int,std::string> > name2file_; | |
+ static std::map<std::string, std::pair<int,std::string> > name2file_; | |
static std::map<std::string, std::string> memory_fonts_; | |
}; | |
@@ -149,7 +91,7 @@ template <typename T> | |
class MAPNIK_DECL face_manager : private mapnik::noncopyable | |
{ | |
typedef T font_engine_type; | |
- typedef std::map<std::string,face_ptr> face_ptr_cache_type; | |
+ typedef std::map<std::string, face_ptr> face_ptr_cache_type; | |
public: | |
face_manager(T & engine) | |
@@ -157,74 +99,13 @@ public: | |
stroker_(engine_.create_stroker()), | |
face_ptr_cache_() {} | |
- face_ptr get_face(std::string const& name) | |
- { | |
- face_ptr_cache_type::iterator itr; | |
- itr = face_ptr_cache_.find(name); | |
- if (itr != face_ptr_cache_.end()) | |
- { | |
- return itr->second; | |
- } | |
- else | |
- { | |
- face_ptr face = engine_.create_face(name); | |
- if (face) | |
- { | |
- face_ptr_cache_.insert(make_pair(name,face)); | |
- } | |
- return face; | |
- } | |
- } | |
+ face_ptr get_face(std::string const& name); | |
+ face_set_ptr get_face_set(std::string const& name); | |
+ face_set_ptr get_face_set(font_set const& fset); | |
+ face_set_ptr get_face_set(std::string const& name, boost::optional<font_set> fset); | |
- face_set_ptr get_face_set(std::string const& name) | |
- { | |
- face_set_ptr face_set = std::make_shared<font_face_set>(); | |
- if (face_ptr face = get_face(name)) | |
- { | |
- face_set->add(face); | |
- } | |
- return face_set; | |
- } | |
- face_set_ptr get_face_set(font_set const& fset) | |
- { | |
- std::vector<std::string> const& names = fset.get_face_names(); | |
- face_set_ptr face_set = std::make_shared<font_face_set>(); | |
- for ( std::string const& name : names) | |
- { | |
- face_ptr face = get_face(name); | |
- if (face) | |
- { | |
- face_set->add(face); | |
- } | |
-#ifdef MAPNIK_LOG | |
- else | |
- { | |
- MAPNIK_LOG_DEBUG(font_engine_freetype) | |
- << "Failed to find face '" << name | |
- << "' in font set '" << fset.get_name() << "'\n"; | |
- } | |
-#endif | |
- } | |
- return face_set; | |
- } | |
- | |
- face_set_ptr get_face_set(std::string const& name, boost::optional<font_set> fset) | |
- { | |
- if (fset && fset->size() > 0) | |
- { | |
- return get_face_set(*fset); | |
- } | |
- else | |
- { | |
- return get_face_set(name); | |
- } | |
- } | |
- | |
- inline stroker_ptr get_stroker() | |
- { | |
- return stroker_; | |
- } | |
+ inline stroker_ptr get_stroker() { return stroker_; } | |
private: | |
font_engine_type & engine_; | |
@@ -232,31 +113,6 @@ private: | |
face_ptr_cache_type face_ptr_cache_; | |
}; | |
-template <typename T> | |
-struct text_renderer : private mapnik::noncopyable | |
-{ | |
- | |
- typedef boost::ptr_vector<glyph_t> glyphs_t; | |
- typedef T pixmap_type; | |
- | |
- text_renderer (pixmap_type & pixmap, | |
- face_manager<freetype_engine> & font_manager, | |
- halo_rasterizer_e rasterizer, | |
- composite_mode_e comp_op = src_over, | |
- double scale_factor=1.0); | |
- box2d<double> prepare_glyphs(text_path const& path); | |
- void render(pixel_position const& pos); | |
- void render_id(mapnik::value_integer feature_id, | |
- pixel_position const& pos); | |
-private: | |
- pixmap_type & pixmap_; | |
- face_manager<freetype_engine> & font_manager_; | |
- halo_rasterizer_e rasterizer_; | |
- glyphs_t glyphs_; | |
- composite_mode_e comp_op_; | |
- double scale_factor_; | |
-}; | |
- | |
typedef face_manager<freetype_engine> face_manager_freetype; | |
} | |
diff --git a/include/mapnik/pixel_position.hpp b/include/mapnik/pixel_position.hpp | |
index 1ac537e..a0d073f 100644 | |
--- a/include/mapnik/pixel_position.hpp | |
+++ b/include/mapnik/pixel_position.hpp | |
@@ -22,13 +22,73 @@ | |
#ifndef MAPNIK_PIXEL_POSITION_HPP | |
#define MAPNIK_PIXEL_POSITION_HPP | |
-// Store a pixel position. | |
+// stl | |
+#include <iomanip> | |
+ | |
+namespace mapnik | |
+{ | |
+ | |
+struct rotation; | |
struct pixel_position | |
{ | |
double x; | |
double y; | |
pixel_position(double x_, double y_) : x(x_), y(y_) { } | |
pixel_position() : x(0), y(0) { } | |
+ pixel_position operator+ (pixel_position const& other) const | |
+ { | |
+ return pixel_position(x + other.x, y + other.y); | |
+ } | |
+ | |
+ pixel_position operator- (pixel_position const& other) const | |
+ { | |
+ return pixel_position(x - other.x, y - other.y); | |
+ } | |
+ | |
+ pixel_position operator* (double other) const | |
+ { | |
+ return pixel_position(x * other, y * other); | |
+ } | |
+ | |
+ void set(double x_, double y_) | |
+ { | |
+ x = x_; | |
+ y = y_; | |
+ } | |
+ | |
+ void clear() | |
+ { | |
+ x = 0; | |
+ y = 0; | |
+ } | |
+ | |
+ pixel_position rotate(rotation const& rot) const; | |
+ pixel_position operator~() const | |
+ { | |
+ return pixel_position(x, -y); | |
+ } | |
}; | |
+inline pixel_position operator* (double factor, pixel_position const& pos) | |
+{ | |
+ return pixel_position(factor * pos.x, factor * pos.y); | |
+} | |
+ | |
+template <class charT, class traits> | |
+inline std::basic_ostream<charT,traits>& | |
+operator << (std::basic_ostream<charT,traits>& out, | |
+ const pixel_position& e) | |
+{ | |
+ std::basic_ostringstream<charT,traits> s; | |
+ s.copyfmt(out); | |
+ s.width(0); | |
+ s << '(' << std::fixed << std::setprecision(16) | |
+ << e.x << ", " << e.y << ')'; | |
+ out << s.str(); | |
+ return out; | |
+} | |
+ | |
+} | |
+ | |
+ | |
#endif // MAPNIK_PIXEL_POSITION_HPP | |
diff --git a/include/mapnik/shield_symbolizer.hpp b/include/mapnik/shield_symbolizer.hpp | |
index 6b28826..7cb80df 100644 | |
--- a/include/mapnik/shield_symbolizer.hpp | |
+++ b/include/mapnik/shield_symbolizer.hpp | |
@@ -54,11 +54,11 @@ struct MAPNIK_DECL shield_symbolizer : public text_symbolizer, | |
bool get_unlock_image() const; // image is not locked to the text placement | |
void set_unlock_image(bool unlock_image); | |
void set_shield_displacement(double shield_dx, double shield_dy); | |
- position const& get_shield_displacement() const; | |
+ pixel_position const& get_shield_displacement() const; | |
private: | |
bool unlock_image_; | |
- position shield_displacement_; | |
+ pixel_position shield_displacement_; | |
}; | |
} | |
diff --git a/include/mapnik/text/char_info.hpp b/include/mapnik/text/char_info.hpp | |
index bd21aea..ad05df2 100644 | |
--- a/include/mapnik/text/char_info.hpp | |
+++ b/include/mapnik/text/char_info.hpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2011 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
diff --git a/include/mapnik/text/face.hpp b/include/mapnik/text/face.hpp | |
index 6e04988..0528cf6 100644 | |
--- a/include/mapnik/text/face.hpp | |
+++ b/include/mapnik/text/face.hpp | |
@@ -19,77 +19,35 @@ | |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
* | |
*****************************************************************************/ | |
+#ifndef MAPNIK_FACE_HPP | |
+#define MAPNIK_FACE_HPP | |
- | |
-#ifndef MAPNIK_FONT_UTIL_HPP | |
-#define MAPNIK_FONT_UTIL_HPP | |
- | |
-// mapnik | |
+//mapnik | |
+#include <mapnik/text/glyph_info.hpp> | |
+#include <mapnik/config.hpp> | |
#include <mapnik/noncopyable.hpp> | |
-#include <string> | |
- | |
// freetype2 | |
extern "C" | |
{ | |
#include <ft2build.h> | |
#include FT_FREETYPE_H | |
-#include FT_GLYPH_H | |
#include FT_STROKER_H | |
} | |
-namespace mapnik | |
-{ | |
-struct char_properties; | |
- | |
-struct glyph_t : mapnik::noncopyable | |
-{ | |
- FT_Glyph image; | |
- char_properties *properties; | |
- glyph_t(FT_Glyph image_, char_properties *properties_) | |
- : image(image_), | |
- properties(properties_) {} | |
- ~glyph_t() | |
- { | |
- FT_Done_Glyph(image); | |
- } | |
-}; | |
- | |
+//stl | |
+#include <map> | |
+#include <memory> | |
+#include <string> | |
+#include <vector> | |
-// FT_Stroker wrapper | |
-class stroker : mapnik::noncopyable | |
+namespace mapnik | |
{ | |
-public: | |
- explicit stroker(FT_Stroker s) | |
- : s_(s) {} | |
- | |
- void init(double radius) | |
- { | |
- FT_Stroker_Set(s_, (FT_Fixed) (radius * (1<<6)), | |
- FT_STROKER_LINECAP_ROUND, | |
- FT_STROKER_LINEJOIN_ROUND, | |
- 0); | |
- } | |
- | |
- FT_Stroker const& get() const | |
- { | |
- return s_; | |
- } | |
- | |
- ~stroker() | |
- { | |
- FT_Stroker_Done(s_); | |
- } | |
-private: | |
- FT_Stroker s_; | |
-}; | |
- | |
class font_face : mapnik::noncopyable | |
{ | |
public: | |
- font_face(FT_Face face) | |
- : face_(face) {} | |
+ font_face(FT_Face face); | |
std::string family_name() const | |
{ | |
@@ -101,45 +59,59 @@ public: | |
return std::string(face_->style_name); | |
} | |
- FT_GlyphSlot glyph() const | |
- { | |
- return face_->glyph; | |
- } | |
- | |
FT_Face get_face() const | |
{ | |
return face_; | |
} | |
- unsigned get_char(unsigned c) const | |
- { | |
- return FT_Get_Char_Index(face_, c); | |
- } | |
+ double get_char_height() const; | |
- bool set_pixel_sizes(unsigned size) | |
- { | |
- if (! FT_Set_Pixel_Sizes( face_, 0, size )) | |
- return true; | |
- return false; | |
- } | |
+ bool set_character_sizes(double size); | |
- bool set_character_sizes(double size) | |
- { | |
- if ( !FT_Set_Char_Size(face_,0,(FT_F26Dot6)(size * (1<<6)),0,0)) | |
- return true; | |
- return false; | |
- } | |
+ void glyph_dimensions(glyph_info &glyph) const; | |
- ~font_face() | |
- { | |
- FT_Done_Face(face_); | |
- } | |
+ ~font_face(); | |
private: | |
FT_Face face_; | |
+ mutable std::map<glyph_index_t, glyph_info> dimension_cache_; | |
+ mutable double char_height_; | |
}; | |
+typedef std::shared_ptr<font_face> face_ptr; | |
-} | |
+class MAPNIK_DECL font_face_set : private mapnik::noncopyable | |
+{ | |
+public: | |
+ typedef std::vector<face_ptr>::iterator iterator; | |
+ font_face_set(void) : faces_(){} | |
+ | |
+ void add(face_ptr face); | |
+ void set_character_sizes(double size); | |
+ | |
+ unsigned size() const { return faces_.size(); } | |
+ iterator begin() { return faces_.begin(); } | |
+ iterator end() { return faces_.end(); } | |
+private: | |
+ std::vector<face_ptr> faces_; | |
+}; | |
+typedef std::shared_ptr<font_face_set> face_set_ptr; | |
+ | |
+ | |
+// FT_Stroker wrapper | |
+class stroker : mapnik::noncopyable | |
+{ | |
+public: | |
+ explicit stroker(FT_Stroker s) | |
+ : s_(s) {} | |
+ ~stroker(); | |
+ | |
+ void init(double radius); | |
+ FT_Stroker const& get() const { return s_; } | |
+private: | |
+ FT_Stroker s_; | |
+}; | |
+ | |
+} //ns mapnik | |
-#endif // MAPNIK_FONT_UTIL_HPP | |
+#endif // FACE_HPP | |
diff --git a/include/mapnik/text/formatting/base.hpp b/include/mapnik/text/formatting/base.hpp | |
index ccb699f..17704ec 100644 | |
--- a/include/mapnik/text/formatting/base.hpp | |
+++ b/include/mapnik/text/formatting/base.hpp | |
@@ -24,16 +24,16 @@ | |
// mapnik | |
#include <mapnik/expression.hpp> | |
+#include <mapnik/text/char_properties_ptr.hpp> | |
// boost | |
#include <boost/property_tree/ptree_fwd.hpp> | |
namespace mapnik { | |
+class text_layout; | |
class feature_impl; | |
-class processed_text; | |
class xml_node; | |
-struct char_properties; | |
namespace formatting { | |
@@ -46,7 +46,7 @@ public: | |
virtual ~node() {} | |
virtual void to_xml(boost::property_tree::ptree &xml) const; | |
static node_ptr from_xml(xml_node const& xml); | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const = 0; | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const = 0; | |
virtual void add_expressions(expression_set &output) const; | |
}; | |
} //ns formatting | |
diff --git a/include/mapnik/text/formatting/expression_format.hpp b/include/mapnik/text/formatting/expression_format.hpp | |
index 8e6f15f..8acbd9e 100644 | |
--- a/include/mapnik/text/formatting/expression_format.hpp | |
+++ b/include/mapnik/text/formatting/expression_format.hpp | |
@@ -32,7 +32,6 @@ | |
namespace mapnik { | |
class feature_impl; | |
-class processed_text; | |
class xml_node; | |
struct char_properties; | |
@@ -41,7 +40,7 @@ class MAPNIK_DECL expression_format: public node { | |
public: | |
void to_xml(boost::property_tree::ptree &xml) const; | |
static node_ptr from_xml(xml_node const& xml); | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const; | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const; | |
virtual void add_expressions(expression_set &output) const; | |
void set_child(node_ptr child); | |
@@ -52,7 +51,6 @@ public: | |
expression_ptr character_spacing; | |
expression_ptr line_spacing; | |
expression_ptr text_opacity; | |
- expression_ptr wrap_before; | |
expression_ptr wrap_char; | |
expression_ptr fill; | |
expression_ptr halo_fill; | |
diff --git a/include/mapnik/text/formatting/format.hpp b/include/mapnik/text/formatting/format.hpp | |
index b6a763e..b7aa862 100644 | |
--- a/include/mapnik/text/formatting/format.hpp | |
+++ b/include/mapnik/text/formatting/format.hpp | |
@@ -36,7 +36,7 @@ class MAPNIK_DECL format_node: public node { | |
public: | |
void to_xml(boost::property_tree::ptree &xml) const; | |
static node_ptr from_xml(xml_node const& xml); | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const; | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const; | |
virtual void add_expressions(expression_set &output) const; | |
void set_child(node_ptr child); | |
diff --git a/include/mapnik/text/formatting/list.hpp b/include/mapnik/text/formatting/list.hpp | |
index 54be03d..d74a772 100644 | |
--- a/include/mapnik/text/formatting/list.hpp | |
+++ b/include/mapnik/text/formatting/list.hpp | |
@@ -35,7 +35,7 @@ class MAPNIK_DECL list_node: public node { | |
public: | |
list_node() : node(), children_() {} | |
virtual void to_xml(boost::property_tree::ptree &xml) const; | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const; | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const; | |
virtual void add_expressions(expression_set &output) const; | |
void push_back(node_ptr n); | |
@@ -49,4 +49,3 @@ protected: | |
} //ns mapnik | |
#endif // FORMATTING_LIST_HPP | |
- | |
diff --git a/include/mapnik/text/formatting/text.hpp b/include/mapnik/text/formatting/text.hpp | |
index b15004d..a868e58 100644 | |
--- a/include/mapnik/text/formatting/text.hpp | |
+++ b/include/mapnik/text/formatting/text.hpp | |
@@ -36,7 +36,7 @@ public: | |
text_node(std::string text): node(), text_(parse_expression(text)) {} | |
void to_xml(boost::property_tree::ptree &xml) const; | |
static node_ptr from_xml(xml_node const& xml); | |
- virtual void apply(char_properties const& p, feature_impl const& feature, processed_text &output) const; | |
+ virtual void apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const; | |
virtual void add_expressions(expression_set &output) const; | |
void set_text(expression_ptr text); | |
diff --git a/include/mapnik/text/placement_finder.hpp b/include/mapnik/text/placement_finder.hpp | |
index f3ca203..92660e7 100644 | |
--- a/include/mapnik/text/placement_finder.hpp | |
+++ b/include/mapnik/text/placement_finder.hpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2011 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
@@ -19,146 +19,95 @@ | |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
* | |
*****************************************************************************/ | |
- | |
#ifndef MAPNIK_PLACEMENT_FINDER_HPP | |
#define MAPNIK_PLACEMENT_FINDER_HPP | |
-// mapnik | |
-#include <mapnik/geometry.hpp> | |
-#include <mapnik/feature.hpp> | |
-#include <mapnik/text/text_properties.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
-#include <mapnik/label_collision_detector.hpp> | |
-#include <mapnik/ctrans.hpp> | |
+//mapnik | |
+#include <mapnik/box2d.hpp> | |
+#include <mapnik/pixel_position.hpp> | |
+#include <mapnik/text/layout.hpp> | |
+#include <mapnik/text/placements/base.hpp> | |
+#include <mapnik/text/placements_list.hpp> | |
+#include <mapnik/text/rotation.hpp> | |
#include <mapnik/noncopyable.hpp> | |
-// agg | |
-#include "agg_conv_clip_polyline.h" | |
- | |
-// stl | |
-#include <queue> | |
- | |
namespace mapnik | |
{ | |
-class text_placement_info; | |
-class string_info; | |
-class text_path; | |
- | |
-typedef agg::conv_clip_polyline<geometry_type> clipped_geometry_type; | |
-typedef coord_transform<CoordTransform,clipped_geometry_type> ClippedPathType; | |
-typedef coord_transform<CoordTransform,geometry_type> PathType; | |
- | |
+class label_collision_detector4; | |
typedef label_collision_detector4 DetectorType; | |
+class feature_impl; | |
+class vertex_cache; | |
-template <typename DetectorT> | |
class placement_finder : mapnik::noncopyable | |
{ | |
public: | |
- placement_finder(text_placement_info const& placement_info, | |
- string_info const& info, | |
- DetectorT & detector, | |
- box2d<double> const& extent); | |
- | |
- // Try place a single label at the given point. */ | |
- 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. */ | |
+ placement_finder(feature_impl const& feature, | |
+ DetectorType & detector, | |
+ box2d<double> const& extent, | |
+ text_placement_info_ptr placement_info, | |
+ face_manager_freetype & font_manager, | |
+ double scale_factor); | |
+ | |
+ /** Try to place a single label at the given point. */ | |
+ bool find_point_placement(pixel_position const& pos); | |
+ /** Iterate over the given path, placing line-following labels or point labels with respect to label_spacing. */ | |
template <typename T> | |
- void find_point_placements(T & path); | |
- | |
- // Iterate over the given path, placing line-following labels with respect to label_spacing. */ | |
- template <typename T> | |
- void find_line_placements(T & path); | |
- | |
- // Add placements to detector. */ | |
- void update_detector(); | |
+ bool find_line_placements(T & path, bool points); | |
+ /** Try next position alternative from placement_info. */ | |
+ bool next_position(); | |
- // Remove old placements. */ | |
- void clear_placements(); | |
- | |
- inline placements_type const& get_results() { return placements_; } | |
- | |
- std::vector<box2d<double> > & additional_boxes() { return additional_boxes_;} | |
- std::vector<box2d<double> > const& additional_boxes() const { return additional_boxes_;} | |
- | |
- void set_collect_extents(bool collect) { collect_extents_ = collect; } | |
- bool get_collect_extents() const { return collect_extents_; } | |
- | |
- box2d<double> const& get_extents() const { return extents_; } | |
+ placements_list const& placements() const { return placements_; } | |
+ void set_marker(marker_info_ptr m, box2d<double> box, bool marker_unlocked, pixel_position const& marker_displacement); | |
private: | |
- ///Helpers for find_line_placement | |
- | |
- ///Returns a possible placement on the given line, does not test for collisions | |
- //index: index of the node the current line ends on | |
- //distance: distance along the given index that the placement should start at, this includes the offset, | |
- // as such it may be > or < the length of the current line, so this must be checked for | |
- //orientation: if set to != 0 the placement will be attempted with the given orientation | |
- // 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::unique_ptr<text_path> get_placement_offset(std::vector<vertex2d> const& path_positions, | |
- std::vector<double> const& path_distances, | |
- int & orientation, std::size_t index, double distance); | |
- | |
- ///Tests whether the given text_path 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(std::unique_ptr<text_path> const& current_placement, int orientation); | |
- | |
- ///Does a line-circle intersect calculation | |
- // NOTE: Follow the strict pre conditions | |
- // Pre Conditions: x1,y1 is within radius distance of cx,cy. x2,y2 is outside radius distance of cx,cy | |
- // This means there is exactly one intersect point | |
- // Result is returned in ix, iy | |
- void find_line_circle_intersection( | |
- double cx, double cy, double radius, | |
- double x1, double y1, double x2, double y2, | |
- double & ix, double & iy); | |
- | |
- void find_line_breaks(); | |
- void init_string_size(); | |
void init_alignment(); | |
- void adjust_position(text_path *current_placement); | |
- void add_line(double width, double height, bool first_line); | |
+ pixel_position alignment_offset() const; | |
+ double jalign_offset(double line_width) const; | |
+ | |
+ bool single_line_placement(vertex_cache &pp, text_upright_e orientation); | |
+ /** Moves dx pixels but makes sure not to fall of the end. */ | |
+ void path_move_dx(vertex_cache &pp); | |
+ /** Normalize angle in range [-pi, +pi]. */ | |
+ static double normalize_angle(double angle); | |
+ /** Adjusts user defined spacing to place an integer number of labels. */ | |
+ double get_spacing(double path_length, double layout_width) const; | |
+ /** Checks for collision. */ | |
+ bool collision(box2d<double> const& box) const; | |
+ /** Adds marker to glyph_positions and to collision detector. Returns false if there is a collision. */ | |
+ bool add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const; | |
+ /** Maps upright==auto, left_only and right_only to left,right to simplify processing. | |
+ angle = angle of at start of line (to estimate best option for upright==auto) */ | |
+ text_upright_e simplify_upright(text_upright_e upright, double angle) const; | |
+ box2d<double> get_bbox(glyph_info const& glyph, pixel_position const& pos, rotation const& rot); | |
+ feature_impl const& feature_; | |
+ DetectorType &detector_; | |
+ box2d<double> const& extent_; | |
+ // Precalculated values for maximum performance | |
+ rotation orientation_; | |
+ text_layout layout_; | |
+ text_placement_info_ptr info_; | |
+ bool valid_; | |
- ///General Internals | |
- DetectorT & detector_; | |
- box2d<double> const& dimensions_; | |
- string_info const& info_; | |
- text_symbolizer_properties const& p; | |
- text_placement_info const& pi; | |
- // Length of the longest line after linebreaks. | |
- // Before find_line_breaks() this is the total length of the string. | |
- double string_width_; | |
- // Height of the string after linebreaks. | |
- // Before find_line_breaks() this is the total length of the string. | |
- double string_height_; | |
- // Height of the tallest font in the first line not including line spacing. | |
- // Used to determine the correct offset for the first line. | |
- double first_line_space_; | |
vertical_alignment_e valign_; | |
- horizontal_alignment_e halign_; | |
+ /** Horizontal alignment for point placements. */ | |
+ horizontal_alignment_e halign_point_; | |
+ /** Horizontal alignment for line placements. */ | |
+ horizontal_alignment_e halign_line_; | |
justify_alignment_e jalign_; | |
- std::vector<std::size_t> line_breaks_; | |
- std::vector<std::pair<double, double> > line_sizes_; | |
- std::queue< box2d<double> > envelopes_; | |
- // Used to return all placements found. */ | |
- placements_type placements_; | |
- // Bounding box of all texts placed. */ | |
- box2d<double> extents_; | |
- // Collect a bounding box of all texts placed. */ | |
- bool collect_extents_; | |
+ double scale_factor_; | |
+ | |
+ placements_list placements_; | |
- // Additional boxes to take into account when finding placement. | |
- // Used for finding line placements where multiple placements are returned. | |
- // Boxes are relative to starting point of current placement. | |
- // Only used for point placements! | |
- std::vector<box2d<double> > additional_boxes_; | |
+ //ShieldSymbolizer | |
+ bool has_marker_; | |
+ marker_info_ptr marker_; | |
+ box2d<double> marker_box_; | |
+ bool marker_unlocked_; | |
+ pixel_position marker_displacement_; | |
}; | |
-} | |
+}//ns mapnik | |
-#endif // MAPNIK_PLACEMENT_FINDER_HPP | |
+#endif // PLACEMENT_FINDER_HPP | |
diff --git a/include/mapnik/text/placements/base.hpp b/include/mapnik/text/placements/base.hpp | |
index d181609..a766e50 100644 | |
--- a/include/mapnik/text/placements/base.hpp | |
+++ b/include/mapnik/text/placements/base.hpp | |
@@ -33,7 +33,7 @@ namespace mapnik | |
typedef std::pair<double,double> dimension_type; | |
class MAPNIK_DECL text_placements; | |
-/** Generate a possible placement and store results of placement_finder. | |
+/** Generate a possible placement. | |
* This placement has first to be tested by placement_finder to verify it | |
* can actually be used. | |
*/ | |
@@ -58,16 +58,7 @@ public: | |
/** Scale factor used by the renderer. */ | |
double scale_factor; | |
- /** Set scale factor. */ | |
- void set_scale_factor(double factor) { scale_factor = factor; } | |
- /** Get scale factor. */ | |
- double get_scale_factor() const { return scale_factor; } | |
- /** Get label spacing taking the scale factor into account. */ | |
- double get_actual_label_spacing() const { return scale_factor * properties.label_spacing; } | |
- /** Get minimum distance taking the scale factor into account. */ | |
- double get_actual_minimum_distance() const { return scale_factor * properties.minimum_distance; } | |
- /** Get minimum padding taking the scale factor into account. */ | |
- double get_actual_minimum_padding() const { return scale_factor * properties.minimum_padding; } | |
+ | |
}; | |
typedef std::shared_ptr<text_placement_info> text_placement_info_ptr; | |
diff --git a/include/mapnik/text/processed_text.hpp b/include/mapnik/text/processed_text.hpp | |
deleted file mode 100644 | |
index 6a43f8e..0000000 | |
--- a/include/mapnik/text/processed_text.hpp | |
+++ /dev/null | |
@@ -1,70 +0,0 @@ | |
-/***************************************************************************** | |
- * | |
- * 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 PROCESSED_TEXT_HPP | |
-#define PROCESSED_TEXT_HPP | |
- | |
-// mapnik | |
-#include <mapnik/text/text_properties.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
-#include <mapnik/noncopyable.hpp> | |
-#include <mapnik/value_types.hpp> | |
- | |
-// stl | |
-#include <list> | |
- | |
-namespace mapnik | |
-{ | |
- | |
-// fwd declares | |
-class freetype_engine; | |
-template <typename T> class face_manager; | |
- | |
-class MAPNIK_DECL processed_text : mapnik::noncopyable | |
-{ | |
-public: | |
- struct processed_expression | |
- { | |
- processed_expression(char_properties const& properties, mapnik::value_unicode_string const& text) | |
- : p(properties), | |
- str(text) {} | |
- char_properties p; | |
- mapnik::value_unicode_string str; | |
- }; | |
-public: | |
- processed_text(face_manager<freetype_engine> & font_manager, double scale_factor); | |
- void push_back(char_properties const& properties, mapnik::value_unicode_string const& text); | |
- std::size_t size() const { return expr_list_.size(); } | |
- unsigned empty() const { return expr_list_.empty(); } | |
- void clear(); | |
- typedef std::list<processed_expression> expression_list; | |
- expression_list::const_iterator begin() const; | |
- expression_list::const_iterator end() const; | |
- string_info const& get_string_info(); | |
-private: | |
- expression_list expr_list_; | |
- face_manager<freetype_engine> &font_manager_; | |
- double scale_factor_; | |
- string_info info_; | |
-}; | |
- | |
-} // ns mapnik | |
-#endif // PROCESSED_TEXT_HPP | |
diff --git a/include/mapnik/text/symbolizer_helpers.hpp b/include/mapnik/text/symbolizer_helpers.hpp | |
index f83c5c6..b167fdf 100644 | |
--- a/include/mapnik/text/symbolizer_helpers.hpp | |
+++ b/include/mapnik/text/symbolizer_helpers.hpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2012 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
@@ -26,34 +26,21 @@ | |
#include <mapnik/text_symbolizer.hpp> | |
#include <mapnik/shield_symbolizer.hpp> | |
#include <mapnik/feature.hpp> | |
+#include <mapnik/marker.hpp> | |
#include <mapnik/marker_cache.hpp> | |
-#include <mapnik/text/processed_text.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
- | |
-//boost | |
- | |
- | |
-// agg | |
-#include "agg_trans_affine.h" | |
- | |
-// fwd declares | |
-namespace mapnik { | |
- class CoordTransform; | |
- class marker; | |
- class proj_transform; | |
- class string_info; | |
- class text_path; | |
- template <typename DetectorT> class placement_finder; | |
-} | |
+#include <mapnik/text/placement_finder.hpp> | |
+#include <mapnik/proj_transform.hpp> | |
+#include <mapnik/ctrans.hpp> | |
namespace mapnik { | |
/** Helper object that does all the TextSymbolizer placement finding | |
* work except actually rendering the object. */ | |
-template <typename FaceManagerT, typename DetectorT> | |
+ | |
class text_symbolizer_helper | |
{ | |
public: | |
+ template <typename FaceManagerT, typename DetectorT> | |
text_symbolizer_helper(text_symbolizer const& sym, | |
feature_impl const& feature, | |
proj_transform const& prj_trans, | |
@@ -65,18 +52,23 @@ public: | |
DetectorT &detector, | |
box2d<double> const& query_extent); | |
- ~text_symbolizer_helper(); | |
- // Return next placement. | |
- // If no more placements are found returns null pointer. | |
- bool next(); | |
+ template <typename FaceManagerT, typename DetectorT> | |
+ text_symbolizer_helper(shield_symbolizer const& sym, | |
+ feature_impl const& feature, | |
+ proj_transform const& prj_trans, | |
+ unsigned width, | |
+ unsigned height, | |
+ double scale_factor, | |
+ CoordTransform const &t, | |
+ FaceManagerT &font_manager, | |
+ DetectorT &detector, | |
+ box2d<double> const& query_extent); | |
- // Get current placement. next() has to be called before! | |
- placements_type const& placements() const; | |
+ /** Return all placements.*/ | |
+ placements_list const& get(); | |
protected: | |
bool next_point_placement(); | |
bool next_line_placement(); | |
- bool next_line_placement_clipped(); | |
- bool next_placement(); | |
void initialize_geometries(); | |
void initialize_points(); | |
@@ -85,103 +77,29 @@ protected: | |
feature_impl const& feature_; | |
proj_transform const& prj_trans_; | |
CoordTransform const& t_; | |
- FaceManagerT & font_manager_; | |
- DetectorT & detector_; | |
box2d<double> dims_; | |
box2d<double> const& query_extent_; | |
//Processing | |
- processed_text text_; | |
- // Using list instead of vector, because we delete random elements and need iterators to stay valid. | |
- // Remaining geometries to be processed. | |
+ /* Using list instead of vector, because we delete random elements and need iterators to stay valid. */ | |
+ /** Remaining geometries to be processed. */ | |
std::list<geometry_type*> geometries_to_process_; | |
- // Geometry currently being processed. | |
+ /** Geometry currently being processed. */ | |
std::list<geometry_type*>::iterator geo_itr_; | |
- // Remaining points to be processed. | |
- std::list<position> points_; | |
- // Point currently being processed. | |
- std::list<position>::iterator point_itr_; | |
- // Text rotation. | |
- double angle_; | |
- // Did last call to next_placement return true? | |
- bool placement_valid_; | |
- // Use point placement. Otherwise line placement is used. | |
+ /** Remaining points to be processed. */ | |
+ std::list<pixel_position> points_; | |
+ /** Point currently being processed. */ | |
+ std::list<pixel_position>::iterator point_itr_; | |
+ /** Use point placement. Otherwise line placement is used. */ | |
bool point_placement_; | |
- // Place text at points on a line instead of following the line (used for ShieldSymbolizer). | |
+ /** Place text at points on a line instead of following the line (used for ShieldSymbolizer) .*/ | |
bool points_on_line_; | |
text_placement_info_ptr placement_; | |
- std::unique_ptr<placement_finder<DetectorT> > finder_; | |
-}; | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-class shield_symbolizer_helper: public text_symbolizer_helper<FaceManagerT, DetectorT> | |
-{ | |
-public: | |
- shield_symbolizer_helper(shield_symbolizer const& sym, | |
- feature_impl const& feature, | |
- proj_transform const& prj_trans, | |
- unsigned width, | |
- unsigned height, | |
- double scale_factor, | |
- CoordTransform const &t, | |
- FaceManagerT &font_manager, | |
- DetectorT &detector, | |
- box2d<double> const& query_extent) : | |
- text_symbolizer_helper<FaceManagerT, DetectorT>(sym, feature, prj_trans, width, height, scale_factor, t, font_manager, detector, query_extent), | |
- sym_(sym) | |
- { | |
- this->points_on_line_ = true; | |
- init_marker(); | |
- } | |
- | |
- box2d<double> const& get_marker_extent() const | |
- { | |
- return marker_ext_; | |
- } | |
- | |
- double get_marker_height() const | |
- { | |
- return marker_h_; | |
- } | |
+ placement_finder finder_; | |
- double get_marker_width() const | |
- { | |
- return marker_w_; | |
- } | |
- | |
- bool next(); | |
- pixel_position get_marker_position(text_path const& p); | |
- marker & get_marker() const; | |
- agg::trans_affine const& get_image_transform() const; | |
-protected: | |
- bool next_point_placement(); | |
- bool next_line_placement(); | |
+ //ShieldSymbolizer only | |
void init_marker(); | |
- shield_symbolizer const& sym_; | |
- box2d<double> marker_ext_; | |
- boost::optional<marker_ptr> marker_; | |
- agg::trans_affine image_transform_; | |
- double marker_w_; | |
- double marker_h_; | |
- double marker_x_; | |
- double marker_y_; | |
- | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::geometries_to_process_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::geo_itr_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::point_itr_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::points_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::font_manager_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::feature_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::t_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::detector_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::dims_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::prj_trans_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_valid_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::point_placement_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::angle_; | |
- using text_symbolizer_helper<FaceManagerT, DetectorT>::finder_; | |
}; | |
+ | |
} //namespace | |
#endif // SYMBOLIZER_HELPERS_HPP | |
diff --git a/include/mapnik/text/text_path.hpp b/include/mapnik/text/text_path.hpp | |
index 2bfdcfe..2c8c5a7 100644 | |
--- a/include/mapnik/text/text_path.hpp | |
+++ b/include/mapnik/text/text_path.hpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2011 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
diff --git a/include/mapnik/text/text_properties.hpp b/include/mapnik/text/text_properties.hpp | |
index 478c9c8..55d7dde 100644 | |
--- a/include/mapnik/text/text_properties.hpp | |
+++ b/include/mapnik/text/text_properties.hpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2012 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
@@ -23,6 +23,7 @@ | |
#define TEXT_PROPERTIES_HPP | |
// mapnik | |
+#include <mapnik/text/char_properties_ptr.hpp> | |
#include <mapnik/color.hpp> | |
#include <mapnik/attribute.hpp> | |
#include <mapnik/value.hpp> | |
@@ -31,6 +32,7 @@ | |
#include <mapnik/enumeration.hpp> | |
#include <mapnik/expression.hpp> | |
#include <mapnik/text/formatting/base.hpp> | |
+#include <mapnik/pixel_position.hpp> | |
// stl | |
#include <map> | |
@@ -67,7 +69,6 @@ struct MAPNIK_DECL char_properties | |
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; | |
@@ -75,7 +76,6 @@ struct MAPNIK_DECL char_properties | |
double halo_radius; | |
}; | |
- | |
enum label_placement_enum | |
{ | |
POINT_PLACEMENT, | |
@@ -120,8 +120,19 @@ enum justify_alignment | |
DEFINE_ENUM(justify_alignment_e, justify_alignment); | |
-typedef std::pair<double, double> position; | |
-class processed_text; | |
+enum text_upright | |
+{ | |
+ UPRIGHT_AUTO, | |
+ UPRIGHT_LEFT, | |
+ UPRIGHT_RIGHT, | |
+ UPRIGHT_LEFT_ONLY, | |
+ UPRIGHT_RIGHT_ONLY, | |
+ text_upright_MAX | |
+}; | |
+ | |
+DEFINE_ENUM(text_upright_e, text_upright); | |
+ | |
+class text_layout; | |
/** Contains all text symbolizer properties which are not directly related to text formatting. */ | |
@@ -136,7 +147,7 @@ struct MAPNIK_DECL text_symbolizer_properties | |
/** 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_impl const& feature) const; | |
+ void process(text_layout &output, feature_impl const& feature) const; | |
/** Automatically create processing instructions for a single expression. */ | |
void set_old_style_expression(expression_ptr expr); | |
/** Sets new format tree. */ | |
@@ -149,7 +160,7 @@ struct MAPNIK_DECL text_symbolizer_properties | |
//Per symbolizer options | |
expression_ptr orientation; | |
- position displacement; | |
+ pixel_position displacement; | |
label_placement_e label_placement; | |
horizontal_alignment_e halign; | |
justify_alignment_e jalign; | |
@@ -170,8 +181,11 @@ struct MAPNIK_DECL text_symbolizer_properties | |
bool largest_bbox_only; | |
double text_ratio; | |
double wrap_width; | |
+ bool wrap_before; | |
+ bool rotate_displacement; | |
+ text_upright_e upright; | |
/** Default values for char_properties. */ | |
- char_properties format; | |
+ char_properties_ptr format; | |
private: | |
/** A tree of formatting::nodes which contain text and formatting information. */ | |
formatting::node_ptr tree_; | |
diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp | |
index c0ee764..667114f 100644 | |
--- a/include/mapnik/text_symbolizer.hpp | |
+++ b/include/mapnik/text_symbolizer.hpp | |
@@ -33,6 +33,7 @@ | |
// boost | |
#include <memory> | |
+ | |
// stl | |
#include <string> | |
@@ -60,10 +61,10 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base | |
// 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, | |
+ double size, color const& fill, | |
text_placements_ptr placements = text_placements_ptr(new text_placements_dummy) | |
); | |
- text_symbolizer(expression_ptr name, float size, color const& fill, | |
+ text_symbolizer(expression_ptr name, double size, color const& fill, | |
text_placements_ptr placements = text_placements_ptr(new text_placements_dummy) | |
); | |
text_symbolizer(text_symbolizer const& rhs); | |
@@ -90,8 +91,8 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base | |
void set_character_spacing(double spacing); | |
double get_label_spacing() const func_deprecated; // spacing between repeated labels on lines | |
void set_label_spacing(double spacing); | |
- double 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(double tolerance); | |
+ 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 func_deprecated; // try render an odd amount of labels | |
void set_force_odd_labels(bool force); | |
double get_max_char_angle_delta() const func_deprecated; // maximum change in angle between adjacent characters | |
@@ -115,8 +116,8 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base | |
void set_vertical_alignment(vertical_alignment_e valign); | |
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 func_deprecated; | |
+ void set_displacement(pixel_position const& p); | |
+ pixel_position const& get_displacement() const func_deprecated; | |
void set_avoid_edges(bool avoid); | |
bool get_avoid_edges() const func_deprecated; | |
void set_minimum_distance(double distance); | |
diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp | |
index f723918..cc0e696 100644 | |
--- a/src/agg/process_shield_symbolizer.cpp | |
+++ b/src/agg/process_shield_symbolizer.cpp | |
@@ -21,15 +21,10 @@ | |
*****************************************************************************/ | |
// mapnik | |
-#include <mapnik/feature.hpp> | |
#include <mapnik/agg_renderer.hpp> | |
-#include <mapnik/agg_rasterizer.hpp> | |
-#include <mapnik/image_util.hpp> | |
+#include <mapnik/graphics.hpp> | |
#include <mapnik/text/symbolizer_helpers.hpp> | |
-#include <mapnik/pixel_position.hpp> | |
-#include <mapnik/text/face.hpp> | |
- | |
-// boost | |
+#include <mapnik/text/renderer.hpp> | |
namespace mapnik { | |
@@ -39,42 +34,24 @@ void agg_renderer<T>::process(shield_symbolizer const& sym, | |
proj_transform const& prj_trans) | |
{ | |
box2d<double> clip_box = clipping_extent(); | |
- shield_symbolizer_helper<face_manager<freetype_engine>, | |
- label_collision_detector4> helper( | |
+ text_symbolizer_helper helper( | |
sym, feature, prj_trans, | |
width_, height_, | |
scale_factor_, | |
t_, font_manager_, *detector_, | |
clip_box); | |
- text_renderer<T> ren(*current_buffer_, | |
- font_manager_, | |
- sym.get_halo_rasterizer(), | |
- sym.comp_op(), | |
- scale_factor_); | |
+ agg_text_renderer<T> ren(*current_buffer_, sym.get_halo_rasterizer(), sym.comp_op(), scale_factor_, font_manager_.get_stroker()); | |
- while (helper.next()) | |
+ placements_list const& placements = helper.get(); | |
+ for (glyph_positions_ptr glyphs : placements) | |
{ | |
- placements_type const& placements = helper.placements(); | |
- for (unsigned int ii = 0; ii < placements.size(); ++ii) | |
- { | |
- // get_marker_position returns (minx,miny) corner position, | |
- // while (currently only) agg_renderer::render_marker newly | |
- // expects center position; | |
- // until all renderers and shield_symbolizer_helper are | |
- // modified accordingly, we must adjust the position here | |
- pixel_position pos = helper.get_marker_position(placements[ii]); | |
- pos.x += 0.5 * helper.get_marker_width(); | |
- pos.y += 0.5 * helper.get_marker_height(); | |
- render_marker(pos, | |
- helper.get_marker(), | |
- helper.get_image_transform(), | |
- sym.get_opacity(), | |
- sym.comp_op()); | |
- | |
- ren.prepare_glyphs(placements[ii]); | |
- ren.render(placements[ii].center); | |
- } | |
+ if (glyphs->marker()) | |
+ render_marker(glyphs->marker_pos(), | |
+ *(glyphs->marker()->marker), | |
+ glyphs->marker()->transform, | |
+ sym.get_opacity(), sym.comp_op()); | |
+ ren.render(*glyphs); | |
} | |
} | |
diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp | |
index 1234045..416e726 100644 | |
--- a/src/agg/process_text_symbolizer.cpp | |
+++ b/src/agg/process_text_symbolizer.cpp | |
@@ -21,12 +21,10 @@ | |
*****************************************************************************/ | |
// mapnik | |
-#include <mapnik/feature.hpp> | |
#include <mapnik/agg_renderer.hpp> | |
-#include <mapnik/agg_rasterizer.hpp> | |
-#include <mapnik/text/symbolizer_helpers.hpp> | |
#include <mapnik/graphics.hpp> | |
-#include <mapnik/text/face.hpp> | |
+#include <mapnik/text/symbolizer_helpers.hpp> | |
+#include <mapnik/text/renderer.hpp> | |
namespace mapnik { | |
@@ -36,28 +34,19 @@ void agg_renderer<T>::process(text_symbolizer const& sym, | |
proj_transform const& prj_trans) | |
{ | |
box2d<double> clip_box = clipping_extent(); | |
- text_symbolizer_helper<face_manager<freetype_engine>, | |
- label_collision_detector4> helper( | |
+ text_symbolizer_helper helper( | |
sym, feature, prj_trans, | |
- width_,height_, | |
+ width_, height_, | |
scale_factor_, | |
t_, font_manager_, *detector_, | |
clip_box); | |
- text_renderer<T> ren(*current_buffer_, | |
- font_manager_, | |
- sym.get_halo_rasterizer(), | |
- sym.comp_op(), | |
- scale_factor_); | |
+ agg_text_renderer<T> ren(*current_buffer_, sym.get_halo_rasterizer(), sym.comp_op(), scale_factor_, font_manager_.get_stroker()); | |
- while (helper.next()) | |
+ placements_list const& placements = helper.get(); | |
+ for (glyph_positions_ptr glyphs : placements) | |
{ | |
- placements_type const& placements = helper.placements(); | |
- for (unsigned int ii = 0; ii < placements.size(); ++ii) | |
- { | |
- ren.prepare_glyphs(placements[ii]); | |
- ren.render(placements[ii].center); | |
- } | |
+ ren.render(*glyphs); | |
} | |
} | |
diff --git a/src/box2d.cpp b/src/box2d.cpp | |
index 6f7001f..3af4d1d 100644 | |
--- a/src/box2d.cpp | |
+++ b/src/box2d.cpp | |
@@ -361,12 +361,37 @@ bool box2d<T>::valid() const | |
} | |
template <typename T> | |
+void box2d<T>::move(T x, T y) | |
+{ | |
+ minx_ += x; | |
+ maxx_ += x; | |
+ miny_ += y; | |
+ maxy_ += y; | |
+} | |
+ | |
+template <typename T> | |
box2d<T>& box2d<T>::operator+=(box2d<T> const& other) | |
{ | |
expand_to_include(other); | |
return *this; | |
} | |
+template <typename T> | |
+box2d<T> box2d<T>::operator+ (T other) const | |
+{ | |
+ return box2d<T>(minx_ - other, miny_ - other, maxx_ + other, maxy_ + other); | |
+} | |
+ | |
+template <typename T> | |
+box2d<T>& box2d<T>::operator+= (T other) | |
+{ | |
+ minx_ -= other; | |
+ miny_ -= other; | |
+ maxx_ += other; | |
+ maxy_ += other; | |
+ return *this; | |
+} | |
+ | |
template <typename T> | |
box2d<T>& box2d<T>::operator*=(T t) | |
diff --git a/src/build.py b/src/build.py | |
index a6f49b2..35e2cf7 100644 | |
--- a/src/build.py | |
+++ b/src/build.py | |
@@ -57,7 +57,7 @@ regex = 'boost_regex%s' % env['BOOST_APPEND'] | |
system = 'boost_system%s' % env['BOOST_APPEND'] | |
# clear out and re-set libs for this env | |
-lib_env['LIBS'] = ['freetype',env['ICU_LIB_NAME'],filesystem,system,regex] | |
+lib_env['LIBS'] = ['freetype',env['ICU_LIB_NAME'],filesystem,system,regex,'harfbuzz', 'harfbuzz-icu'] | |
if '-DMAPNIK_USE_PROJ4' in env['CPPDEFINES']: | |
lib_env['LIBS'].append('proj') | |
@@ -165,7 +165,6 @@ source = Split( | |
parse_transform.cpp | |
palette.cpp | |
path_expression_grammar.cpp | |
- text/placement_finder.cpp | |
plugin.cpp | |
point_symbolizer.cpp | |
polygon_pattern_symbolizer.cpp | |
@@ -183,7 +182,6 @@ source = Split( | |
memory_datasource.cpp | |
stroke.cpp | |
symbolizer.cpp | |
- text/symbolizer_helpers.cpp | |
unicode.cpp | |
markers_symbolizer.cpp | |
raster_colorizer.cpp | |
@@ -203,7 +201,16 @@ source = Split( | |
json/feature_parser.cpp | |
json/feature_collection_parser.cpp | |
json/geojson_generator.cpp | |
- text/processed_text.cpp | |
+ text/vertex_cache.cpp | |
+ text/layout.cpp | |
+ text/text_line.cpp | |
+ text/itemizer.cpp | |
+ text/scrptrun.cpp | |
+ text/face.cpp | |
+ text/placement_finder.cpp | |
+ text/renderer.cpp | |
+ text/symbolizer_helpers.cpp | |
+ text/text_properties.cpp | |
text/formatting/base.cpp | |
text/formatting/expression.cpp | |
text/formatting/list.cpp | |
@@ -215,7 +222,6 @@ source = Split( | |
text/placements/dummy.cpp | |
text/placements/list.cpp | |
text/placements/simple.cpp | |
- text/text_properties.cpp | |
xml_tree.cpp | |
config_error.cpp | |
color_factory.cpp | |
diff --git a/src/cairo_context.cpp b/src/cairo_context.cpp | |
index de6b8ff..7442c3e 100644 | |
--- a/src/cairo_context.cpp | |
+++ b/src/cairo_context.cpp | |
@@ -23,7 +23,6 @@ | |
#include <mapnik/cairo_context.hpp> | |
#include <mapnik/text/face.hpp> | |
#include <mapnik/text/text_properties.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
#include <mapnik/font_set.hpp> | |
#include <cairo-ft.h> | |
@@ -221,7 +220,7 @@ void cairo_context::set_line_width(double width) | |
check_object_status_and_throw_exception(*this); | |
} | |
-void cairo_context::set_dash(dash_array const& dashes, double scale_factor) | |
+void cairo_context::set_dash(dash_array const &dashes, double scale_factor) | |
{ | |
std::valarray<double> d(dashes.size() * 2); | |
dash_array::const_iterator itr = dashes.begin(); | |
@@ -234,7 +233,7 @@ void cairo_context::set_dash(dash_array const& dashes, double scale_factor) | |
d[index++] = itr->second * scale_factor; | |
} | |
- cairo_set_dash(cairo_.get() , &d[0], static_cast<int>(d.size()), 0/*offset*/); | |
+ cairo_set_dash(cairo_.get() , &d[0], d.size(), 0/*offset*/); | |
check_object_status_and_throw_exception(*this); | |
} | |
@@ -404,100 +403,80 @@ void cairo_context::restore() | |
check_object_status_and_throw_exception(*this); | |
} | |
-void cairo_context::show_glyph(unsigned long index, double x, double y) | |
+void cairo_context::show_glyph(unsigned long index, pixel_position const& pos) | |
{ | |
cairo_glyph_t glyph; | |
glyph.index = index; | |
- glyph.x = x; | |
- glyph.y = y; | |
+ glyph.x = pos.x; | |
+ glyph.y = pos.y; | |
cairo_show_glyphs(cairo_.get(), &glyph, 1); | |
check_object_status_and_throw_exception(*this); | |
} | |
-void cairo_context::glyph_path(unsigned long index, double x, double y) | |
+void cairo_context::glyph_path(unsigned long index, pixel_position const& pos) | |
{ | |
cairo_glyph_t glyph; | |
glyph.index = index; | |
- glyph.x = x; | |
- glyph.y = y; | |
+ glyph.x = pos.x; | |
+ glyph.y = pos.y; | |
cairo_glyph_path(cairo_.get(), &glyph, 1); | |
check_object_status_and_throw_exception(*this); | |
} | |
-void cairo_context::add_text(text_path const& path, | |
+void cairo_context::add_text(glyph_positions_ptr pos, | |
cairo_face_manager & manager, | |
face_manager<freetype_engine> & font_manager, | |
double scale_factor) | |
{ | |
- double sx = path.center.x; | |
- double sy = path.center.y; | |
+ pixel_position const& base = pos->get_base_point(); | |
- path.rewind(); | |
- | |
- for (std::size_t iii = 0; iii < path.num_nodes(); ++iii) | |
+ //Render halo | |
+ glyph_positions::const_iterator itr, end = pos->end(); | |
+ for (itr = pos->begin(); itr != end; itr++) | |
{ | |
- char_info_ptr c; | |
- double x, y, angle; | |
- | |
- path.vertex(c, x, y, angle); | |
- | |
- face_set_ptr faces = font_manager.get_face_set(c->format->face_name, c->format->fontset); | |
- double text_size = c->format->text_size * scale_factor; | |
- faces->set_character_sizes(text_size); | |
- | |
- glyph_ptr glyph = faces->get_glyph(c->c); | |
- | |
- if (glyph) | |
- { | |
- cairo_matrix_t matrix; | |
- matrix.xx = text_size * std::cos(angle); | |
- matrix.xy = text_size * std::sin(angle); | |
- matrix.yx = text_size * -std::sin(angle); | |
- matrix.yy = text_size * std::cos(angle); | |
- matrix.x0 = 0; | |
- matrix.y0 = 0; | |
- set_font_matrix(matrix); | |
- set_font_face(manager, glyph->get_face()); | |
- glyph_path(glyph->get_index(), sx + x, sy - y); | |
- set_line_width(2.0 * c->format->halo_radius * scale_factor); | |
- set_line_join(ROUND_JOIN); | |
- set_color(c->format->halo_fill); | |
- stroke(); | |
- } | |
+ glyph_info const& glyph = *(itr->glyph); | |
+ double text_size = glyph.format->text_size * scale_factor; | |
+ glyph.face->set_character_sizes(text_size); | |
+ | |
+ cairo_matrix_t matrix; | |
+ matrix.xx = text_size * itr->rot.cos; | |
+ matrix.xy = text_size * itr->rot.sin; | |
+ matrix.yx = text_size * -itr->rot.sin; | |
+ matrix.yy = text_size * itr->rot.cos; | |
+ matrix.x0 = 0; | |
+ matrix.y0 = 0; | |
+ | |
+ set_font_matrix(matrix); | |
+ set_font_face(manager, glyph.face); | |
+ | |
+ glyph_path(glyph.glyph_index, base + ~(itr->pos + glyph.offset.rotate(itr->rot))); | |
+ set_line_width(2.0 * glyph.format->halo_radius * scale_factor); | |
+ set_line_join(ROUND_JOIN); | |
+ set_color(glyph.format->halo_fill); | |
+ stroke(); | |
} | |
- | |
- path.rewind(); | |
- | |
- for (std::size_t iii = 0; iii < path.num_nodes(); ++iii) | |
+ //Render text | |
+ for (itr = pos->begin(); itr != end; itr++) | |
{ | |
- char_info_ptr c; | |
- double x, y, angle; | |
- | |
- path.vertex(c, x, y, angle); | |
- | |
- face_set_ptr faces = font_manager.get_face_set(c->format->face_name, c->format->fontset); | |
- double text_size = c->format->text_size * scale_factor; | |
- faces->set_character_sizes(text_size); | |
- | |
- glyph_ptr glyph = faces->get_glyph(c->c); | |
- | |
- if (glyph) | |
- { | |
- cairo_matrix_t matrix; | |
- matrix.xx = text_size * std::cos(angle); | |
- matrix.xy = text_size * std::sin(angle); | |
- matrix.yx = text_size * -std::sin(angle); | |
- matrix.yy = text_size * std::cos(angle); | |
- matrix.x0 = 0; | |
- matrix.y0 = 0; | |
- set_font_matrix(matrix); | |
- set_font_face(manager, glyph->get_face()); | |
- set_color(c->format->fill); | |
- show_glyph(glyph->get_index(), sx + x, sy - y); | |
- } | |
+ glyph_info const& glyph = *(itr->glyph); | |
+ double text_size = glyph.format->text_size * scale_factor; | |
+ glyph.face->set_character_sizes(text_size); | |
+ | |
+ cairo_matrix_t matrix; | |
+ matrix.xx = text_size * itr->rot.cos; | |
+ matrix.xy = text_size * itr->rot.sin; | |
+ matrix.yx = text_size * -itr->rot.sin; | |
+ matrix.yy = text_size * itr->rot.cos; | |
+ matrix.x0 = 0; | |
+ matrix.y0 = 0; | |
+ | |
+ set_font_matrix(matrix); | |
+ set_font_face(manager, glyph.face); | |
+ set_color(glyph.format->fill); | |
+ show_glyph(glyph.glyph_index, base + ~(itr->pos + glyph.offset.rotate(itr->rot))); | |
} | |
- | |
-} | |
} | |
+ | |
+} //ns mapnik | |
diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp | |
index 8e5f87b..08f6944 100644 | |
--- a/src/cairo_renderer.cpp | |
+++ b/src/cairo_renderer.cpp | |
@@ -49,7 +49,6 @@ | |
#include <mapnik/expression_evaluator.hpp> | |
#include <mapnik/warp.hpp> | |
#include <mapnik/config.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
#include <mapnik/vertex_converters.hpp> | |
#include <mapnik/marker_helpers.hpp> | |
#include <mapnik/noncopyable.hpp> | |
@@ -61,6 +60,7 @@ | |
#include <cairo-version.h> | |
// boost | |
+ | |
#include <boost/math/special_functions/round.hpp> | |
// agg | |
@@ -711,8 +711,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, | |
mapnik::feature_impl & feature, | |
proj_transform const& prj_trans) | |
{ | |
- shield_symbolizer_helper<face_manager<freetype_engine>, | |
- label_collision_detector4> helper( | |
+ text_symbolizer_helper helper( | |
sym, feature, prj_trans, | |
width_, height_, | |
scale_factor_, | |
@@ -721,21 +720,16 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, | |
cairo_save_restore guard(context_); | |
context_.set_operator(sym.comp_op()); | |
- while (helper.next()) | |
+ placements_list const& placements = helper.get(); | |
+ for (glyph_positions_ptr const& glyphs : placements) | |
{ | |
- placements_type const& placements = helper.placements(); | |
- for (unsigned int ii = 0; ii < placements.size(); ++ii) | |
+ if (glyphs->marker()) | |
{ | |
- pixel_position pos = helper.get_marker_position(placements[ii]); | |
- pos.x += 0.5 * helper.get_marker_width(); | |
- pos.y += 0.5 * helper.get_marker_height(); | |
- render_marker(pos, | |
- helper.get_marker(), | |
- helper.get_image_transform(), | |
- sym.get_opacity()); | |
- | |
- context_.add_text(placements[ii], face_manager_, font_manager_, scale_factor_); | |
+ render_marker(glyphs->marker_pos(), | |
+ *(glyphs->marker()->marker), glyphs->marker()->transform, | |
+ sym.get_opacity()); | |
} | |
+ context_.add_text(glyphs, face_manager_, font_manager_, scale_factor_); | |
} | |
} | |
@@ -1278,8 +1272,7 @@ void cairo_renderer_base::process(text_symbolizer const& sym, | |
mapnik::feature_impl & feature, | |
proj_transform const& prj_trans) | |
{ | |
- text_symbolizer_helper<face_manager<freetype_engine>, | |
- label_collision_detector4> helper( | |
+ text_symbolizer_helper helper( | |
sym, feature, prj_trans, | |
width_, height_, | |
scale_factor_, | |
@@ -1288,13 +1281,10 @@ void cairo_renderer_base::process(text_symbolizer const& sym, | |
cairo_save_restore guard(context_); | |
context_.set_operator(sym.comp_op()); | |
- while (helper.next()) | |
+ placements_list const& placements = helper.get(); | |
+ for (glyph_positions_ptr const& glyphs : placements) | |
{ | |
- placements_type const& placements = helper.placements(); | |
- for (unsigned int ii = 0; ii < placements.size(); ++ii) | |
- { | |
- context_.add_text(placements[ii], face_manager_, font_manager_, scale_factor_); | |
- } | |
+ context_.add_text(glyphs, face_manager_, font_manager_, scale_factor_); | |
} | |
} | |
diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp | |
index fd7994a..fcbbb53 100644 | |
--- a/src/font_engine_freetype.cpp | |
+++ b/src/font_engine_freetype.cpp | |
@@ -23,15 +23,10 @@ | |
// mapnik | |
#include <mapnik/debug.hpp> | |
#include <mapnik/font_engine_freetype.hpp> | |
-#include <mapnik/graphics.hpp> | |
-#include <mapnik/value_types.hpp> | |
#if defined(GRID_RENDERER) | |
-#include <mapnik/grid/grid.hpp> | |
#endif | |
-#include <mapnik/text/text_properties.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
#include <mapnik/pixel_position.hpp> | |
#include <mapnik/text/face.hpp> | |
#include <mapnik/util/fs.hpp> | |
@@ -41,20 +36,20 @@ | |
#include <boost/filesystem.hpp> | |
// stl | |
-#include <sstream> | |
#include <algorithm> | |
#include <stdexcept> | |
-// icu | |
-#include <unicode/ubidi.h> | |
-#include <unicode/ushape.h> | |
-#include <unicode/schriter.h> | |
-#include <unicode/uversion.h> | |
+// freetype2 | |
+extern "C" | |
+{ | |
+#include <ft2build.h> | |
+#include FT_FREETYPE_H | |
+#include FT_STROKER_H | |
+} | |
namespace mapnik | |
{ | |
- | |
freetype_engine::freetype_engine() : | |
library_(NULL) | |
@@ -267,6 +262,7 @@ face_ptr freetype_engine::create_face(std::string const& family_name) | |
return face_ptr(); | |
} | |
+ | |
stroker_ptr freetype_engine::create_stroker() | |
{ | |
FT_Stroker s; | |
@@ -278,440 +274,74 @@ stroker_ptr freetype_engine::create_stroker() | |
return stroker_ptr(); | |
} | |
-void font_face_set::add(face_ptr face) | |
-{ | |
- faces_.push_back(face); | |
- dimension_cache_.clear(); //Make sure we don't use old cached data | |
-} | |
-font_face_set::size_type font_face_set::size() const | |
-{ | |
- return faces_.size(); | |
-} | |
-glyph_ptr font_face_set::get_glyph(unsigned c) const | |
-{ | |
- for ( face_ptr const& face : faces_) | |
- { | |
- FT_UInt g = face->get_char(c); | |
- if (g) return std::make_shared<font_glyph>(face, g); | |
- } | |
- | |
- // Final fallback to empty square if nothing better in any font | |
- return std::make_shared<font_glyph>(*faces_.begin(), 0); | |
-} | |
- | |
-char_info font_face_set::character_dimensions(unsigned int c) | |
+template <typename T> | |
+face_ptr face_manager<T>::get_face(const std::string &name) | |
{ | |
- //Check if char is already in cache | |
- typedef std::map<unsigned, char_info>::const_iterator iterator_type; | |
- iterator_type itr = dimension_cache_.find(c); | |
- if (itr != dimension_cache_.end()) | |
+ face_ptr_cache_type::iterator itr; | |
+ itr = face_ptr_cache_.find(name); | |
+ if (itr != face_ptr_cache_.end()) | |
{ | |
return itr->second; | |
} | |
- | |
- FT_Vector pen; | |
- FT_Error error; | |
- | |
- pen.x = 0; | |
- pen.y = 0; | |
- | |
- FT_BBox glyph_bbox; | |
- FT_Glyph image; | |
- | |
- glyph_ptr glyph = get_glyph(c); | |
- FT_Face face = glyph->get_face()->get_face(); | |
- | |
- FT_Set_Transform(face, 0, &pen); | |
- | |
- error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING); | |
- if ( error ) | |
- return char_info(); | |
- | |
- error = FT_Get_Glyph(face->glyph, &image); | |
- if ( error ) | |
- 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; | |
- | |
- char_info dim(c, tempx, glyph_bbox.yMax, glyph_bbox.yMin, face->size->metrics.height/64.0); | |
- dimension_cache_.insert(std::make_pair(c, dim)); | |
- return dim; | |
-} | |
- | |
- | |
-void font_face_set::get_string_info(string_info & info, mapnik::value_unicode_string const& ustr, char_properties *format) | |
-{ | |
- double avg_height = character_dimensions('X').height(); | |
- UErrorCode err = U_ZERO_ERROR; | |
- mapnik::value_unicode_string reordered; | |
- mapnik::value_unicode_string shaped; | |
- | |
- int32_t length = ustr.length(); | |
- | |
- UBiDi *bidi = ubidi_openSized(length, 0, &err); | |
- ubidi_setPara(bidi, ustr.getBuffer(), length, UBIDI_DEFAULT_LTR, 0, &err); | |
- | |
- ubidi_writeReordered(bidi, reordered.getBuffer(length), | |
- length, UBIDI_DO_MIRRORING, &err); | |
- | |
- reordered.releaseBuffer(length); | |
- | |
- u_shapeArabic(reordered.getBuffer(), length, | |
- shaped.getBuffer(length), length, | |
- U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_NEAR | | |
- U_SHAPE_TEXT_DIRECTION_VISUAL_LTR, &err); | |
- | |
- shaped.releaseBuffer(length); | |
- | |
- if (U_SUCCESS(err)) { | |
- U_NAMESPACE_QUALIFIER StringCharacterIterator iter(shaped); | |
- for (iter.setToStart(); iter.hasNext();) { | |
- UChar ch = iter.nextPostInc(); | |
- char_info char_dim = character_dimensions(ch); | |
- char_dim.format = format; | |
- char_dim.avg_height = avg_height; | |
- info.add_info(char_dim); | |
- } | |
- } | |
- | |
- | |
-#if (U_ICU_VERSION_MAJOR_NUM*100 + U_ICU_VERSION_MINOR_NUM >= 406) | |
- if (ubidi_getBaseDirection(ustr.getBuffer(), length) == UBIDI_RTL) | |
+ else | |
{ | |
- info.set_rtl(true); | |
- } | |
-#endif | |
- | |
- ubidi_close(bidi); | |
-} | |
- | |
-void font_face_set::set_pixel_sizes(unsigned size) | |
-{ | |
- for ( face_ptr const& face : faces_) | |
- { | |
- face->set_pixel_sizes(size); | |
- } | |
-} | |
- | |
-void font_face_set::set_character_sizes(double size) | |
-{ | |
- for ( face_ptr const& face : faces_) | |
- { | |
- face->set_character_sizes(size); | |
- } | |
-} | |
- | |
- | |
-template <typename T> | |
-void composite_bitmap(T & pixmap, | |
- FT_Bitmap *bitmap, | |
- unsigned rgba, | |
- int x, | |
- int y, | |
- double opacity, | |
- composite_mode_e comp_op) | |
-{ | |
- int x_max=x+bitmap->width; | |
- int y_max=y+bitmap->rows; | |
- int i,p,j,q; | |
- | |
- for (i=x,p=0;i<x_max;++i,++p) | |
- { | |
- for (j=y,q=0;j<y_max;++j,++q) | |
+ face_ptr face = engine_.create_face(name); | |
+ if (face) | |
{ | |
- unsigned gray=bitmap->buffer[q*bitmap->width+p]; | |
- if (gray) | |
- { | |
- pixmap.composite_pixel(comp_op, i, j, rgba, gray, opacity); | |
- } | |
+ face_ptr_cache_.insert(make_pair(name,face)); | |
} | |
+ return face; | |
} | |
} | |
template <typename T> | |
-void render_halo(T & pixmap, | |
- FT_Bitmap *bitmap, | |
- unsigned rgba, | |
- int x1, | |
- int y1, | |
- double halo_radius, | |
- double opacity, | |
- composite_mode_e comp_op) | |
+face_set_ptr face_manager<T>::get_face_set(const std::string &name) | |
{ | |
- int width = bitmap->width; | |
- int height = bitmap->rows; | |
- int x, y; | |
- if (halo_radius < 1.0) | |
+ face_set_ptr face_set = std::make_shared<font_face_set>(); | |
+ if (face_ptr face = get_face(name)) | |
{ | |
- for (x=0; x < width; x++) | |
- { | |
- for (y=0; y < height; y++) | |
- { | |
- int gray = bitmap->buffer[y*bitmap->width+x]; | |
- if (gray) | |
- { | |
- pixmap.composite_pixel(comp_op, x+x1-1, y+y1-1, rgba, gray*halo_radius*halo_radius, opacity); | |
- pixmap.composite_pixel(comp_op, x+x1, y+y1-1, rgba, gray*halo_radius, opacity); | |
- pixmap.composite_pixel(comp_op, x+x1+1, y+y1-1, rgba, gray*halo_radius*halo_radius, opacity); | |
- | |
- pixmap.composite_pixel(comp_op, x+x1-1, y+y1, rgba, gray*halo_radius, opacity); | |
- pixmap.composite_pixel(comp_op, x+x1, y+y1, rgba, gray, opacity); | |
- pixmap.composite_pixel(comp_op, x+x1+1, y+y1, rgba, gray*halo_radius, opacity); | |
- | |
- pixmap.composite_pixel(comp_op, x+x1-1, y+y1+1, rgba, gray*halo_radius*halo_radius, opacity); | |
- pixmap.composite_pixel(comp_op, x+x1, y+y1+1, rgba, gray*halo_radius, opacity); | |
- pixmap.composite_pixel(comp_op, x+x1+1, y+y1+1, rgba, gray*halo_radius*halo_radius, opacity); | |
- } | |
- } | |
- } | |
- } else { | |
- for (x=0; x < width; x++) | |
- { | |
- for (y=0; y < height; y++) | |
- { | |
- int gray = bitmap->buffer[y*bitmap->width+x]; | |
- if (gray) | |
- { | |
- for (int n=-halo_radius; n <=halo_radius; ++n) | |
- for (int m=-halo_radius; m <= halo_radius; ++m) | |
- pixmap.composite_pixel(comp_op, x+x1+m, y+y1+n, rgba, gray, opacity); | |
- } | |
- } | |
- } | |
+ face_set->add(face); | |
} | |
+ return face_set; | |
} | |
template <typename T> | |
-void render_halo_id(T & pixmap, | |
- FT_Bitmap *bitmap, | |
- mapnik::value_integer feature_id, | |
- int x1, | |
- int y1, | |
- int halo_radius) | |
+face_set_ptr face_manager<T>::get_face_set(const font_set &fset) | |
{ | |
- int width = bitmap->width; | |
- int height = bitmap->rows; | |
- int x, y; | |
- for (x=0; x < width; x++) | |
+ std::vector<std::string> const& names = fset.get_face_names(); | |
+ face_set_ptr face_set = std::make_shared<font_face_set>(); | |
+ for (std::vector<std::string>::const_iterator name = names.begin(); name != names.end(); ++name) | |
{ | |
- for (y=0; y < height; y++) | |
+ face_ptr face = get_face(*name); | |
+ if (face) | |
{ | |
- int gray = bitmap->buffer[y*bitmap->width+x]; | |
- if (gray) | |
- { | |
- for (int n=-halo_radius; n <=halo_radius; ++n) | |
- for (int m=-halo_radius; m <= halo_radius; ++m) | |
- pixmap.setPixel(x+x1+m,y+y1+n,feature_id); | |
- } | |
+ face_set->add(face); | |
} | |
- } | |
-} | |
- | |
-template <typename T> | |
-text_renderer<T>::text_renderer(pixmap_type & pixmap, | |
- face_manager<freetype_engine> & font_manager, | |
- halo_rasterizer_e rasterizer, | |
- composite_mode_e comp_op, | |
- double scale_factor) | |
- : pixmap_(pixmap), | |
- font_manager_(font_manager), | |
- rasterizer_(rasterizer), | |
- comp_op_(comp_op), | |
- scale_factor_(scale_factor) {} | |
- | |
-template <typename T> | |
-box2d<double> text_renderer<T>::prepare_glyphs(text_path const& 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 (std::size_t i = 0; i < path.num_nodes(); ++i) | |
- { | |
- char_info_ptr c; | |
- double x, y, angle; | |
- | |
- path.vertex(c, x, y, angle); | |
- | |
- // TODO Enable when we have support for setting verbosity | |
- // MAPNIK_LOG_DEBUG(font_engine_freetype) << "text_renderer: prepare_glyphs=" | |
- // << c << "," << x << "," << y << "," << angle; | |
- | |
- 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(c->format->face_name, c->format->fontset); | |
- faces->set_character_sizes(c->format->text_size*scale_factor_); | |
- | |
- glyph_ptr glyph = faces->get_glyph(unsigned(c->c)); | |
- FT_Face face = glyph->get_face()->get_face(); | |
- | |
- matrix.xx = (FT_Fixed)( std::cos( angle ) * 0x10000L ); | |
- matrix.xy = (FT_Fixed)(-std::sin( angle ) * 0x10000L ); | |
- matrix.yx = (FT_Fixed)( std::sin( angle ) * 0x10000L ); | |
- matrix.yy = (FT_Fixed)( std::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 ) | |
+#ifdef MAPNIK_LOG | |
+ else | |
{ | |
- bbox.xMin = 0; | |
- bbox.yMin = 0; | |
- bbox.xMax = 0; | |
- bbox.yMax = 0; | |
+ MAPNIK_LOG_DEBUG(font_engine_freetype) | |
+ << "Failed to find face '" << *name | |
+ << "' in font set '" << fset.get_name() << "'\n"; | |
} | |
- | |
- // take ownership of the glyph | |
- glyphs_.push_back(new glyph_t(image, c->format)); | |
+#endif | |
} | |
- | |
- return box2d<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); | |
+ return face_set; | |
} | |
template <typename T> | |
-void text_renderer<T>::render(pixel_position const& pos) | |
+face_set_ptr face_manager<T>::get_face_set(const std::string &name, boost::optional<font_set> fset) | |
{ | |
- FT_Error error; | |
- FT_Vector start; | |
- int height = pixmap_.height(); | |
- | |
- start.x = static_cast<FT_Pos>(pos.x * (1 << 6)); | |
- start.y = static_cast<FT_Pos>((height - pos.y) * (1 << 6)); | |
- | |
- // now render transformed glyphs | |
- typename glyphs_t::iterator itr; | |
- for (itr = glyphs_.begin(); itr != glyphs_.end(); ++itr) | |
+ if (fset && fset->size() > 0) | |
{ | |
- double halo_radius = itr->properties->halo_radius * scale_factor_; | |
- //make sure we've got reasonable values. | |
- if (halo_radius <= 0.0 || halo_radius > 1024.0) continue; | |
- FT_Glyph g; | |
- error = FT_Glyph_Copy(itr->image, &g); | |
- if (!error) | |
- { | |
- FT_Glyph_Transform(g,0,&start); | |
- if (rasterizer_ == HALO_RASTERIZER_FULL) | |
- { | |
- stroker_ptr stk = font_manager_.get_stroker(); | |
- stk->init(halo_radius); | |
- FT_Glyph_Stroke(&g,stk->get(),1); | |
- error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); | |
- if (!error) | |
- { | |
- FT_BitmapGlyph bit = (FT_BitmapGlyph)g; | |
- composite_bitmap(pixmap_, | |
- &bit->bitmap, | |
- itr->properties->halo_fill.rgba(), | |
- bit->left, | |
- height - bit->top, | |
- itr->properties->text_opacity, | |
- comp_op_); | |
- } | |
- } | |
- else | |
- { | |
- error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); | |
- if (!error) | |
- { | |
- FT_BitmapGlyph bit = (FT_BitmapGlyph)g; | |
- render_halo(pixmap_, | |
- &bit->bitmap, | |
- itr->properties->halo_fill.rgba(), | |
- bit->left, | |
- height - bit->top, | |
- halo_radius, | |
- itr->properties->text_opacity, | |
- comp_op_); | |
- } | |
- } | |
- } | |
- FT_Done_Glyph(g); | |
+ return get_face_set(*fset); | |
} | |
- //render actual text | |
- for (itr = glyphs_.begin(); itr != glyphs_.end(); ++itr) | |
+ else | |
{ | |
- | |
- FT_Glyph_Transform(itr->image,0,&start); | |
- | |
- error = FT_Glyph_To_Bitmap( &(itr->image),FT_RENDER_MODE_NORMAL,0,1); | |
- if ( ! error ) | |
- { | |
- | |
- FT_BitmapGlyph bit = (FT_BitmapGlyph)itr->image; | |
- composite_bitmap(pixmap_, | |
- &bit->bitmap, | |
- itr->properties->fill.rgba(), | |
- bit->left, | |
- height - bit->top, | |
- itr->properties->text_opacity, | |
- comp_op_ | |
- ); | |
- } | |
- } | |
-} | |
- | |
+ return get_face_set(name); | |
#if defined(GRID_RENDERER) | |
-template <typename T> | |
-void text_renderer<T>::render_id(mapnik::value_integer feature_id, | |
- pixel_position const& pos) | |
-{ | |
- FT_Error error; | |
- FT_Vector start; | |
- unsigned height = pixmap_.height(); | |
- | |
- start.x = static_cast<FT_Pos>(pos.x * (1 << 6)); | |
- start.y = static_cast<FT_Pos>((height - pos.y) * (1 << 6)); | |
- | |
- // now render transformed glyphs | |
- typename glyphs_t::iterator itr; | |
- for (itr = glyphs_.begin(); itr != glyphs_.end(); ++itr) | |
- { | |
- FT_Glyph_Transform(itr->image,0,&start); | |
- error = FT_Glyph_To_Bitmap( &(itr->image),FT_RENDER_MODE_NORMAL,0,1); | |
- if ( ! error ) | |
- { | |
- FT_BitmapGlyph bit = (FT_BitmapGlyph)itr->image; | |
- render_halo_id(pixmap_, | |
- &bit->bitmap, | |
- feature_id, | |
- bit->left, | |
- height - bit->top, | |
- static_cast<int>(itr->properties->halo_radius)); | |
- } | |
} | |
} | |
#endif | |
@@ -722,20 +352,8 @@ std::mutex freetype_engine::mutex_; | |
std::map<std::string,std::pair<int,std::string> > freetype_engine::name2file_; | |
std::map<std::string,std::string> freetype_engine::memory_fonts_; | |
-template text_renderer<image_32>::text_renderer(image_32&, | |
- face_manager<freetype_engine>&, | |
- halo_rasterizer_e, | |
- composite_mode_e, | |
- double); | |
-template box2d<double>text_renderer<image_32>::prepare_glyphs(text_path const&); | |
-template void text_renderer<image_32>::render(pixel_position const&); | |
+template class face_manager<freetype_engine>; | |
+ | |
#if defined(GRID_RENDERER) | |
-template void text_renderer<grid>::render_id(mapnik::value_integer, | |
- pixel_position const&); | |
-template text_renderer<grid>::text_renderer(grid&, | |
- face_manager<freetype_engine>&, | |
- halo_rasterizer_e, | |
- composite_mode_e, double); | |
-template box2d<double>text_renderer<grid>::prepare_glyphs(text_path const& ); | |
#endif | |
} | |
diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp | |
index 7f4385a..c88b80e 100644 | |
--- a/src/grid/process_shield_symbolizer.cpp | |
+++ b/src/grid/process_shield_symbolizer.cpp | |
@@ -22,14 +22,9 @@ | |
*****************************************************************************/ | |
// mapnik | |
-#include <mapnik/feature.hpp> | |
-#include <mapnik/grid/grid_rasterizer.hpp> | |
#include <mapnik/grid/grid_renderer.hpp> | |
-#include <mapnik/grid/grid_renderer_base.hpp> | |
-#include <mapnik/grid/grid.hpp> | |
#include <mapnik/text/symbolizer_helpers.hpp> | |
-#include <mapnik/pixel_position.hpp> | |
-#include <mapnik/text/face.hpp> | |
+#include <mapnik/text/renderer.hpp> | |
// agg | |
#include "agg_trans_affine.h" | |
@@ -41,50 +36,28 @@ void grid_renderer<T>::process(shield_symbolizer const& sym, | |
mapnik::feature_impl & feature, | |
proj_transform const& prj_trans) | |
{ | |
- shield_symbolizer_helper<face_manager<freetype_engine>, | |
- label_collision_detector4> helper( | |
+ text_symbolizer_helper helper( | |
sym, feature, prj_trans, | |
width_, height_, | |
- scale_factor_, | |
+ scale_factor_ * (1.0/pixmap_.get_resolution()), | |
t_, font_manager_, *detector_, | |
query_extent_); | |
- bool placement_found = false; | |
- text_renderer<T> ren(pixmap_, | |
- font_manager_, | |
- sym.get_halo_rasterizer(), | |
- sym.comp_op(), | |
- scale_factor_); | |
+ grid_text_renderer<T> ren(pixmap_, sym.comp_op(), scale_factor_); | |
- text_placement_info_ptr placement; | |
- while (helper.next()) | |
+ placements_list const& placements = helper.get(); | |
+ if (placements.empty()) return; | |
+ for (glyph_positions_ptr glyphs : placements) | |
{ | |
- placement_found = true; | |
- placements_type const& placements = helper.placements(); | |
- for (unsigned int ii = 0; ii < placements.size(); ++ii) | |
- { | |
- // get_marker_position returns (minx,miny) corner position, | |
- // while (currently only) agg_renderer::render_marker newly | |
- // expects center position; | |
- // until all renderers and shield_symbolizer_helper are | |
- // modified accordingly, we must adjust the position here | |
- pixel_position pos = helper.get_marker_position(placements[ii]); | |
- pos.x += 0.5 * helper.get_marker_width(); | |
- pos.y += 0.5 * helper.get_marker_height(); | |
- render_marker(feature, | |
- pixmap_.get_resolution(), | |
- pos, | |
- helper.get_marker(), | |
- helper.get_image_transform(), | |
- sym.get_opacity(), | |
- sym.comp_op()); | |
- | |
- ren.prepare_glyphs(placements[ii]); | |
- ren.render_id(feature.id(), placements[ii].center); | |
- } | |
+ if (glyphs->marker()->marker) | |
+ render_marker(feature, pixmap_.get_resolution(), | |
+ glyphs->marker_pos(), | |
+ *(glyphs->marker()->marker), | |
+ glyphs->marker()->transform, | |
+ sym.get_opacity(), sym.comp_op()); | |
+ ren.render(*glyphs, feature.id()); | |
} | |
- if (placement_found) | |
- pixmap_.add_feature(feature); | |
+ pixmap_.add_feature(feature); | |
} | |
template void grid_renderer<grid>::process(shield_symbolizer const&, | |
diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp | |
index 39a6826..1d3c4fd 100644 | |
--- a/src/grid/process_text_symbolizer.cpp | |
+++ b/src/grid/process_text_symbolizer.cpp | |
@@ -21,10 +21,9 @@ | |
*****************************************************************************/ | |
// mapnik | |
-#include <mapnik/feature.hpp> | |
#include <mapnik/grid/grid_renderer.hpp> | |
#include <mapnik/text/symbolizer_helpers.hpp> | |
-#include <mapnik/text/face.hpp> | |
+#include <mapnik/text/renderer.hpp> | |
namespace mapnik { | |
@@ -33,32 +32,22 @@ void grid_renderer<T>::process(text_symbolizer const& sym, | |
mapnik::feature_impl & feature, | |
proj_transform const& prj_trans) | |
{ | |
- text_symbolizer_helper<face_manager<freetype_engine>, | |
- label_collision_detector4> helper( | |
+ text_symbolizer_helper helper( | |
sym, feature, prj_trans, | |
width_, height_, | |
scale_factor_ * (1.0/pixmap_.get_resolution()), | |
t_, font_manager_, *detector_, | |
query_extent_); | |
- bool placement_found = false; | |
- text_renderer<T> ren(pixmap_, | |
- font_manager_, | |
- sym.get_halo_rasterizer(), | |
- sym.comp_op(), | |
- scale_factor_); | |
+ grid_text_renderer<T> ren(pixmap_, sym.comp_op(), scale_factor_); | |
- while (helper.next()) { | |
- placement_found = true; | |
- placements_type const& placements = helper.placements(); | |
- for (unsigned int ii = 0; ii < placements.size(); ++ii) | |
- { | |
- ren.prepare_glyphs(placements[ii]); | |
- ren.render_id(feature.id(), placements[ii].center); | |
- } | |
+ placements_list const& placements = helper.get(); | |
+ if (!placements.size()) return; | |
+ for (glyph_positions_ptr glyphs : placements) | |
+ { | |
+ ren.render(*glyphs, feature.id()); | |
} | |
- if (placement_found) pixmap_.add_feature(feature); | |
- | |
+ pixmap_.add_feature(feature); | |
} | |
template void grid_renderer<grid>::process(text_symbolizer const&, | |
@@ -66,4 +55,3 @@ template void grid_renderer<grid>::process(text_symbolizer const&, | |
proj_transform const&); | |
} | |
- | |
diff --git a/src/load_map.cpp b/src/load_map.cpp | |
index b491a44..c3c771c 100644 | |
--- a/src/load_map.cpp | |
+++ b/src/load_map.cpp | |
@@ -1192,10 +1192,10 @@ void map_parser::parse_text_symbolizer(rule & rule, xml_node const& sym) | |
placement_finder = std::make_shared<text_placements_dummy>(); | |
placement_finder->defaults.from_xml(sym, fontsets_); | |
} | |
- if (strict_ && | |
- !placement_finder->defaults.format.fontset) | |
+ if (strict_ && (!placement_finder->defaults.format->fontset || | |
+ !placement_finder->defaults.format->fontset->size())) | |
{ | |
- ensure_font_face(placement_finder->defaults.format.face_name); | |
+ ensure_font_face(placement_finder->defaults.format->face_name); | |
} | |
text_symbolizer text_symbol = text_symbolizer(placement_finder); | |
@@ -1225,9 +1225,10 @@ void map_parser::parse_shield_symbolizer(rule & rule, xml_node const& sym) | |
} | |
placement_finder->defaults.from_xml(sym, fontsets_); | |
if (strict_ && | |
- !placement_finder->defaults.format.fontset) | |
+ (!placement_finder->defaults.format->fontset || | |
+ !placement_finder->defaults.format->fontset->size())) | |
{ | |
- ensure_font_face(placement_finder->defaults.format.face_name); | |
+ ensure_font_face(placement_finder->defaults.format->face_name); | |
} | |
shield_symbolizer shield_symbol = shield_symbolizer(placement_finder); | |
diff --git a/src/save_map.cpp b/src/save_map.cpp | |
index 1f8bb85..b56ce02 100644 | |
--- a/src/save_map.cpp | |
+++ b/src/save_map.cpp | |
@@ -228,19 +228,19 @@ public: | |
set_attr(sym_node, "unlock-image", sym.get_unlock_image()); | |
} | |
- if (sym.get_placement_options()->defaults.format.text_opacity != | |
- dfl.get_placement_options()->defaults.format.text_opacity || explicit_defaults_) | |
+ if (sym.get_placement_options()->defaults.format->text_opacity != | |
+ dfl.get_placement_options()->defaults.format->text_opacity || explicit_defaults_) | |
{ | |
- set_attr(sym_node, "text-opacity", sym.get_placement_options()->defaults.format.text_opacity); | |
+ set_attr(sym_node, "text-opacity", sym.get_placement_options()->defaults.format->text_opacity); | |
} | |
- position displacement = sym.get_shield_displacement(); | |
- if (displacement.first != dfl.get_shield_displacement().first || explicit_defaults_) | |
+ pixel_position displacement = sym.get_shield_displacement(); | |
+ if (displacement.x != dfl.get_shield_displacement().x || explicit_defaults_) | |
{ | |
- set_attr(sym_node, "shield-dx", displacement.first); | |
+ set_attr(sym_node, "shield-dx", displacement.x); | |
} | |
- if (displacement.second != dfl.get_shield_displacement().second || explicit_defaults_) | |
+ if (displacement.y != dfl.get_shield_displacement().y || explicit_defaults_) | |
{ | |
- set_attr(sym_node, "shield-dy", displacement.second); | |
+ set_attr(sym_node, "shield-dy", displacement.y); | |
} | |
if (sym.get_image_transform()) | |
{ | |
diff --git a/src/shield_symbolizer.cpp b/src/shield_symbolizer.cpp | |
index 8ef650f..c682e4c 100644 | |
--- a/src/shield_symbolizer.cpp | |
+++ b/src/shield_symbolizer.cpp | |
@@ -76,10 +76,10 @@ bool shield_symbolizer::get_unlock_image() const | |
void shield_symbolizer::set_shield_displacement(double shield_dx,double shield_dy) | |
{ | |
- shield_displacement_ = std::make_pair(shield_dx, shield_dy); | |
+ shield_displacement_.set(shield_dx, shield_dy); | |
} | |
-position const& shield_symbolizer::get_shield_displacement() const | |
+pixel_position const& shield_symbolizer::get_shield_displacement() const | |
{ | |
return shield_displacement_; | |
} | |
diff --git a/src/text/formatting/expression.cpp b/src/text/formatting/expression.cpp | |
index f8a8856..c351af7 100644 | |
--- a/src/text/formatting/expression.cpp | |
+++ b/src/text/formatting/expression.cpp | |
@@ -27,10 +27,12 @@ | |
#include <mapnik/expression_string.hpp> | |
#include <mapnik/expression_evaluator.hpp> | |
#include <mapnik/text/text_properties.hpp> | |
+#include <mapnik/color_factory.hpp> | |
#include <mapnik/feature.hpp> | |
#include <mapnik/xml_node.hpp> | |
-// boost | |
+//boost | |
+ | |
#include <boost/property_tree/ptree.hpp> | |
@@ -46,7 +48,6 @@ void expression_format::to_xml(boost::property_tree::ptree &xml) const | |
if (character_spacing) set_attr(new_node, "character-spacing", to_expression_string(*character_spacing)); | |
if (line_spacing) set_attr(new_node, "line-spacing", to_expression_string(*line_spacing)); | |
if (text_opacity) set_attr(new_node, "opacity", to_expression_string(*text_opacity)); | |
- if (wrap_before) set_attr(new_node, "wrap-before", to_expression_string(*wrap_before)); | |
if (wrap_char) set_attr(new_node, "wrap-character", to_expression_string(*wrap_char)); | |
if (fill) set_attr(new_node, "fill", to_expression_string(*fill)); | |
if (halo_fill) set_attr(new_node, "halo-fill", to_expression_string(*halo_fill)); | |
@@ -67,7 +68,6 @@ node_ptr expression_format::from_xml(xml_node const& xml) | |
n->character_spacing = get_expression(xml, "character-spacing"); | |
n->line_spacing = get_expression(xml, "line-spacing"); | |
n->text_opacity = get_expression(xml, "opacity"); | |
- n->wrap_before = get_expression(xml, "wrap-before"); | |
n->wrap_char = get_expression(xml, "wrap-character"); | |
n->fill = get_expression(xml, "fill"); | |
n->halo_fill = get_expression(xml, "halo-fill"); | |
@@ -83,28 +83,26 @@ expression_ptr expression_format::get_expression(xml_node const& xml, std::strin | |
} | |
-void expression_format::apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+void expression_format::apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
- char_properties new_properties = p; | |
- if (face_name) new_properties.face_name = | |
+ char_properties_ptr new_properties = std::make_shared<char_properties>(*p); | |
+ if (face_name) new_properties->face_name = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *face_name).to_string(); | |
- if (text_size) new_properties.text_size = | |
+ if (text_size) new_properties->text_size = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *text_size).to_double(); | |
- if (character_spacing) new_properties.character_spacing = | |
+ if (character_spacing) new_properties->character_spacing = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *character_spacing).to_double(); | |
- if (line_spacing) new_properties.line_spacing = | |
+ if (line_spacing) new_properties->line_spacing = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *line_spacing).to_double(); | |
- if (text_opacity) new_properties.text_opacity = | |
+ if (text_opacity) new_properties->text_opacity = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *text_opacity).to_double(); | |
- if (wrap_before) new_properties.wrap_before = | |
- boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *wrap_before).to_bool(); | |
- if (wrap_char) new_properties.wrap_char = | |
+ if (wrap_char) new_properties->wrap_char = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *character_spacing).to_unicode()[0]; | |
-// if (fill) new_properties.fill = | |
-// boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *fill).to_color(); | |
-// if (halo_fill) new_properties.halo_fill = | |
-// boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_fill).to_color(); | |
- if (halo_radius) new_properties.halo_radius = | |
+ if (fill) new_properties->fill = parse_color( | |
+ boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *fill).to_string()); | |
+ if (halo_fill) new_properties->halo_fill = parse_color( | |
+ boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_fill).to_string()); | |
+ if (halo_radius) new_properties->halo_radius = | |
boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *halo_radius).to_double(); | |
if (child_) { | |
@@ -134,7 +132,6 @@ void expression_format::add_expressions(expression_set &output) const | |
output.insert(character_spacing); | |
output.insert(line_spacing); | |
output.insert(text_opacity); | |
- output.insert(wrap_before); | |
output.insert(wrap_char); | |
output.insert(fill); | |
output.insert(halo_fill); | |
diff --git a/src/text/formatting/format.cpp b/src/text/formatting/format.cpp | |
index 812614e..998b0f3 100644 | |
--- a/src/text/formatting/format.cpp | |
+++ b/src/text/formatting/format.cpp | |
@@ -27,7 +27,8 @@ | |
#include <mapnik/ptree_helpers.hpp> | |
#include <mapnik/xml_node.hpp> | |
-// boost | |
+//boost | |
+ | |
#include <boost/property_tree/ptree.hpp> | |
namespace mapnik { | |
@@ -67,8 +68,6 @@ node_ptr format_node::from_xml(xml_node const& xml) | |
n->character_spacing = xml.get_opt_attr<double>("character-spacing"); | |
n->line_spacing = xml.get_opt_attr<double>("line-spacing"); | |
n->text_opacity = xml.get_opt_attr<double>("opacity"); | |
- boost::optional<boolean> wrap = xml.get_opt_attr<boolean>("wrap-before"); | |
- if (wrap) n->wrap_before = *wrap; | |
n->wrap_char = xml.get_opt_attr<unsigned>("wrap-character"); | |
n->text_transform = xml.get_opt_attr<text_transform_e>("text-transform"); | |
n->fill = xml.get_opt_attr<color>("fill"); | |
@@ -78,20 +77,19 @@ node_ptr format_node::from_xml(xml_node const& xml) | |
} | |
-void format_node::apply(char_properties const& p, const feature_impl &feature, processed_text &output) const | |
+void format_node::apply(char_properties_ptr p, const feature_impl &feature, text_layout &output) const | |
{ | |
- char_properties new_properties = p; | |
- if (face_name) new_properties.face_name = *face_name; | |
- if (text_size) new_properties.text_size = *text_size; | |
- if (character_spacing) new_properties.character_spacing = *character_spacing; | |
- if (line_spacing) new_properties.line_spacing = *line_spacing; | |
- if (text_opacity) new_properties.text_opacity = *text_opacity; | |
- if (wrap_before) new_properties.wrap_before = *wrap_before; | |
- if (wrap_char) new_properties.wrap_char = *wrap_char; | |
- if (text_transform) new_properties.text_transform = *text_transform; | |
- if (fill) new_properties.fill = *fill; | |
- if (halo_fill) new_properties.halo_fill = *halo_fill; | |
- if (halo_radius) new_properties.halo_radius = *halo_radius; | |
+ char_properties_ptr new_properties = std::make_shared<char_properties>(*p); | |
+ if (face_name) new_properties->face_name = *face_name; | |
+ if (text_size) new_properties->text_size = *text_size; | |
+ if (character_spacing) new_properties->character_spacing = *character_spacing; | |
+ if (line_spacing) new_properties->line_spacing = *line_spacing; | |
+ if (text_opacity) new_properties->text_opacity = *text_opacity; | |
+ if (wrap_char) new_properties->wrap_char = *wrap_char; | |
+ if (text_transform) new_properties->text_transform = *text_transform; | |
+ if (fill) new_properties->fill = *fill; | |
+ if (halo_fill) new_properties->halo_fill = *halo_fill; | |
+ if (halo_radius) new_properties->halo_radius = *halo_radius; | |
if (child_) { | |
child_->apply(new_properties, feature, output); | |
diff --git a/src/text/formatting/list.cpp b/src/text/formatting/list.cpp | |
index 77d3a57..0aa39d3 100644 | |
--- a/src/text/formatting/list.cpp | |
+++ b/src/text/formatting/list.cpp | |
@@ -32,7 +32,6 @@ namespace mapnik { | |
using boost::property_tree::ptree; | |
namespace formatting { | |
-/************************************************************/ | |
void list_node::to_xml(boost::property_tree::ptree & xml) const | |
{ | |
@@ -43,7 +42,7 @@ void list_node::to_xml(boost::property_tree::ptree & xml) const | |
} | |
-void list_node::apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+void list_node::apply(char_properties_ptr p, feature_impl const& feature, text_layout & output) const | |
{ | |
for (node_ptr const& node : children_) | |
{ | |
diff --git a/src/text/formatting/text.cpp b/src/text/formatting/text.cpp | |
index 0188622..db15545 100644 | |
--- a/src/text/formatting/text.cpp | |
+++ b/src/text/formatting/text.cpp | |
@@ -25,13 +25,16 @@ | |
#include <mapnik/expression_evaluator.hpp> | |
#include <mapnik/feature.hpp> | |
#include <mapnik/text/text_properties.hpp> | |
-#include <mapnik/text/processed_text.hpp> | |
#include <mapnik/xml_node.hpp> | |
-#include <mapnik/value_types.hpp> | |
+#include <mapnik/text/layout.hpp> | |
+ | |
+//boost | |
+ | |
// boost | |
#include <boost/property_tree/ptree.hpp> | |
+ | |
namespace mapnik | |
{ | |
namespace formatting | |
@@ -51,26 +54,26 @@ node_ptr text_node::from_xml(xml_node const& xml) | |
return std::make_shared<text_node>(xml.get_value<expression_ptr>()); | |
} | |
-void text_node::apply(char_properties const& p, feature_impl const& feature, processed_text &output) const | |
+void text_node::apply(char_properties_ptr p, feature_impl const& feature, text_layout &output) const | |
{ | |
mapnik::value_unicode_string text_str = boost::apply_visitor(evaluate<feature_impl,value_type>(feature), *text_).to_unicode(); | |
- if (p.text_transform == UPPERCASE) | |
+ if (p->text_transform == UPPERCASE) | |
{ | |
text_str = text_str.toUpper(); | |
} | |
- else if (p.text_transform == LOWERCASE) | |
+ else if (p->text_transform == LOWERCASE) | |
{ | |
text_str = text_str.toLower(); | |
} | |
#if !UCONFIG_NO_BREAK_ITERATION | |
- else if (p.text_transform == CAPITALIZE) | |
+ else if (p->text_transform == CAPITALIZE) | |
{ | |
// note: requires BreakIterator support in ICU which is optional | |
text_str = text_str.toTitle(NULL); | |
} | |
#endif | |
if (text_str.length() > 0) { | |
- output.push_back(p, text_str); | |
+ output.add_text(text_str, p); | |
} | |
} | |
diff --git a/src/text/placement_finder.cpp b/src/text/placement_finder.cpp | |
index 404c94b..86b8617 100644 | |
--- a/src/text/placement_finder.cpp | |
+++ b/src/text/placement_finder.cpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2011 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
@@ -19,294 +19,147 @@ | |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
* | |
*****************************************************************************/ | |
- | |
//mapnik | |
#include <mapnik/debug.hpp> | |
-#include <mapnik/feature.hpp> | |
+#include <mapnik/label_collision_detector.hpp> | |
+#include <mapnik/ctrans.hpp> | |
+#include <mapnik/expression_evaluator.hpp> | |
#include <mapnik/text/placement_finder.hpp> | |
-#include <mapnik/geometry.hpp> | |
-#include <mapnik/text/text_path.hpp> | |
-#include <mapnik/fastmath.hpp> | |
-#include <mapnik/text/placements/base.hpp> | |
+#include <mapnik/text/layout.hpp> | |
+#include <mapnik/text/text_properties.hpp> | |
+#include <mapnik/text/placements_list.hpp> | |
+#include <mapnik/text/vertex_cache.hpp> | |
// agg | |
-#include "agg_path_length.h" | |
- | |
-// boost | |
-#include <memory> | |
-#include <boost/tuple/tuple.hpp> | |
+#include "agg_conv_clip_polyline.h" | |
- | |
-//stl | |
-#include <string> | |
+// stl | |
#include <vector> | |
-#include <cmath> | |
- | |
-#ifndef M_PI | |
-#define M_PI 3.14159265358979323846 | |
-#endif | |
namespace mapnik | |
{ | |
-template<typename T> | |
-std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path) | |
+class tolerance_iterator | |
{ | |
- double x1 = 0.0; | |
- double y1 = 0.0; | |
- double x2 = 0.0; | |
- double y2 = 0.0; | |
- double distance = 0.0; | |
- bool first = true; | |
- unsigned cmd; | |
- double x = 0.0; | |
- double y = 0.0; | |
- shape_path.rewind(0); | |
- while (!agg::is_stop(cmd = shape_path.vertex(&x2,&y2))) | |
+public: | |
+ tolerance_iterator(double label_position_tolerance, double spacing) | |
+ : tolerance_(label_position_tolerance > 0 ? | |
+ label_position_tolerance : spacing/2.0), | |
+ tolerance_delta_(std::max(1.0, tolerance_/100.0)), | |
+ value_(0), | |
+ initialized_(false), | |
+ values_tried_(0) | |
{ | |
- if (first || agg::is_move_to(cmd)) | |
- { | |
- first = false; | |
- } | |
- else | |
- { | |
- double dx = x2-x1; | |
- double dy = y2-y1; | |
- | |
- double segment_length = std::sqrt(dx*dx + dy*dy); | |
- distance +=segment_length; | |
- | |
- if (distance > target_distance) | |
- { | |
- x = x2 - dx * (distance - target_distance)/segment_length; | |
- y = y2 - dy * (distance - target_distance)/segment_length; | |
- break; | |
- } | |
- } | |
- x1 = x2; | |
- y1 = y2; | |
} | |
- return std::pair<double, double>(x, y); | |
-} | |
- | |
-template<typename T> | |
-double get_total_distance(T & shape_path) | |
-{ | |
- return agg::path_length(shape_path); | |
-} | |
- | |
-template <typename DetectorT> | |
-placement_finder<DetectorT>::placement_finder(text_placement_info const& placement_info, | |
- string_info const& info, | |
- DetectorT & detector, | |
- box2d<double> const& extent) | |
- : detector_(detector), | |
- 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_(), | |
- collect_extents_(false) | |
-{ | |
- init_string_size(); | |
- init_alignment(); | |
-} | |
- | |
-template <typename DetectorT> | |
-template <typename T> | |
-void placement_finder<DetectorT>::find_point_placements(T & shape_path) | |
-{ | |
- unsigned cmd; | |
- double new_x = 0.0; | |
- double new_y = 0.0; | |
- double old_x = 0.0; | |
- double old_y = 0.0; | |
- bool first = true; | |
- double total_distance = get_total_distance<T>(shape_path); | |
- shape_path.rewind(0); | |
- | |
- if (total_distance == 0) //Point data, not a line | |
+ ~tolerance_iterator() | |
{ | |
- double x, y; | |
- shape_path.vertex(&x,&y); | |
- find_point_placement(x, y); | |
- return; | |
+ //std::cout << "values tried:" << values_tried_ << "\n"; | |
} | |
- int num_labels = 1; | |
- if (p.label_spacing > 0) | |
- num_labels = static_cast<int> (floor(total_distance / pi.get_actual_label_spacing())); | |
- | |
- if (p.force_odd_labels && num_labels % 2 == 0) | |
- num_labels--; | |
- if (num_labels <= 0) | |
- num_labels = 1; | |
- | |
- double distance = 0.0; // distance from last label | |
- double spacing = total_distance / num_labels; | |
- double target_distance = spacing / 2; // first label should be placed at half the spacing | |
- | |
- while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape | |
+ double get() const | |
{ | |
+ return -value_; | |
+ } | |
- if (first || agg::is_move_to(cmd)) //Don't do any processing if it is the first node | |
+ bool next() | |
+ { | |
+ values_tried_ ++; | |
+ if (values_tried_ > 255) | |
+ { | |
+ /* This point should not be reached during normal operation. But I can think of | |
+ * cases where very bad spacing and or tolerance values are choosen and the | |
+ * placement finder tries an excessive number of placements. | |
+ * 255 is an arbitrarily chosen limit. | |
+ */ | |
+ MAPNIK_LOG_WARN(placement_finder) << "Tried a huge number of placements. Please check " | |
+ "'label-position-tolerance' and 'spacing' parameters " | |
+ "of your TextSymbolizers.\n"; | |
+ return false; | |
+ } | |
+ if (!initialized_) | |
{ | |
- first = false; | |
+ initialized_ = true; | |
+ return true; //Always return value 0 as the first value. | |
} | |
- else | |
+ if (value_ == 0) | |
{ | |
- //Add the length of this segment to the total we have saved up | |
- double segment_length = std::sqrt(std::pow(old_x-new_x,2) + std::pow(old_y-new_y,2)); //Pythagoras | |
- distance += segment_length; | |
- | |
- //While we have enough distance to place text in | |
- while (distance > target_distance) | |
- { | |
- //Try place at the specified place | |
- double new_weight = (segment_length - (distance - target_distance))/segment_length; | |
- 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. | |
- } | |
+ value_ = tolerance_delta_; | |
+ return true; | |
} | |
- | |
- old_x = new_x; | |
- old_y = new_y; | |
+ value_ = -value_; | |
+ if (value_ > 0) value_ += tolerance_delta_; | |
+ if (value_ > tolerance_) return false; | |
+ return true; | |
} | |
+private: | |
+ double tolerance_; | |
+ double tolerance_delta_; | |
+ double value_; | |
+ bool initialized_; | |
+ unsigned values_tried_; | |
+}; | |
-} | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::init_string_size() | |
+// Output is centered around (0,0) | |
+static void rotated_box2d(box2d<double> &box, rotation const& rot, double width, double height) | |
{ | |
- // Get total string size | |
- if (!info_.num_characters()) return; //At least one character is required | |
- for (std::size_t 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 | |
- string_height_ -= first_line_space_; //First line is a bit smaller | |
+ double new_width = width * rot.cos + height * rot.sin; | |
+ double new_height = width * rot.sin + height * rot.cos; | |
+ box.init(-new_width/2., -new_height/2., new_width/2., new_height/2.); | |
} | |
+pixel_position pixel_position::rotate(rotation const& rot) const | |
+{ | |
+ return pixel_position(x * rot.cos - y * rot.sin, x * rot.sin + y * rot.cos); | |
+} | |
+placement_finder::placement_finder(feature_impl const& feature, DetectorType &detector, box2d<double> const& extent, text_placement_info_ptr placement_info, face_manager_freetype &font_manager, double scale_factor) | |
+ : feature_(feature), detector_(detector), extent_(extent), layout_(font_manager, scale_factor), info_(placement_info), valid_(true), scale_factor_(scale_factor), placements_(), has_marker_(false), marker_(), marker_box_() | |
+{ | |
+} | |
- | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::find_line_breaks() | |
+bool placement_finder::next_position() | |
{ | |
- if (!line_sizes_.empty()) return; | |
- bool first_line = true; | |
- // check if we need to wrap the string | |
- double wrap_at = string_width_ + 1.0; | |
- if (p.wrap_width && string_width_ > p.wrap_width) | |
+ if (!valid_) | |
{ | |
- 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) ; | |
- } | |
- else | |
- { | |
- wrap_at = p.wrap_width; | |
- } | |
+ MAPNIK_LOG_WARN(placement_finder) << "next_position() called while last call already returned false!\n"; | |
+ return false; | |
} | |
- | |
- // work out where our line breaks need to be and the resultant width to the 'wrapped' string | |
- if ((wrap_at < string_width_) || info_.has_line_breaks()) | |
+ if (!info_->next()) | |
{ | |
- 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 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 and word_width include char width + spacing, but not the spacing after the last char | |
- | |
- for (std::size_t ii = 0; ii < info_.num_characters(); ii++) | |
- { | |
- char_info const& ci = info_.at(ii); | |
- unsigned c = ci.c; | |
+ valid_ = false; | |
+ return false; | |
+ } | |
- if ((c == ci.format->wrap_char) || (c == '\n')) | |
- { | |
- 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; //Current one is included in last_wrap_char_width | |
- 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); | |
- //TODO: I think this calculation could be wrong if height changes for the first word in the second line | |
- if (first_line) first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height); | |
- } | |
+ info_->properties.process(layout_, feature_); | |
+ layout_.layout(info_->properties.wrap_width * scale_factor_, info_->properties.text_ratio, info_->properties.wrap_before); | |
- // 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 > wrap_at && !ci.format->wrap_before) || | |
- ((line_width + last_wrap_char_width + word_width) > wrap_at && ci.format->wrap_before)) ) | |
- ) | |
- { | |
- add_line(line_width, line_height, first_line); | |
- line_breaks_.push_back(last_wrap_char_pos); | |
- line_width = 0.0; | |
- line_height = 0.0; | |
- last_wrap_char_width = 0; //Wrap char supressed | |
- first_line = false; | |
- } | |
- } | |
- line_width += last_wrap_char_width + word_width; | |
- line_height = std::max(line_height, word_height); | |
- add_line(line_width, line_height, first_line); | |
+ if (info_->properties.orientation) | |
+ { | |
+ // https://github.com/mapnik/mapnik/issues/1352 | |
+ mapnik::evaluate<feature_impl, value_type> evaluator(feature_); | |
+ orientation_.init( | |
+ boost::apply_visitor( | |
+ evaluator, | |
+ *(info_->properties.orientation)).to_double() * M_PI / 180.0); | |
} else { | |
- //No linebreaks | |
- line_sizes_.push_back(std::make_pair(string_width_, string_height_)); | |
+ orientation_.reset(); | |
} | |
- line_breaks_.push_back(static_cast<unsigned>(info_.num_characters())); | |
-} | |
- | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::add_line(double width, double height, bool first_line) | |
-{ | |
- if (first_line) height -= first_line_space_; | |
- string_width_ = std::max(string_width_, width); //Total width is the longest line | |
- string_height_ += height; | |
- line_sizes_.push_back(std::make_pair(width, height)); | |
+ init_alignment(); | |
+ return true; | |
} | |
- | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::init_alignment() | |
+void placement_finder::init_alignment() | |
{ | |
+ text_symbolizer_properties const& p = info_->properties; | |
valign_ = p.valign; | |
if (valign_ == V_AUTO) | |
{ | |
- if (p.displacement.second > 0.0) | |
+ if (p.displacement.y > 0.0) | |
{ | |
valign_ = V_BOTTOM; | |
- } else if (p.displacement.second < 0.0) | |
+ } else if (p.displacement.y < 0.0) | |
{ | |
valign_ = V_TOP; | |
} else | |
@@ -315,28 +168,32 @@ void placement_finder<DetectorT>::init_alignment() | |
} | |
} | |
- halign_ = p.halign; | |
- if (halign_ == H_AUTO) | |
+ halign_point_ = p.halign; | |
+ halign_line_ = p.halign; | |
+ if (halign_point_ == H_AUTO) | |
{ | |
- if (p.displacement.first > 0.0) | |
+ if (p.displacement.x > 0.0) | |
{ | |
- halign_ = H_RIGHT; | |
- } else if (p.displacement.first < 0.0) | |
+ halign_point_ = H_RIGHT; | |
+ halign_line_ = H_LEFT; | |
+ } else if (p.displacement.x < 0.0) | |
{ | |
- halign_ = H_LEFT; | |
+ halign_point_ = H_LEFT; | |
+ halign_line_= H_RIGHT; | |
} else | |
{ | |
- halign_ = H_MIDDLE; | |
+ halign_point_ = H_MIDDLE; | |
+ halign_line_ = H_MIDDLE; | |
} | |
} | |
jalign_ = p.jalign; | |
if (jalign_ == J_AUTO) | |
{ | |
- if (p.displacement.first > 0.0) | |
+ if (p.displacement.x > 0.0) | |
{ | |
jalign_ = J_LEFT; | |
- } else if (p.displacement.first < 0.0) | |
+ } else if (p.displacement.x < 0.0) | |
{ | |
jalign_ = J_RIGHT; | |
} else { | |
@@ -346,54 +203,57 @@ void placement_finder<DetectorT>::init_alignment() | |
} | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::adjust_position(text_path *current_placement) | |
+pixel_position placement_finder::alignment_offset() const //TODO | |
{ | |
+ pixel_position result(0,0); | |
// if needed, adjust for desired vertical alignment | |
if (valign_ == V_TOP) | |
{ | |
- current_placement->center.y -= 0.5 * string_height_; // move center up by 1/2 the total height | |
+ result.y = -0.5 * layout_.height(); // move center up by 1/2 the total height | |
} else if (valign_ == V_BOTTOM) | |
{ | |
- current_placement->center.y += 0.5 * string_height_; // move center down by the 1/2 the total height | |
+ result.y = 0.5 * layout_.height(); // move center down by the 1/2 the total height | |
} | |
// set horizontal position to middle of text | |
- if (halign_ == H_LEFT) | |
+ if (halign_point_ == H_LEFT) | |
{ | |
- current_placement->center.x -= 0.5 * string_width_; // move center left by 1/2 the string width | |
- } else if (halign_ == H_RIGHT) | |
+ result.x = -0.5 * layout_.width(); // move center left by 1/2 the string width | |
+ } else if (halign_point_ == H_RIGHT) | |
{ | |
- current_placement->center.x += 0.5 * string_width_; // move center right by 1/2 the string width | |
+ result.x = 0.5 * layout_.width(); // move center right by 1/2 the string width | |
} | |
- | |
- // adjust text envelope position by user's x-y displacement (dx, dy) | |
- current_placement->center.x += pi.get_scale_factor() * p.displacement.first; | |
- current_placement->center.y += pi.get_scale_factor() * p.displacement.second; | |
- | |
+ return result; | |
} | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::find_point_placement(double label_x, | |
- double label_y, | |
- double angle) | |
+double placement_finder::jalign_offset(double line_width) const //TODO | |
{ | |
- find_line_breaks(); | |
+ if (jalign_ == J_MIDDLE) return -(line_width / 2.0); | |
+ if (jalign_ == J_LEFT) return -(layout_.width() / 2.0); | |
+ if (jalign_ == J_RIGHT) return (layout_.width() / 2.0) - line_width; | |
+ return 0; | |
+} | |
- double rad = M_PI * angle/180.0; | |
- double cosa = std::cos(rad); | |
- double sina = std::sin(rad); | |
- double x, y; | |
- std::unique_ptr<text_path> current_placement(new text_path(label_x, label_y)); | |
- adjust_position(current_placement.get()); | |
- // presets for first line | |
- std::size_t line_number = 0; | |
- std::size_t index_to_wrap_at = line_breaks_[0]; | |
- double line_width = line_sizes_[0].first; | |
- double line_height = line_sizes_[0].second; | |
+bool placement_finder::find_point_placement(pixel_position const& pos) | |
+{ | |
+ glyph_positions_ptr glyphs = std::make_shared<glyph_positions>(); | |
+ | |
+ /* Find text origin. */ | |
+ pixel_position displacement = scale_factor_ * info_->properties.displacement + alignment_offset(); | |
+ if (info_->properties.rotate_displacement) displacement = displacement.rotate(!orientation_); | |
+ glyphs->set_base_point(pos + displacement); | |
+ box2d<double> bbox; | |
+ rotated_box2d(bbox, orientation_, layout_.width(), layout_.height()); | |
+ bbox.re_center(glyphs->get_base_point().x, glyphs->get_base_point().y); | |
+ | |
+ /* For point placements it is faster to just check the bounding box. */ | |
+ if (collision(bbox)) return false; | |
+ /* add_marker first checks for collision and then updates the detector.*/ | |
+ if (has_marker_ && !add_marker(glyphs, pos)) return false; | |
+ if (layout_.num_lines()) detector_.insert(bbox, layout_.text()); | |
/* IMPORTANT NOTE: | |
x and y are relative to the center of the text | |
@@ -401,657 +261,376 @@ void placement_finder<DetectorT>::find_point_placement(double label_x, | |
x: grows from left to right | |
y: grows from bottom to top (opposite of normal computer graphics) | |
*/ | |
+ double x, y; | |
- // set for upper left corner of text envelope for the first line, bottom left of first character | |
- y = string_height_ / 2.0 - line_height; | |
- // RTL text is converted to a mirrored representation in get_string_info() | |
- // so we have to fix line break order here | |
- if (info_.get_rtl()) y = -y; | |
- | |
- // adjust for desired justification | |
- if (jalign_ == J_LEFT) | |
- x = -(string_width_ / 2.0); | |
- else if (jalign_ == J_RIGHT) | |
- x = (string_width_ / 2.0) - line_width; | |
- else /* J_MIDDLE */ | |
- x = -(line_width / 2.0); | |
- | |
- // save each character rendering position and build envelope as go thru loop | |
- std::queue< box2d<double> > c_envelopes; | |
+ // set for upper left corner of text envelope for the first line, top left of first character | |
+ y = layout_.height() / 2.0; | |
+ glyphs->reserve(layout_.glyphs_count()); | |
- for (std::size_t i = 0; i < info_.num_characters(); i++) | |
+ for ( auto const& line : layout_) | |
{ | |
- char_info const& ci = info_.at(i); | |
- | |
- double cwidth = ci.width + ci.format->character_spacing; | |
+ y -= line.height(); //Automatically handles first line differently | |
+ x = jalign_offset(line.width()); | |
- if (i == index_to_wrap_at) | |
- { | |
- index_to_wrap_at = line_breaks_[++line_number]; | |
- line_width = line_sizes_[line_number].first; | |
- line_height= line_sizes_[line_number].second; | |
- | |
- if (info_.get_rtl()) | |
- { | |
- y += line_height; | |
- } else | |
- { | |
- y -= line_height; // move position down to line start | |
- } | |
- | |
- // reset to begining of line position | |
- if (jalign_ == J_LEFT) | |
- x = -(string_width_ / 2.0); | |
- else if (jalign_ == J_RIGHT) | |
- x = (string_width_ / 2.0) - line_width; | |
- else | |
- x = -(line_width / 2.0); | |
- continue; | |
- } | |
- else | |
+ for (auto const& glyph : line) | |
{ | |
// place the character relative to the center of the string envelope | |
- double dx = x * cosa - y*sina; | |
- double dy = x * sina + y*cosa; | |
- | |
- current_placement->add_node(&ci, dx, dy, rad); | |
- | |
- // compute the Bounding Box for each character and test for: | |
- // overlap, minimum distance or edge avoidance - exit if condition occurs | |
- box2d<double> e; | |
- /*x axis: left to right, y axis: top to bottom (negative values higher)*/ | |
- e.init(current_placement->center.x + dx, // Bottom Left | |
- current_placement->center.y - dy - ci.ymin, // ymin usually <0 | |
- current_placement->center.x + dx + ci.width, // Top Right | |
- current_placement->center.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, pi.get_actual_minimum_distance()))) | |
- { | |
- return; | |
- } | |
- | |
- // if avoid_edges test dimensions contains e | |
- if (p.avoid_edges && !dimensions_.contains(e)) | |
+ glyphs->push_back(glyph, pixel_position(x, y).rotate(orientation_), orientation_); | |
+ if (glyph.width) | |
{ | |
- return; | |
+ //Only advance if glyph is not part of a multiple glyph sequence | |
+ x += glyph.width + glyph.format->character_spacing * scale_factor_; | |
} | |
- | |
- if (p.minimum_padding > 0) | |
- { | |
- box2d<double> epad = e; | |
- epad.pad(pi.get_actual_minimum_padding()); | |
- if (!dimensions_.contains(epad)) | |
- { | |
- return; | |
- } | |
- } | |
- | |
- | |
- c_envelopes.push(e); // add character's envelope to temp storage | |
} | |
- x += cwidth; // move position to next character | |
} | |
- | |
- // check the placement of any additional envelopes | |
- if (!p.allow_overlap && !additional_boxes_.empty()) | |
- { | |
- for (box2d<double> const& box : additional_boxes_) | |
- { | |
- box2d<double> pt(box.minx() + current_placement->center.x, | |
- box.miny() + current_placement->center.y, | |
- box.maxx() + current_placement->center.x, | |
- box.maxy() + current_placement->center.y); | |
- | |
- // abort the whole placement if the additional envelopes can't be placed. | |
- if (!detector_.has_point_placement(pt, p.minimum_distance)) return; | |
- | |
- c_envelopes.push(pt); | |
- } | |
- } | |
- | |
- // since there was no early exit, add the character envelopes to the placements' envelopes | |
- while (!c_envelopes.empty()) | |
- { | |
- envelopes_.push(c_envelopes.front()); | |
- c_envelopes.pop(); | |
- } | |
- | |
- placements_.push_back(current_placement.release()); | |
+ placements_.push_back(glyphs); | |
+ return true; | |
} | |
- | |
-template <typename DetectorT> | |
-template <typename PathT> | |
-void placement_finder<DetectorT>::find_line_placements(PathT & shape_path) | |
+template <typename T> | |
+bool placement_finder::find_line_placements(T & path, bool points) | |
{ | |
-#ifdef MAPNIK_LOG | |
- if (! line_sizes_.empty()) | |
- { | |
- MAPNIK_LOG_WARN(placement_finder) << "Internal error. Text contains line breaks, but line placement is used. Please file a bug report!"; | |
- } | |
-#endif | |
- | |
- unsigned cmd; | |
- double new_x = 0.0; | |
- double new_y = 0.0; | |
- double old_x = 0.0; | |
- double old_y = 0.0; | |
- bool first = true; | |
- | |
- //Pre-Cache all the path_positions and path_distances | |
- //This stops the PathT from having to do multiple re-projections if we need to reposition ourself | |
- // and lets us know how many points are in the shape. | |
- std::vector<vertex2d> path_positions; | |
- std::vector<double> path_distances; // distance from node x-1 to node x | |
- double total_distance = 0; | |
- | |
- shape_path.rewind(0); | |
- while (!agg::is_stop(cmd = shape_path.vertex(&new_x,&new_y))) //For each node in the shape | |
+ if (!layout_.num_lines()) return true; //TODO | |
+ vertex_cache pp(path); | |
+ | |
+ bool success = false; | |
+ while (pp.next_subpath()) | |
{ | |
- if (!first && agg::is_line_to(cmd)) | |
- { | |
- double dx = old_x - new_x; | |
- double dy = old_y - new_y; | |
- double distance = std::sqrt(dx*dx + dy*dy); | |
- total_distance += distance; | |
- path_distances.push_back(distance); | |
- } | |
- else | |
+ if (points) | |
{ | |
- path_distances.push_back(0); | |
+ if (pp.length() <= 0.001) | |
+ { | |
+ success = find_point_placement(pp.current_position()) || success; | |
+ continue; | |
+ } | |
+ } else { | |
+ if ((pp.length() < info_->properties.minimum_path_length * scale_factor_) | |
+ || | |
+ (pp.length() <= 0.001) /* Clipping removed whole geometry */ | |
+ || | |
+ (pp.length() < layout_.width())) continue; | |
} | |
- first = false; | |
- path_positions.push_back(vertex2d(new_x, new_y, cmd)); | |
- old_x = new_x; | |
- old_y = new_y; | |
- } | |
- //Now path_positions is full and total_distance is correct | |
- //shape_path shouldn't be used from here | |
- // Ensure lines have a minimum length. | |
- if (total_distance < p.minimum_path_length) | |
- return; | |
+ double spacing = get_spacing(pp.length(), points ? 0. : layout_.width()); | |
- double distance = 0.0; | |
- | |
- double displacement = p.displacement.second; // 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 | |
- 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<int>(floor(total_distance / (pi.get_actual_label_spacing() + string_width_))); | |
- | |
- 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 | |
- | |
- //Calculate or read out the tolerance | |
- double tolerance_delta, tolerance; | |
- if (p.label_position_tolerance > 0) | |
- { | |
- tolerance = p.label_position_tolerance; | |
- tolerance_delta = std::max ( 1.0, p.label_position_tolerance/100.0 ); | |
- } | |
- else | |
- { | |
- tolerance = spacing/2.0; | |
- tolerance_delta = std::max ( 1.0, spacing/100.0 ); | |
- } | |
- | |
- | |
- first = true; | |
- for (std::size_t index = 0; index < path_positions.size(); index++) //For each node in the shape | |
- { | |
- cmd = path_positions[index].cmd; | |
- new_x = path_positions[index].x; | |
- new_y = path_positions[index].y; | |
- | |
- if (first || agg::is_move_to(cmd)) //Don't do any processing if it is the first node | |
+ horizontal_alignment_e halign = info_->properties.halign; | |
+ if (halign == H_LEFT) | |
+ { | |
+ //Don't move | |
+ } else if (halign == H_MIDDLE || halign == H_AUTO) | |
{ | |
- first = false; | |
+ pp.forward(spacing/2); | |
+ } else if (halign == H_RIGHT) | |
+ { | |
+ pp.forward(pp.length()); | |
} | |
- else | |
+ path_move_dx(pp); | |
+ do | |
{ | |
- //Add the length of this segment to the total we have saved up | |
- double segment_length = path_distances[index]; | |
- distance += segment_length; | |
- | |
- //While we have enough distance to place text in | |
- while (distance > target_distance) | |
+ tolerance_iterator tolerance_offset(info_->properties.label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign | |
+ while (tolerance_offset.next()) | |
{ | |
- for (double diff = 0; diff < tolerance; diff += tolerance_delta) | |
+ vertex_cache::scoped_state state(pp); | |
+ if (pp.move(tolerance_offset.get()) | |
+ && ( | |
+ (points && find_point_placement(pp.current_position())) | |
+ || (!points && single_line_placement(pp, info_->properties.upright)))) | |
{ | |
- for(int dir = -1; dir < 2; dir+=2) //-1, +1 | |
- { | |
- //Record details for the start of the string placement | |
- int orientation = 0; | |
- std::unique_ptr<text_path> 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() == nullptr) | |
- continue; | |
- | |
- //Apply displacement | |
- //NOTE: The text is centered on the line in get_placement_offset, so we are offsetting from there | |
- if (displacement != 0) | |
- { | |
- //Average the angle of all characters and then offset them all by that angle | |
- double anglesum = 0; | |
- for (std::size_t i = 0; i < current_placement->nodes_.size(); i++) | |
- { | |
- double angle = current_placement->nodes_[i].angle; | |
- //Normalize angle in range -PI ... PI | |
- while (angle > M_PI) { | |
- angle -= 2*M_PI; | |
- } | |
- anglesum += angle; | |
- } | |
- anglesum /= current_placement->nodes_.size(); //Now it is angle average | |
- double cosa = orientation * std::cos(anglesum); | |
- double sina = orientation * std::sin(anglesum); | |
- | |
- //Offset all the characters by this angle | |
- for (std::size_t i = 0; i < current_placement->nodes_.size(); i++) | |
- { | |
- current_placement->nodes_[i].pos.x -= | |
- pi.get_scale_factor() * displacement * sina; | |
- current_placement->nodes_[i].pos.y += | |
- pi.get_scale_factor() * displacement * cosa; | |
- } | |
- } | |
- | |
- bool status = test_placement(current_placement, orientation); | |
- | |
- if (status) //We have successfully placed one | |
- { | |
- placements_.push_back(current_placement.release()); | |
- update_detector(); | |
- | |
- //Totally break out of the loops | |
- diff = tolerance; | |
- break; | |
- } | |
- else | |
- { | |
- //If we've failed to place, remove all the envelopes we've added up | |
- while (!envelopes_.empty()) | |
- envelopes_.pop(); | |
- } | |
- | |
- //Don't need to loop twice when diff = 0 | |
- if (diff == 0) | |
- break; | |
- } | |
+ success = true; | |
+ break; | |
} | |
- | |
- 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. | |
} | |
- } | |
- | |
- old_x = new_x; | |
- old_y = new_y; | |
+ } while (pp.forward(spacing)); | |
} | |
+ return success; | |
} | |
-template <typename DetectorT> | |
-std::unique_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(std::vector<vertex2d> const& path_positions, | |
- std::vector<double> const& path_distances, | |
- int & orientation, | |
- std::size_t index, | |
- double distance) | |
+text_upright_e placement_finder::simplify_upright(text_upright_e upright, double angle) const | |
{ | |
- //Check that the given distance is on the given index and find the correct index and distance if not | |
- while (distance < 0 && index > 1) | |
+ if (upright == UPRIGHT_AUTO) | |
{ | |
- index--; | |
- distance += path_distances[index]; | |
+ return (fabs(normalize_angle(angle)) > 0.5*M_PI) ? UPRIGHT_LEFT : UPRIGHT_RIGHT; | |
} | |
- if (index <= 1 && distance < 0) //We've gone off the start, fail out | |
- return std::unique_ptr<text_path>(nullptr); | |
- | |
- //Same thing, checking if we go off the end | |
- while (index < path_distances.size() && distance > path_distances[index]) | |
+ if (upright == UPRIGHT_LEFT_ONLY) | |
{ | |
- distance -= path_distances[index]; | |
- index++; | |
+ return UPRIGHT_LEFT; | |
} | |
- if (index >= path_distances.size()) | |
- return std::unique_ptr<text_path>(nullptr); | |
- | |
- //Keep track of the initial index,distance incase we need to re-call get_placement_offset | |
- const std::size_t initial_index = index; | |
- const double initial_distance = distance; | |
- | |
- double old_x = path_positions[index-1].x; | |
- double old_y = path_positions[index-1].y; | |
+ if (upright == UPRIGHT_RIGHT_ONLY) | |
+ { | |
+ return UPRIGHT_RIGHT; | |
+ } | |
+ return upright; | |
+} | |
- double new_x = path_positions[index].x; | |
- double new_y = path_positions[index].y; | |
- double dx = new_x - old_x; | |
- double dy = new_y - old_y; | |
+bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e orientation) | |
+{ | |
+ /******************************************************************************** | |
+ * IMPORTANT NOTE: See note about coordinate systems in find_point_placement()! * | |
+ ********************************************************************************/ | |
+ vertex_cache::scoped_state s(pp); | |
- double segment_length = path_distances[index]; | |
- if (segment_length == 0) { | |
- // Not allowed to place across on 0 length segments or discontinuities | |
- return std::unique_ptr<text_path>(nullptr); | |
- } | |
+ glyph_positions_ptr glyphs = std::make_shared<glyph_positions>(); | |
+ std::vector<box2d<double> > bboxes; | |
+ bboxes.reserve(layout_.text().length()); | |
+ unsigned upside_down_glyph_count = 0; | |
- std::unique_ptr<text_path> current_placement( | |
- new text_path((old_x + dx*distance/segment_length), | |
- (old_y + dy*distance/segment_length) | |
- ) | |
- ); | |
- double angle = atan2(-dy, dx); | |
+ text_upright_e real_orientation = simplify_upright(orientation, pp.angle()); | |
- bool orientation_forced = (orientation != 0); // Whether the orientation was set by the caller | |
- if (!orientation_forced) | |
- orientation = (angle > 0.55*M_PI || angle < -0.45*M_PI) ? -1 : 1; | |
+ double sign = (real_orientation == UPRIGHT_LEFT) ? -1 : 1; | |
+ double offset = alignment_offset().y + info_->properties.displacement.y * scale_factor_ + sign * layout_.height()/2.; | |
- std::size_t upside_down_char_count = 0; //Count of characters that are placed upside down. | |
+ glyphs->reserve(layout_.glyphs_count()); | |
- for (std::size_t i = 0; i < info_.num_characters(); ++i) | |
+ for (auto const& line : layout_) | |
{ | |
- // grab the next character according to the orientation | |
- char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1); | |
- double cwidth = ci.width + ci.format->character_spacing; | |
- | |
- double last_character_angle = angle; | |
+ //Only subtract half the line height here and half at the end because text is automatically | |
+ //centered on the line | |
+ offset -= sign * line.height()/2; | |
+ vertex_cache & off_pp = pp.get_offseted(offset, sign*layout_.width()); | |
+ vertex_cache::scoped_state off_state(off_pp); //TODO: Remove this when a clean implementation in vertex_cache::get_offseted was done | |
- //Coordinates this character will start at | |
- if (segment_length == 0) { | |
- // Not allowed to place across on 0 length segments or discontinuities | |
- return std::unique_ptr<text_path>(nullptr); | |
- } | |
- double start_x = old_x + dx*distance/segment_length; | |
- double start_y = old_y + dy*distance/segment_length; | |
- //Coordinates this character ends at, calculated below | |
- double end_x = 0; | |
- double end_y = 0; | |
+ if (!off_pp.move(sign * jalign_offset(line.width()) - alignment_offset().x)) return false; | |
- if (segment_length - distance >= cwidth) | |
- { | |
- //if the distance remaining in this segment is enough, we just go further along the segment | |
- distance += cwidth; | |
+ double last_cluster_angle = 999; | |
+ signed current_cluster = -1; | |
+ pixel_position cluster_offset; | |
+ double angle; | |
+ rotation rot; | |
+ double last_glyph_spacing = 0.; | |
- end_x = old_x + dx*distance/segment_length; | |
- end_y = old_y + dy*distance/segment_length; | |
- } | |
- else | |
+ for (auto const& glyph : line) | |
{ | |
- //If there isn't enough distance left on this segment | |
- // then we need to search until we find the line segment that ends further than ci.width away | |
- do | |
+ if (current_cluster != glyph.char_index) | |
{ | |
- old_x = new_x; | |
- old_y = new_y; | |
- index++; | |
- if (index >= path_positions.size()) //Bail out if we run off the end of the shape | |
+ if (!off_pp.move(sign * (layout_.cluster_width(current_cluster) + last_glyph_spacing))) | |
{ | |
- //MAPNIK_LOG_ERROR(placement_finder) << "FAIL: Out of space"; | |
- return std::unique_ptr<text_path>(nullptr); | |
+ return false; | |
} | |
- new_x = path_positions[index].x; | |
- new_y = path_positions[index].y; | |
- dx = new_x - old_x; | |
- dy = new_y - old_y; | |
- | |
- segment_length = path_distances[index]; | |
+ current_cluster = glyph.char_index; | |
+ last_glyph_spacing = glyph.format->character_spacing * scale_factor_; | |
+ //Only calculate new angle at the start of each cluster! | |
+ angle = normalize_angle(off_pp.angle(sign * layout_.cluster_width(current_cluster))); | |
+ rot.init(angle); | |
+ if ((info_->properties.max_char_angle_delta > 0) && (last_cluster_angle != 999) && | |
+ fabs(normalize_angle(angle-last_cluster_angle)) > info_->properties.max_char_angle_delta) | |
+ { | |
+ return false; | |
+ } | |
+ cluster_offset.clear(); | |
+ last_cluster_angle = angle; | |
} | |
- while (std::sqrt(std::pow(start_x - new_x, 2) + std::pow(start_y - new_y, 2)) < cwidth); //Distance from start_ to new_ | |
+ if (abs(angle) > M_PI/2) upside_down_glyph_count++; | |
- //Calculate the position to place the end of the character on | |
- find_line_circle_intersection( | |
- start_x, start_y, cwidth, | |
- old_x, old_y, new_x, new_y, | |
- end_x, end_y); //results are stored in end_x, end_y | |
+ pixel_position pos = off_pp.current_position() + cluster_offset; | |
+ //Center the text on the line | |
+ double char_height = line.max_char_height(); | |
+ pos.y = -pos.y - char_height/2.0*rot.cos; | |
+ pos.x = pos.x + char_height/2.0*rot.sin; | |
- //Need to calculate distance on the new segment | |
- distance = std::sqrt(std::pow(old_x - end_x, 2) + std::pow(old_y - end_y, 2)); | |
- } | |
+ cluster_offset.x += rot.cos * glyph.width; | |
+ cluster_offset.y -= rot.sin * glyph.width; | |
- //Calculate angle from the start of the character to the end based on start_/end_ position | |
- angle = fast_atan2(start_y-end_y, end_x-start_x); | |
- | |
- //Test last_character_angle vs angle | |
- // since our rendering angle has changed then check against our | |
- // max allowable angle change. | |
- double angle_delta = last_character_angle - angle; | |
- // normalise between -180 and 180 | |
- while (angle_delta > M_PI) | |
- angle_delta -= 2*M_PI; | |
- while (angle_delta < -M_PI) | |
- angle_delta += 2*M_PI; | |
- if (p.max_char_angle_delta > 0 && | |
- std::fabs(angle_delta) > p.max_char_angle_delta) | |
- { | |
- //MAPNIK_LOG_ERROR(placement_finder) << "FAIL: Too Bendy!"; | |
- return std::unique_ptr<text_path>(nullptr); | |
+ box2d<double> bbox = get_bbox(glyph, pos, rot); | |
+ if (collision(bbox)) return false; | |
+ bboxes.push_back(bbox); | |
+ glyphs->push_back(glyph, pos, rot); | |
} | |
- | |
- double render_angle = angle; | |
- double cosa = fast_cos(angle); | |
- double sina = fast_sin(angle); | |
- | |
- double render_x = start_x; | |
- double render_y = start_y; | |
- | |
- //Center the text on the line | |
- double char_height = ci.avg_height; | |
- render_x += char_height/2.0*sina; | |
- render_y += char_height/2.0*cosa; | |
- | |
- if (orientation < 0) | |
- { | |
- // rotate in place | |
- render_x += cwidth*cosa - char_height*sina; | |
- render_y -= cwidth*sina + char_height*cosa; | |
- render_angle += M_PI; | |
- } | |
- current_placement->add_node(&ci, | |
- render_x - current_placement->center.x, | |
- -render_y + current_placement->center.y, | |
- render_angle); | |
- | |
- //Normalise to 0 <= angle < 2PI | |
- while (render_angle >= 2*M_PI) | |
- render_angle -= 2*M_PI; | |
- while (render_angle < 0) | |
- render_angle += 2*M_PI; | |
- | |
- if (render_angle > M_PI/2 && render_angle < 1.5*M_PI) | |
- upside_down_char_count++; | |
+ //See comment above | |
+ offset -= sign * line.height()/2; | |
} | |
- | |
- //If we placed too many characters upside down | |
- if (upside_down_char_count >= info_.num_characters()/2.0) | |
+ if (upside_down_glyph_count > layout_.text().length()/2) | |
{ | |
- //if we auto-detected the orientation then retry with the opposite orientation | |
- if (!orientation_forced) | |
+ if (orientation == UPRIGHT_AUTO) | |
{ | |
- orientation = -orientation; | |
- current_placement = get_placement_offset(path_positions, | |
- path_distances, | |
- orientation, | |
- initial_index, | |
- initial_distance); | |
+ //Try again with oposite orientation | |
+ s.restore(); | |
+ return single_line_placement(pp, real_orientation == UPRIGHT_RIGHT ? UPRIGHT_LEFT : UPRIGHT_RIGHT); | |
} | |
- else | |
+ //upright==left_only or right_only and more than 50% of characters upside down => no placement | |
+ if (orientation == UPRIGHT_LEFT_ONLY || orientation == UPRIGHT_RIGHT_ONLY) | |
{ | |
- //Otherwise we have failed to find a placement | |
- //MAPNIK_LOG_ERROR(placement_finder) << "FAIL: Double upside-down!"; | |
- return std::unique_ptr<text_path>(nullptr); | |
+ return false; | |
} | |
} | |
- | |
- return std::move(current_placement); | |
+ for (box2d<double> const& bbox : bboxes) | |
+ { | |
+ detector_.insert(bbox, layout_.text()); | |
+ } | |
+ placements_.push_back(glyphs); | |
+ return true; | |
} | |
-template <typename DetectorT> | |
-bool placement_finder<DetectorT>::test_placement(std::unique_ptr<text_path> const& current_placement, | |
- int orientation) | |
+void placement_finder::path_move_dx(vertex_cache &pp) | |
{ | |
- //Create and test envelopes | |
- bool status = true; | |
- for (std::size_t i = 0; i < info_.num_characters(); ++i) | |
+ double dx = info_->properties.displacement.x * scale_factor_; | |
+ if (dx != 0.0) | |
{ | |
- //TODO: I think this can be simplified by taking the char_info from vertex() but this needs to be carefully tested! | |
- // grab the next character according to the orientation | |
- char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1); | |
- double cwidth = ci.width + ci.format->character_spacing; | |
- char_info_ptr c; | |
- double x, y, angle; | |
- current_placement->vertex(c, x, y, angle); | |
- x = current_placement->center.x + x; | |
- y = current_placement->center.y - y; | |
- | |
- double sina = fast_sin(angle); | |
- double cosa = fast_cos(angle); | |
- if (orientation < 0) | |
- { | |
- // rotate in place | |
- x += cwidth * cosa - string_height_ * sina; | |
- y -= cwidth * sina + string_height_ * cosa; | |
- angle += M_PI; | |
- //sin(x+PI) = -sin(x) | |
- sina = -sina; | |
- cosa = -cosa; | |
- } | |
+ vertex_cache::state state = pp.save_state(); | |
+ if (!pp.move(dx)) pp.restore_state(state); | |
+ } | |
+} | |
- box2d<double> e(x, y, x + cwidth*cosa, y - cwidth*sina); | |
- // put four corners of the letter into envelope | |
- e.expand_to_include(x - ci.height()*sina, | |
- y - ci.height()*cosa); | |
- e.expand_to_include(x + (cwidth*cosa - ci.height()*sina), | |
- y - (cwidth*sina + ci.height()*cosa)); | |
- | |
- if (!detector_.extent().intersects(e) || | |
- (!p.allow_overlap && | |
- !detector_.has_placement(e, info_.get_string(), pi.get_actual_minimum_distance()) | |
- ) | |
- ) | |
- { | |
- //MAPNIK_LOG_ERROR(placement_finder) << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_; | |
- //MAPNIK_LOG_ERROR(placement_finder) << "No Placements:" << !detector_.has_placement(e, info.get_string(), p.minimum_distance); | |
- status = false; | |
- break; | |
- } | |
+double placement_finder::normalize_angle(double angle) | |
+{ | |
+ while (angle >= M_PI) | |
+ angle -= 2*M_PI; | |
+ while (angle < -M_PI) | |
+ angle += 2*M_PI; | |
+ return angle; | |
+} | |
- if (p.avoid_edges && !dimensions_.contains(e)) | |
- { | |
- //MAPNIK_LOG_ERROR(placement_finder) << "Fail avoid edges"; | |
- status = false; | |
- break; | |
- } | |
- if (p.minimum_padding > 0) | |
- { | |
+double placement_finder::get_spacing(double path_length, double layout_width) const | |
+{ | |
+ int num_labels = 1; | |
+ if (info_->properties.label_spacing > 0) | |
+ num_labels = static_cast<int>(floor( | |
+ path_length / (info_->properties.label_spacing * scale_factor_ + layout_width))); | |
- box2d<double> epad = e; | |
- epad.pad(pi.get_actual_minimum_padding()); | |
- if (!dimensions_.contains(epad)) | |
- { | |
- status = false; | |
- break; | |
- } | |
- } | |
- envelopes_.push(e); | |
+ if (info_->properties.force_odd_labels && num_labels % 2 == 0) | |
+ num_labels--; | |
+ if (num_labels <= 0) | |
+ num_labels = 1; | |
+ | |
+ return path_length / num_labels; | |
+} | |
+ | |
+bool placement_finder::collision(const box2d<double> &box) const | |
+{ | |
+ if (!detector_.extent().intersects(box) | |
+ || | |
+ (info_->properties.avoid_edges && !extent_.contains(box)) | |
+ || | |
+ (info_->properties.minimum_padding > 0 && | |
+ !extent_.contains(box + (scale_factor_ * info_->properties.minimum_padding))) | |
+ || | |
+ (!info_->properties.allow_overlap && | |
+ !detector_.has_point_placement(box, info_->properties.minimum_distance * scale_factor_)) | |
+ ) | |
+ { | |
+ return true; | |
} | |
+ return false; | |
+} | |
+ | |
+void placement_finder::set_marker(marker_info_ptr m, box2d<double> box, bool marker_unlocked, pixel_position const& marker_displacement) | |
+{ | |
+ marker_ = m; | |
+ marker_box_ = box * scale_factor_; | |
+ marker_displacement_ = marker_displacement * scale_factor_; | |
+ marker_unlocked_ = marker_unlocked; | |
+ has_marker_ = true; | |
+} | |
- current_placement->rewind(); | |
- return status; | |
+bool placement_finder::add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const | |
+{ | |
+ pixel_position real_pos = (marker_unlocked_ ? pos : glyphs->get_base_point()) + marker_displacement_; | |
+ box2d<double> bbox = marker_box_; | |
+ bbox.move(real_pos.x, real_pos.y); | |
+ glyphs->set_marker(marker_, real_pos); | |
+ if (collision(bbox)) return false; | |
+ detector_.insert(bbox); | |
+ return true; | |
} | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::find_line_circle_intersection( | |
- double cx, double cy, double radius, | |
- double x1, double y1, double x2, double y2, | |
- double & ix, double & iy) | |
+box2d<double> placement_finder::get_bbox(glyph_info const& glyph, pixel_position const& pos, rotation const& rot) | |
{ | |
- double dx = x2 - x1; | |
- double dy = y2 - y1; | |
+ /* | |
+ | |
+ (0/ymax) (width/ymax) | |
+ *************** | |
+ * * | |
+ (0/0)* * | |
+ * * | |
+ *************** | |
+ (0/ymin) (width/ymin) | |
+ Add glyph offset in y direction, but not in x direction (as we use the full cluster width anyways)! | |
+ */ | |
+ double width = layout_.cluster_width(glyph.char_index); | |
+ if (glyph.width <= 0) width = -width; | |
+ pixel_position tmp, tmp2; | |
+ tmp.set(0, glyph.ymax); | |
+ tmp = tmp.rotate(rot); | |
+ tmp2.set(width, glyph.ymax); | |
+ tmp2 = tmp2.rotate(rot); | |
+ box2d<double> bbox(tmp.x, -tmp.y, | |
+ tmp2.x, -tmp2.y); | |
+ tmp.set(width, glyph.ymin); | |
+ tmp = tmp.rotate(rot); | |
+ bbox.expand_to_include(tmp.x, -tmp.y); | |
+ tmp.set(0, glyph.ymin); | |
+ tmp = tmp.rotate(rot); | |
+ bbox.expand_to_include(tmp.x, -tmp.y); | |
+ pixel_position pos2 = pos + pixel_position(0, glyph.offset.y).rotate(rot); | |
+ bbox.move(pos2.x , -pos2.y); | |
+ return bbox; | |
+} | |
- double A = dx * dx + dy * dy; | |
- double B = 2 * (dx * (x1 - cx) + dy * (y1 - cy)); | |
- double C = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - radius * radius; | |
- double det = B * B - 4 * A * C; | |
- if (A <= 0.0000001 || det < 0) | |
- { | |
- //Should never happen | |
- //' No real solutions. | |
- return; | |
- } | |
- else if (det == 0) | |
- { | |
- //Could potentially happen.... | |
- //One solution. | |
- double t = -B / (2 * A); | |
- ix = x1 + t * dx; | |
- iy = y1 + t * dy; | |
- return; | |
- } | |
- else | |
- { | |
- //Two solutions. | |
+/*********************************************************************************************/ | |
- //Always use the 1st one | |
- //We only really have one solution here, as we know the line segment will start in the circle and end outside | |
- double t = (-B + std::sqrt(det)) / (2 * A); | |
- ix = x1 + t * dx; | |
- iy = y1 + t * dy; | |
- //t = (-B - std::sqrt(det)) / (2 * A); | |
- //ix = x1 + t * dx; | |
- //iy = y1 + t * dy; | |
+glyph_positions::glyph_positions() | |
+ : data_(), base_point_(), marker_(), marker_pos_(), bbox_() | |
+{ | |
- return; | |
- } | |
} | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::update_detector() | |
+glyph_positions::const_iterator glyph_positions::begin() const | |
{ | |
- if (collect_extents_) extents_.init(0,0,0,0); | |
- // add the bboxes to the detector and remove from the placement | |
- while (!envelopes_.empty()) | |
- { | |
- box2d<double> e = envelopes_.front(); | |
- detector_.insert(e, info_.get_string()); | |
- envelopes_.pop(); | |
+ return data_.begin(); | |
+} | |
- if (collect_extents_) | |
- { | |
- extents_.expand_to_include(e); | |
- } | |
- } | |
+glyph_positions::const_iterator glyph_positions::end() const | |
+{ | |
+ return data_.end(); | |
} | |
-template <typename DetectorT> | |
-void placement_finder<DetectorT>::clear_placements() | |
+void glyph_positions::push_back(glyph_info const& glyph, pixel_position const offset, rotation const& rot) | |
{ | |
- placements_.clear(); | |
- while (!envelopes_.empty()) envelopes_.pop(); | |
+ data_.push_back(glyph_position(glyph, offset, rot)); | |
} | |
-template class placement_finder<DetectorType>; | |
-template void placement_finder<DetectorType>::find_point_placements<ClippedPathType>(ClippedPathType &); | |
-template void placement_finder<DetectorType>::find_line_placements<ClippedPathType>(ClippedPathType &); | |
-template void placement_finder<DetectorType>::find_point_placements<PathType>(PathType &); | |
-template void placement_finder<DetectorType>::find_line_placements<PathType>(PathType &); | |
-} // namespace | |
+void glyph_positions::reserve(unsigned count) | |
+{ | |
+ data_.reserve(count); | |
+} | |
+ | |
+pixel_position const& glyph_positions::get_base_point() const | |
+{ | |
+ return base_point_; | |
+} | |
+ | |
+void glyph_positions::set_base_point(pixel_position const base_point) | |
+{ | |
+ base_point_ = base_point; | |
+} | |
+ | |
+void glyph_positions::set_marker(marker_info_ptr marker, pixel_position const& marker_pos) | |
+{ | |
+ marker_ = marker; | |
+ marker_pos_ = marker_pos; | |
+} | |
+ | |
+marker_info_ptr glyph_positions::marker() const | |
+{ | |
+ return marker_; | |
+} | |
+ | |
+pixel_position const& glyph_positions::marker_pos() const | |
+{ | |
+ return marker_pos_; | |
+} | |
+ | |
+ | |
+/*************************************************************************************/ | |
+typedef agg::conv_clip_polyline<geometry_type> clipped_geometry_type; | |
+typedef coord_transform<CoordTransform,clipped_geometry_type> ClippedPathType; | |
+typedef coord_transform<CoordTransform,geometry_type> PathType; | |
+template bool placement_finder::find_line_placements<ClippedPathType>(ClippedPathType &, bool); | |
+template bool placement_finder::find_line_placements<PathType>(PathType &, bool); | |
+ | |
+ | |
+}// ns mapnik | |
diff --git a/src/text/placements/base.cpp b/src/text/placements/base.cpp | |
index 02f1c12..faa9e02 100644 | |
--- a/src/text/placements/base.cpp | |
+++ b/src/text/placements/base.cpp | |
@@ -19,9 +19,11 @@ | |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
* | |
*****************************************************************************/ | |
+// mapnik | |
#include <mapnik/text/placements/base.hpp> | |
namespace mapnik { | |
+ | |
text_placements::text_placements() : defaults() | |
{ | |
} | |
@@ -39,7 +41,7 @@ text_placement_info::text_placement_info(text_placements const* parent, | |
: properties(parent->defaults), | |
scale_factor(scale_factor_) | |
{ | |
- | |
+ properties.format = std::make_shared<char_properties>(*(properties.format)); | |
} | |
} //ns mapnik | |
diff --git a/src/text/placements/list.cpp b/src/text/placements/list.cpp | |
index 39e3682..1bfdb19 100644 | |
--- a/src/text/placements/list.cpp | |
+++ b/src/text/placements/list.cpp | |
@@ -27,7 +27,6 @@ | |
//boost | |
#include <boost/property_tree/ptree.hpp> | |
- | |
namespace mapnik | |
{ | |
@@ -99,6 +98,8 @@ text_placements_ptr text_placements_list::from_xml(xml_node const &xml, fontset_ | |
{ | |
if (itr->is_text() || !itr->is("Placement")) continue; | |
text_symbolizer_properties &p = list->add(); | |
+ p.format = std::make_shared<char_properties>(*(p.format)); //Make a deep copy | |
+ //TODO: This needs a real copy constructor for text_symbolizer_properties | |
p.from_xml(*itr, fontsets); | |
//TODO: if (strict_ && | |
// !p.format.fontset.size()) | |
diff --git a/src/text/placements/simple.cpp b/src/text/placements/simple.cpp | |
index ee08527..f590d9e 100644 | |
--- a/src/text/placements/simple.cpp | |
+++ b/src/text/placements/simple.cpp | |
@@ -30,6 +30,7 @@ | |
#include <boost/spirit/include/qi.hpp> | |
#include <boost/spirit/include/phoenix_core.hpp> | |
#include <boost/spirit/include/phoenix_stl.hpp> | |
+ | |
#include <boost/property_tree/ptree.hpp> | |
namespace mapnik | |
@@ -48,7 +49,7 @@ bool text_placement_info_simple::next() | |
if (state > 0) | |
{ | |
if (state > parent_->text_sizes_.size()) return false; | |
- properties.format.text_size = parent_->text_sizes_[state-1]; | |
+ properties.format->text_size = parent_->text_sizes_[state-1]; | |
} | |
if (!next_position_only()) { | |
state++; | |
@@ -62,8 +63,8 @@ bool text_placement_info_simple::next() | |
bool text_placement_info_simple::next_position_only() | |
{ | |
- const position &pdisp = parent_->defaults.displacement; | |
- position &displacement = properties.displacement; | |
+ pixel_position const& pdisp = parent_->defaults.displacement; | |
+ pixel_position &displacement = properties.displacement; | |
if (position_state >= parent_->direction_.size()) return false; | |
directions_t dir = parent_->direction_[position_state]; | |
switch (dir) { | |
@@ -71,28 +72,28 @@ bool text_placement_info_simple::next_position_only() | |
displacement = pdisp; | |
break; | |
case NORTH: | |
- displacement = std::make_pair(0, -abs(pdisp.second)); | |
+ displacement.set(0, -abs(pdisp.y)); | |
break; | |
case EAST: | |
- displacement = std::make_pair(abs(pdisp.first), 0); | |
+ displacement.set(abs(pdisp.x), 0); | |
break; | |
case SOUTH: | |
- displacement = std::make_pair(0, abs(pdisp.second)); | |
+ displacement.set(0, abs(pdisp.y)); | |
break; | |
case WEST: | |
- displacement = std::make_pair(-abs(pdisp.first), 0); | |
+ displacement.set(-abs(pdisp.x), 0); | |
break; | |
case NORTHEAST: | |
- displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second)); | |
+ displacement.set(abs(pdisp.x), -abs(pdisp.y)); | |
break; | |
case SOUTHEAST: | |
- displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second)); | |
+ displacement.set(abs(pdisp.x), abs(pdisp.y)); | |
break; | |
case NORTHWEST: | |
- displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second)); | |
+ displacement.set(-abs(pdisp.x), -abs(pdisp.y)); | |
break; | |
case SOUTHWEST: | |
- displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second)); | |
+ displacement.set(-abs(pdisp.x), abs(pdisp.y)); | |
break; | |
default: | |
MAPNIK_LOG_WARN(text_placements) << "Unknown placement"; | |
diff --git a/src/text/processed_text.cpp b/src/text/processed_text.cpp | |
deleted file mode 100644 | |
index 69c3a04..0000000 | |
--- a/src/text/processed_text.cpp | |
+++ /dev/null | |
@@ -1,104 +0,0 @@ | |
-/***************************************************************************** | |
- * | |
- * 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 | |
- * | |
- *****************************************************************************/ | |
- | |
-#include <mapnik/text/processed_text.hpp> | |
-#include <mapnik/config_error.hpp> | |
-#include <mapnik/font_engine_freetype.hpp> | |
-#include <mapnik/value_types.hpp> | |
- | |
-namespace mapnik | |
-{ | |
- | |
-void processed_text::push_back(char_properties const& properties, mapnik::value_unicode_string const& text) | |
-{ | |
- expr_list_.push_back(processed_expression(properties, text)); | |
-} | |
- | |
-processed_text::expression_list::const_iterator processed_text::begin() const | |
-{ | |
- return expr_list_.begin(); | |
-} | |
- | |
-processed_text::expression_list::const_iterator processed_text::end() const | |
-{ | |
- return expr_list_.end(); | |
-} | |
- | |
-processed_text::processed_text(face_manager<freetype_engine> & font_manager, double scale_factor) | |
- : font_manager_(font_manager), scale_factor_(scale_factor) | |
-{ | |
- | |
-} | |
- | |
-void processed_text::clear() | |
-{ | |
- info_.clear(); | |
- expr_list_.clear(); | |
-} | |
- | |
- | |
-string_info const& processed_text::get_string_info() | |
-{ | |
- info_.clear(); //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) | |
- { | |
- if (p.fontset && !p.fontset->get_name().empty()) | |
- { | |
- if (p.fontset->size()) | |
- { | |
- if (!p.face_name.empty()) | |
- { | |
- throw config_error("Unable to find specified font face '" + p.face_name + "' in font set: '" + p.fontset->get_name() + "'"); | |
- } | |
- else | |
- { | |
- throw config_error("No valid font face could be loaded for font set: '" + p.fontset->get_name() + "'"); | |
- } | |
- } | |
- else | |
- { | |
- throw config_error("Font set '" + p.fontset->get_name() + "' does not contain any Font face-name entries"); | |
- } | |
- } | |
- else if (!p.face_name.empty()) | |
- { | |
- throw config_error("Unable to find specified font face '" + p.face_name + "'"); | |
- } | |
- else | |
- { | |
- throw config_error("Both font set and face name are empty!"); | |
- } | |
- } | |
- 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_; | |
-} | |
- | |
-} //ns mapnik | |
diff --git a/src/text/symbolizer_helpers.cpp b/src/text/symbolizer_helpers.cpp | |
index 392bc0f..56bae90 100644 | |
--- a/src/text/symbolizer_helpers.cpp | |
+++ b/src/text/symbolizer_helpers.cpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2011 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
@@ -21,120 +21,58 @@ | |
*****************************************************************************/ | |
// mapnik | |
-#include <mapnik/debug.hpp> | |
-#include <mapnik/value.hpp> | |
-#include <mapnik/feature.hpp> | |
-#include <mapnik/geom_util.hpp> | |
-#include <mapnik/parse_path.hpp> | |
#include <mapnik/text/symbolizer_helpers.hpp> | |
#include <mapnik/label_collision_detector.hpp> | |
-#include <mapnik/text/placement_finder.hpp> | |
+#include <mapnik/font_engine_freetype.hpp> | |
+#include <mapnik/text/layout.hpp> | |
#include <mapnik/geom_util.hpp> | |
-#include <mapnik/marker.hpp> | |
-#include <mapnik/expression_evaluator.hpp> | |
-#include <mapnik/pixel_position.hpp> | |
+#include <mapnik/parse_path.hpp> | |
+#include <mapnik/debug.hpp> | |
-// agg | |
+//agg | |
#include "agg_conv_clip_polyline.h" | |
namespace mapnik { | |
template <typename FaceManagerT, typename DetectorT> | |
-text_symbolizer_helper<FaceManagerT, DetectorT>::text_symbolizer_helper(text_symbolizer const& sym, | |
- feature_impl const& feature, | |
- proj_transform const& prj_trans, | |
- unsigned width, | |
- unsigned height, | |
- double scale_factor, | |
- CoordTransform const& t, | |
- FaceManagerT &font_manager, | |
- DetectorT &detector, | |
- box2d<double> const& query_extent) | |
- : sym_(sym), | |
- feature_(feature), | |
- prj_trans_(prj_trans), | |
- t_(t), | |
- font_manager_(font_manager), | |
- detector_(detector), | |
- dims_(0, 0, width, height), | |
- query_extent_(query_extent), | |
- text_(font_manager, scale_factor), | |
- angle_(0.0), | |
- placement_valid_(false), | |
- points_on_line_(false), | |
- finder_() | |
- { | |
- initialize_geometries(); | |
- if (!geometries_to_process_.size()) return; | |
- placement_ = sym_.get_placement_options()->get_placement_info(scale_factor); | |
- next_placement(); | |
- initialize_points(); | |
- } | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-text_symbolizer_helper<FaceManagerT, DetectorT>::~text_symbolizer_helper() | |
-{} | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool text_symbolizer_helper<FaceManagerT, DetectorT>::next() | |
+text_symbolizer_helper::text_symbolizer_helper(const text_symbolizer &sym, const feature_impl &feature, const proj_transform &prj_trans, unsigned width, unsigned height, double scale_factor, const CoordTransform &t, FaceManagerT &font_manager, DetectorT &detector, const box2d<double> &query_extent) | |
+ : sym_(sym), | |
+ feature_(feature), | |
+ prj_trans_(prj_trans), | |
+ t_(t), | |
+ dims_(0, 0, width, height), | |
+ query_extent_(query_extent), | |
+ points_on_line_(false), | |
+ placement_(sym_.get_placement_options()->get_placement_info(scale_factor)), | |
+ finder_(feature, detector, dims_, placement_, font_manager, scale_factor) | |
{ | |
- if (!placement_valid_) return false; | |
- if (point_placement_) | |
- return next_point_placement(); | |
- else if (sym_.clip()) | |
- return next_line_placement_clipped(); | |
- else | |
- return next_line_placement(); | |
+ initialize_geometries(); | |
+ if (!geometries_to_process_.size()) return; | |
+ finder_.next_position(); | |
+ initialize_points(); | |
} | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement() | |
+placements_list const& text_symbolizer_helper::get() | |
{ | |
- while (!geometries_to_process_.empty()) | |
+ if (point_placement_) | |
{ | |
- if (geo_itr_ == geometries_to_process_.end()) | |
- { | |
- //Just processed the last geometry. Try next placement. | |
- if (!next_placement()) return false; //No more placements | |
- //Start again from begin of list | |
- geo_itr_ = geometries_to_process_.begin(); | |
- continue; //Reexecute size check | |
- } | |
- | |
- typedef coord_transform<CoordTransform,geometry_type> path_type; | |
- path_type path(t_, **geo_itr_, prj_trans_); | |
- | |
- finder_->clear_placements(); | |
- if (points_on_line_) { | |
- finder_->find_point_placements(path); | |
- } else { | |
- finder_->find_line_placements(path); | |
- } | |
- if (!finder_->get_results().empty()) | |
- { | |
- //Found a placement | |
- if (points_on_line_) | |
- { | |
- finder_->update_detector(); | |
- } | |
- geo_itr_ = geometries_to_process_.erase(geo_itr_); | |
- return true; | |
- } | |
- //No placement for this geometry. Keep it in geometries_to_process_ for next try. | |
- geo_itr_++; | |
+ while (next_point_placement()); | |
} | |
- return false; | |
+ else | |
+ { | |
+ while (next_line_placement()); | |
+ } | |
+ return finder_.placements(); | |
} | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement_clipped() | |
+bool text_symbolizer_helper::next_line_placement() | |
{ | |
while (!geometries_to_process_.empty()) | |
{ | |
if (geo_itr_ == geometries_to_process_.end()) | |
{ | |
//Just processed the last geometry. Try next placement. | |
- if (!next_placement()) return false; //No more placements | |
+ if (!finder_.next_position()) return false; //No more placements | |
//Start again from begin of list | |
geo_itr_ = geometries_to_process_.begin(); | |
continue; //Reexecute size check | |
@@ -142,23 +80,15 @@ bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement_clippe | |
typedef agg::conv_clip_polyline<geometry_type> clipped_geometry_type; | |
typedef coord_transform<CoordTransform,clipped_geometry_type> path_type; | |
+ | |
clipped_geometry_type clipped(**geo_itr_); | |
- clipped.clip_box(query_extent_.minx(),query_extent_.miny(),query_extent_.maxx(),query_extent_.maxy()); | |
+ clipped.clip_box(query_extent_.minx(), query_extent_.miny(), | |
+ query_extent_.maxx(), query_extent_.maxy()); | |
path_type path(t_, clipped, prj_trans_); | |
- | |
- finder_->clear_placements(); | |
- if (points_on_line_) { | |
- finder_->find_point_placements(path); | |
- } else { | |
- finder_->find_line_placements(path); | |
- } | |
- if (!finder_->get_results().empty()) | |
+ bool success = finder_.find_line_placements(path, points_on_line_); | |
+ if (success) | |
{ | |
//Found a placement | |
- if (points_on_line_) | |
- { | |
- finder_->update_detector(); | |
- } | |
geo_itr_ = geometries_to_process_.erase(geo_itr_); | |
return true; | |
} | |
@@ -168,26 +98,22 @@ bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement_clippe | |
return false; | |
} | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_point_placement() | |
+bool text_symbolizer_helper::next_point_placement() | |
{ | |
while (!points_.empty()) | |
{ | |
if (point_itr_ == points_.end()) | |
{ | |
//Just processed the last point. Try next placement. | |
- if (!next_placement()) return false; //No more placements | |
+ if (!finder_.next_position()) return false; //No more placements | |
//Start again from begin of list | |
point_itr_ = points_.begin(); | |
continue; //Reexecute size check | |
} | |
- finder_->clear_placements(); | |
- finder_->find_point_placement(point_itr_->first, point_itr_->second, angle_); | |
- if (!finder_->get_results().empty()) | |
+ if (finder_.find_point_placement(*point_itr_)) | |
{ | |
//Found a placement | |
point_itr_ = points_.erase(point_itr_); | |
- finder_->update_detector(); | |
return true; | |
} | |
//No placement for this point. Keep it in points_ for next try. | |
@@ -207,19 +133,17 @@ struct largest_bbox_first | |
}; | |
-template <typename FaceManagerT, typename DetectorT> | |
-void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries() | |
+void text_symbolizer_helper::initialize_geometries() | |
{ | |
bool largest_box_only = false; | |
std::size_t num_geom = feature_.num_geometries(); | |
- | |
- for (std::size_t i = 0; i < num_geom; ++i) | |
+ for (std::size_t i=0; i<num_geom; ++i) | |
{ | |
geometry_type const& geom = feature_.get_geometry(i); | |
// don't bother with empty geometries | |
if (geom.size() == 0) continue; | |
- geometry_type::types type = geom.type(); | |
+ mapnik::geometry_type::types type = geom.type(); | |
if (type == geometry_type::types::Polygon) | |
{ | |
largest_box_only = sym_.largest_bbox_only(); | |
@@ -245,8 +169,7 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries() | |
geo_itr_ = geometries_to_process_.begin(); | |
} | |
-template <typename FaceManagerT, typename DetectorT> | |
-void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points() | |
+void text_symbolizer_helper::initialize_points() | |
{ | |
label_placement_enum how_placed = placement_->properties.label_placement; | |
if (how_placed == LINE_PLACEMENT) | |
@@ -276,7 +199,7 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points() | |
geom.vertex(&label_x, &label_y); | |
prj_trans_.backward(label_x, label_y, z); | |
t_.forward(&label_x, &label_y); | |
- points_.push_back(std::make_pair(label_x, label_y)); | |
+ points_.push_back(pixel_position(label_x, label_y)); | |
} | |
} | |
else | |
@@ -304,205 +227,83 @@ void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points() | |
{ | |
prj_trans_.backward(label_x, label_y, z); | |
t_.forward(&label_x, &label_y); | |
- points_.push_back(std::make_pair(label_x, label_y)); | |
+ points_.push_back(pixel_position(label_x, label_y)); | |
} | |
} | |
} | |
point_itr_ = points_.begin(); | |
} | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement() | |
-{ | |
- if (!placement_->next()) { | |
- placement_valid_ = false; | |
- return false; | |
- } | |
- placement_->properties.process(text_, feature_); | |
- if (placement_->properties.orientation) | |
- { | |
- // https://github.com/mapnik/mapnik/issues/1352 | |
- mapnik::evaluate<feature_impl, value_type> evaluator(feature_); | |
- angle_ = boost::apply_visitor( | |
- evaluator, | |
- *(placement_->properties.orientation)).to_double(); | |
- } else { | |
- angle_ = 0.0; | |
- } | |
- | |
- finder_.reset(new placement_finder<DetectorT>(*placement_, | |
- text_.get_string_info(), | |
- detector_, dims_)); | |
- placement_valid_ = true; | |
- return true; | |
-} | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-placements_type const& text_symbolizer_helper<FaceManagerT, DetectorT>::placements() const | |
-{ | |
- return finder_->get_results(); | |
-} | |
- | |
- | |
/*****************************************************************************/ | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool shield_symbolizer_helper<FaceManagerT, DetectorT>::next() | |
-{ | |
- if (!placement_valid_ || !marker_) return false; | |
- if (point_placement_) | |
- return next_point_placement(); | |
- else | |
- return next_line_placement(); | |
-} | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-bool shield_symbolizer_helper<FaceManagerT, DetectorT>::next_point_placement() | |
-{ | |
- position const& shield_pos = sym_.get_shield_displacement(); | |
- while (!points_.empty()) | |
- { | |
- if (point_itr_ == points_.end()) | |
- { | |
- //Just processed the last point. Try next placement. | |
- if (!next_placement()) return false; //No more placements | |
- //Start again from begin of list | |
- point_itr_ = points_.begin(); | |
- continue; //Reexecute size check | |
- } | |
- position const& text_disp = placement_->properties.displacement; | |
- double label_x = point_itr_->first + shield_pos.first; | |
- double label_y = point_itr_->second + shield_pos.second; | |
- | |
- finder_->clear_placements(); | |
- finder_->find_point_placement(label_x, label_y, angle_); | |
- if (finder_->get_results().empty()) | |
- { | |
- //No placement for this point. Keep it in points_ for next try. | |
- point_itr_++; | |
- continue; | |
- } | |
- //Found a label placement but not necessarily also a marker placement | |
- // check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies | |
- if (!sym_.get_unlock_image()) | |
- { | |
- // center image at text center position | |
- // remove displacement from image label | |
- placements_type const& p = finder_->get_results(); | |
- double lx = p[0].center.x - text_disp.first; | |
- double ly = p[0].center.y - text_disp.second; | |
- marker_x_ = lx - 0.5 * marker_w_; | |
- marker_y_ = ly - 0.5 * marker_h_; | |
- marker_ext_.re_center(lx, ly); | |
- } | |
- else | |
- { // center image at reference location | |
- marker_x_ = label_x - 0.5 * marker_w_; | |
- marker_y_ = label_y - 0.5 * marker_h_; | |
- marker_ext_.re_center(label_x, label_y); | |
- } | |
- | |
- if (placement_->properties.allow_overlap || detector_.has_placement(marker_ext_)) | |
- { | |
- detector_.insert(marker_ext_); | |
- finder_->update_detector(); | |
- point_itr_ = points_.erase(point_itr_); | |
- return true; | |
- } | |
- //No placement found. Try again | |
- point_itr_++; | |
- } | |
- return false; | |
-} | |
- | |
- | |
template <typename FaceManagerT, typename DetectorT> | |
-bool shield_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement() | |
+text_symbolizer_helper::text_symbolizer_helper( | |
+ const shield_symbolizer &sym, const feature_impl &feature, | |
+ const proj_transform &prj_trans, | |
+ unsigned width, unsigned height, double scale_factor, | |
+ const CoordTransform &t, FaceManagerT &font_manager, | |
+ DetectorT &detector, const box2d<double> &query_extent) | |
+ : sym_(sym), | |
+ feature_(feature), | |
+ prj_trans_(prj_trans), | |
+ t_(t), | |
+ dims_(0, 0, width, height), | |
+ query_extent_(query_extent), | |
+ points_on_line_(true), | |
+ placement_(sym_.get_placement_options()->get_placement_info(scale_factor)), | |
+ finder_(feature, detector, dims_, placement_, font_manager, scale_factor) | |
{ | |
- position const& pos = placement_->properties.displacement; | |
- finder_->additional_boxes().clear(); | |
- //Markers are automatically centered | |
- finder_->additional_boxes().push_back( | |
- box2d<double>(-0.5 * marker_ext_.width() - pos.first, | |
- -0.5 * marker_ext_.height() - pos.second, | |
- 0.5 * marker_ext_.width() - pos.first, | |
- 0.5 * marker_ext_.height() - pos.second)); | |
- if ( sym_.clip()) | |
- return text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement_clipped(); | |
- else | |
- return text_symbolizer_helper<FaceManagerT, DetectorT>::next_line_placement(); | |
+ initialize_geometries(); | |
+ if (!geometries_to_process_.size()) return; | |
+ finder_.next_position(); | |
+ initialize_points(); | |
+ init_marker(); | |
} | |
-template <typename FaceManagerT, typename DetectorT> | |
-void shield_symbolizer_helper<FaceManagerT, DetectorT>::init_marker() | |
+void text_symbolizer_helper::init_marker() | |
{ | |
- std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_); | |
- evaluate_transform(image_transform_, feature_, sym_.get_image_transform()); | |
- marker_.reset(); | |
+ shield_symbolizer const& sym = static_cast<shield_symbolizer const&>(sym_); | |
+ std::string filename = path_processor_type::evaluate(*sym.get_filename(), feature_); | |
+ agg::trans_affine trans; | |
+ evaluate_transform(trans, feature_, sym.get_image_transform()); | |
+ boost::optional<marker_ptr> opt_marker; //TODO: Why boost::optional? | |
if (!filename.empty()) | |
{ | |
- marker_ = marker_cache::instance().find(filename, true); | |
- } | |
- if (!marker_) { | |
- marker_w_ = 0; | |
- marker_h_ = 0; | |
- marker_ext_.init(0, 0, 0, 0); | |
- return; | |
+ opt_marker = marker_cache::instance().find(filename, true); | |
} | |
- marker_w_ = (*marker_)->width(); | |
- marker_h_ = (*marker_)->height(); | |
- double px0 = - 0.5 * marker_w_; | |
- double py0 = - 0.5 * marker_h_; | |
- double px1 = 0.5 * marker_w_; | |
- double py1 = 0.5 * marker_h_; | |
+ marker_ptr m; | |
+ if (opt_marker) m = *opt_marker; | |
+ if (!m) return; | |
+ double width = m->width(); | |
+ double height = m->height(); | |
+ double px0 = - 0.5 * width; | |
+ double py0 = - 0.5 * height; | |
+ double px1 = 0.5 * width; | |
+ double py1 = 0.5 * height; | |
double px2 = px1; | |
double py2 = py0; | |
double px3 = px0; | |
double py3 = py1; | |
- image_transform_.transform(&px0,&py0); | |
- image_transform_.transform(&px1,&py1); | |
- image_transform_.transform(&px2,&py2); | |
- image_transform_.transform(&px3,&py3); | |
- marker_ext_.init(px0, py0, px1, py1); | |
- marker_ext_.expand_to_include(px2, py2); | |
- marker_ext_.expand_to_include(px3, py3); | |
-} | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-pixel_position shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker_position(text_path const& p) | |
-{ | |
- position const& pos = placement_->properties.displacement; | |
- if (placement_->properties.label_placement == LINE_PLACEMENT) { | |
- double lx = p.center.x - pos.first; | |
- double ly = p.center.y - pos.second; | |
- double px = lx - 0.5*marker_w_; | |
- double py = ly - 0.5*marker_h_; | |
- marker_ext_.re_center(lx, ly); | |
- //label is added to detector by get_line_placement(), but marker isn't | |
- detector_.insert(marker_ext_); | |
- return pixel_position(px, py); | |
- } else { | |
- //collision_detector is already updated for point placement in get_point_placement() | |
- return pixel_position(marker_x_, marker_y_); | |
- } | |
-} | |
- | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-marker& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker() const | |
-{ | |
- return **marker_; | |
-} | |
- | |
-template <typename FaceManagerT, typename DetectorT> | |
-agg::trans_affine const& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_image_transform() const | |
-{ | |
- return image_transform_; | |
+ trans.transform(&px0, &py0); | |
+ trans.transform(&px1, &py1); | |
+ trans.transform(&px2, &py2); | |
+ trans.transform(&px3, &py3); | |
+ box2d<double> bbox(px0, py0, px1, py1); | |
+ bbox.expand_to_include(px2, py2); | |
+ bbox.expand_to_include(px3, py3); | |
+ finder_.set_marker(std::make_shared<marker_info>(m, trans), bbox, sym.get_unlock_image(), sym.get_shield_displacement()); | |
} | |
-template class text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>; | |
-template class shield_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>; | |
+template text_symbolizer_helper::text_symbolizer_helper(const text_symbolizer &sym, const feature_impl &feature, | |
+const proj_transform &prj_trans, | |
+unsigned width, unsigned height, double scale_factor, | |
+const CoordTransform &t, face_manager<freetype_engine> &font_manager, | |
+label_collision_detector4 &detector, const box2d<double> &query_extent); | |
+ | |
+template text_symbolizer_helper::text_symbolizer_helper(const shield_symbolizer &sym, const feature_impl &feature, | |
+const proj_transform &prj_trans, | |
+unsigned width, unsigned height, double scale_factor, | |
+const CoordTransform &t, face_manager<freetype_engine> &font_manager, | |
+label_collision_detector4 &detector, const box2d<double> &query_extent); | |
} //namespace | |
diff --git a/src/text/text_properties.cpp b/src/text/text_properties.cpp | |
index 7279f02..1b81640 100644 | |
--- a/src/text/text_properties.cpp | |
+++ b/src/text/text_properties.cpp | |
@@ -2,7 +2,7 @@ | |
* | |
* This file is part of Mapnik (c++ mapping toolkit) | |
* | |
- * Copyright (C) 2011 Artem Pavlenko | |
+ * Copyright (C) 2013 Artem Pavlenko | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
@@ -20,10 +20,10 @@ | |
* | |
*****************************************************************************/ | |
// mapnik | |
+#include <mapnik/text/text_properties.hpp> | |
+#include <mapnik/text/layout.hpp> | |
#include <mapnik/debug.hpp> | |
#include <mapnik/feature.hpp> | |
-#include <mapnik/text/text_properties.hpp> | |
-#include <mapnik/text/processed_text.hpp> | |
#include <mapnik/ptree_helpers.hpp> | |
#include <mapnik/expression_string.hpp> | |
#include <mapnik/text/formatting/text.hpp> | |
@@ -31,21 +31,80 @@ | |
#include <mapnik/config_error.hpp> | |
// boost | |
+ | |
#include <boost/property_tree/ptree.hpp> | |
namespace mapnik | |
{ | |
using boost::optional; | |
+ | |
+static const char * label_placement_strings[] = { | |
+ "point", | |
+ "line", | |
+ "vertex", | |
+ "interior", | |
+ "" | |
+}; | |
+IMPLEMENT_ENUM(label_placement_e, label_placement_strings) | |
+ | |
+static const char * vertical_alignment_strings[] = { | |
+ "top", | |
+ "middle", | |
+ "bottom", | |
+ "auto", | |
+ "" | |
+}; | |
+IMPLEMENT_ENUM(vertical_alignment_e, vertical_alignment_strings) | |
+ | |
+static const char * horizontal_alignment_strings[] = { | |
+ "left", | |
+ "middle", | |
+ "right", | |
+ "auto", | |
+ "" | |
+}; | |
+IMPLEMENT_ENUM(horizontal_alignment_e, horizontal_alignment_strings) | |
+ | |
+static const char * justify_alignment_strings[] = { | |
+ "left", | |
+ "center", // not 'middle' in order to match CSS | |
+ "right", | |
+ "auto", | |
+ "" | |
+}; | |
+IMPLEMENT_ENUM(justify_alignment_e, justify_alignment_strings) | |
+ | |
+static const char * text_transform_strings[] = { | |
+ "none", | |
+ "uppercase", | |
+ "lowercase", | |
+ "capitalize", | |
+ "" | |
+}; | |
+IMPLEMENT_ENUM(text_transform_e, text_transform_strings) | |
+ | |
+ | |
+static const char * text_upright_strings[] = { | |
+ "auto", | |
+ "left", | |
+ "right", | |
+ "left_only", | |
+ "right_only", | |
+ "" | |
+}; | |
+IMPLEMENT_ENUM(text_upright_e, text_upright_strings) | |
+ | |
+ | |
text_symbolizer_properties::text_symbolizer_properties() : | |
orientation(), | |
- displacement(0.0,0.0), | |
+ displacement(0.,0.), | |
label_placement(POINT_PLACEMENT), | |
halign(H_AUTO), | |
jalign(J_AUTO), | |
valign(V_AUTO), | |
- label_spacing(0.0), | |
- label_position_tolerance(0.0), | |
+ label_spacing(0.), | |
+ label_position_tolerance(0.), | |
avoid_edges(false), | |
minimum_distance(0.0), | |
minimum_padding(0.0), | |
@@ -54,15 +113,18 @@ text_symbolizer_properties::text_symbolizer_properties() : | |
force_odd_labels(false), | |
allow_overlap(false), | |
largest_bbox_only(true), | |
- text_ratio(0.0), | |
- wrap_width(0.0), | |
- format(), | |
+ text_ratio(0.), | |
+ wrap_width(0.), | |
+ wrap_before(false), | |
+ rotate_displacement(false), | |
+ upright(UPRIGHT_AUTO), | |
+ format(std::make_shared<char_properties>()), | |
tree_() | |
{ | |
} | |
-void text_symbolizer_properties::process(processed_text &output, feature_impl const& feature) const | |
+void text_symbolizer_properties::process(text_layout &output, feature_impl const& feature) const | |
{ | |
output.clear(); | |
if (tree_) { | |
@@ -92,6 +154,8 @@ void text_symbolizer_properties::from_xml(xml_node const &sym, fontset_map const | |
if (text_ratio_) text_ratio = *text_ratio_; | |
optional<double> wrap_width_ = sym.get_opt_attr<double>("wrap-width"); | |
if (wrap_width_) wrap_width = *wrap_width_; | |
+ optional<boolean> wrap_before_ = sym.get_opt_attr<boolean>("wrap-before"); | |
+ if (wrap_before_) wrap_before = *wrap_before_; | |
optional<double> label_position_tolerance_ = sym.get_opt_attr<double>("label-position-tolerance"); | |
if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_; | |
optional<double> spacing_ = sym.get_opt_attr<double>("spacing"); | |
@@ -119,10 +183,14 @@ void text_symbolizer_properties::from_xml(xml_node const &sym, fontset_map const | |
if (jalign_) jalign = *jalign_; | |
optional<expression_ptr> orientation_ = sym.get_opt_attr<expression_ptr>("orientation"); | |
if (orientation_) orientation = *orientation_; | |
+ optional<boolean> rotate_displacement_ = sym.get_opt_attr<boolean>("rotate-displacement"); | |
+ if (rotate_displacement_) rotate_displacement = *rotate_displacement_; | |
+ optional<text_upright_e> upright_ = sym.get_opt_attr<text_upright_e>("upright"); | |
+ if (upright_) upright = *upright_; | |
optional<double> dx = sym.get_opt_attr<double>("dx"); | |
- if (dx) displacement.first = *dx; | |
+ if (dx) displacement.x = *dx; | |
optional<double> dy = sym.get_opt_attr<double>("dy"); | |
- if (dy) displacement.second = *dy; | |
+ if (dy) displacement.y = *dy; | |
optional<double> max_char_angle_delta_ = sym.get_opt_attr<double>("max-char-angle-delta"); | |
if (max_char_angle_delta_) max_char_angle_delta=(*max_char_angle_delta_)*(M_PI/180); | |
@@ -134,7 +202,7 @@ void text_symbolizer_properties::from_xml(xml_node const &sym, fontset_map const | |
set_old_style_expression(*name_); | |
} | |
- format.from_xml(sym, fontsets); | |
+ format->from_xml(sym, fontsets); | |
formatting::node_ptr n(formatting::node::from_xml(sym)); | |
if (n) set_format_tree(n); | |
} | |
@@ -151,13 +219,13 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, | |
} | |
} | |
- if (displacement.first != dfl.displacement.first || explicit_defaults) | |
+ if (displacement.x != dfl.displacement.x || explicit_defaults) | |
{ | |
- set_attr(node, "dx", displacement.first); | |
+ set_attr(node, "dx", displacement.x); | |
} | |
- if (displacement.second != dfl.displacement.second || explicit_defaults) | |
+ if (displacement.y != dfl.displacement.y || explicit_defaults) | |
{ | |
- set_attr(node, "dy", displacement.second); | |
+ set_attr(node, "dy", displacement.y); | |
} | |
if (label_placement != dfl.label_placement || explicit_defaults) | |
{ | |
@@ -175,6 +243,10 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, | |
{ | |
set_attr(node, "wrap-width", wrap_width); | |
} | |
+ if (wrap_before != dfl.wrap_before || explicit_defaults) | |
+ { | |
+ set_attr(node, "wrap-before", wrap_before); | |
+ } | |
if (label_position_tolerance != dfl.label_position_tolerance || explicit_defaults) | |
{ | |
set_attr(node, "label-position-tolerance", label_position_tolerance); | |
@@ -223,7 +295,15 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, | |
{ | |
set_attr(node, "vertical-alignment", valign); | |
} | |
- format.to_xml(node, explicit_defaults, dfl.format); | |
+ if (rotate_displacement != dfl.rotate_displacement || explicit_defaults) | |
+ { | |
+ set_attr(node, "rotate-displacement", rotate_displacement); | |
+ } | |
+ if (upright != dfl.upright || explicit_defaults) | |
+ { | |
+ set_attr(node, "upright", upright); | |
+ } | |
+ format->to_xml(node, explicit_defaults, *(dfl.format)); | |
if (tree_) tree_->to_xml(node); | |
} | |
@@ -246,7 +326,6 @@ char_properties::char_properties() : | |
character_spacing(0), | |
line_spacing(0), | |
text_opacity(1.0), | |
- wrap_before(false), | |
wrap_char(' '), | |
text_transform(NONE), | |
fill(color(0,0,0)), | |
@@ -268,8 +347,6 @@ void char_properties::from_xml(xml_node const& sym, fontset_map const& fontsets) | |
if (halo_fill_) halo_fill = *halo_fill_; | |
optional<double> halo_radius_ = sym.get_opt_attr<double>("halo-radius"); | |
if (halo_radius_) halo_radius = *halo_radius_; | |
- optional<boolean> wrap_before_ = sym.get_opt_attr<boolean>("wrap-before"); | |
- if (wrap_before_) wrap_before = *wrap_before_; | |
optional<text_transform_e> tconvert_ = sym.get_opt_attr<text_transform_e>("text-transform"); | |
if (tconvert_) text_transform = *tconvert_; | |
optional<double> line_spacing_ = sym.get_opt_attr<double>("line-spacing"); | |
@@ -334,10 +411,6 @@ void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_de | |
{ | |
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)); | |
diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp | |
index 20eef11..5c5adc0 100644 | |
--- a/src/text_symbolizer.cpp | |
+++ b/src/text_symbolizer.cpp | |
@@ -38,62 +38,6 @@ static const char * halo_rasterizer_strings[] = { | |
IMPLEMENT_ENUM( halo_rasterizer_e, halo_rasterizer_strings ) | |
-static const char * label_placement_strings[] = { | |
- "point", | |
- "line", | |
- "vertex", | |
- "interior", | |
- "" | |
-}; | |
- | |
- | |
-IMPLEMENT_ENUM( label_placement_e, label_placement_strings ) | |
- | |
-static const char * vertical_alignment_strings[] = { | |
- "top", | |
- "middle", | |
- "bottom", | |
- "auto", | |
- "" | |
-}; | |
- | |
- | |
-IMPLEMENT_ENUM( vertical_alignment_e, vertical_alignment_strings ) | |
- | |
-static const char * horizontal_alignment_strings[] = { | |
- "left", | |
- "middle", | |
- "right", | |
- "auto", | |
- "" | |
-}; | |
- | |
- | |
-IMPLEMENT_ENUM( horizontal_alignment_e, horizontal_alignment_strings ) | |
- | |
-static const char * justify_alignment_strings[] = { | |
- "left", | |
- "center", // not 'middle' in order to match CSS | |
- "right", | |
- "auto", | |
- "" | |
-}; | |
- | |
- | |
-IMPLEMENT_ENUM( justify_alignment_e, justify_alignment_strings ) | |
- | |
-static const char * text_transform_strings[] = { | |
- "none", | |
- "uppercase", | |
- "lowercase", | |
- "capitalize", | |
- "" | |
-}; | |
- | |
- | |
-IMPLEMENT_ENUM( text_transform_e, text_transform_strings ) | |
- | |
- | |
text_symbolizer::text_symbolizer(text_placements_ptr placements) | |
: symbolizer_base(), | |
placement_options_(placements), | |
@@ -103,7 +47,7 @@ text_symbolizer::text_symbolizer(text_placements_ptr placements) | |
} | |
text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_name, | |
- float size, color const& fill, | |
+ double size, color const& fill, | |
text_placements_ptr placements) | |
: symbolizer_base(), | |
placement_options_(placements), | |
@@ -115,7 +59,7 @@ text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_na | |
set_fill(fill); | |
} | |
-text_symbolizer::text_symbolizer(expression_ptr name, float size, color const& fill, | |
+text_symbolizer::text_symbolizer(expression_ptr name, double size, color const& fill, | |
text_placements_ptr placements) | |
: symbolizer_base(), | |
placement_options_(placements), | |
@@ -167,22 +111,22 @@ void text_symbolizer::set_orientation(expression_ptr orientation) | |
std::string const& text_symbolizer::get_face_name() const | |
{ | |
- return placement_options_->defaults.format.face_name; | |
+ return placement_options_->defaults.format->face_name; | |
} | |
void text_symbolizer::set_face_name(std::string face_name) | |
{ | |
- placement_options_->defaults.format.face_name = face_name; | |
+ placement_options_->defaults.format->face_name = face_name; | |
} | |
void text_symbolizer::set_fontset(font_set const& fontset) | |
{ | |
- placement_options_->defaults.format.fontset = fontset; | |
+ placement_options_->defaults.format->fontset = fontset; | |
} | |
boost::optional<font_set> const& text_symbolizer::get_fontset() const | |
{ | |
- return placement_options_->defaults.format.fontset; | |
+ return placement_options_->defaults.format->fontset; | |
} | |
double text_symbolizer::get_text_ratio() const | |
@@ -207,62 +151,62 @@ void text_symbolizer::set_wrap_width(double width) | |
bool text_symbolizer::get_wrap_before() const | |
{ | |
- return placement_options_->defaults.format.wrap_before; | |
+ return placement_options_->defaults.wrap_before; | |
} | |
void text_symbolizer::set_wrap_before(bool wrap_before) | |
{ | |
- placement_options_->defaults.format.wrap_before = wrap_before; | |
+ placement_options_->defaults.wrap_before = wrap_before; | |
} | |
unsigned char text_symbolizer::get_wrap_char() const | |
{ | |
- return placement_options_->defaults.format.wrap_char; | |
+ return placement_options_->defaults.format->wrap_char; | |
} | |
std::string text_symbolizer::get_wrap_char_string() const | |
{ | |
- return std::string(1, placement_options_->defaults.format.wrap_char); | |
+ return std::string(1, placement_options_->defaults.format->wrap_char); | |
} | |
void text_symbolizer::set_wrap_char(unsigned char character) | |
{ | |
- placement_options_->defaults.format.wrap_char = character; | |
+ placement_options_->defaults.format->wrap_char = character; | |
} | |
void text_symbolizer::set_wrap_char_from_string(std::string const& character) | |
{ | |
- placement_options_->defaults.format.wrap_char = (character)[0]; | |
+ placement_options_->defaults.format->wrap_char = (character)[0]; | |
} | |
text_transform_e text_symbolizer::get_text_transform() const | |
{ | |
- return placement_options_->defaults.format.text_transform; | |
+ return placement_options_->defaults.format->text_transform; | |
} | |
void text_symbolizer::set_text_transform(text_transform_e convert) | |
{ | |
- placement_options_->defaults.format.text_transform = convert; | |
+ placement_options_->defaults.format->text_transform = convert; | |
} | |
double text_symbolizer::get_line_spacing() const | |
{ | |
- return placement_options_->defaults.format.line_spacing; | |
+ return placement_options_->defaults.format->line_spacing; | |
} | |
void text_symbolizer::set_line_spacing(double spacing) | |
{ | |
- placement_options_->defaults.format.line_spacing = spacing; | |
+ placement_options_->defaults.format->line_spacing = spacing; | |
} | |
double text_symbolizer::get_character_spacing() const | |
{ | |
- return placement_options_->defaults.format.character_spacing; | |
+ return placement_options_->defaults.format->character_spacing; | |
} | |
void text_symbolizer::set_character_spacing(double spacing) | |
{ | |
- placement_options_->defaults.format.character_spacing = spacing; | |
+ placement_options_->defaults.format->character_spacing = spacing; | |
} | |
double text_symbolizer::get_label_spacing() const | |
@@ -275,12 +219,12 @@ void text_symbolizer::set_label_spacing(double spacing) | |
placement_options_->defaults.label_spacing = spacing; | |
} | |
-double text_symbolizer::get_label_position_tolerance() const | |
+unsigned text_symbolizer::get_label_position_tolerance() const | |
{ | |
return placement_options_->defaults.label_position_tolerance; | |
} | |
-void text_symbolizer::set_label_position_tolerance(double tolerance) | |
+void text_symbolizer::set_label_position_tolerance(unsigned tolerance) | |
{ | |
placement_options_->defaults.label_position_tolerance = tolerance; | |
} | |
@@ -307,42 +251,42 @@ void text_symbolizer::set_max_char_angle_delta(double angle) | |
void text_symbolizer::set_text_size(double size) | |
{ | |
- placement_options_->defaults.format.text_size = size; | |
+ placement_options_->defaults.format->text_size = size; | |
} | |
double text_symbolizer::get_text_size() const | |
{ | |
- return placement_options_->defaults.format.text_size; | |
+ return placement_options_->defaults.format->text_size; | |
} | |
void text_symbolizer::set_fill(color const& fill) | |
{ | |
- placement_options_->defaults.format.fill = fill; | |
+ placement_options_->defaults.format->fill = fill; | |
} | |
color const& text_symbolizer::get_fill() const | |
{ | |
- return placement_options_->defaults.format.fill; | |
+ return placement_options_->defaults.format->fill; | |
} | |
void text_symbolizer::set_halo_fill(color const& fill) | |
{ | |
- placement_options_->defaults.format.halo_fill = fill; | |
+ placement_options_->defaults.format->halo_fill = fill; | |
} | |
color const& text_symbolizer::get_halo_fill() const | |
{ | |
- return placement_options_->defaults.format.halo_fill; | |
+ return placement_options_->defaults.format->halo_fill; | |
} | |
void text_symbolizer::set_halo_radius(double radius) | |
{ | |
- placement_options_->defaults.format.halo_radius = radius; | |
+ placement_options_->defaults.format->halo_radius = radius; | |
} | |
double text_symbolizer::get_halo_radius() const | |
{ | |
- return placement_options_->defaults.format.halo_radius; | |
+ return placement_options_->defaults.format->halo_radius; | |
} | |
void text_symbolizer::set_halo_rasterizer(halo_rasterizer_e rasterizer_p) | |
@@ -367,15 +311,15 @@ label_placement_e text_symbolizer::get_label_placement() const | |
void text_symbolizer::set_displacement(double x, double y) | |
{ | |
- placement_options_->defaults.displacement = std::make_pair(x,y); | |
+ placement_options_->defaults.displacement.set(x, y); | |
} | |
-void text_symbolizer::set_displacement(position const& p) | |
+void text_symbolizer::set_displacement(const pixel_position &p) | |
{ | |
placement_options_->defaults.displacement = p; | |
} | |
-position const& text_symbolizer::get_displacement() const | |
+pixel_position const& text_symbolizer::get_displacement() const | |
{ | |
return placement_options_->defaults.displacement; | |
} | |
@@ -442,12 +386,12 @@ bool text_symbolizer::get_allow_overlap() const | |
void text_symbolizer::set_text_opacity(double text_opacity) | |
{ | |
- placement_options_->defaults.format.text_opacity = text_opacity; | |
+ placement_options_->defaults.format->text_opacity = text_opacity; | |
} | |
double text_symbolizer::get_text_opacity() const | |
{ | |
- return placement_options_->defaults.format.text_opacity; | |
+ return placement_options_->defaults.format->text_opacity; | |
} | |
void text_symbolizer::set_vertical_alignment(vertical_alignment_e valign) | |
diff --git a/src/xml_tree.cpp b/src/xml_tree.cpp | |
index 98ccc4b..292266a 100644 | |
--- a/src/xml_tree.cpp | |
+++ b/src/xml_tree.cpp | |
@@ -400,6 +400,7 @@ compile_get_opt_attr(label_placement_e); | |
compile_get_opt_attr(vertical_alignment_e); | |
compile_get_opt_attr(horizontal_alignment_e); | |
compile_get_opt_attr(justify_alignment_e); | |
+compile_get_opt_attr(text_upright_e); | |
compile_get_opt_attr(halo_rasterizer_e); | |
compile_get_opt_attr(expression_ptr); | |
compile_get_attr(std::string); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment