Skip to content

Instantly share code, notes, and snippets.

@springmeyer
Created November 9, 2013 03:22
Show Gist options
  • Save springmeyer/7381163 to your computer and use it in GitHub Desktop.
Save springmeyer/7381163 to your computer and use it in GitHub Desktop.
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